StructArray
엔티티의 Struct 필드 배열 또는 StructArray 필드는 정렬된 Struct 요소 집합을 저장합니다. 배열의 각 Struct는 여러 벡터와 스칼라 필드로 구성된 동일한 사전 정의된 스키마를 공유합니다.
다음은 StructArray 필드를 포함하는 컬렉션의 엔티티 예시입니다.
{
'id': 0,
'title': 'Walden',
'title_vector': [0.1, 0.2, 0.3, 0.4, 0.5],
'author': 'Henry David Thoreau',
'year_of_publication': 1845,
'chunks': [
{
'text': 'When I wrote the following pages, or rather the bulk of them...',
'text_vector': [0.3, 0.2, 0.3, 0.2, 0.5],
'chapter': 'Economy',
},
{
'text': 'I would fain say something, not so much concerning the Chinese and...',
'text_vector': [0.7, 0.4, 0.2, 0.7, 0.8],
'chapter': 'Economy'
}
]
// hightlight-end
}
위의 예에서 chunks 필드는 StructArray 필드이며, 각 Struct 요소에는 text, text_vector, chapter 과 같은 자체 필드가 포함되어 있습니다.
사용 시기
자율 주행에서 멀티모달 검색에 이르기까지 최신 AI 애플리케이션은 점점 더 중첩된 이기종 데이터에 의존하고 있습니다. 기존의 플랫 데이터 모델은'주석이 달린 청크가 많은 하나의 문서' 또는'여러 개의 주행 장면이 관찰되는 하나의 주행 장면'과 같은 복잡한 관계를 표현하는 데 어려움을 겪습니다. 바로 이 부분에서 Milvus의 StructArray 데이터 유형이 빛을 발합니다.
StructArray 필드가 애플리케이션 시나리오에 적합한지 빠르게 결정하려면 다음 사항을 고려하세요:
데이터가 많은 주석이 달린 청크가 있는 하나의 문서와 같이 계층적 구조로 되어 있습니다.
위의 예에서처럼 검색 결과는 청크가 아닌 문서여야 합니다.
검색 결과에 대량의 중복 엔터티가 포함되어 있어 그룹화, 중복 제거, 순위 재지정과 같은 기술을 사용하여 최종 결과를 검색하는 데 어려움을 겪고 있습니다.
위의 질문에 대한 답변이 '예'라면 StructArray를 사용해야 합니다.
제한 사항
데이터 유형
컬렉션을 만들 때 배열 필드에 있는 요소의 데이터 유형으로 Struct 유형을 사용할 수 있습니다. 그러나 기존 컬렉션에 StructArray를 추가할 수 없으며, Milvus는 컬렉션 필드의 데이터 유형으로 Struct 유형을 사용하는 것을 지원하지 않습니다.
배열 필드의 구조체는 동일한 스키마를 공유하므로 배열 필드를 만들 때 정의해야 합니다.
Struct 스키마에는 아래 목록과 같이 벡터 필드와 스칼라 필드가 모두 포함됩니다:
<div> Applicable vector fields: - `FLOAT_VECTOR` - `FLOAT16_VECTOR` - `BFLOAT16_VECTOR` - `INT8_VECTOR` - `BINARY_VECTOR` </div> <div> Applicable scalar fields: - `VARCHAR` - `INT8/16/32/64` - `FLOAT` - `DOUBLE` - `BOOL` </div>컬렉션 수준과 구조체 모두에서 벡터 필드의 수를 10보다 크지 않게 유지합니다.
널 가능 및 기본값
StructArray 필드는 널러블이 아니며 기본값을 허용하지 않습니다.
함수
함수를 사용하여 구조체 내의 스칼라 필드에서 벡터 필드를 파생할 수 없습니다.
인덱스 유형 및 메트릭 유형
컬렉션의 모든 벡터 필드는 색인화되어야 합니다. StructArray 필드 내의 벡터 필드를 인덱싱하기 위해 Milvus는 임베딩 목록을 사용하여 각 Struct 요소의 벡터 임베딩을 구성하고 전체 임베딩 목록을 전체적으로 인덱싱합니다.
AUTOINDEX또는HNSW을 인덱스 유형으로 사용하고 아래 나열된 메트릭 유형을 사용하여 StructArray 필드에 있는 임베딩 목록에 대한 인덱스를 만들 수 있습니다.인덱스 유형
메트릭 유형
설명
AUTOINDEXHNSWIVF_FLATDISKANN
MAX_SIM_COSINEMAX_SIM_IPMAX_SIM_L2
다음 유형의 임베딩 목록의 경우:
FLOAT_VECTORFLOAT16_VECTORBFLOAT16_VECTORINT8_VECTORBINARY_VECTOR
Milvus가 쿼리와 임베딩 목록 간의 유사도를 계산하는 방법에 대한 자세한 내용은 최대 유사도를 참조하세요.
StructArray 필드의 스칼라 필드는 다음과 같은 인덱스 유형을 지원합니다:
데이터 업서트
구조체는 병합 모드에서 업서트를 지원하지 않습니다. 그러나 재정의 모드에서는 여전히 업서트를 수행하여 구조체의 데이터를 업데이트할 수 있습니다. 병합 모드와 재정의 모드에서 업서트의 차이점에 대한 자세한 내용은 엔티티 업서트를 참조하십시오.
스칼라 필터링
일치 계열의 요소 필터 및 연산자를 사용하여 StructArray 필드의 스칼라 하위 필드에 대해 스칼라 필터링을 수행할 수 있습니다. 자세한 내용은 StructArray 필드에서 스칼라 필터링을 참조하십시오.
StructArray 추가하기
Milvus에서 StructArray 필드를 추가하려면 컬렉션을 생성할 때 배열 필드를 정의하고 해당 요소의 데이터 유형을 Struct로 설정해야 합니다. 그 과정은 다음과 같습니다:
컬렉션 스키마에 필드를 배열 필드로 추가할 때 필드의 데이터 유형을
DataType.ARRAY로 설정합니다.필드의
element_type속성을DataType.STRUCT으로 설정하여 필드를 Struct 배열로 만듭니다.Struct 스키마를 만들고 필수 필드를 포함합니다. 그런 다음 필드의
struct_schema속성에서 Struct 스키마를 참조합니다.필드의
max_capacity속성을 적절한 값으로 설정하여 각 엔티티가 이 필드에 포함할 수 있는 구조체의 최대 개수를 지정합니다.(선택 사항) Struct 요소 내의 모든 필드에
mmap.enabled을 설정하여 Struct의 핫 데이터와 콜드 데이터의 균형을 맞출 수 있습니다.
다음은 StructArray 필드를 포함하는 컬렉션 스키마를 정의하는 방법입니다:
from pymilvus import MilvusClient, DataType
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
schema = client.create_schema()
# add the primary field to the collection
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
# add some scalar fields to the collection
schema.add_field(field_name="title", datatype=DataType.VARCHAR, max_length=512)
schema.add_field(field_name="author", datatype=DataType.VARCHAR, max_length=512)
schema.add_field(field_name="year_of_publication", datatype=DataType.INT64)
# add a vector field to the collection
schema.add_field(field_name="title_vector", datatype=DataType.FLOAT_VECTOR, dim=5)
# Create a struct schema
struct_schema = client.create_struct_field_schema()
# add a scalar field to the struct
struct_schema.add_field("text", DataType.VARCHAR, max_length=65535)
struct_schema.add_field("chapter", DataType.VARCHAR, max_length=512)
# add a vector field to the struct with mmap enabled
struct_schema.add_field("text_vector", DataType.FLOAT_VECTOR, mmap_enabled=True, dim=5)
# reference the struct schema in an Array field with its
# element type set to `DataType.STRUCT`
schema.add_field("chunks", datatype=DataType.ARRAY, element_type=DataType.STRUCT,
struct_schema=struct_schema, max_capacity=1000)
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
.build();
collectionSchema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("title")
.dataType(DataType.VarChar)
.maxLength(512)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("author")
.dataType(DataType.VarChar)
.maxLength(512)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("year_of_publication")
.dataType(DataType.Int64)
.build());
collectionSchema.addField(AddFieldReq.builder()
.fieldName("title_vector")
.dataType(DataType.FloatVector)
.dimension(5)
.build());
Map<String, String> params = new HashMap<>();
params.put("mmap_enabled", "true");
collectionSchema.addField(AddFieldReq.builder()
.fieldName("chunks")
.dataType(DataType.Array)
.elementType(DataType.Struct)
.maxCapacity(1000)
.addStructField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(65535)
.build())
.addStructField(AddFieldReq.builder()
.fieldName("chapter")
.dataType(DataType.VarChar)
.maxLength(512)
.build())
.addStructField(AddFieldReq.builder()
.fieldName("text_vector")
.dataType(DataType.FloatVector)
.dimension(VECTOR_DIM)
.typeParams(params)
.build())
.build());
// go
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const milvusClient = new MilvusClient("http://localhost:19530");
const schema = [
{
name: "id",
data_type: DataType.INT64,
is_primary_key: true,
auto_id: true,
},
{
name: "title",
data_type: DataType.VARCHAR,
max_length: 512,
},
{
name: "author",
data_type: DataType.VARCHAR,
max_length: 512,
},
{
name: "year_of_publication",
data_type: DataType.INT64,
},
{
name: "title_vector",
data_type: DataType.FLOAT_VECTOR,
dim: 5,
},
{
name: "chunks",
data_type: DataType.ARRAY,
element_type: DataType.STRUCT,
fields: [
{
name: "text",
data_type: DataType.VARCHAR,
max_length: 65535,
},
{
name: "chapter",
data_type: DataType.VARCHAR,
max_length: 512,
},
{
name: "text_vector",
data_type: DataType.FLOAT_VECTOR,
dim: 5,
mmap_enabled: true,
},
],
max_capacity: 1000,
},
];
# restful
SCHEMA='{
"autoID": true,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "title",
"dataType": "VarChar",
"elementTypeParams": { "max_length": "512" }
},
{
"fieldName": "author",
"dataType": "VarChar",
"elementTypeParams": { "max_length": "512" }
},
{
"fieldName": "year_of_publication",
"dataType": "Int64"
},
{
"fieldName": "title_vector",
"dataType": "FloatVector",
"elementTypeParams": { "dim": "5" }
}
],
"structArrayFields": [
{
"name": "chunks",
"description": "Array of document chunks with text and vectors",
"elementTypeParams":{
"max_capacity": 1000
},
"fields": [
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": { "max_length": "65535" }
},
{
"fieldName": "chapter",
"dataType": "VarChar",
"elementTypeParams": { "max_length": "512" }
},
{
"fieldName": "text_vector",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": "5",
"mmap_enabled": "true"
}
}
]
}
]
}'
위 코드 예제에서 강조 표시된 줄은 컬렉션 스키마에 StructArray를 포함하는 방법을 보여줍니다.
인덱스 매개변수 설정
컬렉션의 벡터 필드와 Struct 요소에 정의된 벡터 필드를 포함한 모든 벡터 필드에 대해 인덱싱은 필수입니다.
적용 가능한 인덱스 매개변수는 인덱스 유형에 따라 다릅니다. 적용 가능한 인덱스 매개변수에 대한 자세한 내용은 인덱스 설명 및 선택한 인덱스 유형에 대한 설명서를 참조하세요.
임베딩 목록 색인하기
임베딩 목록을 색인하려면 해당 인덱스 유형을 AUTOINDEX 또는 위에 나열된 적용 가능한 인덱스 유형 중 하나로 설정하고 나열된 Milvus용 메트릭 유형을 사용하여 임베딩 목록 간의 유사성을 측정해야 합니다.
# Create index parameters
index_params = client.prepare_index_params()
# Create an index for the vector field in the collection
index_params.add_index(
field_name="title_vector",
index_type="AUTOINDEX",
metric_type="L2",
)
# Create an index for the vector field in the element Struct
index_params.add_index(
field_name="chunks[text_vector]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE",
)
import io.milvus.v2.common.IndexParam;
List<IndexParam> indexParams = new ArrayList<>();
indexParams.add(IndexParam.builder()
.fieldName("title_vector")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.L2)
.build());
indexParams.add(IndexParam.builder()
.fieldName("chunks[text_vector]")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.MAX_SIM_COSINE)
.build());
// go
await milvusClient.createCollection({
collection_name: "books",
fields: schema,
});
const indexParams = [
{
field_name: "title_vector",
index_type: "AUTOINDEX",
metric_type: "L2",
},
{
field_name: "chunks[text_vector]",
index_type: "AUTOINDEX",
metric_type: "MAX_SIM_COSINE",
},
];
# restful
INDEX_PARAMS='[
{
"fieldName": "title_vector",
"indexName": "title_vector_index",
"indexType": "AUTOINDEX",
"metricType": "L2"
},
{
"fieldName": "chunks[text_vector]",
"indexName": "chunks_text_vector_index",
"indexType": "AUTOINDEX",
"metricType": "MAX_SIM_COSINE"
}
]'
스칼라 구조체 하위 필드 색인하기
스칼라 구조체 하위 필드에 인덱스를 생성할 때, Milvus는 실제로 행 수준이 아닌 요소 수준에서 인덱스를 생성하여 스칼라 필터링 속도를 높입니다.
다음 코드 스니펫은 chunks[text] 이라는 이름의 스칼라 구조체 하위 필드에 인덱스를 생성합니다.
index_params.add_index(
field_name="chunks[text]",
index_type="INVERTED"
)
indexParams.add(IndexParam.builder()
.fieldName("chunks[text]")
.indexType(IndexParam.IndexType.INVERTED)
.build());
// go
indexParams.push({
field_name: "chunks[text]",
index_type: "INVERTED"
})
INDEX_PARAMS += '{
"fieldName": "chunks[text]",
"indexName": "chunks_text_vector_index",
"indexType": "INVERTED"
}'
컬렉션 만들기
스키마와 인덱스가 준비되면 StructArray 필드를 포함하는 컬렉션을 만들 수 있습니다.
client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)
import io.milvus.v2.service.collection.request.CreateCollectionReq;
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(collectionSchema)
.indexParams(indexParams)
.build();
client.createCollection(requestCreate);
// go
await milvusClient.createCollection({
collection_name: "my_collection",
fields: schema,
indexes: indexParams,
});
# restful
curl -X POST "http://localhost:19530/v2/vectordb/collections/create" \
-H "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_collection\",
\"description\": \"A collection for storing book information with struct array chunks\",
\"schema\": $SCHEMA,
\"indexParams\": $INDEX_PARAMS
}"
데이터 삽입
컬렉션을 생성한 후 다음과 같이 구조체 배열을 포함하는 데이터를 삽입할 수 있습니다.
# Sample data
data = {
'title': 'Walden',
'title_vector': [0.1, 0.2, 0.3, 0.4, 0.5],
'author': 'Henry David Thoreau',
'year_of_publication': 1845,
'chunks': [
{
'text': 'When I wrote the following pages, or rather the bulk of them...',
'text_vector': [0.3, 0.2, 0.3, 0.2, 0.5],
'chapter': 'Economy',
},
{
'text': 'I would fain say something, not so much concerning the Chinese and...',
'text_vector': [0.7, 0.4, 0.2, 0.7, 0.8],
'chapter': 'Economy'
}
]
}
# insert data
client.insert(
collection_name="my_collection",
data=[data]
)
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;
Gson gson = new Gson();
JsonObject row = new JsonObject();
row.addProperty("title", "Walden");
row.add("title_vector", gson.toJsonTree(Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f, 0.5f)));
row.addProperty("author", "Henry David Thoreau");
row.addProperty("year_of_publication", 1845);
JsonArray structArr = new JsonArray();
JsonObject struct1 = new JsonObject();
struct1.addProperty("text", "When I wrote the following pages, or rather the bulk of them...");
struct1.add("text_vector", gson.toJsonTree(Arrays.asList(0.3f, 0.2f, 0.3f, 0.2f, 0.5f)));
struct1.addProperty("chapter", "Economy");
structArr.add(struct1);
JsonObject struct2 = new JsonObject();
struct2.addProperty("text", "I would fain say something, not so much concerning the Chinese and...");
struct2.add("text_vector", gson.toJsonTree(Arrays.asList(0.7f, 0.4f, 0.2f, 0.7f, 0.8f)));
struct2.addProperty("chapter", "Economy");
structArr.add(struct2);
row.add("chunks", structArr);
InsertResp insertResp = client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(row))
.build());
// go
{
id: 0,
title: "Walden",
title_vector: [0.1, 0.2, 0.3, 0.4, 0.5],
author: "Henry David Thoreau",
"year-of-publication": 1845,
chunks: [
{
text: "When I wrote the following pages, or rather the bulk of them...",
text_vector: [0.3, 0.2, 0.3, 0.2, 0.5],
chapter: "Economy",
},
{
text: "I would fain say something, not so much concerning the Chinese and...",
text_vector: [0.7, 0.4, 0.2, 0.7, 0.8],
chapter: "Economy",
},
],
},
];
await milvusClient.insert({
collection_name: "books",
data: data,
});
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/insert" \
-H "Content-Type: application/json" \
-d '{
"collectionName": "my_collection",
"data": [
{
"title": "Walden",
"title_vector": [0.1, 0.2, 0.3, 0.4, 0.5],
"author": "Henry David Thoreau",
"year_of_publication": 1845,
"chunks": [
{
"text": "When I wrote the following pages, or rather the bulk of them...",
"text_vector": [0.3, 0.2, 0.3, 0.2, 0.5],
"chapter": "Economy"
},
{
"text": "I would fain say something, not so much concerning the Chinese and...",
"text_vector": [0.7, 0.4, 0.2, 0.7, 0.8],
"chapter": "Economy"
}
]
}
]
}'
import json
import random
from typing import List, Dict, Any
# Real classic books (title, author, year)
BOOKS = [
("Pride and Prejudice", "Jane Austen", 1813),
("Moby Dick", "Herman Melville", 1851),
("Frankenstein", "Mary Shelley", 1818),
("The Picture of Dorian Gray", "Oscar Wilde", 1890),
("Dracula", "Bram Stoker", 1897),
("The Adventures of Sherlock Holmes", "Arthur Conan Doyle", 1892),
("Alice's Adventures in Wonderland", "Lewis Carroll", 1865),
("The Time Machine", "H.G. Wells", 1895),
("The Scarlet Letter", "Nathaniel Hawthorne", 1850),
("Leaves of Grass", "Walt Whitman", 1855),
("The Brothers Karamazov", "Fyodor Dostoevsky", 1880),
("Crime and Punishment", "Fyodor Dostoevsky", 1866),
("Anna Karenina", "Leo Tolstoy", 1877),
("War and Peace", "Leo Tolstoy", 1869),
("Great Expectations", "Charles Dickens", 1861),
("Oliver Twist", "Charles Dickens", 1837),
("Wuthering Heights", "Emily Brontë", 1847),
("Jane Eyre", "Charlotte Brontë", 1847),
("The Call of the Wild", "Jack London", 1903),
("The Jungle Book", "Rudyard Kipling", 1894),
]
# Common chapter names for classics
CHAPTERS = [
"Introduction", "Prologue", "Chapter I", "Chapter II", "Chapter III",
"Chapter IV", "Chapter V", "Chapter VI", "Chapter VII", "Chapter VIII",
"Chapter IX", "Chapter X", "Epilogue", "Conclusion", "Afterword",
"Economy", "Where I Lived", "Reading", "Sounds", "Solitude",
"Visitors", "The Bean-Field", "The Village", "The Ponds", "Baker Farm"
]
# Placeholder text snippets (mimicking 19th-century prose)
TEXT_SNIPPETS = [
"When I wrote the following pages, or rather the bulk of them...",
"I would fain say something, not so much concerning the Chinese and...",
"It is a truth universally acknowledged, that a single man in possession...",
"Call me Ishmael. Some years ago—never mind how long precisely...",
"It was the best of times, it was the worst of times...",
"All happy families are alike; each unhappy family is unhappy in its own way.",
"Whether I shall turn out to be the hero of my own life, or whether that station...",
"You will rejoice to hear that no disaster has accompanied the commencement...",
"The world is too much with us; late and soon, getting and spending...",
"He was an old man who fished alone in a skiff in the Gulf Stream..."
]
def random_vector() -> List[float]:
return [round(random.random(), 1) for _ in range(5)]
def generate_chunk() -> Dict[str, Any]:
return {
"text": random.choice(TEXT_SNIPPETS),
"text_vector": random_vector(),
"chapter": random.choice(CHAPTERS)
}
def generate_record(record_id: int) -> Dict[str, Any]:
title, author, year = random.choice(BOOKS)
num_chunks = random.randint(1, 5) # 1 to 5 chunks per book
chunks = [generate_chunk() for _ in range(num_chunks)]
return {
"title": title,
"title_vector": random_vector(),
"author": author,
"year_of_publication": year,
"chunks": chunks
}
# Generate 1000 records
data = [generate_record(i) for i in range(1000)]
# Insert the generated data
client.insert(collection_name="my_collection", data=data)
StructArray 필드에서 벡터 검색하기
컬렉션의 벡터 필드와 StructArray에서 벡터 검색을 수행할 수 있습니다.
구체적으로, 검색 요청의 anns_field 매개변수 값으로 StructArray 필드 이름과 Struct 요소 내의 대상 벡터 필드 이름을 연결하고 EmbeddingList 을 사용하여 쿼리 벡터를 깔끔하게 정리해야 합니다.
Milvus는 StructArray의 임베딩 목록에 대한 검색 쿼리 벡터를 보다 깔끔하게 정리할 수 있도록 EmbeddingList 을 제공합니다. 각 EmbeddingList 은 최소한 벡터 임베딩을 포함하며 그 대가로 여러 개의 topK 엔티티를 기대합니다.
그러나 EmbeddingList 은 범위 검색이나 그룹화 검색 매개변수 없이 search() 요청에만 사용할 수 있으며 search_iterator() 요청에는 사용할 수 없습니다.
from pymilvus.client.embedding_list import EmbeddingList
# each query embedding list triggers a single search
embeddingList1 = EmbeddingList()
embeddingList1.add([0.2, 0.9, 0.4, -0.3, 0.2])
embeddingList2 = EmbeddingList()
embeddingList2.add([-0.2, -0.2, 0.5, 0.6, 0.9])
embeddingList2.add([-0.4, 0.3, 0.5, 0.8, 0.2])
# a search with a single embedding list
results = client.search(
collection_name="my_collection",
data=[ embeddingList1 ],
anns_field="chunks[text_vector]",
search_params={"metric_type": "MAX_SIM_COSINE"},
limit=3,
output_fields=["chunks[text]"]
)
import io.milvus.v2.service.vector.request.data.EmbeddingList;
import io.milvus.v2.service.vector.request.data.FloatVec;
EmbeddingList embeddingList1 = new EmbeddingList();
embeddingList1.add(new FloatVec(new float[]{0.2f, 0.9f, 0.4f, -0.3f, 0.2f}));
EmbeddingList embeddingList2 = new EmbeddingList();
embeddingList2.add(new FloatVec(new float[]{-0.2f, -0.2f, 0.5f, 0.6f, 0.9f}));
embeddingList2.add(new FloatVec(new float[]{-0.4f, 0.3f, 0.5f, 0.8f, 0.2f}));
Map<String, Object> params = new HashMap<>();
params.put("metric_type", "MAX_SIM_COSINE");
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.annsField("chunks[text_vector]")
.data(Collections.singletonList(embeddingList1))
.searchParams(params)
.limit(3)
.outputFields(Collections.singletonList("chunks[text]"))
.build());
// go
const embeddingList1 = [[0.2, 0.9, 0.4, -0.3, 0.2]];
const embeddingList2 = [
[-0.2, -0.2, 0.5, 0.6, 0.9],
[-0.4, 0.3, 0.5, 0.8, 0.2],
];
const results = await milvusClient.search({
collection_name: "books",
data: embeddingList1,
anns_field: "chunks[text_vector]",
search_params: { metric_type: "MAX_SIM_COSINE" },
limit: 3,
output_fields: ["chunks[text]"],
});
# restful
embeddingList1='[[0.2,0.9,0.4,-0.3,0.2]]'
embeddingList2='[[-0.2,-0.2,0.5,0.6,0.9],[-0.4,0.3,0.5,0.8,0.2]]'
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_collection\",
\"data\": [$embeddingList1],
\"annsField\": \"chunks[text_vector]\",
\"searchParams\": {\"metric_type\": \"MAX_SIM_COSINE\"},
\"limit\": 3,
\"outputFields\": [\"chunks[text]\"]
}"
위의 검색 요청은 chunks[text_vector] 을 사용하여 Struct 요소의 text_vector 필드를 참조합니다. 이 구문을 사용하여 anns_field 및 output_fields 매개 변수를 설정할 수 있습니다.
출력은 가장 유사한 세 개의 엔티티 목록이 될 것입니다.
# [
# [
# {
# 'id': 461417939772144945,
# 'distance': 0.9675756096839905,
# 'entity': {
# 'chunks': [
# {'text': 'The world is too much with us; late and soon, getting and spending...'},
# {'text': 'All happy families are alike; each unhappy family is unhappy in its own way.'}
# ]
# }
# },
# {
# 'id': 461417939772144965,
# 'distance': 0.9555778503417969,
# 'entity': {
# 'chunks': [
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'He was an old man who fished alone in a skiff in the Gulf Stream...'},
# {'text': 'When I wrote the following pages, or rather the bulk of them...'},
# {'text': 'It was the best of times, it was the worst of times...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'}
# ]
# }
# },
# {
# 'id': 461417939772144962,
# 'distance': 0.9469035863876343,
# 'entity': {
# 'chunks': [
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'},
# {'text': 'He was an old man who fished alone in a skiff in the Gulf Stream...'},
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'}
# ]
# }
# }
# ]
# ]
data 매개변수에 여러 임베딩 목록을 포함시켜 각 임베딩 목록에 대한 검색 결과를 검색할 수도 있습니다.
# a search with multiple embedding lists
results = client.search(
collection_name="my_collection",
data=[ embeddingList1, embeddingList2 ],
anns_field="chunks[text_vector]",
search_params={"metric_type": "MAX_SIM_COSINE"},
limit=3,
output_fields=["chunks[text]"]
)
print(results)
Map<String, Object> params = new HashMap<>();
params.put("metric_type", "MAX_SIM_COSINE");
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.annsField("chunks[text_vector]")
.data(Arrays.asList(embeddingList1, embeddingList2))
.searchParams(params)
.limit(3)
.outputFields(Collections.singletonList("chunks[text]"))
.build());
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (int i = 0; i < searchResults.size(); i++) {
System.out.println("Results of No." + i + " embedding list");
List<SearchResp.SearchResult> results = searchResults.get(i);
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// go
const results2 = await milvusClient.search({
collection_name: "books",
data: [embeddingList1, embeddingList2],
anns_field: "chunks[text_vector]",
search_params: { metric_type: "MAX_SIM_COSINE" },
limit: 3,
output_fields: ["chunks[text]"],
});
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-d "{
\"collectionName\": \"my_collection\",
\"data\": [$embeddingList1, $embeddingList2],
\"annsField\": \"chunks[text_vector]\",
\"searchParams\": {\"metric_type\": \"MAX_SIM_COSINE\"},
\"limit\": 3,
\"outputFields\": [\"chunks[text]\"]
}"
출력은 각 임베딩 목록에 대해 가장 유사한 세 개의 엔티티 목록입니다.
# [
# [
# {
# 'id': 461417939772144945,
# 'distance': 0.9675756096839905,
# 'entity': {
# 'chunks': [
# {'text': 'The world is too much with us; late and soon, getting and spending...'},
# {'text': 'All happy families are alike; each unhappy family is unhappy in its own way.'}
# ]
# }
# },
# {
# 'id': 461417939772144965,
# 'distance': 0.9555778503417969,
# 'entity': {
# 'chunks': [
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'He was an old man who fished alone in a skiff in the Gulf Stream...'},
# {'text': 'When I wrote the following pages, or rather the bulk of them...'},
# {'text': 'It was the best of times, it was the worst of times...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'}
# ]
# }
# },
# {
# 'id': 461417939772144962,
# 'distance': 0.9469035863876343,
# 'entity': {
# 'chunks': [
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'},
# {'text': 'He was an old man who fished alone in a skiff in the Gulf Stream...'},
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'},
# {'text': 'The world is too much with us; late and soon, getting and spending...'}
# ]
# }
# }
# ],
# [
# {
# 'id': 461417939772144663,
# 'distance': 1.9761409759521484,
# 'entity': {
# 'chunks': [
# {'text': 'It was the best of times, it was the worst of times...'},
# {'text': 'It is a truth universally acknowledged, that a single man in possession...'},
# {'text': 'Whether I shall turn out to be the hero of my own life, or whether that station...'},
# {'text': 'He was an old man who fished alone in a skiff in the Gulf Stream...'}
# ]
# }
# },
# {
# 'id': 461417939772144692,
# 'distance': 1.974656581878662,
# 'entity': {
# 'chunks': [
# {'text': 'It is a truth universally acknowledged, that a single man in possession...'},
# {'text': 'Call me Ishmael. Some years ago—never mind how long precisely...'}
# ]
# }
# },
# {
# 'id': 461417939772144662,
# 'distance': 1.9406685829162598,
# 'entity': {
# 'chunks': [
# {'text': 'It is a truth universally acknowledged, that a single man in possession...'}
# ]
# }
# }
# ]
# ]
위의 코드 예시에서 embeddingList1 은 하나의 벡터를 포함하는 임베딩 목록이고 embeddingList2 은 두 개의 벡터를 포함합니다. 각각은 별도의 검색 요청을 트리거하고 상위 K개의 유사한 엔티티 목록을 기대합니다.
StructArray 필드에서 스칼라 필터링하기
일치 계열의 요소 필터와 연산자를 사용하여 StructArray의 스칼라 하위 필드에 대해 스칼라 필터링을 수행할 수 있습니다. 위의 두 연산자 유형에 대한 자세한 내용과 예제는 구조체 연산자 배열을 참조하세요.
요소 필터
엔티티의 StructArray 필드에 있는 하나 이상의 요소가 술어를 만족하는지 확인하는 엔티티 수준 필터입니다. 예를 들어, 다음 요소 필터는 text 하위 필드에 "Red"로 시작하는 청크를 하나 이상 포함하는 엔티티를 반환합니다.
element_filter(chunks, $[text] LIKE "Red%")
요소별로 평가되는 술어에는 거의 모든 비교, 범위 및 산술 연산자를 사용할 수 있으며 논리 연산자를 사용하여 동일한 요소에 여러 조건을 결합할 수 있습니다. 자세한 내용은 기본 연산자를 참조하세요.
필터링된 검색 또는 쿼리 요청에 여러 개의 스칼라 필터링 표현식이 있는 경우 아래와 같이 모든 엔티티 수준 필터 표현식 뒤에 요소 필터 표현식을 배치합니다.
# correct
id > 0 && element_filter(chunks, $[x] > 1)
# incorrect, resulting errors
element_filter(chunks, $[x] > 1) && id > 0
일치 패밀리 연산자
일치 패밀리 연산자는 StructArray 필드에서도 작동합니다. 단순히 요소의 존재 여부를 확인하는 대신 요소 술어를 만족해야 하는 요소의 수(또는 비율)를 결정할 수 있습니다.
MATCH_ANY(chunks, $[text] LIKE "Red%")이는
text하위 필드에 "Red"로 시작하는 청크를 하나 이상 포함하는 엔티티를 반환합니다(의미상 이는element_filter과 동일).MATCH_ALL(chunks, $[text] LIKE "Red%")모든 청크의 텍스트 하위 필드가 "Red"로 시작하는 엔티티를 반환합니다.
MATCH_LEAST(chunks, $[text] LIKE "Red%", k)text하위 필드에 "Red"로 시작하는 청크가 최소k개 이상 포함된 엔터티를 반환합니다.MATCH_MOST(chunks, $[text] LIKE "Red%", k)text하위 필드에 "Red"로 시작하는 청크가 최대k개 포함된 엔터티를 반환합니다.MATCH_EXACT(chunks, $[text] LIKE "Red%", k)text하위 필드에 "Red"로 시작하는k청크가 정확히 포함된 엔터티를 반환합니다.
다음 단계
기본 StructArray 데이터 타입의 개발은 복잡한 데이터 구조를 처리하는 Milvus의 기능이 크게 발전했음을 의미합니다. 사용 사례를 더 잘 이해하고 이 새로운 기능을 최대한 활용하려면 구조 배열을 사용한 스키마 설계를 읽어보시기 바랍니다.