メインコンテンツまでスキップ

NGRAM

Zilliz Cloud の NGRAM インデックスは、VARCHAR フィールドまたは JSON フィールド内の特定の JSON パスに対する LIKE クエリと対象となる正規表現フィルターを高速化します。インデックスを構築する前に、Zilliz Cloud はテキストを固定長 n の短く重複する部分文字列(n-gram)に分割します。たとえば、n = 3 の場合、単語 "Milvus" は 3-gram の "Mil"、"ilv"、"lvu"、"vus" に分割されます。これらの n-gram は、各グラムをそれが出現するドキュメント ID にマッピングする転置インデックスに格納されます。クエリ時には、このインデックスにより、Zilliz Cloud は元のフィルター条件を検証する前に検索対象を少数の候補にすばやく絞り込むことができます。

高速な前方一致、後方一致、中間一致、ワイルドカード、または対象となる正規表現フィルタリングが必要な場合に使用します。たとえば、次のとおりです。

  • name LIKE "data%"

  • title LIKE "%vector%"

  • path LIKE "%json"

  • message =~ "error.*timeout"

  • url =~ "/api/v[0-9]+/users"

📘Notes

LIKE および正規表現フィルター式の構文の詳細については、Pattern Matching を参照してください。

仕組み​

NGRAM の仕組みを表示するには展開してください

Zilliz Cloud は NGRAM インデックスを 2 段階のプロセスで実装します:

  1. インデックスを構築する: 取り込み中に各ドキュメントの n-gram を生成し、転置インデックスを構築します。

  2. クエリを高速化する: インデックスを使用して少数の候補セットに絞り込み、完全一致を検証します。

フェーズ 1: インデックスを構築する​

データの取り込み中、Zilliz Cloud は次の 2 つの主要なステップを実行して NGRAM インデックスを構築します:

  1. テキストを n-gram に分解する: Zilliz Cloud は、対象フィールドの各文字列にわたって幅 n のウィンドウをスライドさせ、重複する部分文字列(n-gram)を抽出します。これらの部分文字列の長さは、設定可能な範囲 [min_gram, max_gram] 内に収まります。

    • min_gram: 生成する最短の n-gram です。これは、インデックスを活用できるクエリ部分文字列の最小長も定義します。

    • max_gram: 生成する最長の n-gram です。クエリ時には、長いクエリ文字列を分割する際の最大ウィンドウサイズとしても使用されます。

    たとえば、min_gram=2 および max_gram=3 の場合、文字列 "AI database" は次のように分割されます:

    NGRAM インデックスの構築

    • 2-gram: AI, I_, _d, da, at, ...

    • 3-gram: AI_, I_d, _da, dat, ata, ...

    📘Notes
    • 範囲 [min_gram, max_gram] に対して、Zilliz Cloud は 2 つの値の間(両端を含む)のすべての長さの n-gram を生成します。たとえば、[2,4] と単語 "text" の場合、Zilliz Cloud は次を生成します:

    • 2-gram: te, ex, xt

    • 3-gram: tex, ext

    • 4-gram: text

    • N-gram の分解は文字ベースで言語に依存しません。たとえば、中国語では、min_gram = 2 を指定した "向量数据库" は "向量"、"量数"、"数据"、"据库" に分解されます。

    • 分解時、スペースと句読点は文字として扱われます。

    • 分解では元の大文字と小文字が保持され、マッチングは大文字と小文字を区別します。たとえば、"Database" と "database" は異なる n-gram を生成し、クエリ時には大文字と小文字の完全一致が必要です。

  2. 転置インデックスを構築する: 生成された各 n-gram を、それを含むドキュメント ID のリストにマッピングする転置インデックスが作成されます。

    たとえば、2-gram の "AI" が ID 1、5、6、8、9 のドキュメントに出現する場合、インデックスには {"AI": [1, 5, 6, 8, 9]} が記録されます。このインデックスはクエリ時に検索範囲をすばやく絞り込むために使用されます。

    NGRAM インデックスの構築 2

    📘Notes

    範囲 [min_gram, max_gram] を広げると、生成されるグラムが増え、マッピングリストが大きくなります。メモリが厳しい場合は、非常に大きなポスティングリストに対して mmap モードの使用を検討してください。詳細については、Use mmap を参照してください。

