FMINDEX
MilvusのFMINDEX インデックスは、VARCHAR フィールド向けのバイト単位の完全一致部分文字列インデックスです。このインデックスは、接頭辞、中置、または接尾辞の一致を指定したアンカー付きLIKE クエリに応答し、候補の再確認を行うことなく完全一致の結果を返します。
次のような部分文字列フィルタを頻繁に実行する場合は、FMINDEX インデックスを使用してください。
title LIKE "vector%"(接頭辞)title LIKE "%database"(接尾辞)title LIKE "%vector database%"(中置 / 部分文字列)
候補を絞り込んでから再チェックを行う「NGRAM 」とは異なり、「FMINDEX 」は、アンカー付きの前置、後置、および中置パターンをインデックスから直接解決できます。
サポートされているデータ型
VARCHARフィールド。詳細については、「文字列フィールド」を参照してください。
FMINDEX は、VARCHAR フィールドでのみサポートされています。JSON フィールドや、VARCHAR にキャストされたJSON パスはサポートされていません。
仕組み
FMINDEX これはフルテキスト FM インデックスであり、連結されたフィールド値の Burrows-Wheeler 変換 (BWT) から構築された圧縮構造に、サンプリングされたサフィックス配列およびランククエリ用のウェーブレットツリーが組み合わさったものです。 このレイアウトは逆方向検索をサポートしています。パターンが指定されると、Milvus はそのパターンの最後の文字から最初の文字へと順にたどって、テキストにそのパターンが含まれるエンティティの正確な範囲を見つけ出します。
クエリ実行時、Milvus はこのインデックスを使用して以下の処理を行います。
LIKEパターンから抽出されたアンカー付きリテラルに対する一致箇所を特定します。元の値を再確認することなく、接頭辞、接尾辞、および中置リテラルに対する正確なエンティティIDを返す。
複数の非隣接リテラル(一般的なLIKE )や複雑な正規表現を含むパターンの場合、FMINDEX は候補を生成し、Milvusはそれを元の値と照合して検証します。
FMINDEXインデックスの作成
VARCHAR フィールド上にFMINDEX インデックスを作成できます。両方のビルドパラメータはオプションです。設定しない場合は、サーバーのデフォルト値が使用されます。
| パラメータ | 説明 | 許容値 | デフォルト |
|---|---|---|---|
fm_sa_sample_rate | サフィックス配列のサンプリングレート。インデックスのサイズとロケート処理のレイテンシのバランスを調整します。カウントのみのクエリには影響しません。 | 4 から 256 までの整数 | 8 |
fm_block_bytes | ランクディレクトリのブロック単位(バイト単位)。ブロックを大きくすると、常駐ディレクトリのサイズが縮小します。 | 8、16、32、64、または128 | 64 |
以下の例は、VARCHAR フィールドに対してFMINDEX インデックスを作成する方法を示しています:
from pymilvus import MilvusClient
client = MilvusClient(uri="http://localhost:19530")
# Assume you have defined a VARCHAR field named "text" in your collection schema
# Prepare index parameters
index_params = client.prepare_index_params()
# Add FMINDEX index on the "text" field
index_params.add_index(
field_name="text", # Target VARCHAR field
index_type="FMINDEX", # Index type is FMINDEX
index_name="fmindex_index", # Custom name for the index
fm_sa_sample_rate=8, # Optional: suffix-array sampling rate (4-256, default 8)
fm_block_bytes=64 # Optional: block granularity in bytes (8/16/32/64/128, default 64)
)
# Create the index on the collection
client.create_index(
collection_name="log_catalog",
index_params=index_params
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.index.request.CreateIndexReq;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
Map<String, Object> params = new HashMap<>();
params.put("fm_sa_sample_rate", "8");
params.put("fm_block_bytes", "64");
client.createIndex(CreateIndexReq.builder()
.collectionName("log_catalog")
.indexParams(Arrays.asList(
IndexParam.builder().fieldName("text").indexType(IndexParam.IndexType.FMINDEX)
.indexName("fmindex_index")
.extraParams(params)
.build()))
.build());
import (
"context"
"log"
"github.com/milvus-io/milvus/client/v3/index"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "http://localhost:19530",
})
if err != nil {
log.Fatal("failed to connect to milvus server: ", err.Error())
}
idx := index.NewFMIndex().WithSaSampleRate(8).WithBlockBytes(64)
_, err = cli.CreateIndex(ctx, milvusclient.NewCreateIndexOption("log_catalog", "text", idx).WithIndexName("fmindex_index"))
if err != nil {
log.Fatal("failed to create index: ", err.Error())
}
#include <iostream>
#include "milvus/MilvusClientV2.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("http://localhost:19530"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
milvus::IndexDesc fmindex_index("text", "fmindex_index", milvus::IndexType::FMINDEX);
fmindex_index.AddExtraParam("fm_sa_sample_rate", "8");
fmindex_index.AddExtraParam("fm_block_bytes", "64");
status = client->CreateIndex(milvus::CreateIndexRequest().WithCollectionName("log_catalog")
.AddIndex(fmindex_index));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: "http://localhost:19530" });
await client.createIndex({
collection_name: "log_catalog",
field_name: "text",
index_type: "FMINDEX",
index_name: "fmindex_index",
params: { fm_sa_sample_rate: 8, fm_block_bytes: 64 },
});
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "log_catalog",
"indexParams": [
{
"fieldName": "text",
"indexName": "fmindex_index",
"params": {
"index_type": "FMINDEX",
"fm_sa_sample_rate": 8,
"fm_block_bytes": 64
}
}
]
}'
# {
# "code": 0,
# "data": {}
# }
FMINDEXによって高速化されるクエリ
FMINDEX は、VARCHAR フィールドに対する以下のフィルタパターンを高速化します。アンカー付きの前置、後置、および中置リテラルはインデックスから正確に解決されます。一般的なLIKE および正規表現パターンは候補を生成し、Milvusはそれらを元の値と照合して検証します。
プレフィックスマッチ
filter = 'text LIKE "vector%"'接尾辞一致
filter = 'text LIKE "%database"'インフィックス/部分文字列一致
filter = 'text LIKE "%vector database%"'一般的なLIKE(候補)
filter = 'text LIKE "a%b_c"'正規表現 (候補)
filter = 'text =~ "error.*timeout"'
フィルタ式の構文に関する詳細については、「パターンマッチング」を参照してください。
インデックスの削除
drop_index() メソッドを使用して、コレクションから既存のインデックスを削除します。
v2.6.3以前では、スカラーインデックスを削除する前にコレクションを解放する必要があります。
v2.6.4以降では、スカラーインデックスが不要になった時点で、コレクションを解放することなく直接削除できます。
client.drop_index(
collection_name="log_catalog", # Name of the collection
index_name="fmindex_index" # Name of the index to drop
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.index.request.DropIndexReq;
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("http://localhost:19530")
.build());
client.dropIndex(DropIndexReq.builder()
.collectionName("log_catalog")
.indexName("fmindex_index")
.build());
import (
"context"
"log"
"github.com/milvus-io/milvus/client/v3/milvusclient"
)
ctx := context.Background()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "http://localhost:19530",
})
if err != nil {
log.Fatal("failed to connect to milvus server: ", err.Error())
}
err = cli.DropIndex(ctx, milvusclient.NewDropIndexOption("log_catalog", "fmindex_index"))
if err != nil {
log.Fatal("failed to drop index: ", err.Error())
}
#include <iostream>
#include "milvus/MilvusClientV2.h"
auto client = milvus::MilvusClientV2::Create();
auto status = client->Connect(milvus::ConnectParam("http://localhost:19530"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
status = client->DropIndex(milvus::DropIndexRequest()
.WithCollectionName("log_catalog")
.WithIndexName("fmindex_index"));
if (!status.IsOk()) {
std::cout << status.Message() << std::endl;
}
import { MilvusClient } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: "http://localhost:19530" });
await client.dropIndex({
collection_name: "log_catalog",
index_name: "fmindex_index",
});
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/drop" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"collectionName": "log_catalog",
"indexName": "fmindex_index"
}'
# {
# "code": 0,
# "data": {}
# }
使用上の注意
フィールド型:
VARCHARフィールドでのみ動作します。データ型に関する詳細については、「文字列フィールド」を参照してください。バージョンの互換性:
FMINDEXには、スカラーインデックスエンジンバージョン5以降が必要です。SDKの対応状況:Rust v2 SDKには、現時点では
FMINDEXインデックス型が含まれていません。完全一致:アンカー付きの前置、後置、および中置の
LIKEリテラルは、候補の再確認を行わずに完全一致を返します。候補:一般的な
LIKEおよび正規表現パターンでは、Milvusが元の値に対して検証を行う候補が生成される場合があります。パターンによるフィルタリング効果が乏しい場合や、解決に過度なコストがかかる場合、Milvusは代わりにフィールドをスキャンすることがあります。不変性:
fm_sa_sample_rateおよびfm_block_bytesはその場で変更できません。これらを調整するには、インデックスを再構築してください。
ベストプラクティス
長い `
VARCHAR` フィールドに対する完全な部分文字列検索には、`FMINDEX` を使用してください。特に、`NGRAM` による候補の再検証にコストがかかる場合はそうです。ほとんどのワークロードでは、
fm_block_bytesをデフォルト値の 64 に維持してください。メモリが逼迫している場合は、この値を上げて常駐ディレクトリを縮小してください。fm_sa_sample_rateを小さくすると、インデックスサイズが大きくなる代わりに locate のレイテンシが改善されます。クエリが主にカウントのみの場合、この値を大きくしてインデックスサイズを縮小してください。