文本匹配
Milvus 中的文本匹配可以基于特定术语实现精确的文档检索。此功能主要用于满足特定条件的过滤搜索,可以结合标量过滤来细化查询结果,允许在满足标量条件的向量内进行相似性搜索。
文本匹配专注于查找查询术语的精确出现,不对匹配文档的相关性进行评分。如果您想基于查询术语的语义含义和重要性检索最相关的文档,我们建议您使用全文搜索。
概述
Milvus 集成了 Tantivy 来支持其底层倒排索引和基于术语的文本搜索。对于每个文本条目,Milvus 按以下流程对其进行索引:
-
分析器:分析器通过将输入文本分词为单个词或 token,然后根据需要应用过滤器来处理输入文本。这允许 Milvus 基于这些 token 构建索引。
-
索引:文本分析后,Milvus 创建一个倒排索引,将每个唯一 token 映射到包含它的文档。
当用户执行文本匹配时,倒排索引用于快速检索包含术语的所有文档。这比逐个扫描每个文档要快得多。
启用文本匹配
文本匹配作用于 VARCHAR
field 类型,这本质上是 Milvus 中的字符串数据类型。要启用文本匹配,在定义 collection schema 时将 enable_analyzer
和 enable_match
都设置为 True
,然后可选择配置分析器进行文本分析。
设置 enable_analyzer
和 enable_match
要为特定的 VARCHAR
field 启用文本匹配,在定义 field schema 时将 enable_analyzer
和 enable_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 中包含术语 machine
或 deep
的文档,请使用以下表达式:
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 中同时包含machine
和deep
的文档,请使用以下表达式: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 中同时包含machine
和learning
但不含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
表达式将搜索结果过滤为仅包含匹配指定术语 keyword1
或 keyword2
的文档。然后在这个过滤后的文档子集上执行向量相似性搜索。
# 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 同时包含术语 keyword1
和 keyword2
的文档。
# 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\\""
。
-