フェーズ 2: クエリを高速化する​

LIKE フィルターまたは対象となる正規表現フィルターが実行されると、Zilliz Cloud は次の手順で NGRAM インデックスを使用してクエリを高速化します:

クエリの高速化

  1. クエリ語を抽出する: LIKE 式からワイルドカードを含まない連続した部分文字列が抽出されます(たとえば、"%database%" は "database" になります)。正規表現フィルターの場合、Zilliz Cloud は可能なときに正規表現パターンから固定のリテラル部分文字列を抽出します。たとえば、message =~ "error.*timeout" にはリテラル error と timeout が含まれています。

  2. クエリ語を分解する: クエリ語は、その長さ(L)と min_gram および max_gram の設定に基づいて n-gram に分解されます。

    • L < min_gram の場合、インデックスは使用できず、クエリはフルスキャンにフォールバックします。

    • min_gram ≤ L ≤ max_gram の場合、クエリ語全体が単一の n-gram として扱われ、それ以上の分解は不要です。

    • L > max_gram の場合、クエリ語は、max_gram に等しいウィンドウサイズを使用して、重複するグラムに分割されます。

    たとえば、max_gram が 3 に設定され、クエリ語が長さ 8 の "database" である場合、それは "dat"、"ata"、"tab" などの 3-gram 部分文字列に分解されます。

  3. 各グラムを検索して交差させる: Zilliz Cloud は、クエリの各グラムを転置インデックスで検索し、得られたドキュメント ID リストを交差させて、少数の候補ドキュメントを見つけます。これらの候補には、クエリのすべてのグラムが含まれています。

  4. 結果を検証して返す: 最後に、元の LIKE または正規表現フィルターが、完全一致を見つけるための最終チェックとして、少数の候補セットにのみ適用されます。

NGRAM インデックスを作成する​

NGRAM インデックスは、VARCHAR フィールドまたは JSON フィールド内の特定のパスに作成できます。

例 1: VARCHAR フィールドに作成する​

VARCHAR フィールドの場合は、field_name を指定し、min_gram と max_gram を設定するだけです。

python
from pymilvus import MilvusClient

client = MilvusClient(uri="YOUR_CLUSTER_ENDPOINT") # Replace with your server address

# Assume you have defined a VARCHAR field named "text" in your collection schema

# Prepare index parameters
index_params = client.prepare_index_params()

# Add NGRAM index on the "text" field
index_params.add_index(
field_name="text", # Target VARCHAR field
index_type="NGRAM", # Index type is NGRAM
index_name="ngram_index", # Custom name for the index
min_gram=2, # Minimum substring length (e.g., 2-gram: "st")
max_gram=3 # Maximum substring length (e.g., 3-gram: "sta")
)

# Create the index on the collection
client.create_index(
collection_name="Documents",
index_params=index_params
)

この設定では、text 内の各文字列に対して 2-gram と 3-gram が生成され、転置インデックスに格納されます。

例 2: JSON パスに作成する​

JSON フィールドの場合は、グラム設定に加えて、次も指定する必要があります:

  • params.json_path:インデックスを作成する値への JSON パスです。

  • params.json_cast_type:"varchar"(大文字と小文字を区別しない)である必要があります。NGRAM インデックスは文字列に対して動作するためです。

python
# Assume you have defined a JSON field named "json_field" in your collection schema, with a JSON path named "body"

# Prepare index parameters
index_params = client.prepare_index_params()

# Add NGRAM index on a JSON field
index_params.add_index(
field_name="json_field", # Target JSON field
index_type="NGRAM", # Index type is NGRAM
index_name="json_ngram_index", # Custom index name
min_gram=2, # Minimum n-gram length
max_gram=4, # Maximum n-gram length
params={
"json_path": "json_field[\"body\"]", # Path to the value inside the JSON field
"json_cast_type": "varchar" # Required: cast the value to varchar
}
)

# Create the index on the collection
client.create_index(
collection_name="Documents",
index_params=index_params
)

この例では:

  • json_field["body"] の値のみがインデックス化されます。

  • 値は n-gram トークン化の前に VARCHAR にキャストされます。

  • Zilliz Cloud は長さ 2 から 4 の部分文字列を生成し、それらを転置インデックスに格納します。

