• 文章
  • A Complete Guide to Filtering in Vector Search
返回到向量搜索手册

向量搜索中的过滤完整指南

萨布rina·阿基诺,戴维·米里尔

·

2024年9月10日

A Complete Guide to Filtering in Vector Search

想象一下您在销售计算机硬件。为了帮助购物者轻松找到您网站上的产品,您需要拥有一个 用户友好的 搜索引擎

vector-search-ecommerce

如果您销售计算机并拥有关于笔记本电脑、台式机和配件的详细数据,那么您的搜索功能应该引导客户找到他们想要的确切设备——或者至少是一个 非常相似 的匹配结果。

在Qdrant中存储数据时,每个产品是一个点,由一个 id、一个 vectorpayload 组成:

{
  "id": 1, 
  "vector": [0.1, 0.2, 0.3, 0.4],
  "payload": {
    "price": 899.99,
    "category": "laptop"
  }
}

id 是您集合中点的唯一标识符。 vector 是与集合中其他点相似性的数学表示。最后, payload 包含直接描述该点的元数据。

虽然我们可能无法解读向量,但我们能够从其元数据中获取有关该项的额外信息。在这个特定的案例中,我们正在查看一款售价为$899.99的笔记本电脑的数据点

什么是过滤?

在搜索完美电脑时,您的客户可能会得到与搜索条目在数学上相似但不完全相同的结果。例如,如果他们搜索的是低于1000美元的笔记本电脑,那么简单的向量搜索如果没有限制,可能仍然会显示其他超过1000美元的笔记本电脑。

这就是为什么 语义搜索 单独 可能不够。为了获得确切的结果,您需要对 price 强制执行一个有效载荷过滤器。只有这样,您才能确保搜索结果符合所选特征。

这称为 过滤,这是 向量数据库 的关键特性之一。

这里是过滤向量搜索的幕后工作原理。我们将在接下来的部分中介绍其机制。

POST /collections/online_store/points/search
{
  "vector": [ 0.2, 0.1, 0.9, 0.7 ],
  "filter": {
    "must": [
      {
        "key": "category",
        "match": { "value": "laptop" }
      },
      {
        "key": "price",
        "range": {
          "gt": null,
          "gte": null,
          "lt": null,
          "lte": 1000
        }
      }
    ]
  },
  "limit": 3,
  "with_payload": true,
  "with_vector": false
}

过滤后的结果将是语义搜索与施加在查询上的过滤条件的组合。在接下来的页面中,我们将展示过滤在向量搜索中是一个关键实践,原因有两个:

  1. 通过在Qdrant中进行过滤,您可以显著提高搜索精度。有关更多信息,请参见下一部分。
  2. 过滤有助于控制资源并减少计算使用。有关更多信息,请参阅载荷索引

您将在本指南中学习的内容:

向量搜索 中,过滤和排序的相互依赖性超过了传统数据库。虽然像 SQL 这样的数据库使用 WHEREORDER BY 等命令,但在向量搜索中,这些过程之间的相互作用要复杂得多。

大多数人使用默认设置,构建的向量搜索应用程序配置不当,甚至没有为精确检索设置。在本指南中,我们将向您展示如何使用过滤来充分利用向量搜索,提供一些易于实现的基本和高级策略。

记得在Qdrant的仪表板中运行所有教程代码

达到“Hello World”时刻的最简单方法是尝试在实时集群中进行过滤。我们的互动教程将向您展示如何创建一个集群,添加数据并尝试一些过滤条件。

qdrant-filtering-tutorial

Qdrant的过滤方法

Qdrant遵循特定的方法通过密集向量进行搜索和过滤。

让我们来看看这个 三阶段图。在这种情况下,我们正在尝试找到查询向量 (绿色) 的最近邻。您的搜索旅程从底部开始 (橙色)

默认情况下,Qdrant 将您所有的数据点连接在一起,位于 向量索引 中。在您 引入过滤器 后,一些数据点变得不再连接。向量搜索无法跨越灰色区域,因此无法到达最近邻居。 我们如何解决这个问题?

图 1: Qdrant 如何维持一个可筛选的向量索引。 filterable-vector-index

可过滤的矢量索引: 该技术在剩余数据点之间建立额外的链接(橙色)。留下来的经过过滤的点现在可以再次遍历。Qdrant使用基于特定类别的方法来连接这些数据点。

