Campo dinamico

Milvus consente di inserire entità con strutture flessibili ed evolutive attraverso una funzione speciale chiamata campo dinamico. Questo campo è implementato come un campo JSON nascosto chiamato $meta, che memorizza automaticamente tutti i campi dei dati che non sono esplicitamente definiti nello schema della collezione.

Come funziona

Quando il campo dinamico è abilitato, Milvus aggiunge un campo nascosto $meta a ogni entità. Questo campo è di tipo JSON, il che significa che può memorizzare qualsiasi struttura di dati compatibile con JSON e può essere indicizzato utilizzando la sintassi dei percorsi JSON.

Durante l'inserimento dei dati, qualsiasi campo non dichiarato nello schema viene automaticamente memorizzato come coppia chiave-valore all'interno di questo campo dinamico.

Non è necessario gestire manualmente $meta: Milvus lo fa in modo trasparente.

Ad esempio, se lo schema della collezione definisce solo id e vector e si inserisce la seguente entità:

{
  "id": 1,
  "vector": [0.1, 0.2, 0.3],
  "name": "Item A",    // Not in schema
  "category": "books"  // Not in schema
}

Con la funzione di campo dinamico abilitata, Milvus la memorizza internamente come:

{
  "id": 1,
  "vector": [0.1, 0.2, 0.3],
  "$meta": {
    "name": "Item A",
    "category": "books"
  }
}

Ciò consente di evolvere la struttura dei dati senza alterare lo schema.

I casi d'uso più comuni sono:

  • Memorizzazione di campi opzionali o recuperati di rado.

  • Catturare metadati che variano a seconda dell'entità

  • Supporto di un filtraggio flessibile tramite indici su chiavi di campi dinamici specifici.

Tipi di dati supportati

Il campo dinamico supporta tutti i tipi di dati scalari forniti da Milvus, compresi i valori semplici e complessi. Questi tipi di dati si applicano ai **valori delle chiavi memorizzate in $meta.

I tipi supportati includono:

  • Stringa (VARCHAR)

  • Numero intero (INT8, INT32, INT64)

  • Virgola mobile (FLOAT, DOUBLE)

  • Booleano (BOOL)

  • Array di valori scalari (ARRAY)

  • Oggetti JSON (JSON)

Esempio:

{
  "brand": "Acme",
  "price": 29.99,
  "in_stock": true,
  "tags": ["new", "hot"],
  "specs": {
    "weight": "1.2kg",
    "dimensions": { "width": 10, "height": 20 }
  }
}

Ciascuna delle chiavi e dei valori di cui sopra verrebbe memorizzata all'interno del campo $meta.

Abilitare il campo dinamico

Per utilizzare la funzione di campo dinamico, impostare enable_dynamic_field=True durante la creazione dello schema della collezione:

from pymilvus import MilvusClient, DataType

# Initialize client
client = MilvusClient(uri="http://localhost:19530")

# Create schema with dynamic field enabled
schema = client.create_schema(
    auto_id=False,
    enable_dynamic_field=True,
)

