跳到主要内容

文本匹配

Milvus 中的文本匹配可以基于特定术语实现精确的文档检索。此功能主要用于满足特定条件的过滤搜索,可以结合标量过滤来细化查询结果,允许在满足标量条件的向量内进行相似性搜索。

文本匹配专注于查找查询术语的精确出现,不对匹配文档的相关性进行评分。如果您想基于查询术语的语义含义和重要性检索最相关的文档,我们建议您使用全文搜索

概述

Milvus 集成了 Tantivy 来支持其底层倒排索引和基于术语的文本搜索。对于每个文本条目,Milvus 按以下流程对其进行索引:

  1. 分析器:分析器通过将输入文本分词为单个词或 token,然后根据需要应用过滤器来处理输入文本。这允许 Milvus 基于这些 token 构建索引。

  2. 索引:文本分析后,Milvus 创建一个倒排索引,将每个唯一 token 映射到包含它的文档。

当用户执行文本匹配时,倒排索引用于快速检索包含术语的所有文档。这比逐个扫描每个文档要快得多。

Keyword Match

启用文本匹配

文本匹配作用于 VARCHAR field 类型,这本质上是 Milvus 中的字符串数据类型。要启用文本匹配,在定义 collection schema 时将 enable_analyzerenable_match 都设置为 True,然后可选择配置分析器进行文本分析。

设置 enable_analyzerenable_match

要为特定的 VARCHAR field 启用文本匹配,在定义 field schema 时将 enable_analyzerenable_match 参数都设置为 True。这指示 Milvus 对指定 field 进行分词并创建倒排索引,允许快速高效的文本匹配。

from pymilvus import MilvusClient, DataType

schema = MilvusClient.create_schema(enable_dynamic_field=False)
schema.add_field(
field_name="id",
datatype=DataType.INT64,
is_primary=True,
auto_id=True
)
schema.add_field(
field_name='text',
datatype=DataType.VARCHAR,
max_length=1000,
enable_analyzer=True, # Whether to enable text analysis for this field
enable_match=True # Whether to enable text match
)
schema.add_field(
field_name="embeddings",
datatype=DataType.FLOAT_VECTOR,
dim=5
)
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.enableDynamicField(false)
.build();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(1000)
.enableAnalyzer(true)
.enableMatch(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("embeddings")
.dataType(DataType.FloatVector)
.dimension(5)
.build());
import "github.com/milvus-io/milvus/client/v2/entity"

schema := entity.NewSchema().WithDynamicFieldEnabled(false)
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
).WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithEnableAnalyzer(true).
WithEnableMatch(true).
WithMaxLength(1000),
).WithField(entity.NewField().
WithName("embeddings").
WithDataType(entity.FieldTypeFloatVector).
WithDim(5),
)
const schema = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "text",
data_type: DataType.VarChar,
enable_analyzer: true,
enable_match: true,
max_length: 1000,
},
{
name: "embeddings",
data_type: DataType.FloatVector,
dim: 5,
},
];
export schema='{
"autoId": true,
"enabledDynamicField": false,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 1000,
"enable_analyzer": true,
"enable_match": true
}
},
{
"fieldName": "embeddings",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "5"
}
}
]
}'

Optional: Configure an analyzer

关键词匹配的性能和准确性取决于所选的分析器。不同的分析器针对各种语言和文本结构进行了调整,因此选择合适的分析器可以显著影响您特定用例的搜索结果。

默认情况下,Milvus 使用 standard 分析器,它基于空白字符和标点符号对文本进行分词,删除长度超过 40 个字符的 token,并将文本转换为小写。应用此默认设置不需要额外参数。有关更多信息,请参阅标准

在需要不同分析器的情况下,您可以使用 analyzer_params 参数配置一个。例如,要应用 english 分析器来处理英文文本:

