6. 检索查询

检索是 Chroma 最核心的功能。前面CRUD 部分已经做了一些介绍

6.1 基础语义检索

const results = await collection.query({
  queryTexts: ['什么是量子纠缠?'],  // 查询文本(自动向量化)
  nResults: 3,                       // 返回最相似的3条
  include: ['documents', 'metadatas', 'distances'],
})

// 结果结构
console.log(results.ids)        // [['doc_001', 'doc_003', 'doc_002']]  注意是二维数组
console.log(results.documents)  // [['量子纠缠是...', '波粒二象性...', '薛定谔的猫...']]
console.log(results.distances)  // [[0.05, 0.18, 0.32]]  数值越小越相似(余弦距离)
console.log(results.metadatas)  // [[{source:'...'}, ...]]

注意:results 都是二维数组,因为支持同时传多个查询(queryTexts 可以是数组)。

6.2 同时查询多个问题

const results = await collection.query({
  queryTexts: [
    '什么是量子纠缠?',
    '薛定谔的猫说明了什么?',
  ],
  nResults: 2,
})

// results.documents[0] → 第一个问题的检索结果
// results.documents[1] → 第二个问题的检索结果

6.3 用向量查询(自己传 embedding)

// 先把查询文本转成向量
const queryEmbedding = await getEmbeddings(['什么是量子纠缠?'])

const results = await collection.query({
  queryEmbeddings: queryEmbedding,  // 传向量而不是文本
  nResults: 3,
  include: ['documents', 'distances'],
})

6.4 理解相似度分数

// 余弦距离(hnsw:space = 'cosine'):
// 0.0  → 完全相同
// 0.0 ~ 0.3  → 高度相关
// 0.3 ~ 0.6  → 有一定关联
// 0.6 以上   → 基本不相关

results.distances[0].forEach((distance, i) => {
  const similarity = 1 - distance  // 转换为相似度(0~1,越高越好)
  console.log(`结果${i+1}:相似度 ${similarity.toFixed(3)}`)
  console.log(results.documents[0][i])
})

// 过滤低质量结果
const threshold = 0.6  // 距离阈值
const goodResults = results.documents[0].filter(
  (_, i) => results.distances[0][i] < threshold
)

7. 元数据过滤

元数据过滤让你在语义检索的同时,加上精确的条件筛选。

7.1 基础过滤

// 只搜索来自特定文件的内容
const results = await collection.query({
  queryTexts: ['退款流程'],
  nResults: 5,
  where: {
    source: '退款政策.pdf',  // 精确匹配
  },
})

// 按数值过滤
const results2 = await collection.query({
  queryTexts: ['量子力学基础'],
  nResults: 5,
  where: {
    page: { $gte: 10 },  // page >= 10
  },
})

7.2 支持的过滤操作符

// 比较操作符
where: { page: { $eq: 5 } }     // 等于
where: { page: { $ne: 5 } }     // 不等于
where: { page: { $gt: 5 } }     // 大于
where: { page: { $gte: 5 } }    // 大于等于
where: { page: { $lt: 10 } }    // 小于
where: { page: { $lte: 10 } }   // 小于等于

// 集合操作符
where: { source: { $in: ['文件A.pdf', '文件B.pdf'] } }   // 在列表中
where: { source: { $nin: ['旧文件.pdf'] } }               // 不在列表中

// 逻辑操作符
where: {
  $and: [
    { chapter: '第一章' },
    { page: { $lte: 20 } },
  ]
}

where: {
  $or: [
    { source: '文档A.pdf' },
    { source: '文档B.pdf' },
  ]
}

// 文本内容过滤(对 document 字段)
whereDocument: {
  $contains: '量子',    // 文档内容包含"量子"
}

whereDocument: {
  $not_contains: '已废弃',  // 文档内容不包含"已废弃"
}

7.3 实用场景示例

// 场景:多租户系统,每个用户只能查自己的数据
async function queryUserDocs(userId: string, question: string) {
  return collection.query({
    queryTexts: [question],
    nResults: 5,
    where: { userId },  // 只返回该用户的文档
  })
}

// 场景:只查最新版本的文档
async function queryLatestDocs(question: string) {
  return collection.query({
    queryTexts: [question],
    nResults: 5,
    where: {
      $and: [
        { version: { $eq: 'v2' } },
        { status: { $ne: '已下架' } },
      ],
    },
  })
}

// 场景:全文搜索(不需要向量,只按内容关键词过滤)
async function fullTextSearch(keyword: string) {
  return collection.get({
    whereDocument: { $contains: keyword },
    include: ['documents', 'metadatas'],
  })
}