# Add explicitly defined fields
schema.add_field(field_name="my_id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="my_vector", datatype=DataType.FLOAT_VECTOR, dim=5)

# Create the collection
client.create_collection(
    collection_name="my_collection",
    schema=schema
)
import io.milvus.v2.client.*;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.AddFieldReq;

ConnectConfig config = ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build();
MilvusClientV2 client = new MilvusClientV2(config);

CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
        .enableDynamicField(true)
        .build();
schema.addField(AddFieldReq.builder()
        .fieldName("my_id")
        .dataType(DataType.Int64)
        .isPrimaryKey(Boolean.TRUE)
        .build());
schema.addField(AddFieldReq.builder()
        .fieldName("my_vector")
        .dataType(DataType.FloatVector)
        .dimension(5)
        .build());

CreateCollectionReq requestCreate = CreateCollectionReq.builder()
        .collectionName("my_collection")
        .collectionSchema(schema)
        .build();
client.createCollection(requestCreate);
import { MilvusClient, DataType, CreateCollectionReq } from '@zilliz/milvus2-sdk-node';

// Initialize client
const client = new MilvusClient({ address: 'localhost:19530' });

// Create collection
const res = await client.createCollection({
  collection_name: 'my_collection',
  schema:  [
      {
        name: 'my_id',
        data_type: DataType.Int64,
        is_primary_key: true,
        autoID: false,
      },
      {
        name: 'my_vector',
        data_type: DataType.FloatVector,
        type_params: {
          dim: '5',
      }
   ],
   enable_dynamic_field: true
});

import (
    "context"

    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
    Address: "localhost:19530",
})
if err != nil {
    return err
}

schema := entity.NewSchema().WithDynamicFieldEnabled(true)
schema.WithField(entity.NewField().
    WithName("my_id").pk
    WithDataType(entity.FieldTypeInt64).
    WithIsPrimaryKey(true),
).WithField(entity.NewField().
    WithName("my_vector").
    WithDataType(entity.FieldTypeFloatVector).
    WithDim(5),
)

err = client.CreateCollection(ctx, milvusclient.NewCreateCollectionOption("my_collection", schema))
if err != nil {
    return err
}
# restful
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"

export myIdField='{
  "fieldName": "my_id",
  "dataType": "Int64",
  "isPrimary": true,
  "autoID": false
}'

export myVectorField='{
  "fieldName": "my_vector",
  "dataType": "FloatVector",
  "elementTypeParams": {
    "dim": 5
  }
}'

export schema="{
  \"autoID\": false,
  \"enableDynamicField\": true,
  \"fields\": [
    $myIdField,
    $myVectorField
  ]
}"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
  \"collectionName\": \"my_collection\",
  \"schema\": $schema
}"

Inserire entità nella collezione

Il campo dinamico consente di inserire campi extra non definiti nello schema. Questi campi saranno memorizzati automaticamente in $meta.

entities = [
    {
        "my_id": 1, # Explicitly defined primary field
        "my_vector": [0.1, 0.2, 0.3, 0.4, 0.5], # Explicitly defined vector field
        "overview": "Great product",       # Scalar key not defined in schema
        "words": 150,                      # Scalar key not defined in schema
        "dynamic_json": {                  # JSON key not defined in schema
            "varchar": "some text",
            "nested": {
                "value": 42.5
            },
            "string_price": "99.99"        # Number stored as string
        }
    }
]

client.insert(collection_name="my_collection", data=entities)
import com.google.gson.Gson;
import com.google.gson.JsonObject;

import io.milvus.v2.service.vector.request.InsertReq;

Gson gson = new Gson();
JsonObject row = new JsonObject();
row.addProperty("my_id", 1);
row.add("my_vector", gson.toJsonTree(Arrays.asList(0.1, 0.2, 0.3, 0.4, 0.5)));
row.addProperty("overview", "Great product");
row.addProperty("words", 150);

JsonObject dynamic = new JsonObject();
dynamic.addProperty("varchar", "some text");
dynamic.addProperty("string_price", "99.99");

JsonObject nested = new JsonObject();
nested.addProperty("value", 42.5);

dynamic.add("nested", nested);
row.add("dynamic_json", dynamic);

client.insert(InsertReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(row))
        .build());

const entities = [
  {
    my_id: 1,
    my_vector: [0.1, 0.2, 0.3, 0.4, 0.5],
    overview: 'Great product',
    words: 150,
    dynamic_json: {
      varchar: 'some text',
      nested: {
        value: 42.5,
      },
      string_price: '99.99',
    },
  },
];
const res = await client.insert({
    collection_name: 'my_collection',
    data: entities,
});
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
    WithInt64Column("my_id", []int64{1}).
    WithFloatVectorColumn("my_vector", 5, [][]float32{
        {0.1, 0.2, 0.3, 0.4, 0.5},
    }).WithColumns(
    column.NewColumnVarChar("overview", []string{"Great product"}),
    column.NewColumnInt32("words", []int32{150}),
    column.NewColumnJSONBytes("dynamic_json", [][]byte{
        []byte(`{
            varchar: 'some text',
            nested: {
                value: 42.5,
            },
            string_price: '99.99',
        }`),
    }),
))
if err != nil {
    return err
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data '{
  "data": [
    {
      "my_id": 1,
      "my_vector": [0.1, 0.2, 0.3, 0.4, 0.5],
      "overview": "Great product",
      "words": 150,
      "dynamic_json": {
        "varchar": "some text",
        "nested": {
          "value": 42.5
        },
        "string_price": "99.99"
      }
    }
  ],
  "collectionName": "my_collection"
}'

