NGRAM
MilvusのNGRAM インデックスは、VARCHAR フィールドまたはJSON フィールド内の特定のJSONパスに対するLIKE クエリを高速化するために構築されています。インデックスを構築する前に、Milvusはテキストをn-gramと呼ばれる固定長nの短く重なり合った部分文字列に分割する。例えば、n = 3の場合、"Milvus "という単語は3つのグラムに分割される:「Mil"、 "ilv"、 "lvu"、 "vus "である。これらのn-gramは、各gramとそれが出現する文書IDを対応付ける転置インデックスに格納される。クエリ実行時に、このインデックスによりmilvusは素早く検索候補を絞り込むことができ、その結果、クエリの実行が非常に速くなります。
などのプレフィックス、サフィックス、インフィックス、ワイルドカードによるフィルタリングを高速に行う必要がある場合に使用します:
name LIKE "data%"title LIKE "%vector%"path LIKE "%json"
フィルタ式の構文の詳細については、「基本演算子」を参照してください。
動作原理
milvusは2段階のプロセスでNGRAM インデックスを実装します:
インデックスを構築する:各文書のn-gramを生成し、インジェスト時に転置インデックスを構築する。
クエリーの高速化:インデックスを使用して小さな候補セットにフィルタリングし、完全一致を検証する。
フェーズ1:インデックスの構築
Milvusはデータ取り込み中に、主に2つのステップを実行することによりNGRAMインデックスを構築する:
テキストをn-gramに分解する:Milvusはターゲットフィールドの各文字列を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-grams:** `AI`, `I_`, `_d`, `da`, `at`, ...
- **3-grams:** `AI_`, `I_d`, `_da`, `dat`, `ata`, ...
<div class="alert note">
- For a range `[min_gram, max_gram]`, Milvus generates all n-grams for every length between the two values (inclusive). For example, with `[2,4]` and the word `"text"`, Milvus generates:
- **2-grams:** `te`, `ex`, `xt`
- **3-grams:** `tex`, `ext`
- **4-grams:** `text`
- N-gram decomposition is character-based and language-agnostic. For example, in Chinese, `"向量数据库"` with `min_gram = 2` is decomposed into: `"向量"`, `"量数"`, `"数据"`, `"据库"`.
- Spaces and punctuation are treated as characters during decomposition.
- Decomposition preserves original case, and matching is case-sensitive. For example, `"Database"` and `"database"` will generate different n-grams and require exact case matching during queries.
</div>
転置インデックスを構築する:生成された各n-gramを、それを含む文書IDのリストに対応付ける転置インデックスが作成される。
例えば、
"AI"という2-gramがID 1, 5, 6, 8, 9の文書に出現する場合、{"AI": [1, 5, 6, 8, 9]}がインデックスに記録される。このインデックスをクエリー時に使用することで、検索範囲を素早く絞り込むことができる。
Ngramインデックス2の構築
<div class="alert note">
A wider `[min_gram, max_gram]` range creates more grams and larger mapping lists. If memory is tight, consider mmap mode for very large posting lists. For details, refer to [Use mmap](https://zilliverse.feishu.cn/wiki/P3wrwSMNNihy8Vkf9p6cTsWYnTb).
</div>
フェーズ2:クエリの高速化
LIKE フィルタが実行されると、MilvusはNGRAMインデックスを使い、以下のステップでクエリを高速化する:
クエリの高速化
クエリー用語を抽出する:ワイルドカードを含まない連続した部分文字列が
LIKE式から抽出される(例えば、"%database%"は"database"になる)。クエリ語の分解:クエリ語の長さ (
L) とmin_gramおよびmax_gramの設定に基づいて、クエリ語がn-gramに分解される。L < min_gramの場合、インデックスは使用できず、クエリはフルスキャンに戻る。min_gram ≤ L ≤ max_gramの場合、クエリ語全体が1つのn-gramとして扱われ、それ以上の分解は必要ない。L > max_gramの場合、max_gramに等しいウィンドウ・サイズを使用して、クエリ用語は重複するグラムに分解される。
たとえば、
max_gramが3に設定され、クエリ語の長さが8 の"database"の場合、"dat"、"ata"、"tab"などの 3-gram の部分文字列に分解される。各グラムの検索と交差:Milvusは転置インデックスで各クエリーグラムを検索し、その結果の文書IDリストを交差させて、候補文書の小さなセットを見つける。これらの候補にはクエリのグラムがすべて含まれている。
検証して結果を返す:オリジナルの
LIKEフィルタが、完全一致を見つけるために、小さな候補集合のみに最終チェックとして適用される。
NGRAMインデックスの作成
VARCHAR フィールドまたはJSON フィールド内の特定のパスに NGRAM インデックスを作成できます。
例1:VARCHARフィールドへの作成
VARCHAR フィールドの場合、field_name を指定し、min_gram とmax_gram を構成するだけです。
from pymilvus import MilvusClient
client = MilvusClient(uri="http://localhost:19530") # 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インデックスは文字列に対して動作するからです。
# 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にキャストされます。milvusは長さ2から4の部分文字列を生成し、それらを転置インデックスに格納します。
JSONフィールドにインデックスを付ける方法の詳細については、JSONインデックスを参照してください。
NGRAMによって高速化されるクエリー
NGRAMインデックスを適用するには
クエリは、
NGRAMインデックスを持つVARCHARフィールド(または JSON パス)をターゲットにする必要があります。LIKEパターンのリテラル部分は、少なくともmin_gram文字の長さでなければなりません(例えば、予想される最も短いクエリー項が 2 文字の場合、インデックス作成時に min_gram=2 を設定します)。
サポートされるクエリー型
接頭辞マッチ
# Match any string that starts with the substring "database" filter = 'text LIKE "database%"'接尾辞マッチ
# Match any string that ends with the substring "database" filter = 'text LIKE "%database"'接尾辞マッチ
# Match any string that contains the substring "database" anywhere filter = 'text LIKE "%database%"'ワイルドカード一致
Milvus は
%(0文字以上) と_(正確に1文字) の両方をサポートしています。# Match any string where "st" appears first, and "um" appears later in the text filter = 'text LIKE "%st%um%"'JSONパスクエリ
filter = 'json_field["body"] LIKE "%database%"'
フィルタ式の構文の詳細については、基本演算子を参照してください。
インデックスの削除
コレクションから既存のインデックスを削除するには、drop_index() メソッドを使用します。
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"の両方を指定してください。Unicode:NGRAM分解は文字ベースであり、言語にとらわれず、空白と句読点を含む。
スペー ス と 時間の ト レー ド オ フ : グ ラ ム範囲
[min_gram, max_gram]を広 く す る と 、 グ ラ ム数が増え、 イ ンデ ッ ク ス も 大 き く な り ます。メモリが限られている場合は、大きな投稿リスト用にmmapモードを検討してください。詳しくはmmapを使うを参照してください。不変性:
min_gramとmax_gramは、その場で変更することはできません。
ベストプラクティス
検索動作に合わせてmin_gramとmax_gramを選択する。
min_gram=2,max_gram=3から始める。min_gram、ユーザーが入力すると思われる最も短いリテラルに設定する。max_gram。意味のある部分文字列の典型的な長さの近くに設定します。max_gramを大きくするとフィルタリングは向上しますが、スペースが増えます。
選択性の低いグラムは避ける
繰り返しの多いパターン(例:
"aaaaaa")はフィルタリング効果が弱く、得られる効果も限定的です。一貫した正規化
取り込まれたテキストとクエリーリテラルに同じ正規化(例えば、小文字化、トリミング)を適用する。