milvus-logo
LFAI
Casa
  • Guida per l'utente

Campo Array

Il tipo Array viene utilizzato per memorizzare campi contenenti più valori dello stesso tipo di dati. Offre un modo flessibile per memorizzare attributi con più elementi, rendendolo particolarmente utile in scenari in cui è necessario salvare una serie di dati correlati. In Milvus è possibile memorizzare campi Array insieme a dati vettoriali, consentendo così di effettuare query e filtraggi più complessi.

Ad esempio, in un sistema di raccomandazione musicale, un campo Array può memorizzare un elenco di tag per una canzone; nell'analisi del comportamento degli utenti, può memorizzare le valutazioni degli utenti per le canzoni. Di seguito è riportato un esempio di un tipico campo Array.

{
  "tags": ["pop", "rock", "classic"],
  "ratings": [5, 4, 3]
}

In questo esempio, tags e ratings sono entrambi campi Array. Il campo tags è un array di stringhe che rappresenta generi di canzoni come pop, rock e classico, mentre il campo ratings è un array di numeri interi che rappresenta le valutazioni degli utenti per la canzone, da 1 a 5. Questi campi Array forniscono un modo flessibile per memorizzare dati a più valori, facilitando l'esecuzione di analisi dettagliate durante le query e i filtri.

Aggiungere un campo Array

Per utilizzare i campi Array in Milvus, è necessario definire il tipo di campo corrispondente durante la creazione dello schema della collezione. Questo processo comprende.

  1. Impostare datatype sul tipo di dati Array supportato, ARRAY.

  2. Usare il parametro element_type per specificare il tipo di dati degli elementi dell'array. Questo può essere un qualsiasi tipo di dato scalare supportato da Milvus, come VARCHAR o INT64. Tutti gli elementi di una stessa matrice devono avere lo stesso tipo di dato.

  3. Usare il parametro max_capacity per definire la capacità massima dell'array, cioè il numero massimo di elementi che può contenere.

Ecco come definire uno schema di collezione che includa campi Array.

from pymilvus import MilvusClient, DataType

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

schema = client.create_schema(
    auto_id=False,
    enable_dynamic_fields=True,
)

# Add an Array field with elements of type VARCHAR
schema.add_field(field_name="tags", datatype=DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=10)
# Add an Array field with elements of type INT64
schema.add_field(field_name="ratings", datatype=DataType.ARRAY, element_type=DataType.INT64, max_capacity=5)

# Add primary field
schema.add_field(field_name="pk", datatype=DataType.INT64, is_primary=True)

# Add vector field
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());
        
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);

schema.addField(AddFieldReq.builder()
        .fieldName("tags")
        .dataType(DataType.Array)
        .elementType(DataType.VarChar)
        .maxCapacity(10)
        .build());

schema.addField(AddFieldReq.builder()
        .fieldName("ratings")
        .dataType(DataType.Array)
        .elementType(DataType.Int64)
        .maxCapacity(5)
        .build());

schema.addField(AddFieldReq.builder()
        .fieldName("pk")
        .dataType(DataType.Int64)
        .isPrimaryKey(true)
        .build());

schema.addField(AddFieldReq.builder()
        .fieldName("embedding")
        .dataType(DataType.FloatVector)
        .dimension(3)
        .build());

import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const schema = [
  {
    name: "tags",
    data_type: DataType.Array,
    element_type: DataType.VarChar,
    max_capacity: 10,
    max_length: 65535
  },
  {
    name: "rating",
    data_type: DataType.Array,
    element_type: DataType.Int64,
    max_capacity: 5,
  },
  {
    name: "pk",
    data_type: DataType.Int64,
    is_primary_key: true,
  },
  {
    name: "embedding",
    data_type: DataType.FloatVector,
    dim: 3,
  },
];

export arrayField1='{
    "fieldName": "tags",
    "dataType": "Array",
    "elementDataType": "VarChar",
    "elementTypeParams": {
        "max_capacity": 10,
        "max_length": 100
    }
}'

export arrayField2='{
    "fieldName": "ratings",
    "dataType": "Array",
    "elementDataType": "Int64",
    "elementTypeParams": {
        "max_capacity": 5
    }
}'

export pkField='{
    "fieldName": "pk",
    "dataType": "Int64",
    "isPrimary": true
}'