Indicizzare le chiavi nel campo dinamicoCompatible with Milvus 2.5.11+

Milvus consente di utilizzare l'indicizzazione dei percorsi JSON per creare indici su chiavi specifiche all'interno del campo dinamico. Queste possono essere valori scalari o valori annidati in oggetti JSON.

L'indicizzazione delle chiavi del campo dinamico è facoltativa. È comunque possibile eseguire interrogazioni o filtri in base alle chiavi dei campi dinamici senza un indice, ma ciò può comportare un rallentamento delle prestazioni a causa della ricerca bruta.

Sintassi dell'indicizzazione del percorso JSON

Per creare un indice di percorso JSON, specificare:

  • Percorso JSON (json_path): Il percorso della chiave o del campo annidato nell'oggetto JSON che si vuole indicizzare.

    • Esempio: metadata["category"]

      Questo definisce dove il motore di indicizzazione deve cercare all'interno della struttura JSON.

  • Tipo di cast JSON (json_cast_type): Il tipo di dati che Milvus deve utilizzare per interpretare e indicizzare il valore nel percorso specificato.

    • Questo tipo deve corrispondere al tipo di dati effettivo del campo da indicizzare.

    • Per un elenco completo, consultare i tipi di cast JSON supportati.

Utilizzare il percorso JSON per indicizzare le chiavi dei campi dinamici

Poiché il campo dinamico è un campo JSON, è possibile indicizzare qualsiasi chiave al suo interno utilizzando la sintassi del percorso JSON. Questo funziona sia per semplici valori scalari che per strutture complesse annidate.

Esempi di percorso JSON:

  • Per le chiavi semplici: overview, words

  • Per chiavi annidate: dynamic_json['varchar'], dynamic_json['nested']['value']

index_params = client.prepare_index_params()

# Index a simple string key
index_params.add_index(
    field_name="overview",  # Key name in the dynamic field
    index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
    index_name="overview_index",  # Unique index name
    params={
        "json_cast_type": "varchar",   # Data type that Milvus uses when indexing the values
        "json_path": "overview"        # JSON path to the key
    }
)

# Index a simple numeric key
index_params.add_index(
    field_name="words",  # Key name in the dynamic field
    index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
    index_name="words_index",  # Unique index name
    params={
        "json_cast_type": "double",  # Data type that Milvus uses when indexing the values
        "json_path": "words" # JSON path to the key
    }
)

# Index a nested key within a JSON object
index_params.add_index(
    field_name="dynamic_json", # JSON key name in the dynamic field
    index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
    index_name="json_varchar_index", # Unique index name
    params={
        "json_cast_type": "varchar", # Data type that Milvus uses when indexing the values
        "json_path": "dynamic_json['varchar']" # JSON path to the nested key
    }
)

# Index a deeply nested key
index_params.add_index(
    field_name="dynamic_json",
    index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
    index_name="json_nested_index", # Unique index name
    params={
        "json_cast_type": "double",
        "json_path": "dynamic_json['nested']['value']"
    }
)
import io.milvus.v2.common.IndexParam;

