Aperçu des champs JSON

Lorsque vous créez des applications telles que des catalogues de produits, des systèmes de gestion de contenu ou des moteurs de préférences utilisateur, vous avez souvent besoin de stocker des métadonnées flexibles avec vos encastrements vectoriels. Les attributs des produits varient en fonction de la catégorie, les préférences des utilisateurs évoluent dans le temps et les propriétés des documents ont des structures imbriquées complexes. Les champs JSON de Milvus relèvent ce défi en vous permettant de stocker et d'interroger des données structurées flexibles sans sacrifier les performances.

Qu'est-ce qu'un champ JSON ?

Un champ JSON est un type de données défini par le schéma (DataType.JSON) dans Milvus qui stocke des données clé-valeur structurées. Contrairement aux colonnes rigides des bases de données traditionnelles, les champs JSON prennent en charge les objets imbriqués, les tableaux et les types de données mixtes, tout en offrant plusieurs options d'indexation pour des requêtes rapides.

Exemple de structure de champ 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"
      }
    }
  }
}

Dans cet exemple, metadata est un champ JSON unique qui contient un mélange de valeurs plates (par exemple category, in_stock), de tableaux (tags) et d'objets imbriqués (supplier).

Convention d'appellation : Utilisez uniquement des lettres, des chiffres et des traits de soulignement dans les clés JSON. Évitez les caractères spéciaux, les espaces ou les points, car ils peuvent entraîner des problèmes d'analyse dans les requêtes.

Champ JSON et champ dynamique

La différence entre un champ JSON et un champ dynamique est souvent source de confusion. Bien qu'ils soient tous deux liés à JSON, ils ont des objectifs différents.

Le tableau ci-dessous résume les principales différences entre un champ JSON et un champ dynamique :

Fonctionnalité

Champ JSON

Champ dynamique

Définition du schéma

Champ scalaire qui doit être explicitement déclaré dans le schéma de la collection avec le type DataType.JSON.

Un champ JSON caché (nommé $meta) qui stocke automatiquement les champs non déclarés.

Cas d'utilisation

Stocke des données structurées dont le schéma est connu et cohérent.

Stocke des données flexibles, évolutives ou semi-structurées qui ne correspondent pas à un schéma fixe.

Contrôle

Vous contrôlez le nom et la structure du champ.

Les champs non définis sont gérés par le système.

Interrogation

Interrogation à l'aide du nom du champ ou de la clé cible à l'intérieur du champ JSON : metadata["key"].

Interrogation directe à l'aide de la clé de champ dynamique : "dynamic_key" ou via $meta: $meta["dynamic_key"]

Opérations de base

Le flux de travail fondamental pour l'utilisation d'un champ JSON consiste à le définir dans votre schéma, à insérer des données, puis à interroger les données à l'aide d'expressions de filtrage spécifiques.

Définir un champ JSON

Pour utiliser un champ JSON, définissez-le explicitement dans votre schéma de collection lors de la création de la collection. L'exemple suivant montre comment créer une collection avec un champ metadata de type DataType.JSON:

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
)

Dans cet exemple, le champ JSON défini dans le schéma de la collection autorise les valeurs nulles avec nullable=True. Pour plus d'informations, reportez-vous à la section Nullable & Default.

Insérer des données

Une fois la collection créée, insérez des entités contenant des objets JSON structurés dans le champ JSON désigné. Vos données doivent être formatées comme une liste de dictionnaires.

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)

Opérations de filtrage

Avant de pouvoir effectuer des opérations de filtrage sur les champs JSON, assurez-vous que

  • Vous avez créé un index sur chaque champ vectoriel.

  • La collection est chargée en mémoire.

Afficher le code

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")

Une fois ces conditions remplies, vous pouvez utiliser les expressions ci-dessous pour filtrer votre collection sur la base des valeurs contenues dans le champ JSON. Ces expressions de filtrage s'appuient sur une syntaxe de chemin JSON spécifique et sur des opérateurs dédiés.

Filtrage avec la syntaxe de chemin JSON

Pour interroger une clé spécifique, utilisez la notation entre crochets pour accéder aux clés JSON : json_field_name["key"]. Pour les clés imbriquées, enchaînez-les : json_field_name["key1"]["key2"].

