Qdrant 数据结构、索引与过滤机制详解

Qdrant 数据结构、索引与过滤机制详解

Qdrant 是一个面向向量检索的数据库,常用于语义搜索、RAG、推荐系统和知识库等场景。相比传统关系型数据库,Qdrant 最核心的能力是对高维向量进行高效的相似度检索。要理解 Qdrant 的使用方式,首先需要掌握 Collection、Point、Vector、Payload、索引和过滤这几个核心概念。

Collection 和 Point

Qdrant 中最基本的数据组织单位是 Collection 和 Point。可以把 Collection 类比成关系型数据库中的 Table,用来存储同一类数据;Point 则是 Collection 中的一条数据记录。

例如可以创建一个名为 defectdojo_knowledge 的 Collection,用于保存知识库中的文本和对应向量。一个 Collection 可以包含大量 Point,而每个 Point 都有自己的唯一 ID、向量以及 Payload。

需要注意的是,这只是帮助理解的类比。Collection 并不是传统数据库表的简单复制,它在创建时还需要定义向量相关配置,例如向量维度、距离计算方式以及是否使用稀疏向量等。

一个 Point 的完整结构

Qdrant 中一个 Point 通常包含三个核心部分:

Point(
    id=uuid5(NAMESPACE_URL, "audit:{workflow_id}:0"),
    vector=[dense embedding…],
    payload={
        "source_type": "audit",
        "source_id": workflow_id,
        "text": "...",
        "content_hash": "sha256(...)",
        "embedding_model": "text-embedding-...",
        "source": f"workflow:{workflow_id}",
        "workflow_id": workflow_id,
        "intent": "triage",
        "intents": ["import_scan", "triage"],
        "confidence": 0.92,
        "outcome": "completed",
        "verification_status": "observed",
    }
)

其中,id 用于唯一标识 Point,可以使用整数或 UUID。vector 是文本经过 Embedding 模型转换后的向量,用于后续的向量相似度搜索。payload 则用于保存与这条向量相关的业务信息和元数据。

因此,可以简单理解为:

Point
├── ID
├── Vector
└── Payload

Vector 和 Payload 虽然都属于 Point,但用途完全不同。Vector 主要用于“计算相似度”,Payload 主要用于“保存和描述数据”。

Vector:Qdrant 进行语义检索的基础

向量是 Qdrant 最核心的数据类型之一。在进行语义搜索之前,需要先使用 Embedding 模型将文本转换为固定维度的浮点数组。

例如:

SQL injection vulnerability

经过 Embedding 模型之后,可能变成:

[0.128, -0.372, 0.614, 0.051, ...]

这个数组就是向量。

当用户输入新的查询语句时,系统同样会将查询转换成向量,然后计算查询向量与数据库中已有向量之间的相似度,最后返回最相近的数据。

常见的向量距离或相似度方式包括:

Cosine
Dot Product
Euclidean

不同距离指标适用于不同的 Embedding 模型和业务场景,因此在创建 Collection 时通常需要提前确定。

Payload:保存业务数据和元数据

如果说 Vector 负责“找到相关数据”,那么 Payload 负责“告诉我们找到的数据是什么”。

Payload 使用 JSON 风格的数据结构,可以保存字符串、数字、布尔值、数组以及对象等信息。例如:

{
  "source_type": "audit",
  "workflow_id": 1024,
  "intent": "triage",
  "confidence": 0.92,
  "outcome": "completed"
}

这里的 source_typeworkflow_idintent 等字段都属于 Payload。

实际使用中,Payload 通常会保存三类信息:第一类是原始文本,例如用于生成 Embedding 的 text;第二类是数据来源,例如文档 ID、业务 ID、数据类型;第三类是检索过程中需要使用的结构化属性,例如分类、标签、状态和时间等。

因此,在 RAG 系统中经常会出现这样的关系:

文本
 ↓
Embedding
 ↓
Vector ───────→ 用于相似度搜索

原始文本 + Metadata
 ↓
Payload ──────→ 用于返回结果和条件过滤

Content Hash 和 Embedding Model

在实际项目中,Payload 往往还会保存一些用于数据同步和数据管理的字段。

例如:

{
  "text": "...",
  "content_hash": "9f86d081884c...",
  "embedding_model": "text-embedding-3-small"
}

content_hash 可以用于判断文本内容是否发生变化。当数据重新同步时,如果新旧文本对应的 Hash 相同,就可以跳过重新 Embedding,从而减少计算成本。

embedding_model 用于记录当前向量是由哪个 Embedding 模型生成的。当系统更换 Embedding 模型后,可以通过这个字段识别不同模型产生的向量,避免由于向量空间不同而造成数据混用。

什么是 Payload Index

当 Collection 中只有少量 Point 时,即使没有索引,也可以进行简单过滤。但随着数据量增长,如果每次查询都需要检查大量 Point 的 Payload,效率会受到影响。

因此,Qdrant 提供了 Payload Index。