export vectorField='{
    "fieldName": "embedding",
    "dataType": "FloatVector",
    "elementTypeParams": {
        "dim": 3
    }
}'

export schema="{
    \"autoID\": false,
    \"fields\": [
        $arrayField1,
        $arrayField2,
        $pkField,
        $vectorField
    ]
}"

In questo esempio.

  • tags è un array di stringhe con element_type impostato su VARCHAR, a indicare che gli elementi dell'array devono essere stringhe. max_capacity è impostato su 10, a significare che l'array può contenere fino a 10 elementi.

  • ratings è un array di interi con element_type impostato su INT64, a indicare che gli elementi devono essere interi. max_capacity è impostato su 5, consentendo un massimo di 5 valutazioni.

  • Aggiungiamo anche un campo chiave primaria pk e un campo vettore embedding.

Il campo primario e il campo vettore sono obbligatori quando si crea una collezione. Il campo primario identifica in modo univoco ogni entità, mentre il campo vettoriale è fondamentale per la ricerca delle somiglianze. Per maggiori dettagli, consultare Campo primario e AutoID, Vettore denso, Vettore binario o Vettore sparso.

Impostare i parametri dell'indice

L'impostazione dei parametri dell'indice per i campi Array è facoltativa, ma può migliorare significativamente l'efficienza del recupero.

Nell'esempio seguente, creiamo un AUTOINDEX per il campo tags, il che significa che Milvus creerà automaticamente un indice scalare appropriato in base al tipo di dati.

# Prepare index parameters
index_params = client.prepare_index_params()  # Prepare IndexParams object

index_params.add_index(
    field_name="tags",  # Name of the Array field to index
    index_type="AUTOINDEX",  # Index type
    index_name="inverted_index"  # Index name
)

import io.milvus.v2.common.IndexParam;
import java.util.*;

List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
        .fieldName("tags")
        .indexName("inverted_index")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .build());

const indexParams = [{
    index_name: 'inverted_index',
    field_name: 'tags',
    index_type: IndexType.AUTOINDEX,
)];

export indexParams='[
        {
            "fieldName": "tags",
            "indexName": "inverted_index",
            "indexType": "AUTOINDEX"
        }
    ]'

Oltre a AUTOINDEX, è possibile specificare altri tipi di indici scalari come INVERTED o BITMAP. Per i tipi di indice supportati, fare riferimento a Indici scalari.

Inoltre, è necessario creare un indice per il campo vettoriale prima di creare la collezione. In questo esempio, si usa AUTOINDEX per semplificare l'impostazione dell'indice vettoriale.

# Add vector index
index_params.add_index(
    field_name="embedding",
    index_type="AUTOINDEX",  # Use automatic indexing to simplify complex index settings
    metric_type="COSINE"  # Specify similarity metric type, such as L2, COSINE, or IP
)

indexes.add(IndexParam.builder()
        .fieldName("embedding")
        .indexType(IndexParam.IndexType.AUTOINDEX)
        .metricType(IndexParam.MetricType.COSINE)
        .build());

 indexParams.push({
    index_name: 'embedding_index',
    field_name: 'embedding',
    index_type: IndexType.AUTOINDEX,
});

export indexParams='[
        {
            "fieldName": "tags",
            "indexName": "inverted_index",
            "indexType": "AUTOINDEX"
        },
        {
            "fieldName": "embedding",
            "metricType": "COSINE",
            "indexType": "AUTOINDEX"
        }
    ]'

Creare la raccolta

Utilizzare i parametri dello schema e dell'indice definiti per creare una collezione.

client.create_collection(
    collection_name="my_array_collection",
    schema=schema,
    index_params=index_params
)

CreateCollectionReq requestCreate = CreateCollectionReq.builder()
        .collectionName("my_array_collection")
        .collectionSchema(schema)
        .indexParams(indexes)
        .build();
client.createCollection(requestCreate);

client.create_collection({
    collection_name: "my_array_collection",
    schema: schema,
    index_params: indexParams
})

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
    \"collectionName\": \"my_array_collection\",
    \"schema\": $schema,
    \"indexParams\": $indexParams
}"

Inserire i dati

Dopo aver creato la collezione, è possibile inserire i dati che includono i campi Array.

