最近在做一个文档检索系统时,遇到了一个典型场景:需要对海量文档进行语义搜索,但返回结果需要按文档来源分组展示。经过一番技术选型,最终选择了DashVector的query_group_by功能来实现这个需求。今天就把我的实战经验完整分享出来,包含你可能遇到的所有坑和解决方案。
先说说为什么需要分组查询。假设你有一个包含百万级文档片段的数据库,当用户搜索"机器学习"时,传统向量搜索会返回一堆相关片段,但这些片段可能来自同一篇论文的不同部分,导致结果列表中出现大量重复文档的内容。分组查询就能完美解决这个问题——它先找到所有相关片段,然后按文档ID分组,最后从每个文档中选出最相关的几个片段返回。
首先需要安装DashVector的Python SDK:
bash复制pip install dashvector
初始化客户端时有两个关键参数需要注意:
python复制import dashvector
client = dashvector.Client(
api_key='YOUR_API_KEY', # 控制台获取的API密钥
endpoint='YOUR_CLUSTER_ENDPOINT' # 集群地址如dashvector.aliyuncs.com
)
重要提示:生产环境千万不要把API密钥硬编码在代码里!建议通过环境变量或密钥管理服务获取。我曾经因为密钥泄露导致产生高额账单,血的教训。
创建一个支持分组查询的集合需要特别注意schema设计:
python复制ret = client.create(
name='doc_search',
dimension=768, # 向量维度需与模型匹配
fields_schema={
'doc_id': str, # 文档唯一标识
'chunk_no': int, # 片段编号
'content': str # 原始文本
}
)
插入数据时的一个性能优化技巧:批量插入比单条插入效率高10倍以上。我测试过,批量插入1000条耗时约2秒,而单条插入1000条需要20+秒。
python复制docs = [
('1', vector1, {'doc_id': 'paper1', 'chunk_no': 1, 'content': '...'}),
('2', vector2, {'doc_id': 'paper1', 'chunk_no': 2, 'content': '...'}),
# ...更多文档
]
collection.insert(docs)
最简分组查询示例:
python复制results = collection.query_group_by(
vector=query_vec, # 查询向量
group_by_field='doc_id', # 按文档ID分组
group_count=5, # 返回5个文档组
group_topk=3 # 每组返回3个最相关片段
)
参数选择经验:
实际业务中经常需要带条件过滤:
python复制results = collection.query_group_by(
vector=query_vec,
group_by_field='doc_id',
filter='chunk_no < 5', # 只检索前5个片段
output_fields=['content'] # 只返回内容字段
)
踩坑记录:过滤条件字段必须提前在schema中定义,且类型匹配。有次我误用filter='page_no < 5',但实际字段是chunk_no,导致查询静默失败。
DashVector支持三种检索方式:
python复制# 主键检索分组
collection.query_group_by(
id='chunk123', # 直接指定主键
group_by_field='doc_id'
)
# 混合检索(向量+过滤)
collection.query_group_by(
vector=query_vec,
id='chunk123', # 同时使用时会以vector为主
group_by_field='doc_id'
)
创建集合时可以通过metric参数选择相似度计算方式:
python复制client.create(
name='optimized_collection',
dimension=768,
metric='cosine', # 显式指定
fields_schema=...
)
通过调整以下参数可以显著影响性能:
实测数据(维度768,100万数据量):
| 参数组合 | QPS | 延迟 |
|---|---|---|
| group_topk=1 | 150 | 50ms |
| group_topk=3 | 90 | 100ms |
| async_req=True | 300+ | 30ms |
对热点查询实现缓存能大幅提升性能:
python复制from functools import lru_cache
@lru_cache(maxsize=1000)
def cached_query(query_vec):
return collection.query_group_by(
vector=query_vec,
group_by_field='doc_id'
)
缓存失效策略建议:
检查清单:
可能原因:
应急措施:
常见情况:
一个实用的debug方法:
python复制# 打印完整中间结果
import json
print(json.dumps(results, indent=2))
需求特点:
实现方案:
python复制results = collection.query_group_by(
vector=query_vec,
group_by_field='paper_id',
group_count=10,
group_topk=3,
output_fields=['chapter_title', 'content']
)
特殊需求:
对应查询:
python复制results = collection.query_group_by(
vector=query_vec,
group_by_field='shop_id',
group_count=20,
group_topk=2,
filter='status="on_shelf"',
output_fields=['title', 'price', 'cover_url']
)
经过三个月的生产环境验证,这套方案在1000万级数据量下,P99延迟稳定在200ms以内。最关键的是分组查询让结果展示更加符合用户心智模型,点击率提升了35%。