Map<String,Object> extraParams1 = new HashMap<>();
extraParams1.put("json_path", "overview");
extraParams1.put("json_cast_type", "varchar");
indexParams.add(IndexParam.builder()
        .fieldName("overview")
        .indexName("overview_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .extraParams(extraParams1)
        .build());

Map<String,Object> extraParams2 = new HashMap<>();
extraParams2.put("json_path", "words");
extraParams2.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
        .fieldName("words")
        .indexName("words_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .extraParams(extraParams2)
        .build());

Map<String,Object> extraParams3 = new HashMap<>();
extraParams3.put("json_path", "dynamic_json['varchar']");
extraParams3.put("json_cast_type", "varchar");
indexParams.add(IndexParam.builder()
        .fieldName("dynamic_json")
        .indexName("json_varchar_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .extraParams(extraParams3)
        .build());

Map<String,Object> extraParams4 = new HashMap<>();
extraParams4.put("json_path", "dynamic_json['nested']['value']");
extraParams4.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
        .fieldName("dynamic_json")
        .indexName("json_nested_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .extraParams(extraParams4)
        .build());
const indexParams = [
    {
      collection_name: 'my_collection',
      field_name: 'overview',
      index_name: 'overview_index',
      index_type: 'AUTOINDEX',
      metric_type: 'NONE',
      params: {
        json_path: 'overview',
        json_cast_type: 'varchar',
      },
    },
    {
      collection_name: 'my_collection',
      field_name: 'words',
      index_name: 'words_index',
      index_type: 'AUTOINDEX',
      metric_type: 'NONE',
      params: {
        json_path: 'words',
        json_cast_type: 'double',
      },
    },
    {
      collection_name: 'my_collection',
      field_name: 'dynamic_json',
      index_name: 'json_varchar_index',
      index_type: 'AUTOINDEX',
      metric_type: 'NONE',
      params: {
        json_cast_type: 'varchar',
        json_path: "dynamic_json['varchar']",
      },
    },
    {
      collection_name: 'my_collection',
      field_name: 'dynamic_json',
      index_name: 'json_nested_index',
      index_type: 'AUTOINDEX',
      metric_type: 'NONE',
      params: {
        json_cast_type: 'double',
        json_path: "dynamic_json['nested']['value']",
      },
    },
  ];
import (
    "github.com/milvus-io/milvus/client/v2/index"
)

jsonIndex1 := index.NewJSONPathIndex(index.AUTOINDEX, "varchar", "overview")
    .WithIndexName("overview_index")
jsonIndex2 := index.NewJSONPathIndex(index.AUTOINDEX, "double", "words")
    .WithIndexName("words_index")
jsonIndex3 := index.NewJSONPathIndex(index.AUTOINDEX, "varchar", `dynamic_json['varchar']`)
    .WithIndexName("json_varchar_index")
jsonIndex4 := index.NewJSONPathIndex(index.AUTOINDEX, "double", `dynamic_json['nested']['value']`)
    .WithIndexName("json_nested_index")

indexOpt1 := milvusclient.NewCreateIndexOption("my_collection", "overview", jsonIndex1)
indexOpt2 := milvusclient.NewCreateIndexOption("my_collection", "words", jsonIndex2)
indexOpt3 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex3)
indexOpt4 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex4)
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"

export overviewIndex='{
  "fieldName": "dynamic_json",
  "indexName": "overview_index",
  "params": {
    "index_type": "AUTOINDEX",
    "json_cast_type": "varchar",
    "json_path": "dynamic_json[\"overview\"]"
  }
}'

export wordsIndex='{
  "fieldName": "dynamic_json",
  "indexName": "words_index",
  "params": {
    "index_type": "AUTOINDEX",
    "json_cast_type": "double",
    "json_path": "dynamic_json[\"words\"]"
  }
}'

export varcharIndex='{
  "fieldName": "dynamic_json",
  "indexName": "json_varchar_index",
  "params": {
    "index_type": "AUTOINDEX",
    "json_cast_type": "varchar",
    "json_path": "dynamic_json[\"varchar\"]"
  }
}'

export nestedIndex='{
  "fieldName": "dynamic_json",
  "indexName": "json_nested_index",
  "params": {
    "index_type": "AUTOINDEX",
    "json_cast_type": "double",
          "json_path": "dynamic_json[\"nested\"][\"value\"]"
    }
  }'

