Visión general del analizador
En el tratamiento de textos, un analizador es un componente crucial que convierte el texto en bruto en un formato estructurado que permite realizar búsquedas. Cada analizador suele constar de dos elementos básicos: un tokenizador y un filtro. Juntos, transforman el texto de entrada en tokens, refinan estos tokens y los preparan para una indexación y recuperación eficientes.
En Milvus, los analizadores se configuran durante la creación de la colección cuando se añaden los campos VARCHAR al esquema de la colección. Los tokens producidos por un analizador pueden utilizarse para construir un índice para la concordancia de palabras clave o convertirse en incrustaciones dispersas para la búsqueda de texto completo. Para obtener más información, consulte Coincidencia de texto o Búsqueda de texto completo.
El uso de analizadores puede afectar al rendimiento:
Búsqueda de texto completo: Para la búsqueda de texto completo, los canales DataNode y QueryNode consumen datos más lentamente porque deben esperar a que se complete la tokenización. Como resultado, los datos recién ingestados tardan más en estar disponibles para la búsqueda.
Coincidencia de palabras clave: En el caso de la concordancia de palabras clave, la creación de índices también es más lenta, ya que la tokenización debe finalizar antes de que se pueda crear un índice.
Anatomía de un analizador
Un analizador en Milvus consiste exactamente en un tokenizador y cero o más filtros.
Tokenizador: El tokenizador divide el texto de entrada en unidades discretas llamadas tokens. Estos tokens pueden ser palabras o frases, dependiendo del tipo de tokenizador.
Filtros: Se pueden aplicar filtros a los tokens para refinarlos aún más, por ejemplo, poniéndolos en minúsculas o eliminando palabras comunes.
Los tokenizadores sólo admiten el formato UTF-8. En futuras versiones se añadirá compatibilidad con otros formatos.
El siguiente flujo de trabajo muestra cómo procesa el texto un analizador.
Flujo de trabajo del proceso de análisis
Tipos de analizadores
Milvus proporciona dos tipos de analizadores para satisfacer diferentes necesidades de procesamiento de texto:
Analizador incorporado: Se trata de configuraciones predefinidas que cubren tareas comunes de procesamiento de texto con una configuración mínima. Los analizadores integrados son ideales para búsquedas generales, ya que no requieren una configuración compleja.
Analizador personalizado: Para requisitos más avanzados, los analizadores personalizados le permiten definir su propia configuración especificando tanto el tokenizador como cero o más filtros. Este nivel de personalización es especialmente útil para casos de uso especializados en los que se necesita un control preciso sobre el procesamiento del texto.
- Si omite las configuraciones del analizador durante la creación de la colección, Milvus utiliza por defecto el analizador
standardpara todo el procesamiento de texto. Para más detalles, consulte Analizador estándar. - Para un rendimiento óptimo de la búsqueda y la consulta, elija un analizador que coincida con el idioma de sus datos de texto. Por ejemplo, aunque el analizador
standardes versátil, puede no ser la mejor opción para idiomas con estructuras gramaticales únicas, como el chino, el japonés o el coreano. En estos casos, es preferible utilizar un analizador específico de la lengua comochineseo analizadores personalizados con tokenizadores especializados (comolindera,icu) y filtros para garantizar una tokenización precisa y mejores resultados de búsqueda.
Analizador incorporado
Los analizadores integrados en Milvus están preconfigurados con tokenizadores y filtros específicos, lo que le permite utilizarlos inmediatamente sin necesidad de definir estos componentes usted mismo. Cada analizador incorporado sirve como plantilla que incluye un tokenizador y filtros preestablecidos, con parámetros opcionales para su personalización.
Por ejemplo, para utilizar el analizador integrado standard, basta con especificar su nombre standard como type y, opcionalmente, incluir configuraciones adicionales específicas para este tipo de analizador, como stop_words:
analyzer_params = {
"type": "standard", # Uses the standard built-in analyzer
"stop_words": ["a", "an", "for"] # Defines a list of common words (stop words) to exclude from tokenization
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("type", "standard");
analyzerParams.put("stop_words", Arrays.asList("a", "an", "for"));
const analyzer_params = {
"type": "standard", // Uses the standard built-in analyzer
"stop_words": ["a", "an", "for"] // Defines a list of common words (stop words) to exclude from tokenization
};
analyzerParams := map[string]any{"type": "standard", "stop_words": []string{"a", "an", "for"}}
export analyzerParams='{
"type": "standard",
"stop_words": ["a", "an", "for"]
}'
Para comprobar el resultado de la ejecución de un analizador, utilice el método run_analyzer:
# Sample text to analyze
text = "An efficient system relies on a robust analyzer to correctly process text for various applications."
# Run analyzer
result = client.run_analyzer(
text,
analyzer_params
)
import io.milvus.v2.service.vector.request.RunAnalyzerReq;
import io.milvus.v2.service.vector.response.RunAnalyzerResp;
List<String> texts = new ArrayList<>();
texts.add("An efficient system relies on a robust analyzer to correctly process text for various applications.");
RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder()
.texts(texts)
.analyzerParams(analyzerParams)
.build());
List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();
// javascrip# Sample text to analyze
const text = "An efficient system relies on a robust analyzer to correctly process text for various applications."
// Run analyzer
const result = await client.run_analyzer({
text,
analyzer_params
});
import (
"context"
"encoding/json"
"fmt"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
bs, _ := json.Marshal(analyzerParams)
texts := []string{"An efficient system relies on a robust analyzer to correctly process text for various applications."}
option := milvusclient.NewRunAnalyzerOption(texts).
WithAnalyzerParams(string(bs))
result, err := client.RunAnalyzer(ctx, option)
if err != nil {
fmt.Println(err.Error())
// handle error
}
# restful
La salida será:
['efficient', 'system', 'relies', 'on', 'robust', 'analyzer', 'to', 'correctly', 'process', 'text', 'various', 'applications']
Esto demuestra que el analizador tokeniza correctamente el texto de entrada filtrando las palabras de parada "a", "an", y "for", y devolviendo el resto de tokens significativos.
La configuración del analizador integrado standard anterior es equivalente a la configuración de un analizador personalizado con los siguientes parámetros, donde las opciones tokenizer y filter se definen explícitamente para lograr una funcionalidad similar:
analyzer_params = {
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("tokenizer", "standard");
analyzerParams.put("filter",
Arrays.asList("lowercase",
new HashMap<String, Object>() {{
put("type", "stop");
put("stop_words", Arrays.asList("a", "an", "for"));
}}));
const analyzer_params = {
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
};
analyzerParams = map[string]any{"tokenizer": "standard",
"filter": []any{"lowercase", map[string]any{
"type": "stop",
"stop_words": []string{"a", "an", "for"},
}}}
export analyzerParams='{
"type": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
}'
Milvus ofrece los siguientes analizadores integrados, cada uno diseñado para necesidades específicas de procesamiento de texto:
standard: Adecuado para el procesamiento de texto de uso general, aplicando la tokenización estándar y el filtrado de minúsculas.english: Optimizado para texto en inglés, con soporte para palabras vacías en inglés.chinese: Especializado para el tratamiento de texto chino, con tokenización adaptada a las estructuras del idioma chino.
Analizador personalizado
Para un procesamiento de texto más avanzado, los analizadores personalizados de Milvus le permiten construir una cadena de procesamiento de texto a medida especificando tanto un tokenizador como filtros. Esta configuración es ideal para casos de uso especializado en los que se requiere un control preciso.
Tokenizador
El tokenizador es un componente obligatorio para un analizador personalizado, que inicia el proceso de análisis descomponiendo el texto de entrada en unidades discretas o tokens. La tokenización sigue reglas específicas, como la división por espacios en blanco o signos de puntuación, dependiendo del tipo de tokenizador. Este proceso permite un tratamiento más preciso e independiente de cada palabra o frase.
Por ejemplo, un tokenizador convertiría el texto "Vector Database Built for Scale" en tokens separados:
["Vector", "Database", "Built", "for", "Scale"]
Ejemplo de especificación de un tokenizador:
analyzer_params = {
"tokenizer": "whitespace",
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("tokenizer", "whitespace");
const analyzer_params = {
"tokenizer": "whitespace",
};
analyzerParams = map[string]any{"tokenizer": "whitespace"}
export analyzerParams='{
"type": "whitespace"
}'
Filtro
Los filtros son componentes opcionales que trabajan sobre los tokens producidos por el tokenizador, transformándolos o refinándolos según sea necesario. Por ejemplo, tras aplicar un filtro lowercase a los términos tokenizados ["Vector", "Database", "Built", "for", "Scale"], el resultado podría ser:
["vector", "database", "built", "for", "scale"]
Los filtros de un analizador personalizado pueden ser integrados o personalizados, en función de las necesidades de configuración.
Filtros integrados: Preconfigurados por Milvus, requieren una configuración mínima. Puede utilizar estos filtros directamente especificando sus nombres. Los siguientes filtros están incorporados para su uso directo:
lowercase: Convierte el texto a minúsculas, asegurando una coincidencia insensible a mayúsculas y minúsculas. Para más detalles, consulte Minúsculas.asciifolding: Convierte los caracteres no ASCII en equivalentes ASCII, lo que simplifica el tratamiento de textos multilingües. Para más información, consulte Plegado ASCII.alphanumonly: Conserva sólo los caracteres alfanuméricos eliminando los demás. Para más detalles, consulte Sólo alfanuméricos.cnalphanumonly: Elimina los tokens que contienen caracteres que no sean chinos, letras inglesas o dígitos. Para obtener más información, consulte Cnalphanumonly.cncharonly: Elimina los tokens que contienen caracteres no chinos. Para más información, consulte Cncharonly.
Ejemplo de uso de un filtro incorporado:
analyzer_params = { "tokenizer": "standard", # Mandatory: Specifies tokenizer "filter": ["lowercase"], # Optional: Built-in filter that converts text to lowercase }Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Collections.singletonList("lowercase"));const analyzer_params = { "tokenizer": "standard", // Mandatory: Specifies tokenizer "filter": ["lowercase"], // Optional: Built-in filter that converts text to lowercase }analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{"lowercase"}}export analyzerParams='{ "type": "standard", "filter": ["lowercase"] }'Filtros personalizados: Los filtros personalizados permiten configuraciones especializadas. Puede definir un filtro personalizado eligiendo un tipo de filtro válido (
filter.type) y añadiendo configuraciones específicas para cada tipo de filtro. Ejemplos de tipos de filtro que admiten personalización:stop: Elimina palabras comunes especificadas estableciendo una lista de palabras de detención (por ejemplo,"stop_words": ["of", "to"]). Para obtener más información, consulte Detener.length: Excluye los tokens en función de criterios de longitud, como el establecimiento de una longitud máxima de token. Para obtener más información, consulte Longitud.stemmer: Reduce las palabras a su raíz para una búsqueda más flexible. Para más detalles, consulte Stemmer.
Ejemplo de configuración de un filtro personalizado:
analyzer_params = { "tokenizer": "standard", # Mandatory: Specifies tokenizer "filter": [ { "type": "stop", # Specifies 'stop' as the filter type "stop_words": ["of", "to"], # Customizes stop words for this filter type } ] }Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Collections.singletonList(new HashMap<String, Object>() {{ put("type", "stop"); put("stop_words", Arrays.asList("a", "an", "for")); }}));const analyzer_params = { "tokenizer": "standard", // Mandatory: Specifies tokenizer "filter": [ { "type": "stop", // Specifies 'stop' as the filter type "stop_words": ["of", "to"], // Customizes stop words for this filter type } ] };analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{map[string]any{ "type": "stop", "stop_words": []string{"of", "to"}, }}}export analyzerParams='{ "type": "standard", "filter": [ { "type": "stop", "stop_words": ["a", "an", "for"] } ] }'
Ejemplo de uso
En este ejemplo, creará un esquema de colección que incluye:
Un campo vectorial para incrustaciones.
Dos campos
VARCHARpara el procesamiento de texto:Un campo utiliza un analizador incorporado.
El otro utiliza un analizador personalizado.
Antes de incorporar estas configuraciones a su colección, verificará cada analizador utilizando el método run_analyzer.
Paso 1: Inicializar MilvusClient y crear el esquema
Comience por inicializar el cliente Milvus y crear un nuevo esquema.
from pymilvus import MilvusClient, DataType
# Set up a Milvus client
client = MilvusClient(uri="http://localhost:19530")
# Create a new schema
schema = client.create_schema(auto_id=True, enable_dynamic_field=False)
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;
// Set up a Milvus client
ConnectConfig config = ConnectConfig.builder()
.uri("http://localhost:19530")
.build();
MilvusClientV2 client = new MilvusClientV2(config);
// Create schema
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.enableDynamicField(false)
.build();
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
// Set up a Milvus client
const client = new MilvusClient("http://localhost:19530");
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"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
})
if err != nil {
fmt.Println(err.Error())
// handle err
}
defer client.Close(ctx)
schema := entity.NewSchema().WithAutoID(true).WithDynamicFieldEnabled(false)
# restful
Paso 2: Definir y verificar las configuraciones del analizador
Configure y verifique un analizador incorporado (
english):Configuración: Definir los parámetros del analizador para el analizador incorporado en inglés.
Verificación: Utilice
run_analyzerpara comprobar que la configuración produce la tokenización esperada.
# Built-in analyzer configuration for English text processing analyzer_params_built_in = { "type": "english" } # Verify built-in analyzer configuration sample_text = "Milvus simplifies text analysis for search." result = client.run_analyzer(sample_text, analyzer_params_built_in) print("Built-in analyzer output:", result) # Expected output: # Built-in analyzer output: ['milvus', 'simplifi', 'text', 'analysi', 'search']Map<String, Object> analyzerParamsBuiltin = new HashMap<>(); analyzerParamsBuiltin.put("type", "english"); List<String> texts = new ArrayList<>(); texts.add("Milvus simplifies text ana lysis for search."); RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder() .texts(texts) .analyzerParams(analyzerParams) .build()); List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();// Use a built-in analyzer for VARCHAR field `title_en` const analyzerParamsBuiltIn = { type: "english", }; const sample_text = "Milvus simplifies text analysis for search."; const result = await client.run_analyzer({ text: sample_text, analyzer_params: analyzer_params_built_in });analyzerParams := map[string]any{"type": "english"} bs, _ := json.Marshal(analyzerParams) texts := []string{"Milvus simplifies text analysis for search."} option := milvusclient.NewRunAnalyzerOption(texts). WithAnalyzerParams(string(bs)) result, err := client.RunAnalyzer(ctx, option) if err != nil { fmt.Println(err.Error()) // handle error }# restfulConfigurar y verificar un analizador personalizado:
Configuración: Define un analizador personalizado que utilice un tokenizador estándar junto con un filtro de minúsculas incorporado y filtros personalizados para la longitud de los tokens y las palabras vacías.
Verificación: Utilice
run_analyzerpara asegurarse de que la configuración personalizada procesa el texto según lo previsto.
# Custom analyzer configuration with a standard tokenizer and custom filters analyzer_params_custom = { "tokenizer": "standard", "filter": [ "lowercase", # Built-in filter: convert tokens to lowercase { "type": "length", # Custom filter: restrict token length "max": 40 }, { "type": "stop", # Custom filter: remove specified stop words "stop_words": ["of", "for"] } ] } # Verify custom analyzer configuration sample_text = "Milvus provides flexible, customizable analyzers for robust text processing." result = client.run_analyzer(sample_text, analyzer_params_custom) print("Custom analyzer output:", result) # Expected output: # Custom analyzer output: ['milvus', 'provides', 'flexible', 'customizable', 'analyzers', 'robust', 'text', 'processing']// Configure a custom analyzer Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Arrays.asList("lowercase", new HashMap<String, Object>() {{ put("type", "length"); put("max", 40); }}, new HashMap<String, Object>() {{ put("type", "stop"); put("stop_words", Arrays.asList("of", "for")); }} ) ); List<String> texts = new ArrayList<>(); texts.add("Milvus provides flexible, customizable analyzers for robust text processing."); RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder() .texts(texts) .analyzerParams(analyzerParams) .build()); List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();// Configure a custom analyzer for VARCHAR field `title` const analyzerParamsCustom = { tokenizer: "standard", filter: [ "lowercase", { type: "length", max: 40, }, { type: "stop", stop_words: ["of", "to"], }, ], }; const sample_text = "Milvus provides flexible, customizable analyzers for robust text processing."; const result = await client.run_analyzer({ text: sample_text, analyzer_params: analyzer_params_built_in });analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{"lowercase", map[string]any{ "type": "length", "max": 40, map[string]any{ "type": "stop", "stop_words": []string{"of", "to"}, }}} bs, _ := json.Marshal(analyzerParams) texts := []string{"Milvus provides flexible, customizable analyzers for robust text processing."} option := milvusclient.NewRunAnalyzerOption(texts). WithAnalyzerParams(string(bs)) result, err := client.RunAnalyzer(ctx, option) if err != nil { fmt.Println(err.Error()) // handle error }# curl
Paso 3: Añadir campos al esquema
Ahora que ha verificado las configuraciones de su analizador, añádalas a los campos de su esquema:
# Add VARCHAR field 'title_en' using the built-in analyzer configuration
schema.add_field(
field_name='title_en',
datatype=DataType.VARCHAR,
max_length=1000,
enable_analyzer=True,
analyzer_params=analyzer_params_built_in,
enable_match=True,
)
# Add VARCHAR field 'title' using the custom analyzer configuration
schema.add_field(
field_name='title',
datatype=DataType.VARCHAR,
max_length=1000,
enable_analyzer=True,
analyzer_params=analyzer_params_custom,
enable_match=True,
)
# Add a vector field for embeddings
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)
# Add a primary key field
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.addField(AddFieldReq.builder()
.fieldName("title")
.dataType(DataType.VarChar)
.maxLength(1000)
.enableAnalyzer(true)
.analyzerParams(analyzerParams)
.enableMatch(true) // must enable this if you use TextMatch
.build());
// Add vector field
schema.addField(AddFieldReq.builder()
.fieldName("embedding")
.dataType(DataType.FloatVector)
.dimension(3)
.build());
// Add primary field
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
// Create schema
const schema = {
auto_id: true,
fields: [
{
name: "id",
type: DataType.INT64,
is_primary: true,
},
{
name: "title_en",
data_type: DataType.VARCHAR,
max_length: 1000,
enable_analyzer: true,
analyzer_params: analyzerParamsBuiltIn,
enable_match: true,
},
{
name: "title",
data_type: DataType.VARCHAR,
max_length: 1000,
enable_analyzer: true,
analyzer_params: analyzerParamsCustom,
enable_match: true,
},
{
name: "embedding",
data_type: DataType.FLOAT_VECTOR,
dim: 4,
},
],
};
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
).WithField(entity.NewField().
WithName("embedding").
WithDataType(entity.FieldTypeFloatVector).
WithDim(3),
).WithField(entity.NewField().
WithName("title").
WithDataType(entity.FieldTypeVarChar).
WithMaxLength(1000).
WithEnableAnalyzer(true).
WithAnalyzerParams(analyzerParams).
WithEnableMatch(true),
)
# restful
Paso 4: Preparar los parámetros del índice y crear la colección
# Set up index parameters for the vector field
index_params = client.prepare_index_params()
index_params.add_index(field_name="embedding", metric_type="COSINE", index_type="AUTOINDEX")
# Create the collection with the defined schema and index parameters
client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)
// Set up index params for vector field
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("embedding")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build());
// Create collection with defined schema
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
// Set up index params for vector field
const indexParams = [
{
name: "embedding",
metric_type: "COSINE",
index_type: "AUTOINDEX",
},
];
// Create collection with defined schema
await client.createCollection({
collection_name: "my_collection",
schema: schema,
index_params: indexParams,
});
console.log("Collection created successfully!");
idx := index.NewAutoIndex(index.MetricType(entity.COSINE))
indexOption := milvusclient.NewCreateIndexOption("my_collection", "embedding", idx)
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithIndexOptions(indexOption))
if err != nil {
fmt.Println(err.Error())
// handle error
}
# restful