Qdrant的方法与传统过滤方法

stepping-lens

可过滤的向量索引是Qdrant通过向搜索图添加专门的链接来解决前后过滤问题。它旨在保持向量搜索的速度优势,同时允许进行精确过滤,解决在向量搜索后应用过滤器时可能出现的低效率问题。

预过滤

在预过滤中,搜索引擎首先根据选择的元数据值缩小数据集,然后在过滤后的子集内进行搜索。这减少了对一个可能更大的数据集进行不必要的计算。

在预过滤和使用可过滤的HNSW索引之间的选择取决于过滤器的基数。当元数据基数过低时,过滤器变得限制性,这可能会干扰图中的连接。这导致搜索路径支离破碎(如图 1所示)。当语义搜索过程开始时,它将无法到达那些位置。

然而,Qdrant 在特定条件下仍然受益于预过滤。在低基数的情况下,Qdrant 的查询规划器停止使用 HNSW,而仅切换到有效负载索引。这使得搜索过程比使用 HNSW 时便宜和快速得多。

图 2: 在用户端,过滤的样子是这样的。我们开始时有五款不同价格的产品。首先,应用 $1000 的过滤器,缩小笔记本电脑的选择。然后,矢量搜索在这个过滤后的集合中找到相关的结果

pre-filtering-vector-search

总之,预过滤在使用小型低基数元数据集时是有效的。然而,在大型数据集上不应使用预过滤,因为它会打断HNSW图中的太多链接,导致准确性降低。

后处理

在后过滤中,搜索引擎首先寻找相似的向量并检索出更多的结果。然后,它根据元数据对这些结果应用过滤器。当使用低基数过滤器时,后过滤的问题变得明显。

当您在执行向量搜索后应用低基数过滤器时,您通常会丢弃向量搜索返回的大部分结果。

图 3: 在同一个例子中,我们有五台笔记本电脑。首先,向量搜索找到两个最相关的 结果,但它们可能不符合价格匹配。应用 $1000 的 过滤器 后,其他潜在结果被舍弃。

post-filtering-vector-search

系统将通过首先找到相似向量然后丢弃许多不满足过滤标准的向量来浪费计算资源。您还仅限于从初始的 向量搜索 结果中进行过滤。如果您所需的项目不在此初始集合中,即使它们存在于数据库中,您也找不到它们。

基本过滤示例:电子商务和笔记本电脑

我们知道有三款笔记本电脑适合我们的价格范围。让我们看看Qdrant的可过滤向量索引是如何工作的,以及为什么它是捕捉所有可用结果的最佳方法。

首先,将五台新笔记本电脑添加到您的在线商店。以下是一个示例输入:

laptops = [
    (1, [0.1, 0.2, 0.3, 0.4], {"price": 899.99, "category": "laptop"}),
    (2, [0.2, 0.3, 0.4, 0.5], {"price": 1299.99, "category": "laptop"}),
    (3, [0.3, 0.4, 0.5, 0.6], {"price": 799.99, "category": "laptop"}),
    (4, [0.4, 0.5, 0.6, 0.7], {"price": 1099.99, "category": "laptop"}),
    (5, [0.5, 0.6, 0.7, 0.8], {"price": 949.99, "category": "laptop"})
]

四维向量可以表示特征,例如笔记本电脑的CPU、RAM或电池寿命,但这些并没有具体说明。然而,负载确切地指定了价格和产品类别。

现在,将过滤器设置为“价格低于 $1000”:

{
  "key": "price",
  "range": {
    "gt": null,
    "gte": null,
    "lt": null,
    "lte": 1000
  }
}

当应用价格过滤器小于或等于1000美元时,向量搜索返回以下结果:

[
  {
    "id": 3,
    "score": 0.9978443564622781,
    "payload": {
      "price": 799.99,
      "category": "laptop"
    }
  },
  {
    "id": 1,
    "score": 0.9938079894227599,
    "payload": {
      "price": 899.99,
      "category": "laptop"
    }
  },
  {
    "id": 5,
    "score": 0.9903751498208603,
    "payload": {
      "price": 949.99,
      "category": "laptop"
    }
  }
]

正如您所看到的,Qdrant 的过滤方法更有可能捕获所有可能的搜索结果。

这个具体的例子使用了 range 条件进行过滤。然而,Qdrant 提供了许多其他可能的方式来构建过滤器

有关详细的使用示例,过滤 文档是最佳资源。

