Corrispondenza del testo
La corrispondenza del testo in Milvus consente di recuperare documenti precisi in base a termini specifici. Questa funzione è utilizzata principalmente per la ricerca filtrata per soddisfare condizioni specifiche e può incorporare un filtro scalare per affinare i risultati della query, consentendo ricerche di similarità all'interno di vettori che soddisfano criteri scalari.
La corrispondenza del testo si concentra sulla ricerca delle occorrenze esatte dei termini della query, senza assegnare un punteggio alla rilevanza dei documenti abbinati. Se si desidera recuperare i documenti più rilevanti in base al significato semantico e all'importanza dei termini della query, si consiglia di utilizzare la ricerca full text.
Panoramica
Milvus integra Tantivy per alimentare l'indice inverso e la ricerca testuale basata sui termini. Per ogni inserimento di testo, Milvus lo indicizza seguendo la procedura:
Analizzatore: L'analizzatore elabora il testo in ingresso tokenizzandolo in singole parole, o token, e applicando poi i filtri necessari. Ciò consente a Milvus di costruire un indice basato su questi token.
Indicizzazione: Dopo l'analisi del testo, Milvus crea un indice inverso che mappa ogni singolo token con i documenti che lo contengono.
Quando un utente esegue una corrispondenza di testo, l'indice invertito viene utilizzato per recuperare rapidamente tutti i documenti che contengono i termini. Questo è molto più veloce della scansione di ogni singolo documento.
Corrispondenza di parole chiave
Abilitazione della corrispondenza testuale
La corrispondenza testuale funziona sul tipo di campo VARCHAR che è essenzialmente il tipo di dati stringa di Milvus. Per abilitare la corrispondenza con il testo, impostare enable_analyzer e enable_match su True e poi configurare facoltativamente un analizzatore per l'analisi del testo quando si definisce lo schema della raccolta.
Impostare enable_analyzer e enable_match
Per abilitare la corrispondenza del testo per uno specifico campo VARCHAR, impostare entrambi i parametri enable_analyzer e enable_match su True quando si definisce lo schema del campo. Ciò indica a Milvus di tokenizzare il testo e di creare un indice invertito per il campo specificato, consentendo corrispondenze di testo rapide ed efficienti.
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"
}
}
]
}'
Opzionale: Configurare un analizzatore
Le prestazioni e l'accuratezza della corrispondenza delle parole chiave dipendono dall'analizzatore selezionato. Diversi analizzatori sono adatti a varie lingue e strutture di testo, quindi la scelta di quello giusto può avere un impatto significativo sui risultati della ricerca per il vostro caso d'uso specifico.
Per impostazione predefinita, Milvus utilizza l'analizzatore standard, che tokenizza il testo in base agli spazi bianchi e alla punteggiatura, rimuove i token più lunghi di 40 caratteri e converte il testo in minuscolo. Non sono necessari parametri aggiuntivi per applicare questa impostazione predefinita. Per ulteriori informazioni, consultare Standard.
Nei casi in cui sia necessario un analizzatore diverso, è possibile configurarne uno utilizzando il parametro analyzer_params. Ad esempio, per applicare l'analizzatore english per l'elaborazione del testo inglese:
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"
}
}
]
}'
Milvus fornisce anche altri analizzatori adatti a diversi linguaggi e scenari. Per maggiori dettagli, consultare la sezione Panoramica degli analizzatori.
Utilizzare la corrispondenza del testo
Una volta abilitata la corrispondenza del testo per un campo VARCHAR nello schema della raccolta, è possibile eseguire corrispondenze di testo usando l'espressione TEXT_MATCH.
Sintassi dell'espressione TEXT_MATCH
L'espressione TEXT_MATCH viene usata per specificare il campo e i termini da cercare. La sua sintassi è la seguente:
TEXT_MATCH(field_name, text)
field_name: Il nome del campo VARCHAR da cercare.text: I termini da cercare. I termini multipli possono essere separati da spazi o da altri delimitatori appropriati in base alla lingua e all'analizzatore configurato.
Per impostazione predefinita, TEXT_MATCH utilizza la logica di corrispondenza OR, ovvero restituisce i documenti che contengono uno qualsiasi dei termini specificati. Ad esempio, per cercare documenti contenenti il termine machine o deep nel campo text, utilizzare la seguente espressione:
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')\""
È inoltre possibile combinare più espressioni TEXT_MATCH utilizzando gli operatori logici per eseguire la corrispondenza AND.
Per cercare i documenti contenenti sia
machinechedeepnel campotext, utilizzare la seguente espressione: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')\""Per cercare documenti contenenti sia
machinechelearningma senzadeepnel campotext, utilizzare le seguenti espressioni: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')\""
Ricerca con corrispondenza di testo
La corrispondenza del testo può essere utilizzata in combinazione con la ricerca per similarità vettoriale per restringere l'ambito di ricerca e migliorare le prestazioni della ricerca. Filtrando la raccolta con la corrispondenza del testo prima della ricerca per similarità vettoriale, è possibile ridurre il numero di documenti da ricercare, con conseguenti tempi di interrogazione più rapidi.
In questo esempio, l'espressione filter filtra i risultati della ricerca per includere solo i documenti che corrispondono al termine specificato keyword1 o keyword2. La ricerca di similarità vettoriale viene quindi eseguita su questo sottoinsieme di documenti filtrati.
È possibile evidenziare i termini corrispondenti nei risultati della ricerca configurando un evidenziatore di testo. Per maggiori dettagli, vedere Evidenziatore di testo.
# 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"]
}'
Query con corrispondenza di testo
La corrispondenza del testo può essere utilizzata anche per il filtraggio scalare nelle operazioni di query. Specificando un'espressione TEXT_MATCH nel parametro expr del metodo query(), è possibile recuperare i documenti che corrispondono ai termini indicati.
L'esempio seguente recupera i documenti in cui il campo text contiene entrambi i termini 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"]
}'
Considerazioni
L'abilitazione della corrispondenza dei termini per un campo attiva la creazione di un indice invertito, che consuma risorse di archiviazione. Considerare l'impatto sullo storage quando si decide di abilitare questa funzione, poiché varia in base alla dimensione del testo, ai token unici e all'analizzatore utilizzato.
Una volta definito un analizzatore nello schema, le sue impostazioni diventano permanenti per quella raccolta. Se si decide che un analizzatore diverso è più adatto alle proprie esigenze, si può pensare di eliminare l'insieme esistente e crearne uno nuovo con la configurazione dell'analizzatore desiderata.
Regole di escape nelle espressioni di
filter:I caratteri racchiusi tra doppi apici o apici singoli all'interno delle espressioni vengono interpretati come costanti di stringa. Se la costante di stringa include caratteri di escape, i caratteri di escape devono essere rappresentati con una sequenza di escape. Ad esempio, utilizzare
\\per rappresentare\,\\tper rappresentare una tabulazione\te\\nper rappresentare una newline.Se una costante di stringa è racchiusa da apici singoli, un apice singolo all'interno della costante deve essere rappresentato come
\\'mentre un doppio apice può essere rappresentato come"o\\". Esempio:'It\\'s milvus'.Se una costante di stringa è racchiusa da doppi apici, un doppio apice all'interno della costante deve essere rappresentato come
\\"mentre un apice singolo può essere rappresentato come'o\\'. Esempio:"He said \\"Hi\\"".