JSONフィールドの概要

商品カタログ、コンテンツ管理システム、ユーザー嗜好エンジンのようなアプリケーションを構築する場合、多くの場合、ベクトル埋め込みと一緒に柔軟なメタデータを保存する必要があります。商品の属性はカテゴリによって異なり、ユーザの嗜好は時間とともに変化し、ドキュメントのプロパティは複雑な入れ子構造を持っています。MilvusのJSONフィールドは、パフォーマンスを犠牲にすることなく、柔軟な構造化データの保存とクエリを可能にすることで、この課題を解決します。

JSONフィールドとは?

JSONフィールドとは、Milvusのスキーマ定義データ型(DataType.JSON)の一つで、構造化されたキー・バリューデータを格納するものです。従来の硬直したデータベースカラムとは異なり、JSONフィールドはネストされたオブジェクト、配列、および混合データ型に対応し、高速クエリのための複数のインデックスオプションを提供します。

JSONフィールド構造の例:

{
  "metadata": { 
    "category": "electronics",
    "brand": "BrandA",
    "in_stock": true,
    "price": 99.99,
    "string_price": "99.99",
    "tags": ["clearance", "summer_sale"],
    "supplier": {
      "name": "SupplierX",
      "country": "USA",
      "contact": {
        "email": "support@supplierx.com",
        "phone": "+1-800-555-0199"
      }
    }
  }
}

この例では、metadata は1つのJSONフィールドで、フラット値(例:categoryin_stock )、配列(tags )、入れ子オブジェクト(supplier )が混在している。

命名規則:JSONキーには、文字、数字、アンダースコアのみを使用する。特殊文字、スペース、ドットは、クエリで解析の問題を引き起こす可能性があるため、避けてください。

JSONフィールドとダイナミック・フィールドの違い

よくある混乱は、JSONフィールドとダイナミックフィールドの違いです。どちらもJSONに関連していますが、その目的は異なります。

以下の表は、JSONフィールドとダイナミック・フィールドの主な違いをまとめたものです:

機能

JSONフィールド

ダイナミック・フィールド

スキーマ定義

コレクション・スキーマで、DataType.JSON 型で明示的に宣言する必要があるスカラー・フィールド。

宣言されていないフィールドを自動的に格納する非表示の JSON フィールド($meta )。

使用例

スキーマが既知で一貫性のある構造化データを格納する。

固定スキーマに適合しない、柔軟な、進化する、または半構造化データを格納する。

制御

フィールド名と構造を制御します。

未定義のフィールドはシステム管理。

クエリ

JSONフィールド内のフィールド名またはターゲット・キーを使用してクエリ:metadata["key"]

ダイナミック・フィールドのキーを使って直接クエリーする:"dynamic_key" または$meta を経由して:$meta["dynamic_key"]

基本的な操作

JSONフィールドを使用する基本的なワークフローは、スキーマでフィールドを定義し、データを挿入し、特定のフィルター式を使用してデータをクエリすることです。

JSONフィールドの定義

JSON フィールドを使用するには、コレクションの作成時にコレクションスキーマで明示的に定義します。次の例は、DataType.JSON タイプのmetadata フィールドを持つコレクションを作成する方法を示します:

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530") # Replace with your server address 

# Create schema
schema = client.create_schema(auto_id=False, enable_dynamic_field=True)

schema.add_field(field_name="product_id", datatype=DataType.INT64, is_primary=True) # Primary field
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=5) # Vector field
# Define a JSON field that allows null values
schema.add_field(field_name="metadata", datatype=DataType.JSON, nullable=True)

client.create_collection(
    collection_name="product_catalog",
    schema=schema
)

この例では、コレクションスキーマで定義された JSON フィールドは、nullable=True で NULL 値を許可します。詳細は、Nullable & Defaultを参照してください。

データの挿入

コレクションが作成されたら、構造化 JSON オブジェクトを含むエンティティを指定の JSON フィールドに挿入します。データは、辞書のリストとしてフォーマットする必要があります。

entities = [
    {
        "product_id": 1,
        "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
        "metadata": { # JSON field
            "category": "electronics",
            "brand": "BrandA",
            "in_stock": True,
            "price": 99.99,
            "string_price": "99.99",
            "tags": ["clearance", "summer_sale"],
            "supplier": {
                "name": "SupplierX",
                "country": "USA",
                "contact": {
                    "email": "support@supplierx.com",
                    "phone": "+1-800-555-0199"
                }
            }
        }
    }
]

client.insert(collection_name="product_catalog", data=entities)

フィルタリング操作

JSON フィールドでフィルタリング操作を実行する前に、以下を確認してください:

  • 各ベクトル・フィールドにインデックスが作成されている。

  • コレクションがメモリにロードされている。

コードを表示する

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="vector",
    index_type="AUTOINDEX",
    index_name="vector_index",
    metric_type="COSINE"
)

client.create_index(collection_name="product_catalog", index_params=index_params)

client.load_collection(collection_name="product_catalog")

これらの要件が満たされると、以下の式を使用して、JSON フィールド内の値に基づいてコレクションをフィルタリングできます。これらのフィルター式は、特定の JSON パス構文と専用の演算子を活用しています。

