分析器概述

在文字處理中,分析器是將原始文字轉換成結構化、可搜尋格式的重要元件。每個分析器通常包含兩個核心元件:標記器過濾 。它們共同將輸入文字轉換為標記,精煉這些標記,並為有效的索引和檢索做好準備。

在 Milvus 中,當您將VARCHAR 欄位新增至集合模式時,分析器會在集合建立時設定。分析器產生的標記可用於建立關鍵字比對索引,或轉換成稀疏嵌入(sparse embeddings)用於全文檢索。如需詳細資訊,請參閱全文本搜尋片語比對文字比對

使用分析器可能會影響效能:

  • 全文檢索:對於全文本搜尋,DataNodeQueryNode通道消耗資料的速度較慢,因為它們必須等待標記化完成。因此,新擷取的資料需要較長時間才能可供搜尋。

  • 關鍵字匹配:對於關鍵字匹配,建立索引的速度也較慢,因為在建立索引之前,必須先完成標記化。

分析器剖析

Milvus 的分析器由一個標記化器零個或多個過濾器組成。

  • 標記器:標記器將輸入文字分割成稱為標記的獨立單位。這些標記可以是單字或短語,取決於標記器類型。

  • 篩選器:篩選器可應用於標記,以進一步精細它們,例如,使它們小寫或移除常用字。

標記器只支援 UTF-8 格式。其他格式的支援將在未來的版本中加入。

以下工作流程顯示分析器如何處理文字。

Analyzer Process Workflow 分析器處理工作流程

分析器類型

Milvus 提供兩種類型的分析器,以滿足不同的文字處理需求:

  • 內建分析器:這些是預先定義的配置,只需最少的設定即可涵蓋常見的文字處理工作。內建分析器不需要複雜的設定,是一般用途搜尋的理想選擇。

  • 自訂分析器:對於更進階的需求,自訂分析器可讓您透過指定標記器和零個或多個過濾器來定義您自己的組態。這種層級的自訂對於需要精確控制文字處理的專門用例特別有用。

  • 如果您在建立集合時省略了分析器設定,Milvus 預設會使用standard 分析器來處理所有文字。如需詳細資訊,請參閱標準分析器
  • 為了獲得最佳的搜尋與查詢效能,請選擇符合您文字資料語言的分析器。例如,雖然standard 分析器用途廣泛,但對於具有獨特語法結構的語言 (例如中文、日文或韓文) 而言,它可能不是最佳選擇。在這種情況下,使用特定語言的分析器,例如 chinese或具有專門標記器的自訂分析器 (例如 lindera, icu) 和過濾器,以確保正確的標記化和更好的搜尋結果。

內建分析器

Milvus 的內建分析器預先設定了特定的標記化器和過濾器,讓您可以立即使用,而不需要自己定義這些元件。每個內建分析器都是一個範本,包含預設的標記器和篩選器,以及可選的自訂參數。

例如,若要使用standard 內建分析器,只需指定其名稱standardtype ,並可選擇包含此分析器類型特有的額外配置,例如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"]
    }'

若要檢查分析器的執行結果,請使用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

輸出結果將會是

['efficient', 'system', 'relies', 'on', 'robust', 'analyzer', 'to', 'correctly', 'process', 'text', 'various', 'applications']

這顯示分析器正確地對輸入文字進行標記化,過濾掉停止詞"a","an", 和"for", 並回傳其餘有意義的標記。

上述standard 內建分析器的配置等同於使用下列參數設定自訂分析器,其中tokenizerfilter 選項是明確定義,以達到類似功能:

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 提供下列內建分析器,每個分析器都是針對特定的文字處理需求而設計:

  • standard:適用於一般用途的文字處理,應用標準的標記化和小寫篩選。

  • english:針對英文文字最佳化,支援英文停止詞。

  • chinese:專門處理中文文字,包括針對中文語言結構的標記化。

自訂分析器