滚动而非搜索

您不需要使用我们的 searchquery APIs 来筛选数据。 scroll API 是另一个选项,可以让您检索符合过滤条件的点的列表。

如果您对查找相似点不感兴趣,您可以简单地列出符合给定过滤器的点。虽然搜索会根据某些查询向量为您提供最相似的点,但滚动将为您提供所有符合您过滤器的点,而不考虑相似性。

在Qdrant中,滚动被用于迭代地从集合中检索大量点。当处理大量点而不想一次加载它们时,这特别有用。相反,Qdrant提供了一种逐页滚动浏览点的方法。

您首先向Qdrant发送一个滚动请求,附带特定条件,比如按有效载荷过滤、向量搜索或其他标准。

让我们按价格获取商店中前10款笔记本电脑的列表:

POST /collections/online_store/points/scroll
{
    "filter": {
        "must": [
            {
                "key": "category",
                "match": {
                    "value": "laptop"
                }
            }
        ]
    },
    "limit": 10,
    "with_payload": true,
    "with_vector": false,
    "order_by": [
        {
            "key": "price",
        }
    ]
}

响应包含一批符合条件的点以及一个参考(偏移量或下一页令牌)以获取下一组点。

滚动的设计旨在提高效率。它通过一次只返回可管理的数据块,最小化服务器的负载,并减少客户端的内存消耗。

可用的过滤条件

条件用法条件用法
匹配精确值匹配。范围按值范围过滤。
匹配任意匹配多个值。日期时间范围按日期范围过滤。
匹配除外排除特定值。UUID匹配按唯一ID过滤。
嵌套键根据嵌套数据进行过滤。地理根据位置进行过滤。
嵌套对象按嵌套对象过滤。值计数按元素计数过滤。
全文匹配在文本字段中搜索。为空过滤空字段。
具有 ID按唯一 ID 过滤。为空过滤空值。

所有条款和条件在 Qdrant 的 过滤 文档中列出。

需要记住的过滤条件

条款描述条款描述
必须包括满足条件的项目(类似于 AND)。应该如果至少满足一个条件,则进行过滤(类似于 OR)。
必须不排除满足条件的项(类似于 NOT)。子句组合组合多个子句以细化过滤(类似于 AND)。

高级过滤示例:恐龙饮食

advanced-payload-filtering

我们还可以使用嵌套过滤来查询有效载荷中的对象数组。在这个例子中,我们有两个点。它们各自代表一种恐龙,具有一个食物偏好(饮食)列表,表示它们喜欢或不喜欢哪种食物:

[
  {
    "id": 1,
    "dinosaur": "t-rex",
    "diet": [
      { "food": "leaves", "likes": false},
      { "food": "meat", "likes": true}
    ]
  },
  {
    "id": 2,
    "dinosaur": "diplodocus",
    "diet": [
      { "food": "leaves", "likes": true},
      { "food": "meat", "likes": false}
    ]
  }
]

为了确保两个条件应用于同一个数组元素(例如,food = meat 和 likes = true 必须指的是同一饮食项目),您需要使用嵌套过滤器。

嵌套过滤器用于在对象数组中应用条件。它们确保条件是在每个数组元素中评估,而不是在所有元素之间评估。