data = [
    {
        "tags": ["pop", "rock", "classic"],
        "ratings": [5, 4, 3],
        "pk": 1,
        "embedding": [0.12, 0.34, 0.56]
    },
    {
        "tags": ["jazz", "blues"],
        "ratings": [4, 5],
        "pk": 2,
        "embedding": [0.78, 0.91, 0.23]
    },
    {
        "tags": ["electronic", "dance"],
        "ratings": [3, 3, 4],
        "pk": 3,
        "embedding": [0.67, 0.45, 0.89]
    }
]

client.insert(
    collection_name="my_array_collection",
    data=data
)

import com.google.gson.Gson;
import com.google.gson.JsonObject;

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

List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
rows.add(gson.fromJson("{\"tags\": [\"pop\", \"rock\", \"classic\"], \"ratings\": [5, 4, 3], \"pk\": 1, \"embedding\": [0.1, 0.2, 0.3]}", JsonObject.class));
rows.add(gson.fromJson("{\"tags\": [\"jazz\", \"blues\"], \"ratings\": [4, 5], \"pk\": 2, \"embedding\": [0.4, 0.5, 0.6]}", JsonObject.class));
rows.add(gson.fromJson("{\"tags\": [\"electronic\", \"dance\"], \"ratings\": [3, 3, 4], \"pk\": 3, \"embedding\": [0.7, 0.8, 0.9]}", JsonObject.class));

InsertResp insertR = client.insert(InsertReq.builder()
        .collectionName("my_array_collection")
        .data(rows)
        .build());

const data = [
    {
        "tags": ["pop", "rock", "classic"],
        "ratings": [5, 4, 3],
        "pk": 1,
        "embedding": [0.12, 0.34, 0.56]
    },
    {
        "tags": ["jazz", "blues"],
        "ratings": [4, 5],
        "pk": 2,
        "embedding": [0.78, 0.91, 0.23]
    },
    {
        "tags": ["electronic", "dance"],
        "ratings": [3, 3, 4],
        "pk": 3,
        "embedding": [0.67, 0.45, 0.89]
    }
];

