Correspondance de texte
La correspondance de texte dans Milvus permet une recherche précise de documents en fonction de termes spécifiques. Cette fonctionnalité est principalement utilisée pour la recherche filtrée afin de répondre à des conditions spécifiques et peut intégrer un filtrage scalaire pour affiner les résultats de la requête, permettant ainsi des recherches par similarité au sein de vecteurs répondant à des critères scalaires.
TEXT_MATCH La recherche par correspondance exacte (« Exact Match ») trouve les termes analysés à l’identique, tandis que la recherche par similarité (« TEXT_MATCH_FUZZY ») tolère une faible distance d’édition entre les tokens de la requête et ceux de l’index. Ces deux méthodes sont des opérations de filtrage booléen et n’évaluent pas la pertinence des documents correspondants. Si vous souhaitez récupérer les documents les plus pertinents en fonction de la signification sémantique et de l’importance des termes de la requête, nous vous recommandons d’utiliser la recherche en texte intégral («Full Text Search»).
Présentation
Milvus intègre Tantivy pour alimenter son index inversé sous-jacent et sa recherche textuelle basée sur les termes. Pour chaque entrée de texte, Milvus l’indexe en suivant la procédure suivante :
Analyseur: l’analyseur traite le texte d’entrée en le segmentant en mots individuels, ou tokens, puis en appliquant des filtres si nécessaire. Cela permet à Milvus de construire un index basé sur ces tokens.
Indexation: après l’analyse du texte, Milvus crée un index inversé qui associe chaque token unique aux documents qui le contiennent.
Lorsqu’un utilisateur effectue une recherche textuelle, l’index inversé est utilisé pour récupérer rapidement tous les documents contenant les termes recherchés. Ce processus est bien plus rapide que de parcourir chaque document individuellement.
Recherche par mot-clé
Activer la recherche textuelle
La recherche textuelle fonctionne sur les champs de chaîne de caractères pour lesquels la recherche est activée. Les exemples présentés sur cette page utilisent VARCHAR, qui est pris en charge par tous les SDK clients. Dans Milvus 3.0.x, TEXT les champs prennent également en charge la recherche textuelle lorsque Storage V3 est activé. Pour l’un ou l’autre type de champ, définissez à la fois enable_analyzer et enable_match sur True, puis configurez éventuellement un analyseur lors de la définition du schéma de votre collection.
Définissez enable_analyzer et enable_match
Pour activer la correspondance de texte pour un champ VARCHAR spécifique, définissez les paramètres enable_analyzer et enable_match sur True lors de la définition du schéma du champ. Cela indique à Milvus de tokeniser le texte et de créer un index inversé pour le champ spécifié, ce qui permet des correspondances de texte rapides et efficaces.
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"
}
}
]
}'
Facultatif : configurer un analyseur
Les performances et la précision de la recherche par mot-clé dépendent de l’analyseur sélectionné. Différents analyseurs sont adaptés à divers langages et structures de texte ; le choix du bon analyseur peut donc avoir un impact significatif sur les résultats de recherche pour votre cas d’utilisation spécifique.
Par défaut, Milvus utilise l’analyseur « standard », qui tokenise le texte en fonction des espaces et de la ponctuation, supprime les tokens de plus de 40 caractères et convertit le texte en minuscules. Aucun paramètre supplémentaire n’est nécessaire pour appliquer ce paramètre par défaut. Pour plus d’informations, consultez la section « Standard ».
Si un analyseur différent est nécessaire, vous pouvez en configurer un à l’aide du paramètre ` analyzer_params `. Par exemple, pour appliquer l’analyseur ` english ` au traitement d’un texte en anglais :
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 propose également divers autres analyseurs adaptés à différentes langues et à différents scénarios. Pour plus de détails, consultez la section Présentation des analyseurs.
Utilisation de la correspondance de texte
Une fois que vous avez activé la correspondance de texte pour un champ « VARCHAR » ou « TEXT » dans le schéma de votre collection, vous pouvez effectuer des correspondances de texte à l’aide de l’expression « TEXT_MATCH ».
Syntaxe de l'expression TEXT_MATCH
L’expression TEXT_MATCH est utilisée pour spécifier le champ et les termes à rechercher. Sa syntaxe est la suivante :
TEXT_MATCH(field_name, text)
field_name: Le nom du champ «VARCHAR» ou «TEXT» activé pour la correspondance, dans lequel effectuer la recherche.text: Les termes à rechercher. Plusieurs termes peuvent être séparés par des espaces ou d’autres délimiteurs appropriés en fonction de la langue et de l’analyseur configuré.
Par défaut, TEXT_MATCH utilise la logique de correspondance « OU », ce qui signifie qu’il renverra les documents contenant l’un des termes spécifiés. Par exemple, pour rechercher des documents contenant le terme « machine » ou « deep » dans le champ « text », utilisez l’expression suivante :
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')\""
Vous pouvez également combiner plusieurs expressions « TEXT_MATCH » à l’aide d’opérateurs logiques pour effectuer une recherche « ET ».
Pour rechercher des documents contenant à la fois «
machine» et «deep» dans le champ «text», utilisez l’expression suivante :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')\""Pour rechercher des documents contenant à la fois «
machine» et «learning», mais sans «deep» dans le champ «text», utilisez les expressions suivantes :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')\""
Syntaxe de l’expression TEXT_MATCH_FUZZYCompatible with Milvus 3.0.0+
Utilisez TEXT_MATCH_FUZZY pour tolérer les différences orthographiques entre les tokens de la requête et les tokens indexés. Milvus analyse le texte de la requête à l’aide de l’analyseur du champ et applique une correspondance approximative à chaque token obtenu. Si la requête produit plusieurs tokens, l’expression correspond à une entité dès lors qu’un token quelconque respecte la distance d’édition configurée.
La syntaxe est la suivante :
TEXT_MATCH_FUZZY(field_name, text, max_edit_distance = 1)
field_name: Le nom du champ «VARCHAR» ou «TEXT» activé pour la recherche à effectuer.text: Le texte de la requête à analyser et à comparer aux tokens indexés.max_edit_distance: La distance d’édition maximale autorisée pour chaque token de la requête. Le nom de l’option doit être exactement «max_edit_distance», et sa valeur doit être «0», «1» ou «2». Une valeur de «0» effectue une correspondance exacte des tokens, équivalente à «TEXT_MATCH».
Par exemple, l’expression suivante correspond aux tokens dont la valeur diffère d’au plus un caractère de machne, y compris 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 fait partie de la syntaxe des expressions de filtrage ; les SDK clients n’ont donc pas besoin d’une méthode dédiée à la correspondance approximative. Transmettez l’expression via le même paramètre filter que celui utilisé pour TEXT_MATCH dans les opérations de recherche ou de requête.
Recherche par correspondance textuelle
La correspondance textuelle peut être utilisée en combinaison avec la recherche par similarité vectorielle pour restreindre le champ de recherche et améliorer les performances de recherche. En filtrant la collection à l’aide de la correspondance textuelle avant la recherche par similarité vectorielle, vous pouvez réduire le nombre de documents à parcourir, ce qui accélère les temps de requête.
Dans cet exemple, l’expression filter filtre les résultats de recherche pour n’inclure que les documents correspondant au terme spécifié keyword1 ou keyword2. La recherche par similarité vectorielle est ensuite effectuée sur ce sous-ensemble filtré de documents.
Vous pouvez mettre en évidence les termes correspondants dans les résultats de recherche en configurant un surligneur de texte. Voir Surligneur de texte pour plus de détails.
# 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"]
}'
Requête avec correspondance de texte
La correspondance de texte peut également être utilisée pour le filtrage scalaire dans les opérations de requête. En spécifiant une expression « TEXT_MATCH » dans le paramètre « expr » de la méthode « query() », vous pouvez récupérer les documents qui correspondent aux termes donnés.
L'exemple ci-dessous récupère les documents dont le champ « text » contient à la fois les termes « keyword1 » et « 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"]
}'
Considérations
L’activation de la correspondance de termes pour un champ déclenche la création d’un index inversé, ce qui consomme des ressources de stockage. Tenez compte de l’impact sur le stockage lorsque vous décidez d’activer cette fonctionnalité, car celui-ci varie en fonction de la taille du texte, des tokens uniques et de l’analyseur utilisé.
Une fois que vous avez défini un analyseur dans votre schéma, ses paramètres deviennent permanents pour cette collection. Si vous estimez qu’un autre analyseur répondrait mieux à vos besoins, vous pouvez envisager de supprimer la collection existante et d’en créer une nouvelle avec la configuration d’analyseur souhaitée.
Règles d'échappement dans les expressions d'
filter:Les caractères placés entre guillemets doubles ou simples au sein d’expressions sont interprétés comme des constantes de chaîne. Si la constante de chaîne contient des caractères d’échappement, ceux-ci doivent être représentés à l’aide d’une séquence d’échappement. Par exemple, utilisez
\\pour représenter\,\\tpour représenter une tabulation\t, et\\npour représenter un saut de ligne.Si une constante de chaîne est placée entre guillemets simples, un guillemet simple à l'intérieur de la constante doit être représenté par
\\', tandis qu'un guillemet double peut être représenté soit par", soit par\\". Exemple :'It\\'s milvus'.Si une constante de chaîne est placée entre guillemets doubles, un guillemet double à l'intérieur de la constante doit être représenté par
\\", tandis qu'un guillemet simple peut être représenté soit par', soit par\\'. Exemple :"He said \\"Hi\\"".