Utilizzare le funzioni di cast JSON per la conversione dei tipiCompatible with Milvus 2.5.14+

Se una chiave di un campo dinamico contiene valori in un formato non corretto (ad esempio, numeri memorizzati come stringhe), è possibile utilizzare una funzione di cast per convertirli:

# Convert a string to double before indexing
index_params.add_index(
    field_name="dynamic_json", # JSON key name
    index_type="AUTOINDEX",
    index_name="json_string_price_index",
    params={
        "json_path": "dynamic_json['string_price']",
        "json_cast_type": "double", # Must be the output type of the cast function
        "json_cast_function": "STRING_TO_DOUBLE" # Case insensitive; convert string to double
    }
)
Map<String,Object> extraParams5 = new HashMap<>();
extraParams5.put("json_path", "dynamic_json['string_price']");
extraParams5.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
        .fieldName("dynamic_json")
        .indexName("json_string_price_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .extraParams(extraParams5)
        .build());
indexParams.push({
    collection_name: 'my_collection',
    field_name: 'dynamic_json',
    index_name: 'json_string_price_index',
    index_type: 'AUTOINDEX',
    metric_type: 'NONE',
    params: {
      json_path: "dynamic_json['string_price']",
      json_cast_type: 'double',
      json_cast_function: 'STRING_TO_DOUBLE',
    },
  });
jsonIndex5 := index.NewJSONPathIndex(index.AUTOINDEX, "double", `dynamic_json['string_price']`)
    .WithIndexName("json_string_price_index")
indexOpt5 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex5)
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"

export stringPriceIndex='{
  "fieldName": "dynamic_json",
  "indexName": "json_string_price_index",
  "params": {
    "index_type": "AUTOINDEX",
    "json_path": "dynamic_json[\"string_price\"]",
    "json_cast_type": "double",
    "json_cast_function": "STRING_TO_DOUBLE"
  }
}'

  • Se la conversione del tipo fallisce (ad esempio, il valore "not_a_number" non può essere convertito in un numero), il valore viene saltato e non indicizzato.

  • Per i dettagli sui parametri della funzione cast, fare riferimento a Campo JSON.

Applicare gli indici all'insieme

Dopo aver definito i parametri degli indici, è possibile applicarli all'insieme utilizzando create_index():

client.create_index(
    collection_name="my_collection",
    index_params=index_params
)
import io.milvus.v2.service.index.request.CreateIndexReq;

client.createIndex(CreateIndexReq.builder()
        .collectionName("my_collection")
        .indexParams(indexParams)
        .build());
  await client.createIndex(indexParams);
indexTask1, err := client.CreateIndex(ctx, indexOpt1)
if err != nil {
    return err
}
indexTask2, err := client.CreateIndex(ctx, indexOpt2)
if err != nil {
    return err
}
indexTask3, err := client.CreateIndex(ctx, indexOpt3)
if err != nil {
    return err
}
indexTask4, err := client.CreateIndex(ctx, indexOpt4)
if err != nil {
    return err
}
indexTask5, err := client.CreateIndex(ctx, indexOpt5)
if err != nil {
    return err
}
# restful
export indexParams="[
  $varcharIndex,
  $nestedIndex,
  $overviewIndex,
  $wordsIndex,
  $stringPriceIndex
]"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
  \"collectionName\": \"my_collection\",
  \"indexParams\": $indexParams
}"

Filtrare per chiavi di campo dinamiche

Dopo aver inserito entità con chiavi di campo dinamiche, è possibile filtrarle utilizzando espressioni di filtro standard.

  • Per le chiavi non JSON (ad esempio stringhe, numeri, booleani), si può fare riferimento direttamente al nome della chiave.

  • Per le chiavi che memorizzano oggetti JSON, utilizzare la sintassi del percorso JSON per accedere ai valori annidati.

Sulla base dell 'entità di esempio della sezione precedente, le espressioni di filtro valide sono:

