Array-Feld
Der Typ Array wird verwendet, um Felder zu speichern, die mehrere Werte desselben Datentyps enthalten. Er bietet eine flexible Möglichkeit, Attribute mit mehreren Elementen zu speichern, was ihn besonders in Szenarien nützlich macht, in denen eine Reihe von zusammenhängenden Daten gespeichert werden muss. In Milvus können Sie Array-Felder neben Vektordaten speichern, was komplexere Abfrage- und Filteranforderungen ermöglicht.
In einem Musikempfehlungssystem kann ein Array-Feld zum Beispiel eine Liste von Tags für einen Song speichern; in der Analyse des Benutzerverhaltens kann es Benutzerbewertungen für Songs speichern. Nachfolgend finden Sie ein Beispiel für ein typisches Array-Feld.
{
"tags": ["pop", "rock", "classic"],
"ratings": [5, 4, 3]
}
In diesem Beispiel sind tags
und ratings
beides Array-Felder. Das Feld tags
ist ein String-Array, das Song-Genres wie Pop, Rock und Klassik repräsentiert, während das Feld ratings
ein Integer-Array ist, das Benutzerbewertungen für den Song im Bereich von 1 bis 5 darstellt. Diese Array-Felder bieten eine flexible Möglichkeit, mehrwertige Daten zu speichern, was die Durchführung detaillierter Analysen bei Abfragen und Filterungen erleichtert.
Array-Feld hinzufügen
Um Array-Felder in Milvus zu verwenden, definieren Sie den entsprechenden Feldtyp beim Erstellen des Sammlungsschemas. Dieser Prozess beinhaltet.
Einstellung von
datatype
auf den unterstützten Array-DatentypARRAY
.Verwenden Sie den Parameter
element_type
, um den Datentyp der Elemente im Array anzugeben. Dies kann ein beliebiger skalarer Datentyp sein, der von Milvus unterstützt wird, z. B.VARCHAR
oderINT64
. Alle Elemente in einem Array müssen denselben Datentyp haben.Verwenden Sie den Parameter
max_capacity
, um die maximale Kapazität des Arrays zu definieren, d.h. die maximale Anzahl der Elemente, die es enthalten kann.
So definieren Sie ein Auflistungsschema, das Array-Felder enthält.
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 diesem Beispiel.
tags
ist ein String-Array, wobeielement_type
aufVARCHAR
gesetzt ist, was bedeutet, dass die Elemente im Array Strings sein müssen.max_capacity
ist auf 10 gesetzt, was bedeutet, dass das Array bis zu 10 Elemente enthalten kann.ratings
ist ein Integer-Array, bei demelement_type
aufINT64
gesetzt ist, was bedeutet, dass die Elemente Integer sein müssen.max_capacity
ist auf 5 gesetzt, was bedeutet, dass bis zu 5 Bewertungen möglich sind.Wir fügen auch ein Primärschlüsselfeld
pk
und ein Vektorfeldembedding
hinzu.
Das Primärfeld und das Vektorfeld sind obligatorisch, wenn Sie eine Sammlung erstellen. Das Primärfeld identifiziert jede Entität eindeutig, während das Vektorfeld für die Ähnlichkeitssuche entscheidend ist. Weitere Einzelheiten finden Sie unter Primärfeld & AutoID, Dense Vector, Binary Vector oder Sparse Vector.
Index-Parameter setzen
Das Setzen von Indexparametern für Array-Felder ist optional, kann aber die Abfrageeffizienz erheblich verbessern.
Im folgenden Beispiel erstellen wir einen AUTOINDEX
für das Feld tags
, was bedeutet, dass Milvus automatisch einen passenden skalaren Index basierend auf dem Datentyp erstellt.
# 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"
}
]'
Zusätzlich zu AUTOINDEX
können Sie andere skalare Indextypen wie INVERTED
oder BITMAP
angeben. Unterstützte Indextypen finden Sie unter Skalare Indizes.
Außerdem müssen Sie einen Index für das Vektorfeld erstellen, bevor Sie die Sammlung erstellen. In diesem Beispiel verwenden wir AUTOINDEX
, um die Einrichtung des Vektorindex zu vereinfachen.
# 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"
}
]'
Sammlung erstellen
Verwenden Sie die definierten Schema- und Indexparameter, um eine Sammlung zu erstellen.
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
}"
Daten einfügen
Nachdem Sie die Sammlung erstellt haben, können Sie Daten einfügen, die Array-Felder enthalten.
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 diesem Beispiel.
Jeder Dateneintrag enthält ein Primärfeld (
pk
), währendtags
undratings
Array-Felder sind, die zum Speichern von Tags und Bewertungen verwendet werden.embedding
ist ein 3-dimensionales Vektorfeld, das für Vektorähnlichkeitssuchen verwendet wird.
Suche und Abfrage
Array-Felder ermöglichen eine skalare Filterung während der Suche und erweitern die Vektorsuchfunktionen von Milvus. Sie können Abfragen basierend auf den Eigenschaften von Array-Feldern neben Vektorähnlichkeitssuchen durchführen.
Filter-Abfragen
Sie können Daten auf der Grundlage von Eigenschaften von Array-Feldern filtern, z. B. auf ein bestimmtes Element zugreifen oder prüfen, ob ein Array-Element eine bestimmte Bedingung erfüllt.
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 dieser Abfrage filtert Milvus Entitäten heraus, bei denen das erste Element des Arrays ratings
kleiner als 4 ist, und gibt Entitäten zurück, die die Bedingung erfüllen.
Vektorsuche mit Array-Filterung
Durch die Kombination von Vektorähnlichkeit mit Array-Filterung können Sie sicherstellen, dass die abgerufenen Daten nicht nur in der Semantik ähnlich sind, sondern auch bestimmte Bedingungen erfüllen, wodurch die Suchergebnisse genauer werden und den Geschäftsanforderungen entsprechen.
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 diesem Beispiel gibt Milvus die Top 5 Entitäten zurück, die dem Abfragevektor am ähnlichsten sind, wobei das erste Element des Arrays tags
"pop"
ist.
Darüber hinaus unterstützt Milvus erweiterte Array-Filteroperatoren wie ARRAY_CONTAINS
, ARRAY_CONTAINS_ALL
, ARRAY_CONTAINS_ANY
und ARRAY_LENGTH
, um die Abfragemöglichkeiten weiter zu verbessern. Weitere Details finden Sie unter Metadaten-Filterung.
Grenzwerte
Datentyp: Alle Elemente in einem Array-Feld müssen denselben Datentyp haben, wie in
element_type
angegeben.Array-Kapazität: Die Anzahl der Elemente in einem Array-Feld muss kleiner oder gleich der maximalen Kapazität sein, die bei der Erstellung des Arrays festgelegt wurde, wie unter
max_capacity
angegeben.String-Behandlung: String-Werte in Array-Feldern werden so gespeichert, wie sie sind, ohne semantisches Escaping oder Konvertierung. Zum Beispiel werden
'a"b'
,"a'b"
,'a\'b'
und"a\"b"
wie eingegeben gespeichert, während'a'b'
und"a"b"
als ungültige Werte betrachtet werden.