POST /collections/dinosaurs/points/scroll
{
    "filter": {
        "must": [
            {
                "key": "diet[].food",
                  "match": {
                    "value": "meat"
                }
            },
            {
                "key": "diet[].likes",
                  "match": {
                    "value": true
                }
            }
        ]
    }
}
client.scroll(
    collection_name="dinosaurs",
    scroll_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="diet[].food", match=models.MatchValue(value="meat")
            ),
            models.FieldCondition(
                key="diet[].likes", match=models.MatchValue(value=True)
            ),
        ],
    ),
)
client.scroll("dinosaurs", {
  filter: {
    must: [
      {
        key: "diet[].food",
        match: { value: "meat" },
      },
      {
        key: "diet[].likes",
        match: { value: true },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("dinosaurs").filter(Filter::must([
            Condition::matches("diet[].food", "meat".to_string()),
            Condition::matches("diet[].likes", true),
        ])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;

import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

QdrantClient client =
    new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("dinosaurs")
            .setFilter(
                Filter.newBuilder()
                    .addAllMust(
                        List.of(matchKeyword("diet[].food", "meat"), match("diet[].likes", true)))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "dinosaurs",
	filter: MatchKeyword("diet[].food", "meat") & Match("diet[].likes", true)
);

这发生是因为两个点都符合这两个条件:

  • “t-rex”匹配food=meat在 diet[1].food 和likes=true在 diet[1].likes
  • “梁龙”在 diet[1].food 上匹配食物=肉类,并在 diet[0].likes 上喜欢=true

要获取仅适用于数组中特定元素的点(例如此示例中 id 为 1 的点),您需要使用嵌套对象过滤器。

嵌套对象过滤器允许独立查询对象数组,确保条件在各个数组元素内被检查。

这是通过使用 nested 条件类型完成的,它由一个目标数组的负载键和一个要应用的过滤器组成。键应该引用一个对象数组,可以使用括号表示法写成(例如,“data”或“data[]”)。

POST /collections/dinosaurs/points/scroll
{
    "filter": {
        "must": [{
            "nested": {
                "key": "diet",
                "filter":{
                    "must": [
                        {
                            "key": "food",
                            "match": {
                                "value": "meat"
                            }
                        },
                        {
                            "key": "likes",
                            "match": {
                                "value": true
                            }
                        }
                    ]
                }
            }
        }]
    }
}
client.scroll(
    collection_name="dinosaurs",
    scroll_filter=models.Filter(
        must=[
            models.NestedCondition(
                nested=models.Nested(
                    key="diet",
                    filter=models.Filter(
                        must=[
                            models.FieldCondition(
                                key="food", match=models.MatchValue(value="meat")
                            ),
                            models.FieldCondition(
                                key="likes", match=models.MatchValue(value=True)
                            ),
                        ]
                    ),
                )
            )
        ],
    ),
)
client.scroll("dinosaurs", {
  filter: {
    must: [
      {
        nested: {
          key: "diet",
          filter: {
            must: [
              {
                key: "food",
                match: { value: "meat" },
              },
              {
                key: "likes",
                match: { value: true },
              },
            ],
          },
        },
      },
    ],
  },
});
use qdrant_client::qdrant::{Condition, Filter, NestedCondition, ScrollPointsBuilder};

client
    .scroll(
        ScrollPointsBuilder::new("dinosaurs").filter(Filter::must([NestedCondition {
            key: "diet".to_string(),
            filter: Some(Filter::must([
                Condition::matches("food", "meat".to_string()),
                Condition::matches("likes", true),
            ])),
        }
        .into()])),
    )
    .await?;
import java.util.List;

import static io.qdrant.client.ConditionFactory.match;
import static io.qdrant.client.ConditionFactory.matchKeyword;
import static io.qdrant.client.ConditionFactory.nested;

import io.qdrant.client.grpc.Points.Filter;
import io.qdrant.client.grpc.Points.ScrollPoints;

client
    .scrollAsync(
        ScrollPoints.newBuilder()
            .setCollectionName("dinosaurs")
            .setFilter(
                Filter.newBuilder()
                    .addMust(
                        nested(
                            "diet",
                            Filter.newBuilder()
                                .addAllMust(
                                    List.of(
                                        matchKeyword("food", "meat"), match("likes", true)))
                                .build()))
                    .build())
            .build())
    .get();
using Qdrant.Client;
using static Qdrant.Client.Grpc.Conditions;

var client = new QdrantClient("localhost", 6334);

await client.ScrollAsync(
	collectionName: "dinosaurs",
	filter: Nested("diet", MatchKeyword("food", "meat") & Match("likes", true))
);

匹配逻辑被调整为在有效载荷中对数组内的各个元素级别进行操作,而不是对所有数组元素一起操作。

嵌套过滤器的功能就像对数组的每个元素进行单独评估。如果至少有一个数组元素满足所有嵌套过滤条件,则父文档将被视为匹配。

过滤器的其他创意用途

您可以使用过滤器来检索数据点,而无需知道它们的 id。您可以通过使用过滤器来搜索和管理数据。让我们来看看过滤器的一些创意用途:

操作描述操作描述
删除点删除所有与过滤器匹配的点。设置有效载荷将有效载荷字段添加到所有与过滤器匹配的点。
滚动点数列出所有符合过滤器的点。更新负载更新符合过滤器的点的有效负载字段。
订单积分列出所有点,按过滤器排序。删除负载删除与过滤器匹配的点的字段。
计数点计算与过滤器匹配的积分总数。

使用负载索引进行过滤

vector-search-filtering-vector-search

当您开始使用Qdrant时,您的数据默认组织在一个向量索引中。除了这个,我们建议添加一个辅助数据结构 - 负载索引

向量索引如何组织向量,负载索引将结构化您的元数据。

图 4: 载荷索引是一个额外的数据结构,支持向量搜索。载荷索引(绿色)按基数组织候选结果,以便语义搜索(红色)能够快速遍历向量索引。

payload-index-vector-search

单独进行语义搜索时,处理数以TB计的数据可能会占用大量内存。 过滤索引 是减少计算使用同时仍能获得最佳结果的两种简单策略。请记住,这只是一个指南。要获取完整的过滤选项列表,您应该阅读 过滤文档

以下是如何为元数据字段“类别”创建单个索引:

PUT /collections/computers/index
{
    "field_name": "category",
    "field_schema": "keyword"
}
from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")

client.create_payload_index(
   collection_name="computers",
   field_name="category",
   field_schema="keyword",
)

一旦你标记一个字段为可索引, 你无需做其他任何事情. Qdrant 会在后台处理所有优化。

为什么要为元数据建立索引?

payload-index-filtering

有效负载索引作为一个辅助数据结构,加快了检索速度。每当您使用过滤器运行向量搜索时,Qdrant将咨询有效负载索引 - 如果存在的话。

随着你的数据集变得越来越复杂,Qdrant在处理所有数据点时会消耗更多资源。如果没有适当的数据结构,搜索可能会耗时更长——或者资源会耗尽。

负载索引有助于评估最严格的过滤器

有效载荷索引还用于准确估计 过滤基数,这有助于查询规划选择搜索策略。 过滤基数 指的是过滤器在数据集中可以匹配的不同值的数量。如果基数太低,Qdrant 的搜索策略可以从 HNSW 搜索 切换到 基于有效载荷索引的搜索

它如何影响您的查询: 根据搜索中使用的过滤器 - 查询执行有几种可能的情况。 Qdrant 根据可用索引、条件的复杂性和过滤结果的基数选择查询执行选项之一。

  • 计划者在选择策略之前估算过滤结果的基数。
  • 如果基数低于阈值,Qdrant 使用 载荷索引 检索点。
  • 如果基数超过阈值,Qdrant使用可过滤的向量索引

如果你不使用负载索引,会发生什么?

在查询时使用过滤器时,Qdrant需要估计这些过滤器的基数,以定义合适的查询计划。如果您没有创建有效负载索引,Qdrant将无法做到这一点。这可能会导致选择一种次优的搜索方式,从而导致极其缓慢的搜索时间或低准确度的结果。

如果您仅依赖于搜索最近的向量,Qdrant 将不得不遍历整个向量索引。它将计算与集合中每个向量的相似性,无论相关与否。另一方面,当您借助有效载荷索引进行筛选时,HSNW 算法将不必评估每个点。此外,有效载荷索引将帮助 HNSW 构建具有附加链接的图。

有效载荷索引看起来是什么样的?

有效载荷索引类似于传统的面向文档的数据库。它将元数据字段与其对应的点ID连接,以便快速检索。

在这个例子中,您正在将所有计算机硬件索引到 computers 集合中。让我们来看一下字段 category 的示例有效负载索引。

Payload Index by keyword:
+------------+-------------+
| category   | id          |
+------------+-------------+
| laptop     | 1, 4, 7     |
| desktop    | 2, 5, 9     |
| speakers   | 3, 6, 8     |
| keyboard   | 10, 11      |
+------------+-------------+

当字段正确索引时,搜索引擎大致知道可以从哪里开始它的旅程。它可以开始查找包含相关元数据的点,并且不需要扫描整个数据集。这大大减少了引擎的工作量。因此,查询结果更快,系统可以轻松扩展。

您可以根据需要创建任意多个有效载荷索引,我们建议您对每个您进行过滤的字段这样做。

如果您的用户在查找产品 类别 时经常按 笔记本电脑 进行过滤,索引所有计算机元数据将加快检索速度并使结果更精确。

不同类型的有效载荷索引

索引类型描述
全文索引启用在大型数据集中进行高效文本搜索。
租户索引用于在多租户架构中实现数据隔离和提升检索效率。
主要指数根据用户或账户等主要实体管理数据。
磁盘索引将索引存储在磁盘上,以管理大数据集而不使用内存。
参数化索引允许动态查询,索引可以根据用户提供的不同参数或条件进行调整。对于像价格或时间戳这样的数值数据非常有用。

在多租户设置中索引负载

一些应用程序需要对数据进行隔离,不同的用户需要在同一个程序中查看不同的数据。当为这样的复杂应用程序设置存储时,许多用户认为他们需要多个数据库来服务于隔离的用户。

我们经常看到这个。用户非常频繁地犯错,在同一个集群内部为每个租户创建一个单独的集合。这会迅速耗尽集群的资源。通过过多的集合运行向量搜索可能会开始消耗过多的RAM。您可能会开始看到内存不足(OOM)错误和性能下降。

为了解决这个问题,我们为多租户系统提供了广泛的支持,以便您可以在一个Qdrant集合中构建整个全球应用程序。

在创建或更新集合时,您可以将元数据字段标记为可索引。要将 user_id 标记为共享集合中的租户,请执行以下操作:

PUT /collections/{collection_name}/index
{
   "field_name": "user_id",
   "field_schema": {
       "type": "keyword",
       "is_tenant": true
   }
}

此外,我们提供了一种通过租户索引高效组织数据的方法。这是有效负载索引的另一种变体,使租户数据更易获取。这次,请求将指定字段为租户。这意味着您可以将各种客户类型和用户 ID 标记为 is_tenant: true

了解在多租户环境中设置 租户碎片整理 的更多信息,

过滤和索引的关键要点

best-practices

使用浮点数(十进制)进行过滤

如果您按浮点数据类型过滤,您的搜索精度可能会受到限制并且不准确。

浮点数据类型的数字有一个小数点,大小为64位。这里是一个例子:

{
   "price": 11.99
}

当您筛选特定的浮点数,例如 11.99 时,您可能会得到不同的结果,比如 11.98 或 12.00。由于小数的原因,数字的四舍五入方式不同,因此逻辑上相同的值可能看起来不同。不幸的是,在这种情况下,搜索精确匹配可能不可靠。

为了避免不准确性,请使用不同的过滤方法。我们建议您尝试基于范围的过滤,而不是精确匹配。这种方法考虑到数据中的小变化,并提升性能 - 尤其是在处理大型数据集时。

这是一个示例JSON范围过滤器,用于处理大于或等于11.99且小于或等于同一数字的值。这将检索范围内的任何值,包括那些具有额外小数位的值。

{
 "key": "price",
 "range": {
   "gt": null,
   "gte": 11.99,
   "lt": null,
   "lte": 11.99
  }
}

在查询中处理分页

当您在过滤查询中实现分页时,索引变得更加关键。分页结果时,您通常需要排除已经看到的项。这通常通过应用过滤器来管理,这些过滤器指定哪些ID不应包含在下一组结果中。

然而,Qdrant 数据模型的一个有趣方面是单个点可以对同一字段具有多个值,例如产品的不同颜色选项。这意味着在筛选时,如果 ID 在同一字段的不同值上匹配,它可能会多次出现。

适当的索引确保这些查询高效,防止重复结果并使分页更加顺畅。

结论:过滤的实际应用案例

在像 Qdrant 这样的 向量数据库 中进行过滤可以显著增强搜索能力,通过实现更精准和高效的数据检索。

作为本指南的结论,让我们看看一些过滤至关重要的实际使用案例:

使用案例向量搜索过滤
电子商务产品搜索按风格或视觉相似性搜索产品按价格、颜色、品牌、大小、评分筛选
推荐系统推荐相似内容(例如,电影、歌曲)按发布日期、类型等筛选(例如,2020年后的电影)
共享出行中的地理空间搜索寻找相似的司机或配送伙伴按评分、距离半径、车辆类型过滤
欺诈与异常检测检测与已知欺诈案例相似的交易按金额、时间、地点过滤

在您离开之前 - 所有代码都在Qdrant的仪表板中

达到“Hello World”时刻的最简单方法是尝试在实时集群中进行过滤。我们的互动教程将向您展示如何创建一个集群,添加数据并尝试一些过滤条件。

一切都在你的免费集群中!

qdrant-hybrid-cloud

这个页面有用吗?

感谢您的反馈!🙏

我们很遗憾听到这个消息。 😔 你可以 编辑 这个页面在 GitHub上,或者 create 一个 GitHub 问题。