Correspondência de Texto
A correspondência de texto no Milvus permite a recuperação precisa de documentos com base em termos específicos. Esta funcionalidade é utilizada principalmente para pesquisas filtradas que satisfaçam condições específicas e pode incorporar filtragem escalar para refinar os resultados da consulta, permitindo pesquisas de similaridade dentro de vetores que cumpram critérios escalares.
TEXT_MATCH Encontra termos analisados exatos, enquanto a « TEXT_MATCH_FUZZY » (Pesquisa de semelhança) pode tolerar uma pequena distância de edição entre os tokens da consulta e os tokens indexados. Ambas são operações de filtragem booleana e não atribuem uma pontuação à relevância dos documentos correspondentes. Se pretender recuperar os documentos mais relevantes com base no significado semântico e na importância dos termos da consulta, recomendamos que utilize a «Full Text Search»(Pesquisa de texto completo).
Visão geral
O Milvus integra o Tantivy para alimentar o seu índice invertido subjacente e a pesquisa de texto baseada em termos. Para cada entrada de texto, o Milvus indexa-a seguindo o procedimento:
Analisador: O analisador processa o texto de entrada, tokenizando-o em palavras individuais, ou tokens, e aplicando depois filtros conforme necessário. Isto permite ao Milvus construir um índice com base nestes tokens.
Indexação: Após a análise do texto, o Milvus cria um índice invertido que mapeia cada token único para os documentos que o contêm.
Quando um utilizador efetua uma pesquisa de texto, o índice invertido é utilizado para recuperar rapidamente todos os documentos que contenham os termos. Este processo é muito mais rápido do que analisar cada documento individualmente.
Correspondência de palavras-chave
Ativar a correspondência de texto
A correspondência de texto funciona em campos de cadeia de caracteres com a correspondência ativada. Os exemplos nesta página utilizam VARCHAR, que é suportado em todos os SDKs de cliente. No Milvus 3.0.x, TEXT os campos também suportam a correspondência de texto quando o Storage V3 está ativado. Para qualquer um dos tipos de campo, defina tanto ` enable_analyzer ` como ` enable_match ` para ` True` e, em seguida, configure opcionalmente um analisador ao definir o esquema da sua coleção.
Defina enable_analyzer e enable_match
Para ativar a correspondência de texto para um campo específico de « VARCHAR », defina os parâmetros « enable_analyzer » e « enable_match » como « True » ao definir o esquema do campo. Isto instrui o Milvus a tokenizar o texto e a criar um índice invertido para o campo especificado, permitindo correspondências de texto rápidas e eficientes.
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: "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"
}
}
]
}'
Opcional: Configurar um analisador
O desempenho e a precisão da correspondência de palavras-chave dependem do analisador selecionado. Diferentes analisadores são adaptados a várias linguagens e estruturas de texto, pelo que a escolha do analisador certo pode ter um impacto significativo nos resultados de pesquisa para o seu caso de utilização específico.
Por predefinição, o Milvus utiliza o analisador « standard », que tokeniza o texto com base em espaços em branco e pontuação, remove tokens com mais de 40 caracteres e converte o texto para minúsculas. Não são necessários parâmetros adicionais para aplicar esta configuração predefinida. Para mais informações, consulte «Standard».
Nos casos em que for necessário um analisador diferente, pode configurar um utilizando o parâmetro ` analyzer_params `. Por exemplo, para aplicar o analisador ` english ` ao processamento de texto em inglês:
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: "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"
}
}
]
}'
O Milvus também disponibiliza vários outros analisadores adequados a diferentes linguagens e cenários. Para mais detalhes, consulte a Visão Geral do Analisador.
Utilizar a correspondência de texto
Depois de ativar a correspondência de texto para um campo « VARCHAR » ou « TEXT » no esquema da sua coleção, pode realizar correspondências de texto utilizando a expressão « TEXT_MATCH ».
Sintaxe da expressão TEXT_MATCH
A expressão TEXT_MATCH é utilizada para especificar o campo e os termos a pesquisar. A sua sintaxe é a seguinte:
TEXT_MATCH(field_name, text)
field_name: O nome do campo «VARCHAR» ou «TEXT» com a funcionalidade de correspondência ativada, no qual se pretende efetuar a pesquisa.text: Os termos a pesquisar. É possível utilizar vários termos, separados por espaços ou outros delimitadores adequados, consoante o idioma e o analisador configurado.
Por predefinição, o TEXT_MATCH utiliza a lógica de correspondência «OU», o que significa que irá devolver documentos que contenham qualquer um dos termos especificados. Por exemplo, para pesquisar documentos que contenham o termo machine ou deep no campo text, utilize a seguinte expressão:
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')\""
Também pode combinar várias expressões « TEXT_MATCH » utilizando operadores lógicos para efetuar uma correspondência «AND ».
Para pesquisar documentos que contenham tanto «
machine» como «deep» no campo «text», utilize a seguinte expressão: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')\""Para pesquisar documentos que contenham tanto
machinecomolearning, mas semdeepno campotext, utilize as seguintes expressões: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')\""
Sintaxe da expressão TEXT_MATCH_FUZZYCompatible with Milvus 3.0.0+
Utilize TEXT_MATCH_FUZZY para tolerar diferenças ortográficas entre os tokens da consulta e os tokens indexados. O Milvus analisa o texto da consulta com o analisador do campo e aplica a correspondência aproximada a cada token resultante. Se a consulta produzir vários tokens, a expressão corresponde a uma entidade quando qualquer token satisfizer a distância de edição configurada.
A sintaxe é a seguinte:
TEXT_MATCH_FUZZY(field_name, text, max_edit_distance = 1)
field_name: O nome do campo «VARCHAR» ou «TEXT» com a correspondência ativada, no qual se pretende efetuar a pesquisa.text: O texto da consulta a analisar e a comparar com os tokens indexados.max_edit_distance: A distância de edição máxima permitida para cada token da consulta. O nome da opção deve ser exatamente «max_edit_distance», e o seu valor deve ser «0», «1» ou «2». Um valor de «0» realiza uma correspondência exata de tokens, equivalente a «TEXT_MATCH».
Por exemplo, a expressão seguinte corresponde a tokens que diferem em apenas uma alteração de machne, incluindo machine:
filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)"
String filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)";
filter := "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)"
const filter = "TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)";
export filter="\"TEXT_MATCH_FUZZY(text, 'machne', max_edit_distance = 1)\""
TEXT_MATCH_FUZZY faz parte da sintaxe da expressão de filtro, pelo que os SDKs do cliente não necessitam de um método dedicado de correspondência aproximada. Passe a expressão através do mesmo parâmetro filter utilizado para TEXT_MATCH em operações de pesquisa ou consulta.
Pesquisa com correspondência de texto
A correspondência de texto pode ser utilizada em combinação com a pesquisa de similaridade vetorial para restringir o âmbito da pesquisa e melhorar o desempenho da mesma. Ao filtrar a coleção utilizando a correspondência de texto antes da pesquisa de similaridade vetorial, é possível reduzir o número de documentos que precisam de ser pesquisados, resultando em tempos de consulta mais rápidos.
Neste exemplo, a expressão filter filtra os resultados da pesquisa para incluir apenas documentos que correspondam ao termo especificado keyword1 ou keyword2. A pesquisa de similaridade vetorial é então realizada neste subconjunto filtrado de documentos.
Pode destacar os termos correspondentes nos resultados da pesquisa configurando um realçador de texto. Consulte Realçador de texto para obter mais detalhes.
# 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" \
--header "Request-Timeout: 10" \
-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"]
}'
Consulta com correspondência de texto
A correspondência de texto também pode ser utilizada para filtragem escalar em operações de consulta. Ao especificar uma expressão « TEXT_MATCH » no parâmetro « expr » do método « query() », pode recuperar documentos que correspondam aos termos indicados.
O exemplo abaixo recupera documentos em que o campo « text » contém ambos os termos « keyword1 » e « 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" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "my_collection",
"filter": '"$filter"',
"outputFields": ["id", "text"]
}'
Considerações
Ativar a correspondência de termos para um campo desencadeia a criação de um índice invertido, o que consome recursos de armazenamento. Tenha em conta o impacto no armazenamento ao decidir ativar esta funcionalidade, uma vez que varia consoante o tamanho do texto, os tokens únicos e o analisador utilizado.
Depois de definir um analisador no seu esquema, as suas definições tornam-se permanentes para essa coleção. Se decidir que um analisador diferente se adequaria melhor às suas necessidades, pode considerar eliminar a coleção existente e criar uma nova com a configuração de analisador pretendida.
Regras de escape em expressões «
filter»:Os caracteres entre aspas duplas ou simples dentro de expressões são interpretados como constantes de cadeia de caracteres. Se a constante de cadeia de caracteres incluir caracteres de escape, estes devem ser representados com uma sequência de escape. Por exemplo, utilize
\\para representar\,\\tpara representar uma tabulação\te\\npara representar uma nova linha.Se uma constante de cadeia de caracteres estiver entre aspas simples, uma aspa simples dentro da constante deve ser representada como
\\', enquanto uma aspa dupla pode ser representada como"ou\\". Exemplo:'It\\'s milvus'.Se uma constante de cadeia de caracteres estiver entre aspas duplas, uma aspa dupla dentro da constante deve ser representada como
\\", enquanto uma aspa simples pode ser representada como'ou\\'. Exemplo:"He said \\"Hi\\"".