filter = 'overview == "Great product"'                # Non-JSON key
filter = 'words >= 100'                               # Non-JSON key
filter = 'dynamic_json["nested"]["value"] < 50'       # JSON object key
String filter = 'overview == "Great product"';
String filter = 'words >= 100';
String filter = 'dynamic_json["nested"]["value"] < 50';
filter = 'overview == "Great product"'                // Non-JSON key
filter = 'words >= 100'                               // Non-JSON key
filter = 'dynamic_json["nested"]["value"] < 50'       // JSON object key
filter := 'overview == "Great product"'
filter := 'words >= 100'
filter := 'dynamic_json["nested"]["value"] < 50'
# restful
export filterOverview='overview == "Great product"'
export filterWords='words >= 100'
export filterNestedValue='dynamic_json["nested"]["value"] < 50'

Recupero delle chiavi di campo dinamiche: Per restituire le chiavi dei campi dinamici nei risultati delle ricerche o delle query, è necessario specificarle esplicitamente nel parametro output_fields, utilizzando la stessa sintassi del percorso JSON del filtro:

# Example: Include dynamic field keys in search results
results = client.search(
    collection_name="my_collection",
    data=[[0.1, 0.2, 0.3, 0.4, 0.5]],
    filter=filter,                         # Filter expression defined earlier
    limit=10,
    output_fields=[
        "overview",                        # Simple dynamic field key
        'dynamic_json["varchar"]'          # Nested JSON key
    ]
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("YOUR_CLUSTER_ENDPOINT")
        .token("YOUR_CLUSTER_TOKEN")
        .build());

FloatVec queryVector = new FloatVec(new float[]{0.1, 0.2, 0.3, 0.4, 0.5});
SearchReq searchReq = SearchReq.builder()
        .collectionName("my_collection")
        .data(Collections.singletonList(queryVector))
        .topK(5)
        .filter(filter)
        .outputFields(Arrays.asList("overview", "dynamic_json['varchar']"))
        .build();

SearchResp searchResp = client.search(searchReq);
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const address = "YOUR_CLUSTER_ENDPOINT";
const token = "YOUR_CLUSTER_TOKEN";
const client = new MilvusClient({address, token});

const query_vector = [0.1, 0.2, 0.3, 0.4, 0.5]

const res = await client.search({
    collection_name: "my_collection",
    data: [query_vector],
    limit: 5,
    filters: filter,
    output_fields: ["overview", "dynamic_json['varchar']"]
})
import (
    "context"
    "fmt"

    "github.com/milvus-io/milvus/client/v2/entity"
    "github.com/milvus-io/milvus/client/v2/milvusclient"
)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

milvusAddr := "YOUR_CLUSTER_ENDPOINT"
token := "YOUR_CLUSTER_TOKEN"

client, err := client.New(ctx, &client.ClientConfig{
    Address: milvusAddr,
    APIKey:  token,
})
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
defer client.Close(ctx)

queryVector := []float32{0.1, 0.2, 0.3, 0.4, 0.5}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "my_collection", // collectionName
    5,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClStrong).
    WithANNSField("my_vector").
    WithFilter(filter).
    WithOutputFields("overview", "dynamic_json['varchar']"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
export FILTER='color like "red%" and likes > 50'

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
  \"collectionName\": \"my_collection\",
  \"data\": [
    [0.1, 0.2, 0.3, 0.4, 0.5]
  ],
  \"annsField\": \"my_vector\",
  \"filter\": \"${FILTER}\",
  \"limit\": 5,
  \"outputFields\": [\"overview\", \"dynamic_json.varchar\"]
}"

Le chiavi di campo dinamiche non sono incluse nei risultati per impostazione predefinita e devono essere richieste esplicitamente.

Per un elenco completo degli operatori e delle espressioni di filtro supportate, consultare la sezione Ricerca filtrata.

Mettere tutto insieme

A questo punto si è imparato a usare il campo dinamico per memorizzare e indicizzare in modo flessibile chiavi non definite nello schema. Una volta inserita la chiave di un campo dinamico, è possibile utilizzarla come qualsiasi altro campo nelle espressioni di filtro, senza bisogno di una sintassi speciale.

