Analizzatori multilingueCompatible with Milvus 2.5.11+

Quando Milvus esegue l’analisi del testo, in genere applica un unico analizzatore all’intero campo di testo di una raccolta. Se tale analizzatore è ottimizzato per l’inglese, incontra difficoltà con le regole di tokenizzazione e di stemming molto diverse richieste da altre lingue, come il cinese, lo spagnolo o il francese, con conseguente riduzione del tasso di richiamo. Ad esempio, una ricerca della parola spagnola “teléfono” (che significa “telefono”) metterebbe in difficoltà un analizzatore incentrato sull’inglese: potrebbe omettere l’accento e non applicare alcuna derivazione specifica per lo spagnolo, causando la mancata individuazione di risultati rilevanti.

Gli analizzatori multilingue risolvono questo problema consentendo di configurare più analizzatori per un campo di testo in una singola raccolta. In questo modo, è possibile archiviare documenti multilingue in un campo di testo e Milvus analizza il testo secondo le regole linguistiche appropriate per ciascun documento.

Limiti

  • Questa funzionalità funziona solo con il recupero testuale basato su BM25 e con i vettori sparsi. Per ulteriori informazioni, consultare Ricerca full-text.

  • Ogni documento in una singola raccolta può utilizzare un solo analizzatore, determinato dal valore del campo identificatore della lingua.

  • Le prestazioni possono variare a seconda della complessità degli analizzatori e delle dimensioni dei dati di testo.

Panoramica

Il diagramma seguente illustra il flusso di lavoro relativo alla configurazione e all’utilizzo degli analizzatori multilingue in Milvus:

Multi Language Analyzers Workflow Flusso di lavoro degli analizzatori multilingue

  1. Configurazione degli analizzatori multilingue:

    • Configurare più analizzatori specifici per lingua utilizzando il formato: <analyzer_name>: <analyzer_config>, dove ogni analyzer_config segue la configurazione standard analyzer_params come descritto nella Panoramica degli analizzatori.

    • Definire un campo identificativo speciale che determinerà la selezione dell’analizzatore per ciascun documento.

    • Configurare un analizzatore default per la gestione delle lingue sconosciute.

  2. Creare una raccolta:

    • Definire lo schema con i campi essenziali:

      • primary_key: identificatore univoco del documento.

      • text_field: memorizza il contenuto testuale originale.

      • identifier_field: indica quale analizzatore utilizzare per ciascun documento.

      • vector_field: memorizza gli embedding sparsi che saranno generati dalla funzione BM25.

    • Configurare la funzione BM25 e i parametri di indicizzazione.

  3. Inserimento dei dati con identificatori di lingua:

    • Aggiungere documenti contenenti testo in varie lingue, in cui ogni documento includa un valore identificativo che specifichi quale analizzatore utilizzare.

    • Milvus seleziona l’analizzatore appropriato in base al campo identificatore, mentre i documenti con identificatori sconosciuti utilizzano l’analizzatore default.

  4. Ricerca con analizzatori specifici per lingua:

    • Fornire il testo della query specificando il nome di un analizzatore; Milvus elaborerà la query utilizzando l'analizzatore specificato.

    • La tokenizzazione avviene secondo regole specifiche per la lingua e la ricerca restituisce risultati appropriati alla lingua in base alla somiglianza.

Passaggio 1: Configurare multi_analyzer_params

multi_analyzer_params è un singolo oggetto JSON che determina come Milvus seleziona l’analizzatore appropriato per ciascuna 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"
  }
}'

Parametro

Obbligatorio?

Descrizione

Regole

analyzers

Elenca tutti gli analizzatori specifici per lingua che Milvus può utilizzare per elaborare il testo. Ciascun analizzatore in analyzers segue questo formato: <analyzer_name>: <analyzer_params>.

  • Definire ciascun analizzatore utilizzando la sintassi standard di analyzer_params (vedere Panoramica degli analizzatori).
  • Aggiungere una voce la cui chiave sia default; Milvus ricorre a questo analizzatore ogni volta che il valore memorizzato in by_field non corrisponde al nome di nessun altro analizzatore.

by_field

Nome del campo che memorizza, per ogni documento, la lingua (ovvero il nome dell’analizzatore) che Milvus deve applicare.

  • Deve essere un campo VARCHAR definito nella collezione.

  • Il valore in ogni riga deve corrispondere esattamente a uno dei nomi degli analizzatori (o alias) elencati in analyzers.

  • Se il valore di una riga manca o non viene trovato, Milvus applica automaticamente l’analizzatore default.

