Descripción general de los campos JSON

Al desarrollar aplicaciones como catálogos de productos, sistemas de gestión de contenidos o motores de preferencias de usuario, a menudo es necesario almacenar metadatos flexibles junto con las representaciones vectoriales. Los atributos de los productos varían según la categoría, las preferencias de los usuarios evolucionan con el tiempo y las propiedades de los documentos presentan estructuras anidadas complejas. Los campos JSON de Milvus resuelven este reto al permitir almacenar y consultar datos estructurados de forma flexible sin sacrificar el rendimiento.

¿Qué es un campo JSON?

Un campo JSON es un tipo de datos definido por un esquema (DataType.JSON) en Milvus que almacena datos estructurados de tipo clave-valor. A diferencia de las columnas rígidas de las bases de datos tradicionales, los campos JSON admiten objetos anidados, matrices y tipos de datos mixtos, al tiempo que ofrecen múltiples opciones de indexación para realizar consultas rápidas.

Ejemplo de estructura de un campo 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"
      }
    }
  }
}

En este ejemplo, metadata es un único campo JSON que contiene una mezcla de valores planos (p. ej., category, in_stock), matrices (tags) y objetos anidados (supplier).

Convención de nomenclatura: utiliza únicamente letras, números y guiones bajos en las claves JSON. Evita los caracteres especiales, los espacios o los puntos, ya que pueden provocar problemas de análisis en las consultas.

Campo JSON frente a campo dinámico

Un punto habitual de confusión es la diferencia entre un campo JSON y el campo dinámico. Aunque ambos están relacionados con JSON, tienen finalidades distintas.

La siguiente tabla resume las diferencias clave entre un campo JSON y un campo dinámico:

Característica

Campo JSON

Campo dinámico

Definición del esquema

Un campo escalar que debe declararse explícitamente en el esquema de la colección con el tipo DataType.JSON.

Un campo JSON oculto (denominado « $meta ») que almacena automáticamente los campos no declarados.

Caso de uso

Almacena datos estructurados cuyo esquema es conocido y coherente.

Almacena datos flexibles, en evolución o semiestructurados que no se ajustan a un esquema fijo.

Control

Tú controlas el nombre y la estructura del campo.

Gestión por parte del sistema para los campos no definidos.

Consultas

Realiza consultas utilizando el nombre del campo o la clave de destino dentro del campo JSON: metadata["key"].

Realiza la consulta directamente utilizando la clave del campo dinámico: "dynamic_key" o a través de $meta: $meta["dynamic_key"]

Operaciones básicas

El flujo de trabajo básico para utilizar un campo JSON consiste en definirlo en su esquema, insertar datos y, a continuación, consultar los datos mediante expresiones de filtro específicas.

Definir un campo JSON

Para utilizar un campo JSON, defínelo explícitamente en el esquema de la colección al crearla. El siguiente ejemplo muestra cómo crear una colección con un campo metadata de tipo 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
)

En este ejemplo, el campo JSON definido en el esquema de la colección permite valores nulos con nullable=True. Para obtener más información, consulta «Nullable y por defecto».

Insertar datos

Una vez creada la colección, inserta entidades que contengan objetos JSON estructurados en el campo JSON designado. Tus datos deben tener el formato de una lista de diccionarios.

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)

Operaciones de filtrado

Antes de poder realizar operaciones de filtrado en campos JSON, asegúrate de que:

  • Ha creado un índice en cada campo vectorial.

  • La colección esté cargada en memoria.

Mostrar código

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

Una vez cumplidos estos requisitos, puede utilizar las expresiones que se indican a continuación para filtrar su colección en función de los valores del campo JSON. Estas expresiones de filtrado utilizan una sintaxis específica de ruta JSON y operadores específicos.

Filtrado con la sintaxis de ruta JSON

Para consultar una clave específica, utiliza la notación entre corchetes para acceder a las claves JSON: json_field_name["key"]. En el caso de claves anidadas, encadenalas: json_field_name["key1"]["key2"].

Para filtrar las entidades en las que category es "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
)