Per completare il flusso di lavoro in un'applicazione reale, è necessario anche:

DOMANDE FREQUENTI

Quando è necessario definire un campo esplicitamente nello schema invece di usare una chiave di campo dinamica?

È opportuno definire un campo esplicitamente nello schema invece di usare una chiave di campo dinamica quando:

  • Il campo è spesso incluso in output_fields: Solo i campi definiti esplicitamente sono garantiti per essere recuperati in modo efficiente attraverso output_fields. Le chiavi di campo dinamiche non sono ottimizzate per il recupero ad alta frequenza e possono comportare un sovraccarico di prestazioni.

  • Il campo viene consultato o filtrato frequentemente: Sebbene l'indicizzazione di una chiave di campo dinamica possa fornire prestazioni di filtraggio simili a quelle dei campi dello schema fisso, i campi definiti esplicitamente offrono una struttura più chiara e una migliore manutenibilità.

  • È necessario un controllo completo sul comportamento del campo: I campi espliciti supportano vincoli a livello di schema, convalide e tipizzazioni più chiare, che possono essere utili per gestire l'integrità e la coerenza dei dati.

  • Si vogliono evitare le incoerenze dell'indicizzazione: I dati nelle chiavi di campo dinamiche sono più soggetti a incoerenze nel tipo o nella struttura. L'uso di uno schema fisso aiuta a garantire la qualità dei dati, soprattutto se si prevede di utilizzare l'indicizzazione o il casting.

È possibile creare più indici sulla stessa chiave di campo dinamica con tipi di dati diversi?

No, è possibile creare un solo indice per percorso JSON. Anche se una chiave di campo dinamico contiene valori di tipo misto (ad esempio, alcune stringhe e alcuni numeri), è necessario scegliere un unico json_cast_type quando si indicizza quel percorso. Al momento non sono supportati indici multipli sulla stessa chiave con tipi diversi.

Quando si indicizza una chiave di campo dinamica, cosa succede se il casting dei dati fallisce?

Se si è creato un indice su una chiave di campo dinamico e il casting dei dati non riesce, ad esempio se un valore destinato a essere lanciato su double è una stringa non numerica come "abc", quei valori specifici verranno silenziosamente saltati durante la creazione dell'indice. Non appariranno nell'indice e quindi non saranno restituiti nei risultati di ricerche basate su filtri o query che si basano sull'indice.

Questo ha alcune importanti implicazioni:

  • Nessun fallback alla scansione completa: Se la maggior parte delle entità è indicizzata con successo, le query di filtraggio si baseranno interamente sull'indice. Le entità con errori di casting saranno escluse dal set di risultati, anche se corrispondono logicamente alla condizione del filtro.

  • Rischio di accuratezza della ricerca: In grandi insiemi di dati in cui la qualità dei dati è incoerente (soprattutto nelle chiavi di campo dinamiche), questo comportamento può portare a risultati mancanti inaspettati. È fondamentale garantire una formattazione dei dati coerente e valida prima dell'indicizzazione.

  • Usare con cautela le funzioni cast: Se si usa json_cast_function per convertire le stringhe in numeri durante l'indicizzazione, assicurarsi che i valori delle stringhe siano convertibili in modo affidabile. Una mancata corrispondenza tra json_cast_type e il tipo effettivamente convertito provocherà errori o voci saltate.

Cosa succede se la query utilizza un tipo di dati diverso dal tipo di cast indicizzato?

Se la query confronta una chiave di campo dinamica utilizzando un tipo di dati diverso da quello utilizzato nell'indice (ad esempio, una query con un confronto tra stringhe quando l'indice è stato lanciato in double), il sistema non utilizzerà l'indice e potrà tornare a una scansione completa solo se possibile. Per ottenere prestazioni e precisione ottimali, assicurarsi che il tipo di query corrisponda a json_cast_type utilizzato durante la creazione dell'indice.