alias

No

Crea scorciatoie o nomi alternativi per gli analizzatori, rendendone più facile il riferimento nel codice. Ogni analizzatore può avere uno o più alias.

Ogni alias deve essere associato a una chiave di analizzatore esistente.

Passaggio 2: Creazione della raccolta

La creazione di una raccolta con supporto multilingue richiede la configurazione di campi e indici specifici:

Aggiungi campi

In questo passaggio, definire lo schema della collezione con quattro campi essenziali:

  • Campo chiave primaria (id): un identificatore univoco per ogni entità nella collezione. Impostando l'opzione " auto_id=True " (Genera automaticamente), Milvus genererà automaticamente questi ID.

  • Campo indicatore di lingua (language): questo campo VARCHAR corrisponde all’ by_field specificato nel file multi_analyzer_params. Memorizza l’identificatore di lingua per ciascuna entità, che indica a Milvus quale analizzatore utilizzare.

  • Campo Contenuto testuale (text): Questo campo VARCHAR memorizza i dati testuali effettivi che si desidera analizzare e cercare. L’impostazione di enable_analyzer=True è fondamentale poiché attiva le funzionalità di analisi testuale per questo campo. La configurazione di multi_analyzer_params è associata direttamente a questo campo, stabilendo la connessione tra i dati testuali e gli analizzatori specifici per la lingua.

  • Campo vettoriale (sparse): Questo campo memorizzerà i vettori sparsi generati dalla funzione BM25. Questi vettori rappresentano la forma analizzabile dei dati testuali e costituiscono l’oggetto effettivo della ricerca effettuata da Milvus.

# 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"
}'

Definire la funzione BM25

Definire una funzione BM25 per generare rappresentazioni vettoriali sparse dai dati di testo grezzi:

# 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
  ]
}"

Questa funzione applica automaticamente l’analizzatore appropriato a ciascuna voce di testo in base al relativo identificatore di lingua. Per ulteriori informazioni sul recupero di testo basato su BM25, consultare Ricerca full-text.

Configurare i parametri dell’indice

Per consentire una ricerca efficiente, creare un indice sul campo dei vettori sparsi:

# 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'indice migliora le prestazioni di ricerca organizzando i vettori sparsi per un calcolo efficiente della similarità con l'algoritmo BM25.

Creare la raccolta

Questo passaggio finale di creazione riunisce tutte le configurazioni precedenti:

  • collection_name="multilang_demo" assegna un nome alla collezione per riferimento futuro.

  • schema=schema applica la struttura dei campi e le funzioni che hai definito.

  • index_params=index_params implementa la strategia di indicizzazione per ricerche efficienti.

# 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
}"

A questo punto, Milvus crea una collezione vuota con supporto per l'analizzatore multilingue, pronta a ricevere dati.

Passaggio 3: inserisci dati di esempio

Quando si aggiungono documenti alla propria collezione multilingue, ciascuno deve includere sia il contenuto testuale che un identificatore di lingua:

# 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"
    }
  ]
}'

Durante l’inserimento, Milvus:

  1. Legge il campo ` language ` di ciascun documento

  2. Applica l'analizzatore corrispondente al campo ` text `

  3. Genera una rappresentazione vettoriale sparsa tramite la funzione BM25

  4. Memorizza sia il testo originale che il vettore sparso generato

Non è necessario fornire direttamente il vettore sparso; la funzione BM25 lo genera automaticamente in base al testo e all’analizzatore specificato.

Passaggio 4: Eseguire le operazioni di ricerca

Utilizzare l'analizzatore inglese

Quando si effettua una ricerca con analizzatori multilingue, il file ` search_params ` contiene una configurazione fondamentale:

  • metric_type="BM25" deve corrispondere alla configurazione dell’indice.

  • analyzer_name="english" specifica quale analizzatore applicare al testo della query. Questa impostazione è indipendente dagli analizzatori utilizzati sui documenti archiviati.

  • params={"drop_ratio_search": "0"} controlla il comportamento specifico di BM25; in questo caso, mantiene tutti i termini nella ricerca. Per ulteriori informazioni, consultare Sparse Vector.

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"
}'

Utilizzo dell’analizzatore cinese

Questo esempio illustra come passare all’analizzatore cinese (utilizzando il suo alias "cn") per un testo di query diverso. Tutti gli altri parametri rimangono invariati, ma ora il testo della query viene elaborato utilizzando regole di tokenizzazione specifiche per il cinese.

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"
}'