Campo Array
El tipo Array se utiliza para almacenar campos que contienen múltiples valores del mismo tipo de datos. Proporciona una forma flexible de almacenar atributos con múltiples elementos, por lo que es especialmente útil en escenarios en los que es necesario guardar un conjunto de datos relacionados. En Milvus, puede almacenar campos Array junto con datos vectoriales, lo que permite requisitos de consulta y filtrado más complejos.
Por ejemplo, en un sistema de recomendación de música, un campo Array puede almacenar una lista de etiquetas para una canción; en el análisis del comportamiento del usuario, puede almacenar las valoraciones del usuario para las canciones. A continuación se muestra un ejemplo de un campo Array típico.
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3]
}
En este ejemplo, tags
y ratings
son campos Array. El campo tags
es una matriz de cadenas que representa géneros de canciones como pop, rock y clásica, mientras que el campo ratings
es una matriz de enteros que representa las valoraciones de los usuarios sobre la canción, que van de 1 a 5. Estos campos Array proporcionan una forma flexible de almacenar datos multivalor, facilitando la realización de análisis detallados durante las consultas y el filtrado.
Añadir un campo Array
Para utilizar campos Array en Milvus, defina el tipo de campo relevante al crear el esquema de la colección. Este proceso incluye.
Establecer
datatype
en el tipo de datos Array soportado,ARRAY
.Utilizar el parámetro
element_type
para especificar el tipo de datos de los elementos del array. Este puede ser cualquier tipo de datos escalar soportado por Milvus, comoVARCHAR
oINT64
. Todos los elementos en el mismo Array deben ser del mismo tipo de datos.Utilizando el parámetro
max_capacity
para definir la capacidad máxima del array, es decir, el número máximo de elementos que puede contener.
A continuación se muestra cómo definir un esquema de colección que incluya campos 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
]
}"
En este ejemplo
tags
es un array de cadenas conelement_type
establecido enVARCHAR
, lo que indica que los elementos del array deben ser cadenas.max_capacity
está establecido en 10, lo que significa que el array puede contener hasta 10 elementos.ratings
es una matriz de enteros conelement_type
establecido enINT64
, lo que indica que los elementos deben ser enteros.max_capacity
está establecido en 5, lo que permite hasta 5 clasificaciones.También añadimos un campo de clave primaria
pk
y un campo vectorialembedding
.
El campo primario y el campo vectorial son obligatorios al crear una colección. El campo primario identifica de forma única a cada entidad, mientras que el campo vectorial es crucial para la búsqueda de similitudes. Para más detalles, consulte Campo primario y AutoID, Vector denso, Vector binario o Vector disperso.
Establecer parámetros de índice
Establecer parámetros de índice para los campos de matriz es opcional, pero puede mejorar significativamente la eficacia de la recuperación.
En el siguiente ejemplo, creamos un AUTOINDEX
para el campo tags
, lo que significa que Milvus creará automáticamente un índice escalar apropiado basado en el tipo de datos.
# 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"
}
]'
Además de AUTOINDEX
, puede especificar otros tipos de índices escalares como INVERTED
o BITMAP
. Para conocer los tipos de índice admitidos, consulte Índices escalares.
Además, debes crear un índice para el campo vectorial antes de crear la colección. En este ejemplo, utilizamos AUTOINDEX
para simplificar la configuración del índice vectorial.
# 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"
}
]'
Crear colección
Utiliza los parámetros de esquema e índice definidos para crear una colección.
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
}"
Insertar datos
Después de crear la colección, puedes insertar datos que incluyan campos 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"
}'
En este ejemplo.
Cada entrada de datos incluye un campo primario (
pk
), mientras quetags
yratings
son campos Array utilizados para almacenar etiquetas y valoraciones.embedding
es un campo vectorial tridimensional utilizado para búsquedas de similitud vectorial.
Búsqueda y consulta
Los campos de matriz permiten el filtrado escalar durante las búsquedas, mejorando las capacidades de búsqueda vectorial de Milvus. Puede realizar consultas basadas en las propiedades de los campos de matriz junto con las búsquedas de similitud vectorial.
Filtrar consultas
Puede filtrar datos basándose en las propiedades de los campos Array, como acceder a un elemento específico o comprobar si un elemento del array cumple una determinada condición.
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"]}}}}]}
En esta consulta, Milvus filtra las entidades en las que el primer elemento de la matriz ratings
es inferior a 4, devolviendo las entidades que cumplen la condición.
Búsqueda vectorial con filtrado de matrices
Al combinar la similitud de vectores con el filtrado de matrices, puede asegurarse de que los datos recuperados no solo son similares en semántica, sino que también cumplen condiciones específicas, lo que hace que los resultados de la búsqueda sean más precisos y se ajusten a las necesidades empresariales.
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"]}}}}]}
En este ejemplo, Milvus devuelve las 5 entidades más similares al vector de consulta, siendo el primer elemento de la matriz tags
"pop"
.
Además, Milvus soporta operadores avanzados de filtrado de matrices como ARRAY_CONTAINS
, ARRAY_CONTAINS_ALL
, ARRAY_CONTAINS_ANY
, y ARRAY_LENGTH
para mejorar aún más las capacidades de consulta. Para más detalles, consulte Filtrado de metadatos.
Límites
Tipo de datos: Todos los elementos de un campo de matriz deben tener el mismo tipo de datos, tal y como se especifica en
element_type
.Capacidad dela matriz: El número de elementos de un campo de matriz debe ser inferior o igual a la capacidad máxima definida cuando se creó la matriz, tal y como se especifica en
max_capacity
.Tratamiento de cadenas: Los valores de cadena de los campos de matriz se almacenan tal cual, sin escapes semánticos ni conversiones. Por ejemplo,
'a"b'
,"a'b"
,'a\'b'
, y"a\"b"
se almacenan tal y como se introducen, mientras que'a'b'
y"a"b"
se consideran valores no válidos.