向量数据库Weaviate的踩坑记录

📘 开发记录 · 06-17 · 48 人浏览
向量数据库Weaviate的踩坑记录

向量数据库迁移实录:从 FAISS 到 Weaviate 的那些坑

最近因为需要支持多用户知识库检索,我开始琢磨向量数据库的替换方案。原来系统里用的是本地的 FAISS,搭配 LangChain 用起来还挺顺,但随着需求复杂起来,比如想加 hit\_count 记录、分页、精确查某个文件的所有块,FAISS 显然就有点捉襟见肘了。

🥊 为什么考虑换掉 FAISS?

简单说两点:

  1. FAISS 不支持原生 filter 查询,像我要查某个 kb_id 的内容,或模糊查某个 filename 的所有内容,只能自己手动过滤;
  2. 没有内置分页。每次都得查全量,再手动分页,这在数据量大时非常不友好。

于是我决定换掉 FAISS,试试 Weaviate。

🔁 切换 Weaviate 的过程

✅ 基本查询

Weaviate 的向量检索写法和 FAISS 有点类似,只是接口风格更“声明式”一点。最开始试了下这样一段代码:

results = collection.query.near_vector(
    near_vector=query_vector,
    limit=5,
    filters=Filter.by_property("kb_id").equal(kb_id)
)

这一段是查询向量最相近的内容,且只筛选 kb_id 为指定值的记录。比起 FAISS 自己 filter 一堆,Weaviate 这点真的太方便了。

📈 查询命中的内容并计数

接下来加了一个需求:一旦某个块被召回了,就把它的 hit_count 字段 +1。这其实也很方便:

for item in results.objects:
    collection.data.update(
        uuid=item.uuid,
        properties={"hit_count": item.properties.get("hit_count", 0) + 1}
    )

注意这里 item.uuid 是默认带的,不需要你在 return_metadata 里特地加上(我一开始就踩了这个坑)。

🧩 模糊查询 + 分页

后来又要实现:用户选择一个知识库、一个文件名,分页查看里面所有块,还能模糊查内容。

一开始我尝试构造 filters:

filters = {
    "operator": "And",
    "operands": [
        {"path": ["kb_id"], "operator": "Equal", "valueText": kb_id},
        {"path": ["filename"], "operator": "Equal", "valueText": filename},
        {"path": ["text"], "operator": "Like", "valueText": f"*{query}*"}  # 模糊
    ]
}

但后来我发现 Weaviate 新版 SDK 提供了更优雅的 Filter 方式:

from weaviate.classes.query import Filter

filters = (
    Filter.by_property("kb_id").equal(kb_id)
    & Filter.by_property("filename").equal(filename)
    & Filter.by_property("text").like(f"*{query}*")  # 支持模糊
)

这个组合可以直接传给 collection.query.fetch_objects(...) 方法,分页也只需设置 limitoffset

offset = (page - 1) * pageSize

result = collection.query.fetch_objects(
    filters=filters,
    limit=pageSize,
    offset=offset,
    return_properties=["text", "hit_count", "upload_time", "filename", "kb_id"]
)

❌ 踩坑记录

  1. 不能插入 id 和 vector 到 properties
    批量插入时我傻傻地把 idvector 放进了 properties 字典里,Weaviate 直接报错:

    It is forbidden to insert id or vector inside properties...

    后来我改成使用 DataObject 的方式解决了。

  2. return\_metadata=["uuid"] 会报错
    我以为想取 UUID 要写 return_metadata=["uuid"],结果报了个特别诡异的错误:

    Extra inputs are not permitted [type=extra\_forbidden, input\_value=True, input\_type=bool]

    实际上根本不需要那一行,.uuid 默认就能取。

  3. Filter 的链式调用坑
    一开始我写了个 Filter.all(...) 组合多个 filter,结果提示 Filter 根本没这个方法。正确的写法是用 & 链接多个 Filter.by_property(...),就像上面示例。

💡 写在最后

整体迁移下来,Weaviate 在功能和可扩展性方面明显优于 FAISS,尤其是在过滤器、属性更新、分页这些业务场景上更灵活。如果你正好也面临类似的向量检索多条件查询、知识块统计更新的需求,不妨试试换到 Weaviate。


Under CC BY NC-SA License.
Powered by Typecho | Theme by Jasmine
您是第 31903 位访客