Elasticsearch クエリから Milvus へ
Elasticsearch は Apache Lucene をベースに構築された主要なオープンソース検索エンジンです。しかし、モダンな AI アプリケーションでは、更新コストの高さ、リアルタイム性能の低さ、非効率的なシャード管理、クラウドネイティブでない設計、過剰なリソース要求などの課題に直面しています。クラウドネイティブなベクトルデータベースとして、Milvus はストレージとコンピューティングの分離、高次元データの効率的なインデックス作成、モダンインフラとのシームレスな統合によりこれらの問題を克服します。AIワークロードへの優れた性能とスケーラビリティを提供します。
この記事は、コードベースをElasticsearchからMilvusへ移行する手助けをし、クエリ変換の様々な例を提供することを目的としています。
概要
Elasticsearchでは、クエリコンテキスト内の操作は関連度スコアを生成するのに対し、フィルタコンテキスト内の操作は関連度スコアを生成しません。同様に、Milvusの検索は類似度スコアを生成し、フィルタのようなクエリは関連度スコアを生成しません。コードベースをElasticsearchからMilvusへ移行する際の主な原則は、Elasticsearchのクエリコンテキストで使用されるフィールドをベクトルフィールドに変換し、類似度スコア生成を可能にすることです。
以下の表には、いくつかのElasticsearchクエリパターンとそれに対応するMilvusの同等機能を示しています。
Elasticsearch クエリ | Milvus 対応機能 | 備考 |
|---|---|---|
全文検索クエリ | ||
全文検索 | 両方とも類似した機能セットを提供します。 | |
語句レベルクエリ | ||
| Elasticsearchのこれらのクエリがフィルタコンテキストで使用される場合、両方とも同じまたは類似した機能セットを提供します。 | |
| ||
| ||
| ||
| ||
| ||
| フィルタコンテキストで使用される場合、両方とも類似した機能セットを提供します。 | |
ベクタクエリ | ||
検索 | Milvus はより高度なベクトル検索機能を提供します。 | |
ハイブリッド検索 | Milvus は複数のリランキング戦略をサポートします。 | |
全文検索クエリ
Elasticsearchでは、全文検索クエリを使用すると、電子メールの本文などの解析済みテキストフィールドを検索できます。クエリ文字列は、インデックス作成時のフィールドに適用されたのと同じアナライザーを使用して処理されます。
Match クエリ
Elasticsearchでは、matchクエリは提供されたテキスト、数値、日付、またはブール値に一致するドキュメントを返します。提供されたテキストは一致前に解析されます。
以下は、matchクエリを使用したElasticsearch検索リクエストの例です。
resp = client.search(
query={
"match": {
"message": {
"query": "this is a test"
}
}
},
)
Milvusは、全文検索機能を通じて同じ機能を提供します。上記のElasticsearchクエリをMilvusに次のように変換できます:
res = client.search(
collection_name="my_collection",
data=['How is the weather in Jamaica?'],
anns_field="message_sparse",
output_fields=["id", "message"]
)
上記の例では、message_sparseはmessageという名前のVarCharフィールドから派生したスパースベクトルフィールドです。MilvusはBM25埋め込みモデルを使用してmessageフィールドの値をスパースベクトル埋め込みに変換し、それをmessage_sparseフィールドに保存します。検索リクエストを受け取ると、Milvusは同じBM25モデルを使用してプレーンテキストクエリペイロードを埋め込み、スパースベクトル検索を実行し、output_fieldsパラメータで指定されたidとmessageフィールドと対応する類似度スコアを返します。
この機能を使用するには、messageフィールドでアナライザーを有効にし、message_sparseフィールドをそこから派生させる関数を定義する必要があります。Milvusでアナライザーを有効にする方法と派生関数を作成する手順の詳細については、全文検索を参照してください。
語句レベルクエリ
Elasticsearchでは、語句レベルクエリは日付範囲、IPアドレス、価格、または製品IDなどの構造化データ内の正確な値に基づいてドキュメントを見つけるために使用されます。このセクションでは、Elasticsearchのいくつかの語句レベルクエリのMilvusにおける可能なかなりの等価機能について説明します。このセクションのすべての例は、Milvusの機能と一致するようにフィルタコンテキストで動作するように調整されています。
IDs
Elasticsearchでは、以下のようにフィルタコンテキストでIDに基づいてドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"ids": {
"values": [
"1",
"4",
"100"
]
}
}
}
},
)
Milvusでは、以下のようにIDに基づいてエンティティを見つけることもできます:
# filterパラメータを使用
res = client.query(
collection_name="my_collection",
filter="id in [1, 4, 100]",
output_fields=["id", "title"]
)
# idsパラメータを使用
res = client.query(
collection_name="my_collection",
ids=[1, 4, 100],
output_fields=["id", "title"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusのクエリおよび取得リクエスト、およびフィルター式の詳細については、クエリおよびフィルタリングを参照してください。
Prefix クエリ
Elasticsearchでは、以下のようにフィルタコンテキストで提供されたフィールドに特定のプレフィックスを含むドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"prefix": {
"user": {
"value": "ki"
}
}
}
}
},
)
Milvusでは、次のように指定したプレフィックスで始まる値を持つエンティティを見つけることができます:
res = client.query(
collection_name="my_collection",
filter='user like "ki%"',
output_fields=["id", "user"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusのlike演算子の詳細については、パターンマッチングに LIKE を使用するを参照してください。
Range クエリ
Elasticsearchでは、以下のように提供された範囲内の語句を含むドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 20
}
}
}
}
},
)
Milvusでは、特定のフィールド内の値が提供された範囲内にあるエンティティを見つけることができます:
res = client.query(
collection_name="my_collection",
filter='10 <= age <= 20',
output_fields=["id", "user", "age"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusの比較演算子の詳細については、比較演算子を参照してください。
Term クエリ
Elasticsearchでは、以下のように提供されたフィールドに正確な語句を含むドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"term": {
"status": {
"value": "retired"
}
}
}
}
},
)
Milvusでは、指定されたフィールドの値が正確に指定された語句であるエンティティを見つけることができます:
# == を使用
res = client.query(
collection_name="my_collection",
filter='status=="retired"',
output_fields=["id", "user", "status"]
)
# TEXT_MATCH を使用
res = client.query(
collection_name="my_collection",
filter='TEXT_MATCH(status, "retired")',
output_fields=["id", "user", "status"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusの比較演算子の詳細については、比較演算子を参照してください。
Terms クエリ
Elasticsearchでは、以下のように提供されたフィールドに1つ以上の正確な語句を含むドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"terms": {
"degree": [
"graduate",
"post-graduate"
]
}
}
}
}
)
Milvusにはこれと完全に同等の機能は存在しません。ただし、指定されたフィールドの値が指定された語句のいずれかであるエンティティを見つけることができます:
# in を使用
res = client.query(
collection_name="my_collection",
filter='degree in ["graduate", "post-graduate"]',
output_fields=["id", "user", "degree"]
)
# TEXT_MATCH を使用
res = client.query(
collection_name="my_collection",
filter='TEXT_MATCH(degree, "graduate post-graduate")',
output_fields=["id", "user", "degree"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusの範囲演算子の詳細については、範囲演算子を参照してください。
Wildcard クエリ
Elasticsearchでは、以下のようにワイルドカードパターンに一致する語句を含むドキュメントを見つけることができます:
resp = client.search(
query={
"bool": {
"filter": {
"wildcard": {
"user": {
"value": "ki*y"
}
}
}
}
},
)
Milvusではフィルタリング条件でのワイルドカードをサポートしていません。ただし、like演算子を使用して次のように同様の効果を得ることができます:
res = client.query(
collection_name="my_collection",
filter='user like "ki%" AND user like "%y"',
output_fields=["id", "user"]
)
Elasticsearchの例はこのページで見つけることができます。Milvusの範囲演算子の詳細については、範囲演算子を参照してください。
Boolean クエリ
Elasticsearchでは、booleanクエリは他のクエリのブール結合に一致するドキュメントを一致させるクエリです。
以下の例は、このページのElasticsearchドキュメントの例から調整されています。このクエリは、名前にkimchyが含まれており、productionタグを持つユーザーを返します。
resp = client.search(
query={
"bool": {
"filter": {
"term": {
"user": "kimchy"
}
},
"filter": {
"term": {
"tags": "production"
}
}
}
},
)
Milvusでは、次のように同様のことを行うことができます:
filter =
res = client.query(
collection_name="my_collection",
filter='user like "%kimchy%" AND ARRAY_CONTAINS(tags, "production")',
output_fields=["id", "user", "age", "tags"]
)
上記の例では、対象のコレクションにVarChar型のuserフィールドとArray型のtagsフィールドがあると仮定しています。このクエリは、名前にkimchyが含まれており、productionタグを持つユーザーを返します。
ベクタクエリ
Elasticsearchでは、ベクタクエリはベクトルフィールド上で動作し、効率的に意味検索を実行するための専門的なクエリです。
Knn クエリ
Elasticsearchは、近似kNNクエリと正確なブルートフォースkNNクエリの両方をサポートしています。以下のように、類似度メトリックで測定されたクエリベクトルに最も近いk個のベクトルをいずれかの方法で見つけることができます:
resp = client.search(
index="my-image-index",
size=3,
query={
"knn": {
"field": "image-vector",
"query_vector": [
-5,
9,
-12
],
"k": 10
}
},
)
Milvusは専門的なベクトルデータベースとして、インデックスタイプを使用してベクトル検索を最適化します。通常、高次元ベクトルデータでは近似最近傍(ANN)検索を優先します。FLATインデックスタイプを使用したブルートフォースkNN検索は正確な結果を提供しますが、時間がかかり、リソースを消費します。対照的に、AUTOINDEXや他のインデックスタイプを使用したANN検索は速度と正確さのバランスを取り、kNNよりはるかに高速でリソース効率的なパフォーマンスを提供します。インデックスタイプとAUTOINDEXの詳細については、インデックスの管理およびAUTOINDEXの説明を参照してください。
上記のベクタクエリと同等のMilvusでの例:
res = client.search(
collection_name="my_collection",
anns_field="image-vector"
data=[[-5, 9, -12]],
limit=10
)
Elasticsearchの例はこのページで見つけることができます。MilvusのANN検索の詳細については、基本ANN検索を参照してください。
Reciprocal Rank Fusion
Elasticsearchでは、Reciprocal Rank Fusion (RRF)を使用して、異なる関連性インジケーターを持つ複数の結果セットを単一のランク付き結果セットに結合できます。
以下の例は、従来の語句ベース検索とk最近傍(kNN)ベクトル検索を組み合わせて検索関連性を改善する方法を示しています:
client.search(
index="my_index",
size=10,
query={
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"term": {
"text": "shoes"
}
}
}
},
{
"knn": {
"field": "vector",
"query_vector": [1.25, 2, 3.5], # 例のベクトルです。実際のクエリベクトルに置き換えてください
"k": 50,
"num_candidates": 100
}
}
],
"rank_window_size": 50,
"rank_constant": 20
}
}
}
)
この例では、RRFは2つの取得機能の結果を結合します:
-
textフィールドに語句"shoes"を含むドキュメントの標準語句ベース検索。 -
与えられたクエリベクトルを使用した
vectorフィールドのkNN検索。
各取得機能は最大50位までの一致を提供し、RRFによって再ランク付けされ、最終的な上位10件の結果が返されます。
Milvusでは、複数のベクトルフィールド間で検索を組み合わせ、リランキング戦略を適用し、結合されたリストから上位K件の結果を取得することで、類似したハイブリッド検索を実現できます。MilvusはRRFと加重リランキング戦略の両方をサポートしています。詳細については、リランキングを参照してください。
以下は、上記のElasticsearch例のMilvusでの非厳密な同等機能です。
search_params_dense = {
"data": [[1.25, 2, 3.5]],
"anns_field": "vector",
"param": {
"metric_type": "IP",
},
"limit": 100
}
req_dense = ANNSearchRequest(**search_params_dense)
search_params_sparse = {
"data": ["shoes"],
"anns_field": "text_sparse",
"param": {
"metric_type": "BM25",
"params": {"drop_ratio_search": 0.2}
}
}
req_sparse = ANNSearchRequest(**search_params_sparse)
res = client.hybrid_search(
collection_name="my_collection",
reqs=[req_dense, req_sparse],
reranker=RRFRanker(),
limit=10
)
この例では、Milvusのハイブリッド検索が以下を組み合わせていることを示しています:
- 密ベクトル検索:内積(IP)メトリックを使用した近似最近傍(ANN)検索を
vectorフィールド上で実行。 - スパースベクトル検索:BM25類似度メトリックと
drop_ratio_searchパラメータ0.2を使用してtext_sparseフィールド上で実行。
これらの検索から得られた結果は個別に実行され、結合され、相互ランク融合(RRF)ランカーを使用して再ランク付けされます。ハイブリッド検索は再ランク付けされたリストから上位10個のエンティティを返します。
標準テキストベースクエリとkNN検索の結果を結合するElasticsearchのRRFランク付けとは異なり、Milvusはスパースベクトル検索と密ベクトル検索の結果を結合し、マルチモーダルデータに最適化された独自のハイブリッド検索機能を提供します。
まとめ
この記事では、語句レベルクエリ、ブールクエリ、全文検索クエリ、およびベクトルクエリを含む一般的なElasticsearchクエリをMilvusの同等機能に変換する方法を説明しました。他のElasticsearchクエリの変換についてさらに質問がある場合は、お気軽にお問い合わせください。