StructArray
エンティティ内のArray of Structsフィールド(StructArrayフィールド)は、Struct要素の順序付きセットを格納します。Array内の各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アプリケーションは、ネスト化された異種データにますます依存するようになっている。従来のフラットなデータモデルでは、「1つのドキュメントに多くの注釈が付けられたチャンク」や「1つの運転シーンに複数の観察された操作」のような複雑な関係を表現するのに苦労する。そこでMilvusのStructArrayデータ型が威力を発揮します。
StructArrayフィールドがアプリケーションのシナリオに適しているかどうかを素早く判断するには、以下の点を考慮してください:
データが階層構造になっている(例えば、1つのドキュメントに多くの注釈が付いたチャンク)。
上記の例のように、検索結果はチャンクではなくドキュメントであるべきです。
検索結果には大量の重複エンティティが含まれており、グループ化、重複排除、再ランク付けなどのテクニックを使って最終結果を取り出すのに苦労している。
上記の質問に対する答えが「はい」の場合は、StructArray を使用する必要があります。
制限
データ型
コレクションを作成する際、Array フィールドの要素のデータ型として Struct 型を使用できます。しかし、既存のコレクションにStructArrayを追加することはできませんし、Milvusはコレクションフィールドのデータ型としてStruct型を使用することをサポートしていません。
ArrayフィールドのStructは同じスキーマを共有しており、Arrayフィールドの作成時に定義する必要があります。
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>ベクトル・フィールドの数は、コレクション・レベルでも Structs でも 10 以下にしてください。
Nullable とデフォルト値
StructArray フィールドは null 可能ではなく、デフォルト値も受け付けません。
関数
Struct 内のスカラーフィールドからベクトルフィールドを導出するために関数を使用することはできません。
インデックス型とメトリック型
コレクション内のすべてのベクトルフィールドにはインデックスを付ける必要があります。MilvusはStructArrayフィールド内のベクトルフィールドにインデックスを付けるために、埋め込みリストを使って各Struct要素内のベクトル埋め込みを整理し、埋め込みリスト全体にインデックスを付けます。
インデックスタイプとして
AUTOINDEXまたはHNSWを使用し、StructArray フィールド内のエンベッディングリストのインデックスを作成するために以下に列挙するメトリックタイプを使用することができます。インデックスタイプ
メトリック型
備考
AUTOINDEXHNSWIVF_FLATDISKANN
MAX_SIM_COSINEMAX_SIM_IPMAX_SIM_L2
以下の型の埋め込みリスト用:
FLOAT_VECTORFLOAT16_VECTORBFLOAT16_VECTORINT8_VECTORBINARY_VECTOR
Milvusがどのようにクエリと埋め込みリストの類似度を計算するかについては、最大類似度を参照してください。
StructArray フィールドのスカラーフィールドは以下のインデックスタイプをサポートします:
アップサート・データ
構造体はマージモードでのアップサートをサポートしていません。ただし、オーバーライド・モードでアップサートを実行して Structs 内のデータを更新することはできます。マージ・モードでのアップサートとオーバーライド・モードでのアップサートの違いの詳細については、アップサート・エンティティを参照してください。
スカラー・フィルタリング
match ファミリの 要素フィルタおよび演算子を使用して、StructArray フィールドのスカラー サブフィールドに対してスカラー フィルタリングを実行できます。詳細については、StructArray フィールドでのスカラー・フィルタリングを参照してください。
StructArray の追加
MilvusでStructArrayフィールドを追加するには、コレクション作成時に配列フィールドを定義し、その要素のデータ型をStructに設定する必要があります。手順は以下の通りです:
コレクションスキーマにArrayフィールドとしてフィールドを追加する際に、フィールドのデータ型を
DataType.ARRAY。フィールドの
element_type属性をDataType.STRUCTに設定して、フィールドを Struct Array にします。Struct スキーマを作成し、必須フィールドを含めます。次に、フィールドの
struct_schema属性で Struct スキーマを参照します。フィールドの
max_capacity属性を適切な値に設定し、各エンティティがこのフィールドに含めることができる Struct の最大数を指定します。(オプション)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 内で定義されたベクトルフィールドの両方を含む、すべてのベクトルフィールドに対してインデックスの設定が必須です。
適用可能なインデックス・パラメータは、インデックス・タイプによって異なります。適用可能なインデックス・パラメータの詳細については、Index Explainedおよび選択したインデックス・タイプのドキュメントを参照してください。
埋め込みリストのインデックス
エンベッディングリストにインデックスを付けるには、そのインデックスタイプを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
}"
データの挿入
コレクションを作成したら、次のようにStructのArrayを含むデータを挿入できます。
# 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 パラメータの値として、Struct 要素内の StructArray フィールド名と対象となるベクトルフィールド名を連結し、EmbeddingList を使用してクエリベクトルを整然と整理する必要があります。
Milvusでは、StructArray内のエンベッディングリストに対する検索のクエリベクトルをよりきれいに整理するために、EmbeddingList 。各EmbeddingList は少なくともベクトル埋め込みを含み、返り値として topK エンティティの数を期待します。
しかし、EmbeddingList は、search_iterator() リクエストはおろか、範囲検索やグループ化検索パラメーターのないsearch() リクエストでしか使用できない。
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 パラメータを設定できます。
出力は、最も類似した3つのエンティティのリストになる。
# [
# [
# {
# '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]\"]
}"
出力は、各埋め込みリストについて、最も類似した3つのエンティティのリストとなる。
# [
# [
# {
# '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 は1つのベクトルの埋め込みリストで、embeddingList2 は2つのベクトルを含んでいます。それぞれが別々の検索要求をトリガーし、上位K個の類似エンティティのリストを期待する。
StructArray フィールドでのスカラーフィルタリング
マッチファミリーの 要素フィルターと演算子を使用して、StructArray のスカラーサブフィールドに対してスカラーフィルタリングを行うことができます。上記の 2 つの演算子タイプの詳細と例については、Array of Structs Operators を参照してください。
要素フィルタ
これは、エンティティのStructArrayフィールド内の少なくとも1つの要素が述語を満たすかどうかをチェックするエンティティレベルのフィルタです。たとえば、次の要素フィルタは、text サブフィールドに "Red" で始まるチャンクを少なくとも 1 つ含むエンティティを返します。
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" で始まるチャンクを少なくとも 1 つ含むエンティティを返します。意味的には、これは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の複雑なデータ構造を扱う能力の大きな進歩を意味します。使用例をより理解し、この新機能を最大限に活用するために、構造体の配列を使用したスキーマ設計を読むことをお勧めします。