Pour filtrer les entités dont la clé category est "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
)

Pour filtrer les entités dont la clé imbriquée supplier["country"] est "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)

Filtrage avec des opérateurs spécifiques à JSON

Milvus fournit également des opérateurs spéciaux pour interroger les valeurs de tableau sur des clés de champ JSON spécifiques. Par exemple, Milvus fournit des opérateurs spéciaux pour interroger les valeurs de tableau sur des clés de champ JSON spécifiques :

  • json_contains(identifier, expr): Vérifie si un élément ou un sous-réseau spécifique existe dans un tableau JSON.

  • json_contains_all(identifier, expr): Vérifie que tous les éléments de l'expression JSON spécifiée sont présents dans le champ.

  • json_contains_any(identifier, expr): Filtre les entités dont au moins un membre de l'expression JSON est présent dans le champ.

Pour trouver un produit qui a la valeur "summer_sale" sous la clé tags:

# 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)

Pour trouver un produit qui a au moins une des valeurs "electronics", "new", ou "clearance" sous la clé tags:

# 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)

Pour plus d'informations sur les opérateurs spécifiques à JSON, reportez-vous à Opérateurs JSON.

Suivant : Accélérer les requêtes JSON

Par défaut, les requêtes sur les champs JSON sans accélération effectuent un balayage complet de toutes les lignes, ce qui peut être lent sur les grands ensembles de données. Pour accélérer les requêtes JSON, Milvus fournit des fonctions avancées d'indexation et d'optimisation du stockage.

Le tableau ci-dessous résume leurs différences et les meilleurs scénarios d'utilisation :

Technique

Meilleur pour

Tableaux Accélération

Remarques

Indexation JSON

Petit ensemble de clés fréquemment consultées, tableaux sur une clé de tableau spécifique

Oui (sur la clé du tableau indexé)

Doit présélectionner les clés, maintenance nécessaire si le schéma évolue

Déchiquetage JSON

Accélération générale sur de nombreuses clés, flexible pour des requêtes variées

Non (n'accélère pas les valeurs à l'intérieur des tableaux)

Configuration de stockage supplémentaire, les tableaux ont toujours besoin d'un index par clé

Index NGRAM

Recherches par caractères génériques, correspondance de sous-chaînes dans les champs de texte

NON (N'ACCÉLÈRE PAS LES VALEURS À L'INTÉRIEUR DES TABLEAUX)

Pas pour les filtres numériques/de plage

Conseil : vous pouvez combiner ces approches - par exemple, utiliser le déchiquetage JSON pour accélérer les requêtes générales, l'indexation JSON pour les clés de tableau à haute fréquence et l'indexation NGRAM pour une recherche de texte flexible.

Pour plus de détails sur la mise en œuvre, reportez-vous à la section :

FAQ

Y a-t-il des limites à la taille d'un champ JSON ?

Oui. Chaque champ JSON est limité à 65 536 octets.

Un champ JSON permet-il de définir une valeur par défaut ?

Non, les champs JSON ne prennent pas en charge les valeurs par défaut. Toutefois, vous pouvez définir nullable=True lors de la définition du champ afin d'autoriser les entrées vides.

Pour plus d'informations, reportez-vous à la section Nullable & Default.

Existe-t-il des conventions de dénomination pour les clés des champs JSON ?

Oui, pour assurer la compatibilité avec les requêtes et l'indexation :

  • Utilisez uniquement des lettres, des chiffres et des traits de soulignement dans les clés JSON.

  • Évitez d'utiliser des caractères spéciaux, des espaces ou des points (., /, etc.).

  • Les clés incompatibles peuvent entraîner des problèmes d'analyse dans les expressions de filtre.

Comment Milvus gère-t-il les valeurs de chaîne dans les champs JSON ?

Milvus stocke les valeurs de chaîne exactement comme elles apparaissent dans l'entrée JSON, sans transformation sémantique. Les chaînes mal citées peuvent entraîner des erreurs lors de l'analyse.

Exemples de chaînes valides:

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

Exemples de chaînes non valides:

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