Analyseurs multilinguesCompatible with Milvus 2.5.11+
Lorsque Milvus effectue une analyse de texte, il applique généralement un seul analyseur à l’ensemble d’un champ de texte dans une collection. Si cet analyseur est optimisé pour l’anglais, il rencontre des difficultés avec les règles de tokenisation et de dérivation très différentes requises par d’autres langues, telles que le chinois, l’espagnol ou le français, ce qui se traduit par un taux de rappel plus faible. Par exemple, une recherche du mot espagnol « teléfono » (qui signifie « téléphone ») poserait problème à un analyseur axé sur l’anglais : il pourrait omettre l’accent et ne pas appliquer de lemmatisation spécifique à l’espagnol, ce qui ferait passer à la trappe des résultats pertinents.
Les analyseurs multilingues résolvent ce problème en vous permettant de configurer plusieurs analyseurs pour un champ de texte au sein d’une même collection. Ainsi, vous pouvez stocker des documents multilingues dans un champ de texte, et Milvus analyse le texte selon les règles linguistiques appropriées à chaque document.
Limites
Cette fonctionnalité ne fonctionne qu’avec la recherche de texte basée sur BM25 et les vecteurs clairsemés. Pour plus d’informations, consultez la section Recherche en texte intégral.
Chaque document d’une même collection ne peut utiliser qu’un seul analyseur, déterminé par la valeur de son champ d’identifiant de langue.
Les performances peuvent varier en fonction de la complexité de vos analyseurs et de la taille de vos données textuelles.
Présentation
Le diagramme suivant illustre le flux de travail de configuration et d’utilisation des analyseurs multilingues dans Milvus :
Flux de travail des analyseurs multilingues
Configurer les analyseurs multilingues:
Configurez plusieurs analyseurs spécifiques à chaque langue en utilisant le format :
<analyzer_name>: <analyzer_config>, où chaqueanalyzer_configrespecte la configuration standard d’analyzer_params, telle que décrite dans la section Présentation des analyseurs.Définissez un champ d’identifiant spécifique qui déterminera la sélection de l’analyseur pour chaque document.
Configurez un analyseur «
default» pour la gestion des langues inconnues.
Créer une collection:
Définissez un schéma avec les champs essentiels :
primary_key: identifiant unique du document.
text_field: stocke le contenu textuel d'origine.
identifier_field: indique l'analyseur à utiliser pour chaque document.
vector_field: stocke les représentations clairsemées générées par la fonction BM25.
Configurez la fonction BM25 et les paramètres d'indexation.
Insérer des données avec des identifiants de langue:
Ajoutez des documents contenant du texte dans différentes langues, chaque document incluant une valeur d'identifiant précisant quel analyseur utiliser.
Milvus sélectionne l’analyseur approprié en fonction du champ d’identifiant ; les documents dont l’identifiant est inconnu utilisent l’analyseur «
default».
Recherche avec des analyseurs spécifiques à chaque langue:
Fournissez un texte de requête en précisant le nom d'un analyseur, et Milvus traite la requête à l'aide de l'analyseur spécifié.
La tokenisation s'effectue selon des règles spécifiques à la langue, et la recherche renvoie des résultats adaptés à la langue en fonction de la similarité.
Étape 1 : Configurer multi_analyzer_params
Le paramètre ` multi_analyzer_params ` est un objet JSON unique qui détermine la manière dont Milvus sélectionne l’analyseur approprié pour chaque entité :
multi_analyzer_params = {
# Define language-specific analyzers
# Each analyzer follows this format: <analyzer_name>: <analyzer_params>
"analyzers": {
"english": {"type": "english"}, # English-optimized analyzer
"chinese": {"type": "chinese"}, # Chinese-optimized analyzer
"arabic": {"type": "arabic"}, # Arabic-optimized analyzer
"thai": {"type": "thai"}, # Thai-optimized analyzer
"default": {"tokenizer": "icu"} # Required fallback analyzer
},
"by_field": "language", # Field determining analyzer selection
"alias": {
"ar": "arabic", # Use "ar" as shorthand for Arabic
"cn": "chinese", # Use "cn" as shorthand for Chinese
"en": "english", # Use "en" as shorthand for English
"th": "thai" # Use "th" as shorthand for Thai
}
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("analyzers", new HashMap<String, Object>() {{
put("english", new HashMap<String, Object>() {{
put("type", "english");
}});
put("chinese", new HashMap<String, Object>() {{
put("type", "chinese");
}});
put("arabic", new HashMap<String, Object>() {{
put("type", "arabic");
}});
put("thai", new HashMap<String, Object>() {{
put("type", "thai");
}});
put("default", new HashMap<String, Object>() {{
put("tokenizer", "icu");
}});
}});
analyzerParams.put("by_field", "language");
analyzerParams.put("alias", new HashMap<String, Object>() {{
put("ar", "arabic");
put("cn", "chinese");
put("en", "english");
put("th", "thai");
}});
const multi_analyzer_params = {
// Define language-specific analyzers
// Each analyzer follows this format: <analyzer_name>: <analyzer_params>
"analyzers": {
"english": {"type": "english"}, # English-optimized analyzer
"chinese": {"type": "chinese"}, # Chinese-optimized analyzer
"arabic": {"type": "arabic"}, # Arabic-optimized analyzer
"thai": {"type": "thai"}, # Thai-optimized analyzer
"default": {"tokenizer": "icu"} # Required fallback analyzer
},
"by_field": "language", # Field determining analyzer selection
"alias": {
"ar": "arabic", # Use "ar" as shorthand for Arabic
"cn": "chinese", # Use "cn" as shorthand for Chinese
"en": "english", # Use "en" as shorthand for English
"th": "thai" # Use "th" as shorthand for Thai
}
}
multiAnalyzerParams := map[string]any{
"analyzers": map[string]any{
"english": map[string]string{"type": "english"},
"chinese": map[string]string{"type": "chinese"},
"arabic": map[string]string{"type": "arabic"},
"thai": map[string]string{"type": "thai"},
"default": map[string]string{"tokenizer": "icu"},
},
"by_field": "language",
"alias": map[string]string{
"ar": "arabic",
"cn": "chinese",
"en": "english",
"th": "thai",
},
}
# restful
export multi_analyzer_params='{
"analyzers": {
"english": {
"type": "english"
},
"chinese": {
"type": "chinese"
},
"arabic": {
"type": "arabic"
},
"thai": {
"type": "thai"
},
"default": {
"tokenizer": "icu"
}
},
"by_field": "language",
"alias": {
"ar": "arabic",
"cn": "chinese",
"en": "english",
"th": "thai"
}
}'
Paramètre |
Obligatoire ? |
Description |
Règles |
|---|---|---|---|
|
Oui |
Répertorie tous les analyseurs spécifiques à chaque langue que Milvus peut utiliser pour traiter du texte.
Chaque analyseur dans |
|
|
Oui |
Nom du champ qui stocke, pour chaque document, la langue (c'est-à-dire le nom de l'analyseur) que Milvus doit appliquer. |
|
|
Non |
Crée des raccourcis ou des noms alternatifs pour vos analyseurs, ce qui facilite leur référence dans votre code. Chaque analyseur peut avoir un ou plusieurs alias. |
Chaque alias doit correspondre à une clé d’analyseur existante. |
Étape 2 : Créer une collection
La création d’une collection prenant en charge plusieurs langues nécessite la configuration de champs et d’index spécifiques :
Ajouter des champs
Au cours de cette étape, définissez le schéma de la collection à l'aide de quatre champs essentiels :
Champ de clé primaire (
id) : identifiant unique pour chaque entité de la collection. En activant l’auto_id=True, Milvus génère automatiquement ces identifiants.Champ d’indicateur de langue (
language) : ce champ VARCHAR correspond à l’by_fieldspécifié dans votre fichiermulti_analyzer_params. Il stocke l’identifiant de langue de chaque entité, qui indique à Milvus quel analyseur utiliser.Champ de contenu textuel (
text) : ce champ de type VARCHAR stocke les données textuelles proprement dites que vous souhaitez analyser et dans lesquelles vous souhaitez effectuer des recherches. La configuration de l’enable_analyzer=Trueest essentielle, car elle active les capacités d’analyse textuelle pour ce champ. La configuration de l’multi_analyzer_paramsest directement associée à ce champ, établissant ainsi le lien entre vos données textuelles et les analyseurs spécifiques à chaque langue.Champ « Vector » (
sparse) : ce champ stockera les vecteurs clairsemés générés par la fonction BM25. Ces vecteurs représentent la forme analysable de vos données textuelles et constituent ce que Milvus recherche réellement.
# Import required modules
from pymilvus import MilvusClient, DataType, Function, FunctionType
# Initialize client
client = MilvusClient(
uri="http://localhost:19530",
)
# Initialize a new schema
schema = client.create_schema()
# Step 2.1: Add a primary key field for unique document identification
schema.add_field(
field_name="id", # Field name
datatype=DataType.INT64, # Integer data type
is_primary=True, # Designate as primary key
auto_id=True # Auto-generate IDs (recommended)
)
# Step 2.2: Add language identifier field
# This MUST match the "by_field" value in language_analyzer_config
schema.add_field(
field_name="language", # Field name
datatype=DataType.VARCHAR, # String data type
max_length=255 # Maximum length (adjust as needed)
)
# Step 2.3: Add text content field with multi-language analysis capability
schema.add_field(
field_name="text", # Field name
datatype=DataType.VARCHAR, # String data type
max_length=8192, # Maximum length (adjust based on expected text size)
enable_analyzer=True, # Enable text analysis
multi_analyzer_params=multi_analyzer_params # Connect with our language analyzers
)
# Step 2.4: Add sparse vector field to store the BM25 output
schema.add_field(
field_name="sparse", # Field name
datatype=DataType.SPARSE_FLOAT_VECTOR # Sparse vector data type
)
import com.google.gson.JsonObject;
import io.milvus.common.clientenum.FunctionType;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.DropCollectionReq;
import io.milvus.v2.service.utility.request.FlushReq;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.EmbeddedText;
import io.milvus.v2.service.vector.response.SearchResp;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
.build();
collectionSchema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("language")
.dataType(DataType.VarChar)
.maxLength(255)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(8192)
.enableAnalyzer(true)
.multiAnalyzerParams(analyzerParams)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("sparse")
.dataType(DataType.SparseFloatVector)
.build());
import { MilvusClient, DataType, FunctionType } from "@zilliz/milvus2-sdk-node";
// Initialize client
const client = new MilvusClient({
address: "http://localhost:19530",
});
// Initialize schema array
const schema = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
auto_id: true,
},
{
name: "language",
data_type: DataType.VarChar,
max_length: 255,
},
{
name: "text",
data_type: DataType.VarChar,
max_length: 8192,
enable_analyzer: true,
analyzer_params: multi_analyzer_params,
},
{
name: "sparse",
data_type: DataType.SparseFloatVector,
},
];
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
APIKey: "root:Milvus",
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
schema := entity.NewSchema()
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
).WithField(entity.NewField().
WithName("language").
WithDataType(entity.FieldTypeVarChar).
WithMaxLength(255),
).WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithMaxLength(8192).
WithEnableAnalyzer(true).
WithMultiAnalyzerParams(multiAnalyzerParams),
).WithField(entity.NewField().
WithName("sparse").
WithDataType(entity.FieldTypeSparseVector),
)
# restful
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"
export idField='{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true,
"autoID": true
}'
export languageField='{
"fieldName": "language",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 255
}
}'
export textField='{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 8192,
"enable_analyzer": true,
"multiAnalyzerParam": '"$multi_analyzer_params"'
},
}'
export sparseField='{
"fieldName": "sparse",
"dataType": "SparseFloatVector"
}'
Définir la fonction BM25
Définissez une fonction BM25 pour générer des représentations sous forme de vecteurs creux à partir de vos données textuelles brutes :
# Create the BM25 function
bm25_function = Function(
name="text_to_vector", # Descriptive function name
function_type=FunctionType.BM25, # Use BM25 algorithm
input_field_names=["text"], # Process text from this field
output_field_names=["sparse"] # Store vectors in this field
)
# Add the function to our schema
schema.add_function(bm25_function)
CreateCollectionReq.Function function = CreateCollectionReq.Function.builder()
.functionType(FunctionType.BM25)
.name("text_to_vector")
.inputFieldNames(Collections.singletonList("text"))
.outputFieldNames(Collections.singletonList("sparse"))
.build();
collectionSchema.addFunction(function);
const functions = [
{
name: "text_bm25_emb",
description: "bm25 function",
type: FunctionType.BM25,
input_field_names: ["text"],
output_field_names: ["sparse"],
params: {},
},
];
function := entity.NewFunction()
schema.WithFunction(function.WithName("text_to_vector").
WithType(entity.FunctionTypeBM25).
WithInputFields("text").
WithOutputFields("sparse"))
# restful
export function='{
"name": "text_to_vector",
"type": "BM25",
"inputFieldNames": ["text"],
"outputFieldNames": ["sparse"]
}'
export schema="{
\"autoID\": true,
\"fields\": [
$idField,
$languageField,
$textField,
$sparseField
],
\"functions\": [
$function
]
}"
Cette fonction applique automatiquement l’analyseur approprié à chaque entrée de texte en fonction de son identifiant de langue. Pour plus d’informations sur la recherche de texte basée sur BM25, consultez la section Recherche en texte intégral.
Configurer les paramètres d’index
Pour permettre une recherche efficace, créez un index sur le champ de vecteurs clairsemés :
# Configure index parameters
index_params = client.prepare_index_params()
# Add index for sparse vector field
index_params.add_index(
field_name="sparse", # Field to index (our vector field)
index_type="AUTOINDEX", # Let Milvus choose optimal index type
metric_type="BM25" # Must be BM25 for this feature
)
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("sparse")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.BM25)
.build());
const index_params = [{
field_name: "sparse",
index_type: "AUTOINDEX",
metric_type: "BM25"
}];
idx := index.NewAutoIndex(index.MetricType(entity.BM25))
indexOption := milvusclient.NewCreateIndexOption("multilingual_documents", "sparse", idx)
# restful
export IndexParams='[
{
"fieldName": "sparse",
"indexType": "AUTOINDEX",
"metricType": "BM25",
"params": {}
}
]'
L'index améliore les performances de recherche en organisant les vecteurs creux pour des calculs de similarité BM25 efficaces.
Créer la collection
Cette dernière étape de création rassemble toutes vos configurations précédentes :
collection_name="multilang_demo"donnez un nom à votre collection pour pouvoir vous y référer ultérieurement.schema=schemaapplique la structure et la fonction de champ que vous avez définies.index_params=index_paramsmet en œuvre la stratégie d’indexation pour des recherches efficaces.
# Create collection
COLLECTION_NAME = "multilingual_documents"
# Check if collection already exists
if client.has_collection(COLLECTION_NAME):
client.drop_collection(COLLECTION_NAME) # Remove it for this example
print(f"Dropped existing collection: {COLLECTION_NAME}")
# Create the collection
client.create_collection(
collection_name=COLLECTION_NAME, # Collection name
schema=schema, # Our multilingual schema
index_params=index_params # Our search index configuration
)
client.dropCollection(DropCollectionReq.builder()
.collectionName("multilingual_documents")
.build());
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("multilingual_documents")
.collectionSchema(collectionSchema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
const COLLECTION_NAME = "multilingual_documents";
// Create the collection
await client.createCollection({
collection_name: COLLECTION_NAME,
schema: schema,
index_params: index_params,
functions: functions
});
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("multilingual_documents", schema).
WithIndexOptions(indexOption))
if err != nil {
fmt.Println(err.Error())
// handle error
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
\"collectionName\": \"multilingual_documents\",
\"schema\": $schema,
\"indexParams\": $IndexParams
}"
À ce stade, Milvus crée une collection vide prenant en charge l'analyseur multilingue, prête à recevoir des données.
Étape 3 : Insérer des données d'exemple
Lorsque vous ajoutez des documents à votre collection multilingue, chacun doit inclure à la fois un contenu textuel et un identifiant de langue :
# Prepare multilingual documents
documents = [
# English documents
{
"text": "Artificial intelligence is transforming technology",
"language": "english", # Using full language name
},
{
"text": "Machine learning models require large datasets",
"language": "en", # Using our defined alias
},
# Chinese documents
{
"text": "人工智能正在改变技术领域",
"language": "chinese", # Using full language name
},
{
"text": "机器学习模型需要大型数据集",
"language": "cn", # Using our defined alias
},
]
# Insert the documents
result = client.insert(COLLECTION_NAME, documents)
# Print results
inserted = result["insert_count"]
print(f"Successfully inserted {inserted} documents")
print("Documents by language: 2 English, 2 Chinese")
# Expected output:
# Successfully inserted 4 documents
# Documents by language: 2 English, 2 Chinese
List<String> texts = Arrays.asList(
"Artificial intelligence is transforming technology",
"Machine learning models require large datasets",
"人工智能正在改变技术领域",
"机器学习模型需要大型数据集"
);
List<String> languages = Arrays.asList(
"english", "en", "chinese", "cn"
);
List<JsonObject> rows = new ArrayList<>();
for (int i = 0; i < texts.size(); i++) {
JsonObject row = new JsonObject();
row.addProperty("text", texts.get(i));
row.addProperty("language", languages.get(i));
rows.add(row);
}
client.insert(InsertReq.builder()
.collectionName("multilingual_documents")
.data(rows)
.build());
// Prepare multilingual documents
const documents = [
// English documents
{
text: "Artificial intelligence is transforming technology",
language: "english",
},
{
text: "Machine learning models require large datasets",
language: "en",
},
// Chinese documents
{
text: "人工智能正在改变技术领域",
language: "chinese",
},
{
text: "机器学习模型需要大型数据集",
language: "cn",
},
];
// Insert the documents
const result = await client.insert({
collection_name: COLLECTION_NAME,
data: documents,
});
// Print results
const inserted = result.insert_count;
console.log(`Successfully inserted ${inserted} documents`);
console.log("Documents by language: 2 English, 2 Chinese");
// Expected output:
// Successfully inserted 4 documents
// Documents by language: 2 English, 2 Chinese
column1 := column.NewColumnVarChar("text",
[]string{
"Artificial intelligence is transforming technology",
"Machine learning models require large datasets",
"人工智能正在改变技术领域",
"机器学习模型需要大型数据集",
})
column2 := column.NewColumnVarChar("language",
[]string{"english", "en", "chinese", "cn"})
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption("multilingual_documents").
WithColumns(column1, column2),
)
if err != nil {
fmt.Println(err.Error())
// handle err
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data '{
"collectionName": "multilingual_documents",
"data": [
{
"text": "Artificial intelligence is transforming technology",
"language": "english"
},
{
"text": "Machine learning models require large datasets",
"language": "en"
},
{
"text": "人工智能正在改变技术领域",
"language": "chinese"
},
{
"text": "机器学习模型需要大型数据集",
"language": "cn"
}
]
}'
Lors de l’insertion, Milvus :
Lit le champ «
language» de chaque documentApplique l’analyseur correspondant au champ «
text»Génère une représentation sous forme de vecteur creux via la fonction BM25
Stocke à la fois le texte d'origine et le vecteur creux généré
Vous n’avez pas besoin de fournir directement le vecteur creux ; la fonction BM25 le génère automatiquement en fonction de votre texte et de l’analyseur spécifié.
Étape 4 : Effectuer des opérations de recherche
Utiliser l’analyseur anglais
Lors d'une recherche avec des analyseurs multilingues, le fichier ` search_params ` contient des paramètres de configuration essentiels :
metric_type="BM25"doit correspondre à la configuration de votre index.analyzer_name="english"spécifie l’analyseur à appliquer au texte de votre requête. Ce paramètre est indépendant des analyseurs utilisés sur les documents stockés.params={"drop_ratio_search": "0"}contrôle le comportement spécifique à BM25 ; ici, il conserve tous les termes dans la recherche. Pour plus d’informations, reportez-vous à la section « Vecteur clairsemé ».
search_params = {
"metric_type": "BM25", # Must match index configuration
"analyzer_name": "english", # Analyzer that matches the query language
"drop_ratio_search": "0", # Keep all terms in search (tweak as needed)
}
# Execute the search
english_results = client.search(
collection_name=COLLECTION_NAME, # Collection to search
data=["artificial intelligence"], # Query text
anns_field="sparse", # Field to search against
search_params=search_params, # Search configuration
limit=3, # Max results to return
output_fields=["text", "language"], # Fields to include in the output
consistency_level="Bounded", # Data‑consistency guarantee
)
# Display English search results
print("\n=== English Search Results ===")
for i, hit in enumerate(english_results[0]):
print(f"{i+1}. [{hit.score:.4f}] {hit.entity.get('text')} "
f"(Language: {hit.entity.get('language')})")
# Expected output:
# === English Search Results ===
# 1. [2.7881] Artificial intelligence is transforming technology (Language: english)
Map<String,Object> searchParams = new HashMap<>();
searchParams.put("metric_type", "BM25");
searchParams.put("analyzer_name", "english");
searchParams.put("drop_ratio_search", 0);
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("multilingual_documents")
.data(Collections.singletonList(new EmbeddedText("artificial intelligence")))
.annsField("sparse")
.topK(3)
.searchParams(searchParams)
.outputFields(Arrays.asList("text", "language"))
.build());
System.out.println("\n=== English Search Results ===");
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
for (SearchResp.SearchResult result : results) {
System.out.printf("Score: %f, %s\n", result.getScore(), result.getEntity().toString());
}
}
// Execute the search
const english_results = await client.search({
collection_name: COLLECTION_NAME,
data: ["artificial intelligence"],
anns_field: "sparse",
params: {
metric_type: "BM25",
analyzer_name: "english",
drop_ratio_search: "0",
},
limit: 3,
output_fields: ["text", "language"],
consistency_level: "Bounded",
});
// Display English search results
console.log("\n=== English Search Results ===");
english_results.results.forEach((hit, i) => {
console.log(
`${i + 1}. [${hit.score.toFixed(4)}] ${hit.entity.text} ` +
`(Language: ${hit.entity.language})`
);
});
annSearchParams := index.NewCustomAnnParam()
annSearchParams.WithExtraParam("metric_type", "BM25")
annSearchParams.WithExtraParam("analyzer_name", "english")
annSearchParams.WithExtraParam("drop_ratio_search", 0)
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"multilingual_documents", // collectionName
3, // limit
[]entity.Vector{entity.Text("artificial intelligence")},
).WithANNSField("sparse").
WithAnnParam(annSearchParams).
WithOutputFields("text", "language"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
for i := 0; i < len(resultSet.Scores); i++ {
text, _ := resultSet.GetColumn("text").GetAsString(i)
lang, _ := resultSet.GetColumn("language").GetAsString(i)
fmt.Println("Score: ", resultSet.Scores[i], "Text: ", text, "Language:", lang)
}
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data '{
"collectionName": "multilingual_documents",
"data": ["artificial intelligence"],
"annsField": "sparse",
"limit": 3,
"searchParams": {
"metric_type": "BM25",
"analyzer_name": "english",
"drop_ratio_search": "0"
},
"outputFields": ["text", "language"],
"consistencyLevel": "Bounded"
}'
Utilisation de l’analyseur chinois
Cet exemple montre comment passer à l’analyseur chinois (en utilisant son alias "cn") pour un texte de requête différent. Tous les autres paramètres restent identiques, mais le texte de la requête est désormais traité selon les règles de tokenisation spécifiques au chinois.
search_params["analyzer_name"] = "cn"
chinese_results = client.search(
collection_name=COLLECTION_NAME, # Collection to search
data=["人工智能"], # Query text
anns_field="sparse", # Field to search against
search_params=search_params, # Search configuration
limit=3, # Max results to return
output_fields=["text", "language"], # Fields to include in the output
consistency_level="Bounded", # Data‑consistency guarantee
)
# Display Chinese search results
print("\n=== Chinese Search Results ===")
for i, hit in enumerate(chinese_results[0]):
print(f"{i+1}. [{hit.score:.4f}] {hit.entity.get('text')} "
f"(Language: {hit.entity.get('language')})")
# Expected output:
# === Chinese Search Results ===
# 1. [3.3814] 人工智能正在改变技术领域 (Language: chinese)
searchParams.put("analyzer_name", "cn");
searchResp = client.search(SearchReq.builder()
.collectionName("multilingual_documents")
.data(Collections.singletonList(new EmbeddedText("人工智能")))
.annsField("sparse")
.topK(3)
.searchParams(searchParams)
.outputFields(Arrays.asList("text", "language"))
.build());
System.out.println("\n=== Chinese Search Results ===");
searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
for (SearchResp.SearchResult result : results) {
System.out.printf("Score: %f, %s\n", result.getScore(), result.getEntity().toString());
}
}
// Execute the search
const cn_results = await client.search({
collection_name: COLLECTION_NAME,
data: ["人工智能"],
anns_field: "sparse",
params: {
metric_type: "BM25",
analyzer_name: "cn",
drop_ratio_search: "0",
},
limit: 3,
output_fields: ["text", "language"],
consistency_level: "Bounded",
});
// Display Chinese search results
console.log("\n=== Chinese Search Results ===");
cn_results.results.forEach((hit, i) => {
console.log(
`${i + 1}. [${hit.score.toFixed(4)}] ${hit.entity.text} ` +
`(Language: ${hit.entity.language})`
);
});
annSearchParams.WithExtraParam("analyzer_name", "cn")
resultSets, err = client.Search(ctx, milvusclient.NewSearchOption(
"multilingual_documents", // collectionName
3, // limit
[]entity.Vector{entity.Text("人工智能")},
).WithANNSField("sparse").
WithAnnParam(annSearchParams).
WithOutputFields("text", "language"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
for i := 0; i < len(resultSet.Scores); i++ {
text, _ := resultSet.GetColumn("text").GetAsString(i)
lang, _ := resultSet.GetColumn("language").GetAsString(i)
fmt.Println("Score: ", resultSet.Scores[i], "Text: ", text, "Language:", lang)
}
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data '{
"collectionName": "multilingual_documents",
"data": ["人工智能"],
"annsField": "sparse",
"limit": 3,
"searchParams": {
"analyzer_name": "cn"
},
"outputFields": ["text", "language"],
"consistencyLevel": "Bounded"
}'