client.insert({
  collection_name: "my_array_collection",
  data: data,
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
    "data": [
        {
        "tags": ["pop", "rock", "classic"],
        "ratings": [5, 4, 3],
        "pk": 1,
        "embedding": [0.12, 0.34, 0.56]
    },
    {
        "tags": ["jazz", "blues"],
        "ratings": [4, 5],
        "pk": 2,
        "embedding": [0.78, 0.91, 0.23]
    },
    {
        "tags": ["electronic", "dance"],
        "ratings": [3, 3, 4],
        "pk": 3,
        "embedding": [0.67, 0.45, 0.89]
    }       
    ],
    "collectionName": "my_array_collection"
}'

In questo esempio.

  • Ogni inserimento di dati include un campo primario (pk), mentre tags e ratings sono campi Array utilizzati per memorizzare tag e valutazioni.

  • embedding è un campo vettoriale tridimensionale utilizzato per le ricerche di similarità vettoriale.

Ricerca e interrogazione

I campi array consentono di filtrare gli scalari durante le ricerche, migliorando le capacità di ricerca vettoriale di Milvus. È possibile eseguire interrogazioni basate sulle proprietà dei campi Array insieme alle ricerche di similarità vettoriale.

Filtrare le query

È possibile filtrare i dati in base alle proprietà dei campi Array, ad esempio accedendo a un elemento specifico o verificando se un elemento dell'array soddisfa una determinata condizione.

filter = 'ratings[0] < 4'

res = client.query(
    collection_name="my_array_collection",
    filter=filter,
    output_fields=["tags", "ratings", "embedding"]
)

print(res)

# Output
# data: ["{'pk': 3, 'tags': ['electronic', 'dance'], 'ratings': [3, 3, 4], 'embedding': [np.float32(0.67), np.float32(0.45), np.float32(0.89)]}"] 

import io.milvus.v2.service.vector.request.QueryReq;
import io.milvus.v2.service.vector.response.QueryResp;

String filter = "ratings[0] < 4";
QueryResp resp = client.query(QueryReq.builder()
        .collectionName("my_array_collection")
        .filter(filter)
        .outputFields(Arrays.asList("tags", "ratings", "embedding"))
        .build());

System.out.println(resp.getQueryResults());

// Output
//
// [QueryResp.QueryResult(entity={ratings=[3, 3, 4], pk=3, embedding=[0.7, 0.8, 0.9], tags=[electronic, dance]})]

client.query({
    collection_name: 'my_array_collection',
    filter: 'ratings[0] < 4',
    output_fields: ['tags', 'ratings', 'embedding']
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/query" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
    "collectionName": "my_array_collection",
    "filter": "ratings[0] < 4",
    "outputFields": ["tags", "ratings", "embedding"]
}'
# {"code":0,"cost":0,"data":[{"embedding":[0.67,0.45,0.89],"pk":3,"ratings":{"Data":{"LongData":{"data":[3,3,4]}}},"tags":{"Data":{"StringData":{"data":["electronic","dance"]}}}}]}

In questa query, Milvus filtra le entità in cui il primo elemento dell'array ratings è inferiore a 4, restituendo le entità che soddisfano la condizione.

Ricerca vettoriale con filtraggio di array

Combinando la similarità vettoriale con il filtraggio degli array, è possibile garantire che i dati recuperati non solo siano simili nella semantica, ma soddisfino anche condizioni specifiche, rendendo i risultati della ricerca più accurati e allineati alle esigenze aziendali.

filter = 'tags[0] == "pop"'

res = client.search(
    collection_name="my_array_collection",
    data=[[0.3, -0.6, 0.1]],
    limit=5,
    search_params={"params": {"nprobe": 10}},
    output_fields=["tags", "ratings", "embedding"],
    filter=filter
)

print(res)

# Output
# data: ["[{'id': 1, 'distance': 1.1276001930236816, 'entity': {'ratings': [5, 4, 3], 'embedding': [0.11999999731779099, 0.3400000035762787, 0.5600000023841858], 'tags': ['pop', 'rock', 'classic']}}]"]

import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.response.SearchResp;

String filter = "tags[0] == \"pop\"";
SearchResp resp = client.search(SearchReq.builder()
        .collectionName("my_array_collection")
        .annsField("embedding")
        .data(Collections.singletonList(new FloatVec(new float[]{0.3f, -0.6f, 0.1f})))
        .topK(5)
        .outputFields(Arrays.asList("tags", "ratings", "embedding"))
        .filter(filter)
        .build());

System.out.println(resp.getSearchResults());

// Output
//
// [[SearchResp.SearchResult(entity={ratings=[5, 4, 3], embedding=[0.1, 0.2, 0.3], tags=[pop, rock, classic]}, score=-0.2364331, id=1)]]

client.search({
    collection_name: 'my_array_collection',
    data: [0.3, -0.6, 0.1],
    limit: 5,
    output_fields: ['tags', 'ratings', 'embdding'],
    filter: 'tags[0] == "pop"'
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
    "collectionName": "my_array_collection",
    "data": [
        [0.3, -0.6, 0.1]
    ],
    "annsField": "embedding",
    "limit": 5,
    "filter": "tags[0] == \"pop\"",
    "outputFields": ["tags", "ratings", "embedding"]
}'

# {"code":0,"cost":0,"data":[{"distance":-0.24793813,"embedding":[0.12,0.34,0.56],"id":1,"ratings":{"Data":{"LongData":{"data":[5,4,3]}}},"tags":{"Data":{"StringData":{"data":["pop","rock","classic"]}}}}]}

In questo esempio, Milvus restituisce le 5 entità più simili al vettore della query, con il primo elemento dell'array tags che è "pop".

Inoltre, Milvus supporta operatori avanzati di filtraggio degli array come ARRAY_CONTAINS, ARRAY_CONTAINS_ALL, ARRAY_CONTAINS_ANY e ARRAY_LENGTH per migliorare ulteriormente le capacità di interrogazione. Per maggiori dettagli, consultare Filtraggio dei metadati.

Limiti

  • Tipo di dati: Tutti gli elementi di un campo Array devono avere lo stesso tipo di dati, come specificato da element_type.

  • Capacità della matrice: Il numero di elementi in un campo Array deve essere inferiore o uguale alla capacità massima definita al momento della creazione dell'Array, come specificato da max_capacity.

  • Gestione delle stringhe: I valori delle stringhe nei campi Array sono memorizzati così come sono, senza escape semantico o conversione. Ad esempio, 'a"b', "a'b", 'a\'b' e "a\"b" sono memorizzati come inseriti, mentre 'a'b' e "a"b" sono considerati valori non validi.

Tradotto daDeepL

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
Feedback

Questa pagina è stata utile?