メインコンテンツまでスキップ
バージョン: User Guides (BYOC)

NGRAM

Zilliz CloudのNGRAMインデックスは、VARCHARフィールドまたはJSONフィールド内の特定のJSONパスに対するLIKEクエリを高速化するために構築されています。インデックスを構築する前に、Zilliz Cloudはテキストを長さnの短い重複する部分文字列(n-gramと呼ばれます)に分割します。例えば、n = 3の場合、単語*"Milvus"は3-gram "Mil""ilv""lvu"、および"vus"*に分割されます。これらのn-gramは、各gramが出現するドキュメントIDにマッピングされる逆インデックスに格納されます。クエリ時に、このインデックスによりZilliz Cloudは検索を少数の候補に迅速に絞り込むことができ、結果としてはるかに高速なクエリ実行が可能になります。

次のような高速なプレフィックス、サフィックス、インフィックス、またはワイルドカードによるフィルタリングが必要な場合に使用します。

  • name LIKE "data%"

  • title LIKE "%vector%"

  • path LIKE "%json"

📘備考

フィルター式の構文の詳細については、基本演算子を参照してください。

仕組み

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"は次のように分解されます。

QZqlwniNDhE82ZbzE09cd7uHnWd

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

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

📘備考
  • 範囲[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を生成し、クエリ実行時には正確なケースマッチが必要です。

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

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

BVPlwaN7Lh7UZibGopwcAcYQn1d

📘備考

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

フェーズ2: クエリの高速化

LIKEフィルターが実行されるとき、Zilliz CloudはNGRAMインデックスを使用して以下の手順でクエリを高速化します。

XKwRwOPv6hqzpTb3ue8cbM8WnGe

  1. クエリ語の抽出: LIKE式からワイルドカードを含まない連続した部分文字列が抽出されます(例:"%database%""database"になります)。

  2. クエリ語の分解: クエリ語はその長さ(L)とmin_grammax_gramの設定に基づいてn-gramに分解されます。

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

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

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

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

  3. 各gramの検索と交差: Zilliz Cloudは逆インデックス内のクエリgramそれぞれを検索し、結果のドキュメントIDリストを交差させて少数の候補ドキュメントセットを見つけます。これらの候補はクエリのすべてのgramを含んでいます。

  4. 照合と結果の返却: 元のLIKEフィルターが最終確認として少数の候補セットのみに適用され、正確な一致を見つけます。

NGRAMインデックスの作成

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

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

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

from pymilvus import MilvusClient

client = MilvusClient(uri="YOUR_CLUSTER_ENDPOINT") # サーバーアドレスに置き換えてください

# コレクションスキーマで"テキスト"という名前のVARCHARフィールドを定義したと仮定します

# インデックスパラメータを準備
index_params = client.prepare_index_params()

# "テキスト"フィールドにNGRAMインデックスを追加
index_params.add_index(
field_name="text", # 対象VARCHARフィールド
index_type="NGRAM", # インデックスタイプはNGRAM
index_name="ngram_index", # インデックスのカスタム名
min_gram=2, # 最小部分文字列長 (例:2-gram: "st")
max_gram=3 # 最大部分文字列長 (例:3-gram: "sta")
)

# コレクションにインデックスを作成
client.create_index(
collection_name="Documents",
index_params=index_params
)

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

例2: JSONパスに作成

JSONフィールドの場合、gram設定に加えて、以下も指定する必要があります。

  • params.json_path – インデックス化したい値を指すJSONパス。

  • params.json_cast_type – 必ず"varchar"(大文字小文字を区別しない)です。NGRAMインデックスは文字列で動作するため。

# コレクションスキーマで"json_field"という名前のJSONフィールドを定義し、"body"という名前のJSONパスがあると仮定します

# インデックスパラメータを準備
index_params = client.prepare_index_params()

# JSONフィールドにNGRAMインデックスを追加
index_params.add_index(
field_name="json_field", # 対象JSONフィールド
index_type="NGRAM", # インデックスタイプはNGRAM
index_name="json_ngram_index", # カスタムインデックス名
min_gram=2, # 最小n-gram長
max_gram=4, # 最大n-gram長
params={
"json_path": "json_field[\"body\"]", # JSONフィールド内の値へのパス
"json_cast_type": "varchar" # 必須:値をvarcharにキャスト
}
)

# コレクションにインデックスを作成
client.create_index(
collection_name="Documents",
index_params=index_params
)

この例では:

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

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

  • Zilliz Cloudは長さ2〜4の部分文字列を生成し、逆インデックスに格納します。

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

NGRAMによって高速化されるクエリ

NGRAMインデックスを適用するには:

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

  • LIKEパターンのリテラル部分は少なくともmin_gram文字以上である必要があります。 (たとえば、最も短い予想されるクエリ語が2文字の場合、インデックス作成時にmin_gram=2を設定します。)

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

  • プレフィックスマッチ

    # "database"という部分文字列で始まる任意の文字列にマッチ
    filter = 'text LIKE "database%"'
  • サフィックスマッチ

    # "database"という部分文字列で終わる任意の文字列にマッチ
    filter = 'text LIKE "%database"'
  • インフィックスマッチ

    # どこかに部分文字列"database"を含む任意の文字列にマッチ
    filter = 'text LIKE "%database%"'
  • ワイルドカードマッチ

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

    # "st"が最初に出現し、テキストの後ろの方に"um"が出現する任意の文字列にマッチ
    filter = 'text LIKE "%st%um%"'
  • JSONパスクエリ

    filter = 'json_field["body"] LIKE "%database%"'

フィルター式の構文の詳細については、基本演算子を参照してください。

インデックスの削除

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

client.drop_index(
collection_name="Documents", # コレクション名
index_name="ngram_index" # 削除するインデックス名
)

使用上の注意

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

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

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

  • 不変性: min_grammax_gramは場所で変更できません。調整するにはインデックスを再構築します。

ベストプラクティス

  • 検索動作に合わせたmin_gramとmax_gramの選択

    • min_gram=2max_gram=3から始めてください。

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

    • max_gramは、意味のある部分文字列の典型的な長さに近づけてください。より大きなmax_gramはフィルタリングを改善しますが、スペースが増加します。

  • 低選択性gramの回避

    高く繰り返されるパターン(例:"aaaaaa")は弱いフィルタリングを提供し、限られた利点しか得られない可能性があります。

  • 一貫した正規化

    使用例に必要であれば、取り込まれたテキストとクエリリテラルに同じ正規化を適用してください(例:小文字化、トリミング)。