對於更進階的文字處理,Milvus 的自訂分析器可讓您透過指定標記器過濾器,建立量身打造的文字處理管道。此設定非常適合需要精確控制的特殊使用個案。

標記器

標記器是自訂分析器的必備元件,可將輸入文字分解為離散的單位或標記,從而啟動分析器管道。記號化遵循特定規則,例如依據記號化類型以空白或標點分割。此過程能更精確、獨立地處理每個字或詞組。

例如,令牌化器會將文字"Vector Database Built for Scale" 轉換成獨立的令牌:

["Vector", "Database", "Built", "for", "Scale"]

指定 tokenizer 的範例

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

過濾器

過濾器可選的元件,用來處理 tokenizer 產生的 token,並視需要轉換或精煉它們。例如,在將一個lowercase 過濾器套用到標記化的詞彙["Vector", "Database", "Built", "for", "Scale"] 之後,結果可能是:

["vector", "database", "built", "for", "scale"]

自訂分析器中的篩選器可以是內建自訂的,視配置需求而定。

  • 內建過濾器:由 Milvus 預先設定,只需最少的設定。您只要指定這些篩選器的名稱,就能立即使用這些篩選器。以下篩選器為內建篩選器,可直接使用:

    • lowercase:將文字轉換為小寫,確保大小寫不敏感的匹配。如需詳細資訊,請參閱小寫

    • asciifolding:將非 ASCII 字元轉換為 ASCII 對應字元,簡化多語言文字處理。詳情請參閱ASCII 折疊

    • alphanumonly:只保留字母數字字符,移除其他字符。詳情請參閱Alphanumonly

    • cnalphanumonly:移除包含任何非中文字元、英文字母或數位字元的標記。詳情請參閱Cnalphanumonly

    • cncharonly:移除包含任何非中文字元的標記。詳情請參閱Cncharonly

    使用內建過濾器的範例:

    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"]
        }'
    
  • 自訂篩選器:自訂篩選器允許專門的配置。您可以透過選擇有效的篩選器類型 (filter.type) 來定義自訂篩選器,並為每個篩選器類型加入特定設定。支援自訂的篩選器類型範例:

    • stop:透過設定停止詞清單 (例如"stop_words": ["of", "to"]),移除指定的常用字。詳情請參閱停止

    • length:根據長度標準排除標記,例如設定最大標記長度。詳情請參閱Length

    • stemmer:將字詞縮減為字根形式,以便進行更靈活的匹配。如需詳細資訊,請參閱Stemmer

    設定自訂篩選器的範例:

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

使用範例

在本範例中,您將建立一個集合模式,其中包括

  • 一個向量欄位用於嵌入。

  • 兩個VARCHAR 欄位用於文字處理:

    • 一個欄位使用內建分析器。

    • 另一個使用自訂分析器。

在將這些配置納入您的集合之前,您會使用run_analyzer 方法驗證每個分析器。

步驟 1:初始化 MilvusClient 並建立模式

首先設定 Milvus 用戶端並建立新模式。

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

步驟 2:定義並驗證分析器配置

  1. 設定並驗證內建分析器(english)

    • 配置:定義內建英文分析器的分析器參數。

    • 驗證:使用run_analyzer 檢查配置是否產生預期的標記化。

    # 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
    }
    
    
    # restful
    
  2. 設定並驗證自訂分析器:

    • 配置:定義一個自訂分析器,使用標準的 tokenizer 以及內建的小寫篩選器和自訂的 token 長度與停止字篩選器。

    • 驗證:使用run_analyzer 確保自訂組態按照預期處理文字。

    # 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
    

步驟 3:新增欄位至模式

現在您已經驗證您的分析器配置,請將它們新增至模式欄位:

# 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

步驟 4:準備索引參數並建立集合

# 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

下一步

配置分析器之後,您可以整合 Milvus 提供的文字檢索功能。如需詳細資訊:

免費嘗試托管的 Milvus

Zilliz Cloud 無縫接入,由 Milvus 提供動力,速度提升 10 倍。

開始使用
反饋

這個頁面有幫助嗎?