JSON フィールドのインデックス作成方法の詳細については、JSON Indexing を参照してください。

NGRAM で高速化されるクエリ​

NGRAM インデックスが適用されるには、次の条件を満たす必要があります:

  • クエリは、NGRAM インデックスを持つ VARCHAR フィールド(または JSON パス)を対象とする必要があります。

  • LIKE パターンのリテラル部分は、少なくとも min_gram 文字の長さである必要があります。

    (たとえば、想定される最も短いクエリ語が 2 文字の場合は、インデックス作成時に min_gram=2 を設定します。)

サポートされるクエリタイプ:

  • 前方一致

    python
    # Match any string that starts with the substring "database"
    filter = 'text LIKE "database%"'
  • 後方一致

    python
    # Match any string that ends with the substring "database"
    filter = 'text LIKE "%database"'
  • 中間一致

    python
    # Match any string that contains the substring "database" anywhere
    filter = 'text LIKE "%database%"'
  • ワイルドカード一致

    Zilliz Cloud は、%(0 個以上の文字)と _(正確に 1 文字)の両方をサポートしています。

    python
    # Match any string where "st" appears first, and "um" appears later in the text
    filter = 'text LIKE "%st%um%"'
  • JSON パスクエリ

    python
    filter = 'json_field["body"] LIKE "%database%"'
  • 正規表現フィルター

    python
    # Match log messages that contain "error" followed later by "timeout"
    filter = 'text =~ "error.*timeout"'
  • JSON パスに対する正規表現フィルター

    python
    filter = 'json_field["body"] =~ "error.*timeout"'

フィルター式の構文の詳細については、Pattern Matching を参照してください。

インデックスを削除する​

既存のインデックスをコレクションから削除するには、drop_index() メソッドを使用します。

📘Notes

Milvus v2.6.x と互換性のあるクラスターでは、不要になったスカラーインデックスを直接削除できます。そのために最初にコレクションを解放する必要はありません。

python
client.drop_index(
collection_name="Documents", # Name of the collection
index_name="ngram_index" # Name of the index to drop
)

使用上の注意​

  • フィールドタイプ: VARCHAR フィールドと JSON フィールドでサポートされます。JSON の場合は、params.json_path と params.json_cast_type="varchar" の両方を指定します。

  • 正規表現の高速化: NGRAM は、Zilliz Cloud が正規表現パターンから固定のリテラル部分文字列を抽出できる場合にのみ、正規表現フィルターを高速化します。[a-z]+ などのパターンには固定リテラルが含まれないため、スキャンにフォールバックする可能性があります。

  • 大文字と小文字を区別しない正規表現: (?i) を含む正規表現パターンはサポートされていますが、インデックスは元の大文字と小文字を保持するため、NGRAM 最適化がスキップされる場合があります。

  • 検証ステップ: 正規表現フィルターの場合、NGRAM は候補を生成し、Zilliz Cloud が完全な RE2 正規表現パターンでそれらを検証するため、インデックスの高速化によって一致結果が変わることはありません。

  • Unicode: NGRAM の分解は文字ベースで言語に依存せず、空白と句読点も含みます。

  • 空間と時間のトレードオフ: グラム範囲 [min_gram, max_gram] を広げると、生成されるグラムが増え、インデックスが大きくなります。メモリが厳しい場合は、大きなポスティングリストに mmap モードの使用を検討してください。詳細については、Use mmap を参照してください。

  • 不変性: min_gram と max_gram はその場で変更できません。調整するにはインデックスを再構築してください。

ベストプラクティス​

  • 検索動作に合わせて min_gram と max_gram を選択する

    • min_gram=2、max_gram=3 から始めます。

    • min_gram は、ユーザーが入力すると想定される最短のリテラルに設定します。

    • max_gram は、意味のある部分文字列の一般的な長さに近い値に設定します。max_gram を大きくするとフィルタリングは改善しますが、使用する容量が増えます。

  • 選択性の低いグラムを避ける

    非常に反復性の高いパターン(例:"aaaaaa")はフィルタリング効果が弱く、得られる改善は限定的な場合があります。

  • 一貫して正規化する

    ユースケースで必要な場合は、取り込むテキストとクエリのリテラルに同じ正規化(例:小文字化、トリミング)を適用します。