JSON パス構文を使用したフィルタリング

特定のキーにクエリーするには、JSON キーにアクセスするためにブラケット記法を使用します:json_field_name["key"].入れ子になったキーの場合は、それらを連結してください:json_field_name["key1"]["key2"]

category"electronics" であるエンティティをフィルタリングする:

# Define filter expression
filter = 'metadata["category"] == "electronics"'

client.search(
    collection_name="product_catalog",  # Collection name
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],               # Query vector (must match collection's vector dim)
    limit=5,                           # Max. number of results to return
    filter=filter,                    # Filter expression
    output_fields=["product_id", "metadata"]   # Fields to include in the search results
)

入れ子になったキーsupplier["country"]"USA" であるエンティティをフィルタリングするには:

# Define filter expression
filter = 'metadata["supplier"]["country"] == "USA"'

res = client.search(
    collection_name="product_catalog",  # Collection name
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],               # Query vector (must match collection's vector dim)
    limit=5,                           # Max. number of results to return
    filter=filter,                    # Filter expression
    output_fields=["product_id", "metadata"]   # Fields to include in the search results
)

print(res)

JSON固有の演算子によるフィルタリング

Milvusは、特定のJSONフィールドキーに対して配列値を問い合わせるための特別な演算子も提供しています。例えば

  • json_contains(identifier, expr):JSON配列内に特定の要素またはサブ配列が存在するかどうかをチェックします。

  • json_contains_all(identifier, expr):指定されたJSON式のすべての要素がフィールドに存在するかどうかを確認します。

  • json_contains_any(identifier, expr):JSON式の少なくとも1つのメンバがフィールド内に存在するエンティティをフィルタリングする。

tags キーの下に"summer_sale" の値を持つ製品を検索します:

# Define filter expression
filter = 'json_contains(metadata["tags"], "summer_sale")'

res = client.search(
    collection_name="product_catalog",  # Collection name
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],               # Query vector (must match collection's vector dim)
    limit=5,                           # Max. number of results to return
    filter=filter,                    # Filter expression
    output_fields=["product_id", "metadata"]   # Fields to include in the search results
)

print(res)

tags キーの下に、"electronics""new""clearance" の値の少なくとも 1 つを持つ製品を検索します:

# Define filter expression
filter = 'json_contains_any(metadata["tags"], ["electronics", "new", "clearance"])'

res = client.search(
    collection_name="product_catalog",  # Collection name
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],               # Query vector (must match collection's vector dim)
    limit=5,                           # Max. number of results to return
    filter=filter,                    # Filter expression
    output_fields=["product_id", "metadata"]   # Fields to include in the search results
)

print(res)

JSON固有の演算子の詳細については、「JSON演算子」を参照してください。

次へJSONクエリーの高速化

デフォルトでは、高速化されていないJSONフィールドに対するクエリは、すべての行のフルスキャンを実行します。JSONクエリを高速化するために、Milvusは高度なインデックス機能とストレージ最適化機能を提供しています。

以下の表は、それらの違いと最適な使用シナリオをまとめたものです:

手法

最適

アレイ アクセラレーション

備考

JSONインデックス

頻繁にアクセスされるキーの小さなセット、特定の配列キー上の配列

あり(インデックス付き配列キー)

事前にキーを選択する必要があり、スキーマが進化した場合はメンテナンスが必要

JSONシュレッダー

多様なクエリに柔軟に対応できる。

なし(配列内の値を高速化しない)

余分なストレージ構成、配列にはキーごとのインデックスが必要

NGRAMインデックス

ワイルドカード検索、テキストフィールドの部分文字列マッチング

該当なし

数値/範囲フィルタには使えない

ヒント:これらのアプローチを組み合わせることができます。例えば、JSONシュレッダーを使用して広範なクエリを高速化し、JSONインデックスを高頻度の配列キーに使用し、NGRAMインデックスを使用して柔軟なテキスト検索を行うことができます。

実装の詳細については、以下を参照してください:

よくある質問

JSONフィールドのサイズに制限はありますか?

はい。各JSONフィールドは65,536バイトに制限されています。

JSONフィールドは、デフォルト値の設定をサポートしていますか?

JSONフィールドはデフォルト値をサポートしていません。ただし、フィールドの定義時にnullable=True を設定して、空の入力を許可することはできます。

詳細については、「Nullable & Default」を参照してください。

JSONフィールド・キーの命名規則はありますか?

はい、クエリとインデックス作成の互換性を確保するためです:

  • JSONキーには、文字、数字、アンダースコアのみを使用してください。

  • JSONキーには、文字、数字、アンダースコアのみを使用してください。特殊文字、スペース、ドット(./ など)の使用は避けてください。

  • 互換性のないキーは、フィルタ式において解析上の問題を引き起こす可能性があります。

MilvusはJSONフィールド内の文字列値をどのように扱いますか?

Milvusは、JSON入力に表示されたとおりに、セマンティック変換なしで文字列値を保存します。文字列が不適切に引用符で囲まれていると、パース時にエラーが発生する可能性があります。

有効な文字列の例

"a\"b", "a'b", "a\\b"

無効な文字列の例

'a"b', 'a\'b'