Para filtrar las entidades en las que la clave anidada supplier["country"] sea "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)

Filtrado con operadores específicos de JSON

Milvus también ofrece operadores especiales para consultar valores de matriz en claves de campo JSON específicas. Por ejemplo:

  • json_contains(identifier, expr): Comprueba si existe un elemento o submatriz específico dentro de una matriz JSON

  • json_contains_all(identifier, expr): Garantiza que todos los elementos de la expresión JSON especificada estén presentes en el campo

  • json_contains_any(identifier, expr): Filtra las entidades en las que exista al menos un miembro de la expresión JSON dentro del campo

Para encontrar un producto que tenga el valor « "summer_sale" » bajo la clave « 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)

Para buscar un producto que tenga al menos uno de los valores « "electronics" », « "new" » o « "clearance" » bajo la clave « 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)

Para obtener más información sobre los operadores específicos de JSON, consulta Operadores JSON.

Siguiente: Acelerar consultas JSON

De forma predeterminada, las consultas en campos JSON sin aceleración realizarán un escaneo completo de todas las filas, lo que puede resultar lento en conjuntos de datos de gran tamaño. Para acelerar las consultas JSON, Milvus ofrece funciones avanzadas de indexación y optimización del almacenamiento.

A partir de Milvus 3.0.0, la indexación JSON de objetos completos (json_cast_type="JSON"), también conocida como indexación JSON plana, ha quedado obsoleta. Los índices existentes y las nuevas solicitudes de creación de índices siguen siendo compatibles por motivos de compatibilidad, pero este modo ya no se recomienda para nuevas cargas de trabajo. Utilice la indexación de rutas JSON para rutas de consulta conocidas, o considere el «JSON Shredding» para una aceleración general de las consultas en documentos complejos o en constante evolución.

La tabla siguiente resume sus diferencias y los escenarios de uso más adecuados:

Técnica

Ideal para

Aceleración de matrices

Notas

Indexación JSON

Conjunto reducido de claves a las que se accede con frecuencia, matrices en una clave de matriz específica

Sí (en la clave de matriz indexada)

Es necesario preseleccionar las claves; requiere mantenimiento si el esquema evoluciona

Fragmentación de JSON

Aumento general de la velocidad en muchas claves; flexible para consultas variadas

No (no acelera los valores dentro de las matrices)

Configuración de almacenamiento adicional; los arrays siguen necesitando un índice por clave

Índice NGRAM

Búsquedas con comodines, coincidencia de subcadenas en campos de texto

N/A

No apto para filtros numéricos o de rango

Consejo: Puedes combinar estos enfoques; por ejemplo, utiliza el desglose de JSON para acelerar consultas amplias, la indexación JSON para claves de matriz de alta frecuencia y la indexación NGRAM para una búsqueda de texto flexible.

Para obtener detalles sobre la implementación, consulta:

Preguntas frecuentes

¿Existen limitaciones en cuanto al tamaño de un campo JSON?

Sí. Cada campo JSON tiene un límite de 65 536 bytes.

¿Se puede establecer un valor por defecto en un campo JSON?

No, los campos JSON no admiten valores por defecto. Sin embargo, al definir el campo puedes establecer la opció nullable=True para permitir entradas vacías.

Consulta « Nullable y por defecto » para obtener más detalles.

¿Existen convenciones de nomenclatura para las claves de los campos JSON?

Sí, para garantizar la compatibilidad con las consultas y la indexación:

  • Utiliza únicamente letras, números y guiones bajos en las claves JSON.

  • Evita el uso de caracteres especiales, espacios o puntos (., /, etc.).

  • Las claves incompatibles pueden provocar problemas de análisis en las expresiones de filtro.

¿Cómo gestiona Milvus los valores de cadena en los campos JSON?

Milvus almacena los valores de cadena tal y como aparecen en la entrada JSON, sin transformación semántica. Las cadenas con comillas incorrectas pueden provocar errores durante el análisis sintáctico.

Ejemplos de cadenas válidas:

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

Ejemplos de cadenas no válidas:

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