analyzer_params = {
"type": "english"
}
schema.add_field(
field_name='text',
datatype=DataType.VARCHAR,
max_length=200,
enable_analyzer=True,
analyzer_params = analyzer_params,
enable_match = True,
)
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("type", "english");
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(200)
.enableAnalyzer(true)
.analyzerParams(analyzerParams)
.enableMatch(true)
.build());
analyzerParams := map[string]any{"type": "english"}
schema.WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithEnableAnalyzer(true).
WithEnableMatch(true).
WithAnalyzerParams(analyzerParams).
WithMaxLength(200),
)
const schema = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "text",
data_type: DataType.VarChar,
enable_analyzer: true,
enable_match: true,
max_length: 1000,
analyzer_params: { type: 'english' },
},
{
name: "embeddings",
data_type: DataType.FloatVector,
dim: 5,
},
];
export schema='{
"autoId": true,
"enabledDynamicField": false,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 200,
"enable_analyzer": true,
"enable_match": true,
"analyzer_params": {"type": "english"}
}
},
{
"fieldName": "embeddings",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "5"
}
}
]
}'

Milvus 也提供了各种其他分析器,适用于不同的语言和场景。有关更多详细信息,请参阅分析器概述

使用文本匹配

在 collection schema 中为 VARCHAR field 启用文本匹配后,您可以使用 TEXT_MATCH 表达式执行文本匹配。

TEXT_MATCH 表达式语法

TEXT_MATCH 表达式用于指定要搜索的 field 和术语。其语法如下:

TEXT_MATCH(field_name, text)
  • field_name:要搜索的 VARCHAR field 的名称。

  • text:要搜索的术语。多个术语可以用空格或其他适当的分隔符分隔,具体取决于语言和配置的分析器。

默认情况下,TEXT_MATCH 使用 OR 匹配逻辑,这意味着它将返回包含任何指定术语的文档。例如,要搜索在 text field 中包含术语 machinedeep 的文档,请使用以下表达式:

filter = "TEXT_MATCH(text, 'machine deep')"
String filter = "TEXT_MATCH(text, 'machine deep')";
filter := "TEXT_MATCH(text, 'machine deep')"
const filter = "TEXT_MATCH(text, 'machine deep')";
export filter="\"TEXT_MATCH(text, 'machine deep')\""

您还可以使用逻辑运算符组合多个 TEXT_MATCH 表达式来执行 AND 匹配。

  • 要搜索在 text field 中同时包含 machinedeep 的文档,请使用以下表达式:

    filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    String filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')";
    filter := "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    const filter = "TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')"
    export filter="\"TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'deep')\""
  • 要搜索在 text field 中同时包含 machinelearning 但不含 deep 的文档,请使用以下表达式:

    filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')"
    String filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')";
    filter := "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')"
    const filter = "not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')";
    export filter="\"not TEXT_MATCH(text, 'deep') and TEXT_MATCH(text, 'machine') and TEXT_MATCH(text, 'learning')\""

使用文本匹配搜索

文本匹配可以与向量相似性搜索结合使用,以缩小搜索范围并提高搜索性能。通过在向量相似性搜索之前使用文本匹配过滤 collection,您可以减少需要搜索的文档数量,从而获得更快的查询时间。

在此示例中,filter 表达式将搜索结果过滤为仅包含匹配指定术语 keyword1keyword2 的文档。然后在这个过滤后的文档子集上执行向量相似性搜索。

# Match entities with `keyword1` or `keyword2`
filter = "TEXT_MATCH(text, 'keyword1 keyword2')"

# Assuming 'embeddings' is the vector field and 'text' is the VARCHAR field
result = client.search(
collection_name="my_collection", # Your collection name
anns_field="embeddings", # Vector field name
data=[query_vector], # Query vector
filter=filter,
search_params={"params": {"nprobe": 10}},
limit=10, # Max. number of results to return
output_fields=["id", "text"] # Fields to return
)
String filter = "TEXT_MATCH(text, 'keyword1 keyword2')";

SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.annsField("embeddings")
.data(Collections.singletonList(queryVector)))
.filter(filter)
.topK(10)
.outputFields(Arrays.asList("id", "text"))
.build());
filter := "TEXT_MATCH(text, 'keyword1 keyword2')"

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection", // collectionName
10, // limit
[]entity.Vector{entity.FloatVector(queryVector)},
).WithANNSField("embeddings").
WithFilter(filter).
WithOutputFields("id", "text"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
// Match entities with `keyword1` or `keyword2`
const filter = "TEXT_MATCH(text, 'keyword1 keyword2')";

// Assuming 'embeddings' is the vector field and 'text' is the VARCHAR field
const result = await client.search(
collection_name: "my_collection", // Your collection name
anns_field: "embeddings", // Vector field name
data: [query_vector], // Query vector
filter: filter,
params: {"nprobe": 10},
limit: 10, // Max. number of results to return
output_fields: ["id", "text"] //Fields to return
);
export filter="\"TEXT_MATCH(text, 'keyword1 keyword2')\""

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_collection",
"annsField": "embeddings",
"data": [[0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]],
"filter": '"$filter"',
"searchParams": {
"params": {
"nprobe": 10
}
},
"limit": 10,
"outputFields": ["text","id"]
}'

Query with text match 查询操作中的文本匹配

使用文本匹配查询

Text match can also be used for scalar filtering in query operations. By specifying a TEXT_MATCH expression in the expr parameter of the query() method, you can retrieve documents that match the given terms. 文本匹配也可以用于查询操作中的标量过滤。通过在 query() 方法的 expr 参数中指定 TEXT_MATCH 表达式,您可以检索匹配给定术语的文档。

The example below retrieves documents where the text field contains both terms keyword1 and keyword2. 下面的示例检索 text field 同时包含术语 keyword1keyword2 的文档。

# Match entities with both `keyword1` and `keyword2`
filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')"

result = client.query(
collection_name="my_collection",
filter=filter,
output_fields=["id", "text"]
)
String filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')";

QueryResp queryResp = client.query(QueryReq.builder()
.collectionName("my_collection")
.filter(filter)
.outputFields(Arrays.asList("id", "text"))
.build()
);
filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')"
resultSet, err := client.Query(ctx, milvusclient.NewQueryOption("my_collection").
WithFilter(filter).
WithOutputFields("id", "text"))
if err != nil {
fmt.Println(err.Error())
// handle error
}

// Match entities with both `keyword1` and `keyword2`
const filter = "TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')";

const result = await client.query(
collection_name: "my_collection",
filter: filter,
output_fields: ["id", "text"]
)
export filter="\"TEXT_MATCH(text, 'keyword1') and TEXT_MATCH(text, 'keyword2')\""

export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
"collectionName": "my_collection",
"filter": '"$filter"',
"outputFields": ["id", "text"]
}'

注意事项

  • 为 field 启用术语匹配会触发倒排索引的创建,这会消耗存储资源。在决定启用此功能时要考虑存储影响,因为它根据文本大小、唯一 token 和使用的分析器而有所不同。

  • 一旦您在 schema 中定义了分析器,其设置就会对该 collection 永久生效。如果您决定不同的分析器更适合您的需求,您可以考虑删除现有 collection 并使用所需的分析器配置创建新的 collection。

  • filter 表达式中的转义规则:

    • 表达式中用双引号或单引号括起来的字符被解释为字符串常量。如果字符串常量包含转义字符,则转义字符必须用转义序列表示。例如,使用 \\ 表示 \,使用 \\t 表示制表符 \t,使用 \\n 表示换行符。

    • 如果字符串常量用单引号括起来,常量内的单引号应表示为 \\',而双引号可以表示为 "\\"。示例:'It\\'s milvus'

    • 如果字符串常量用双引号括起来,常量内的双引号应表示为 \\",而单引号可以表示为 '\\'。示例:"He said \\"Hi\\""