例如有下面这样的数据:

{
  "source_type": "audit",
  "severity": "high",
  "cwe_id": "CWE-89"
}

如果经常需要执行:

source_type = "audit"

或者:

cwe_id = "CWE-89"

就可以针对这些字段建立 Payload Index。

例如:

from qdrant_client.models import PayloadSchemaType

client.create_payload_index(
    collection_name="defectdojo_knowledge",
    field_name="source_type",
    field_schema=PayloadSchemaType.KEYWORD,
)

建立索引后,Qdrant 可以更高效地处理对应字段的条件查询。

如何选择 Payload Index 类型

Qdrant 支持针对不同数据类型建立相应的 Payload Index。例如字符串类别通常使用 KEYWORD,数值字段可以使用数值类型索引,时间字段也可以使用相应的范围查询能力。

例如:

source_type → KEYWORD
intent      → KEYWORD
severity    → KEYWORD
cwe_id      → KEYWORD
score       → 数值
created_at  → 时间

并不是所有字段都需要建立索引。通常应该根据实际查询条件决定哪些字段建立索引。一个简单的原则是:

经常出现在过滤条件中的字段值得考虑建立索引,不参与查询的字段通常没有必要专门建立索引。

什么是过滤

Payload Index 解决的是“如何更高效地查找”,而 Filter 解决的是“查询哪些数据”。

例如用户希望查询:

source_type = audit

可以构造这样的过滤条件:

from qdrant_client.models import Filter, FieldCondition, MatchValue

query_filter = Filter(
    must=[
        FieldCondition(
            key="source_type",
            match=MatchValue(value="audit")
        )
    ]
)

这表示只允许 source_typeaudit 的 Point 参与此次检索。

如果需要多个条件,还可以组合:

query_filter = Filter(
    must=[
        FieldCondition(
            key="source_type",
            match=MatchValue(value="audit")
        ),
        FieldCondition(
            key="severity",
            match=MatchValue(value="high")
        )
    ]
)

这相当于:

source_type = "audit"
AND
severity = "high"

过滤和向量检索可以同时存在。Qdrant 先根据条件限制候选数据范围,再进行向量相似度检索。

Filter 和向量检索的区别

这是使用 Qdrant 时一个非常容易混淆的地方。

Filter 解决的是结构化条件筛选,例如:

cwe_id = CWE-89
severity = high
source_type = audit

向量检索解决的是语义相似性判断

例如:

查询:
数据库查询缺少参数化导致安全问题

即使某篇文档没有完全出现相同的句子,只要语义比较接近,它仍然有可能通过向量检索被召回。

所以:

Filter
→ 筛选哪些数据可以参与检索

Vector Search
→ 判断这些数据与查询有多相似

二者是互补关系,而不是相互替代。

Dense Vector 和 Sparse Vector

除了 Dense Vector 之外,Qdrant 还支持 Sparse Vector。

Dense Vector 通常适合进行语义检索。例如:

“SQL injection”

和:

“数据库查询参数缺少安全处理”

虽然字面表达不同,但语义可能非常接近,因此 Dense Vector 能够较好地进行召回。

Sparse Vector 则更适合关键词和专业术语,例如:

CWE-89
CVE-2025-1234
SQL Injection
SSRF

在知识库、搜索系统和技术文档场景中,Dense 与 Sparse 结合可以同时兼顾语义匹配和关键词匹配。

因此一个 Collection 可以设计成:

Collection
├── Dense Vector
├── Sparse Vector
└── Payload

最终执行 Hybrid Search。

Qdrant 中的完整检索流程

结合前面的几个概念,一个典型的 Qdrant 检索过程如下:

用户查询
   ↓
Embedding
   ↓
Query Vector
   ↓
┌──────────────────────────┐
│ Qdrant                   │
│                          │
│ Payload Filter           │
│        ↓                 │
│ Dense / Sparse Search    │
│        ↓                 │
│ Similarity Ranking       │
└──────────────────────────┘
   ↓
Top-K Point
   ↓
读取 Payload
   ↓
返回给应用程序 / LLM

例如查询一个知识库时,可以先使用 source_typecategory 等 Payload 字段缩小搜索范围,然后使用 Dense Vector 判断语义相关性,再结合 Sparse Vector 处理精确关键词,最终返回 Top-K 结果。

总结

Qdrant 可以从几个核心概念来理解:

Collection
    ↓
数据集合

Point
    ↓
一条完整数据

Vector
    ↓
用于相似度检索

Payload
    ↓
保存文本和业务 Metadata

Payload Index
    ↓
加速结构化条件查询

Filter
    ↓
限制检索范围

Dense / Sparse Search
    ↓
进行语义或关键词相关性检索

理解这些概念之后,Qdrant 的整体工作方式就比较清晰了:Payload 负责描述数据,Payload Index 负责提高结构化查询效率,Filter 负责缩小候选范围,而 Vector Search 则负责从候选数据中找到与查询最相关的内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值