다이내믹 필드
Milvus에서는 동적 필드라는 특수 기능을 통해 유연하고 진화하는 구조의 엔티티를 삽입할 수 있습니다. 이 필드는 컬렉션 스키마에 명시적으로 정의되지 않은 모든 필드를 데이터에 자동으로 저장하는 $meta 이라는 이름의 숨겨진 JSON 필드로 구현됩니다.
작동 방식
동적 필드가 활성화되면 Milvus는 각 엔티티에 숨겨진 $meta 필드를 추가합니다. 이 필드는 JSON 유형이므로 모든 JSON 호환 데이터 구조를 저장할 수 있으며 JSON 경로 구문을 사용하여 색인할 수 있습니다.
데이터를 삽입하는 동안 스키마에 선언되지 않은 모든 필드는 이 동적 필드 안에 키-값 쌍으로 자동 저장됩니다.
$meta 을 수동으로 관리할 필요가 없습니다. Milvus는 이를 투명하게 처리합니다.
예를 들어 컬렉션 스키마에 id 와 vector 만 정의되어 있고 다음 엔티티를 삽입하는 경우입니다:
{
"id": 1,
"vector": [0.1, 0.2, 0.3],
"name": "Item A", // Not in schema
"category": "books" // Not in schema
}
동적 필드 기능을 활성화하면 Milvus는 이를 내부적으로 다음과 같이 저장합니다:
{
"id": 1,
"vector": [0.1, 0.2, 0.3],
"$meta": {
"name": "Item A",
"category": "books"
}
}
이렇게 하면 스키마를 변경하지 않고도 데이터 구조를 발전시킬 수 있습니다.
일반적인 사용 사례는 다음과 같습니다:
선택 사항 또는 자주 검색되지 않는 필드 저장
엔티티마다 다른 메타데이터 캡처
특정 동적 필드 키에 대한 인덱스를 통해 유연한 필터링 지원
지원되는 데이터 유형
동적 필드는 단순값과 복합값을 포함하여 Milvus에서 제공하는 모든 스칼라 데이터 유형을 지원합니다. 이러한 데이터 유형은 $meta 에 저장된 키의 **값에 적용됩니다.
지원되는 유형은 다음과 같습니다:
문자열 (
VARCHAR)정수 (
INT8,INT32,INT64)부동 소수점 (
FLOAT,DOUBLE)부울 (
BOOL)스칼라 값 배열 (
ARRAY)JSON 객체 (
JSON)
예시:
{
"brand": "Acme",
"price": 29.99,
"in_stock": true,
"tags": ["new", "hot"],
"specs": {
"weight": "1.2kg",
"dimensions": { "width": 10, "height": 20 }
}
}
위의 각 키와 값은 $meta 필드 안에 저장됩니다.
동적 필드 사용
동적 필드 기능을 사용하려면 컬렉션 스키마를 만들 때 enable_dynamic_field=True 을 설정합니다:
from pymilvus import MilvusClient, DataType
# Initialize client
client = MilvusClient(uri="http://localhost:19530")
# Create schema with dynamic field enabled
schema = client.create_schema(
auto_id=False,
enable_dynamic_field=True,
)
# Add explicitly defined fields
schema.add_field(field_name="my_id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="my_vector", datatype=DataType.FLOAT_VECTOR, dim=5)
# Create the collection
client.create_collection(
collection_name="my_collection",
schema=schema
)
import io.milvus.v2.client.*;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
import io.milvus.v2.service.collection.request.AddFieldReq;
ConnectConfig config = ConnectConfig.builder()
.uri("http://localhost:19530")
.build();
MilvusClientV2 client = new MilvusClientV2(config);
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.enableDynamicField(true)
.build();
schema.addField(AddFieldReq.builder()
.fieldName("my_id")
.dataType(DataType.Int64)
.isPrimaryKey(Boolean.TRUE)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("my_vector")
.dataType(DataType.FloatVector)
.dimension(5)
.build());
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.build();
client.createCollection(requestCreate);
import { MilvusClient, DataType, CreateCollectionReq } from '@zilliz/milvus2-sdk-node';
// Initialize client
const client = new MilvusClient({ address: 'localhost:19530' });
// Create collection
const res = await client.createCollection({
collection_name: 'my_collection',
schema: [
{
name: 'my_id',
data_type: DataType.Int64,
is_primary_key: true,
autoID: false,
},
{
name: 'my_vector',
data_type: DataType.FloatVector,
type_params: {
dim: '5',
}
],
enable_dynamic_field: true
});
import (
"context"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
})
if err != nil {
return err
}
schema := entity.NewSchema().WithDynamicFieldEnabled(true)
schema.WithField(entity.NewField().
WithName("my_id").pk
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true),
).WithField(entity.NewField().
WithName("my_vector").
WithDataType(entity.FieldTypeFloatVector).
WithDim(5),
)
err = client.CreateCollection(ctx, milvusclient.NewCreateCollectionOption("my_collection", schema))
if err != nil {
return err
}
# restful
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"
export myIdField='{
"fieldName": "my_id",
"dataType": "Int64",
"isPrimary": true,
"autoID": false
}'
export myVectorField='{
"fieldName": "my_vector",
"dataType": "FloatVector",
"elementTypeParams": {
"dim": 5
}
}'
export schema="{
\"autoID\": false,
\"enableDynamicField\": true,
\"fields\": [
$myIdField,
$myVectorField
]
}"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
\"collectionName\": \"my_collection\",
\"schema\": $schema
}"
컬렉션에 엔티티 삽입
동적 필드를 사용하면 스키마에 정의되지 않은 추가 필드를 삽입할 수 있습니다. 이러한 필드는 $meta 에 자동으로 저장됩니다.
entities = [
{
"my_id": 1, # Explicitly defined primary field
"my_vector": [0.1, 0.2, 0.3, 0.4, 0.5], # Explicitly defined vector field
"overview": "Great product", # Scalar key not defined in schema
"words": 150, # Scalar key not defined in schema
"dynamic_json": { # JSON key not defined in schema
"varchar": "some text",
"nested": {
"value": 42.5
},
"string_price": "99.99" # Number stored as string
}
}
]
client.insert(collection_name="my_collection", data=entities)
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
Gson gson = new Gson();
JsonObject row = new JsonObject();
row.addProperty("my_id", 1);
row.add("my_vector", gson.toJsonTree(Arrays.asList(0.1, 0.2, 0.3, 0.4, 0.5)));
row.addProperty("overview", "Great product");
row.addProperty("words", 150);
JsonObject dynamic = new JsonObject();
dynamic.addProperty("varchar", "some text");
dynamic.addProperty("string_price", "99.99");
JsonObject nested = new JsonObject();
nested.addProperty("value", 42.5);
dynamic.add("nested", nested);
row.add("dynamic_json", dynamic);
client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(row))
.build());
const entities = [
{
my_id: 1,
my_vector: [0.1, 0.2, 0.3, 0.4, 0.5],
overview: 'Great product',
words: 150,
dynamic_json: {
varchar: 'some text',
nested: {
value: 42.5,
},
string_price: '99.99',
},
},
];
const res = await client.insert({
collection_name: 'my_collection',
data: entities,
});
_, err = client.Insert(ctx, milvusclient.NewColumnBasedInsertOption("my_collection").
WithInt64Column("my_id", []int64{1}).
WithFloatVectorColumn("my_vector", 5, [][]float32{
{0.1, 0.2, 0.3, 0.4, 0.5},
}).WithColumns(
column.NewColumnVarChar("overview", []string{"Great product"}),
column.NewColumnInt32("words", []int32{150}),
column.NewColumnJSONBytes("dynamic_json", [][]byte{
[]byte(`{
varchar: 'some text',
nested: {
value: 42.5,
},
string_price: '99.99',
}`),
}),
))
if err != nil {
return err
}
# restful
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data '{
"data": [
{
"my_id": 1,
"my_vector": [0.1, 0.2, 0.3, 0.4, 0.5],
"overview": "Great product",
"words": 150,
"dynamic_json": {
"varchar": "some text",
"nested": {
"value": 42.5
},
"string_price": "99.99"
}
}
],
"collectionName": "my_collection"
}'
동적 필드의 인덱스 키Compatible with Milvus 2.5.11+
Milvus에서는 JSON 경로 인덱싱을 사용하여 동적 필드 내의 특정 키에 대한 인덱스를 생성할 수 있습니다. 이러한 키는 스칼라 값 또는 JSON 객체의 중첩된 값일 수 있습니다.
동적 필드 키 인덱싱은 선택 사항입니다. 인덱스 없이도 동적 필드 키를 기준으로 쿼리하거나 필터링할 수 있지만 무차별 대입 검색으로 인해 성능이 느려질 수 있습니다.
JSON 경로 인덱싱 구문
JSON 경로 인덱스를 만들려면 다음을 지정합니다:
JSON 경로 (
json_path): 색인하려는 JSON 객체 내의 키 또는 중첩된 필드의 경로입니다.예시:
metadata["category"]이것은 인덱싱 엔진이 JSON 구조 내에서 어디를 찾아야 하는지를 정의합니다.
JSON 캐스트 유형 (
json_cast_type): 지정된 경로의 값을 해석하고 인덱싱할 때 Milvus가 사용해야 하는 데이터 유형입니다.이 유형은 인덱싱되는 필드의 실제 데이터 유형과 일치해야 합니다.
전체 목록은 지원되는 JSON 형 변환 유형을 참조하세요.
JSON 경로를 사용하여 동적 필드 키 색인하기
동적 필드는 JSON 필드이므로 JSON 경로 구문을 사용하여 필드 내의 모든 키를 색인할 수 있습니다. 이는 단순한 스칼라 값과 복잡한 중첩 구조 모두에서 작동합니다.
JSON 경로 예시:
간단한 키의 경우
overview,words중첩된 키의 경우
dynamic_json['varchar'],dynamic_json['nested']['value']
index_params = client.prepare_index_params()
# Index a simple string key
index_params.add_index(
field_name="overview", # Key name in the dynamic field
index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
index_name="overview_index", # Unique index name
params={
"json_cast_type": "varchar", # Data type that Milvus uses when indexing the values
"json_path": "overview" # JSON path to the key
}
)
# Index a simple numeric key
index_params.add_index(
field_name="words", # Key name in the dynamic field
index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
index_name="words_index", # Unique index name
params={
"json_cast_type": "double", # Data type that Milvus uses when indexing the values
"json_path": "words" # JSON path to the key
}
)
# Index a nested key within a JSON object
index_params.add_index(
field_name="dynamic_json", # JSON key name in the dynamic field
index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
index_name="json_varchar_index", # Unique index name
params={
"json_cast_type": "varchar", # Data type that Milvus uses when indexing the values
"json_path": "dynamic_json['varchar']" # JSON path to the nested key
}
)
# Index a deeply nested key
index_params.add_index(
field_name="dynamic_json",
index_type="AUTOINDEX", # Must be set to AUTOINDEX or INVERTED for JSON path indexing
index_name="json_nested_index", # Unique index name
params={
"json_cast_type": "double",
"json_path": "dynamic_json['nested']['value']"
}
)
import io.milvus.v2.common.IndexParam;
Map<String,Object> extraParams1 = new HashMap<>();
extraParams1.put("json_path", "overview");
extraParams1.put("json_cast_type", "varchar");
indexParams.add(IndexParam.builder()
.fieldName("overview")
.indexName("overview_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.extraParams(extraParams1)
.build());
Map<String,Object> extraParams2 = new HashMap<>();
extraParams2.put("json_path", "words");
extraParams2.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
.fieldName("words")
.indexName("words_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.extraParams(extraParams2)
.build());
Map<String,Object> extraParams3 = new HashMap<>();
extraParams3.put("json_path", "dynamic_json['varchar']");
extraParams3.put("json_cast_type", "varchar");
indexParams.add(IndexParam.builder()
.fieldName("dynamic_json")
.indexName("json_varchar_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.extraParams(extraParams3)
.build());
Map<String,Object> extraParams4 = new HashMap<>();
extraParams4.put("json_path", "dynamic_json['nested']['value']");
extraParams4.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
.fieldName("dynamic_json")
.indexName("json_nested_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.extraParams(extraParams4)
.build());
const indexParams = [
{
collection_name: 'my_collection',
field_name: 'overview',
index_name: 'overview_index',
index_type: 'AUTOINDEX',
metric_type: 'NONE',
params: {
json_path: 'overview',
json_cast_type: 'varchar',
},
},
{
collection_name: 'my_collection',
field_name: 'words',
index_name: 'words_index',
index_type: 'AUTOINDEX',
metric_type: 'NONE',
params: {
json_path: 'words',
json_cast_type: 'double',
},
},
{
collection_name: 'my_collection',
field_name: 'dynamic_json',
index_name: 'json_varchar_index',
index_type: 'AUTOINDEX',
metric_type: 'NONE',
params: {
json_cast_type: 'varchar',
json_path: "dynamic_json['varchar']",
},
},
{
collection_name: 'my_collection',
field_name: 'dynamic_json',
index_name: 'json_nested_index',
index_type: 'AUTOINDEX',
metric_type: 'NONE',
params: {
json_cast_type: 'double',
json_path: "dynamic_json['nested']['value']",
},
},
];
import (
"github.com/milvus-io/milvus/client/v2/index"
)
jsonIndex1 := index.NewJSONPathIndex(index.AUTOINDEX, "varchar", "overview")
.WithIndexName("overview_index")
jsonIndex2 := index.NewJSONPathIndex(index.AUTOINDEX, "double", "words")
.WithIndexName("words_index")
jsonIndex3 := index.NewJSONPathIndex(index.AUTOINDEX, "varchar", `dynamic_json['varchar']`)
.WithIndexName("json_varchar_index")
jsonIndex4 := index.NewJSONPathIndex(index.AUTOINDEX, "double", `dynamic_json['nested']['value']`)
.WithIndexName("json_nested_index")
indexOpt1 := milvusclient.NewCreateIndexOption("my_collection", "overview", jsonIndex1)
indexOpt2 := milvusclient.NewCreateIndexOption("my_collection", "words", jsonIndex2)
indexOpt3 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex3)
indexOpt4 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex4)
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"
export overviewIndex='{
"fieldName": "dynamic_json",
"indexName": "overview_index",
"params": {
"index_type": "AUTOINDEX",
"json_cast_type": "varchar",
"json_path": "dynamic_json[\"overview\"]"
}
}'
export wordsIndex='{
"fieldName": "dynamic_json",
"indexName": "words_index",
"params": {
"index_type": "AUTOINDEX",
"json_cast_type": "double",
"json_path": "dynamic_json[\"words\"]"
}
}'
export varcharIndex='{
"fieldName": "dynamic_json",
"indexName": "json_varchar_index",
"params": {
"index_type": "AUTOINDEX",
"json_cast_type": "varchar",
"json_path": "dynamic_json[\"varchar\"]"
}
}'
export nestedIndex='{
"fieldName": "dynamic_json",
"indexName": "json_nested_index",
"params": {
"index_type": "AUTOINDEX",
"json_cast_type": "double",
"json_path": "dynamic_json[\"nested\"][\"value\"]"
}
}'
유형 변환에 JSON 형변환 함수 사용Compatible with Milvus 2.5.14+
동적 필드 키에 잘못된 형식의 값(예: 문자열로 저장된 숫자)이 포함된 경우 형 변환 함수를 사용하여 변환할 수 있습니다:
# Convert a string to double before indexing
index_params.add_index(
field_name="dynamic_json", # JSON key name
index_type="AUTOINDEX",
index_name="json_string_price_index",
params={
"json_path": "dynamic_json['string_price']",
"json_cast_type": "double", # Must be the output type of the cast function
"json_cast_function": "STRING_TO_DOUBLE" # Case insensitive; convert string to double
}
)
Map<String,Object> extraParams5 = new HashMap<>();
extraParams5.put("json_path", "dynamic_json['string_price']");
extraParams5.put("json_cast_type", "double");
indexParams.add(IndexParam.builder()
.fieldName("dynamic_json")
.indexName("json_string_price_index")
.indexType(IndexParam.IndexType.AUTOINDEX)
.extraParams(extraParams5)
.build());
indexParams.push({
collection_name: 'my_collection',
field_name: 'dynamic_json',
index_name: 'json_string_price_index',
index_type: 'AUTOINDEX',
metric_type: 'NONE',
params: {
json_path: "dynamic_json['string_price']",
json_cast_type: 'double',
json_cast_function: 'STRING_TO_DOUBLE',
},
});
jsonIndex5 := index.NewJSONPathIndex(index.AUTOINDEX, "double", `dynamic_json['string_price']`)
.WithIndexName("json_string_price_index")
indexOpt5 := milvusclient.NewCreateIndexOption("my_collection", "dynamic_json", jsonIndex5)
export TOKEN="root:Milvus"
export CLUSTER_ENDPOINT="http://localhost:19530"
export stringPriceIndex='{
"fieldName": "dynamic_json",
"indexName": "json_string_price_index",
"params": {
"index_type": "AUTOINDEX",
"json_path": "dynamic_json[\"string_price\"]",
"json_cast_type": "double",
"json_cast_function": "STRING_TO_DOUBLE"
}
}'
유형 변환에 실패하면(예:
"not_a_number"값을 숫자로 변환할 수 없음) 해당 값은 건너뛰고 색인화되지 않습니다.형변환 함수 매개변수에 대한 자세한 내용은 JSON 필드를 참조하세요.
컬렉션에 인덱스 적용
인덱스 파라미터를 정의한 후 create_index() 을 사용하여 컬렉션에 적용할 수 있습니다:
client.create_index(
collection_name="my_collection",
index_params=index_params
)
import io.milvus.v2.service.index.request.CreateIndexReq;
client.createIndex(CreateIndexReq.builder()
.collectionName("my_collection")
.indexParams(indexParams)
.build());
await client.createIndex(indexParams);
indexTask1, err := client.CreateIndex(ctx, indexOpt1)
if err != nil {
return err
}
indexTask2, err := client.CreateIndex(ctx, indexOpt2)
if err != nil {
return err
}
indexTask3, err := client.CreateIndex(ctx, indexOpt3)
if err != nil {
return err
}
indexTask4, err := client.CreateIndex(ctx, indexOpt4)
if err != nil {
return err
}
indexTask5, err := client.CreateIndex(ctx, indexOpt5)
if err != nil {
return err
}
# restful
export indexParams="[
$varcharIndex,
$nestedIndex,
$overviewIndex,
$wordsIndex,
$stringPriceIndex
]"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/indexes/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
\"collectionName\": \"my_collection\",
\"indexParams\": $indexParams
}"
동적 필드 키로 필터링
동적 필드 키가 있는 엔티티를 삽입한 후 표준 필터 표현식을 사용하여 필터링할 수 있습니다.
JSON이 아닌 키(예: 문자열, 숫자, 부울)의 경우 키 이름으로 직접 참조할 수 있습니다.
JSON 개체를 저장하는 키의 경우 JSON 경로 구문을 사용하여 중첩된 값에 액세스합니다.
이전 섹션의예제 엔티티를 기준으로 유효한 필터 표현식은 다음과 같습니다:
filter = 'overview == "Great product"' # Non-JSON key
filter = 'words >= 100' # Non-JSON key
filter = 'dynamic_json["nested"]["value"] < 50' # JSON object key
String filter = 'overview == "Great product"';
String filter = 'words >= 100';
String filter = 'dynamic_json["nested"]["value"] < 50';
filter = 'overview == "Great product"' // Non-JSON key
filter = 'words >= 100' // Non-JSON key
filter = 'dynamic_json["nested"]["value"] < 50' // JSON object key
filter := 'overview == "Great product"'
filter := 'words >= 100'
filter := 'dynamic_json["nested"]["value"] < 50'
# restful
export filterOverview='overview == "Great product"'
export filterWords='words >= 100'
export filterNestedValue='dynamic_json["nested"]["value"] < 50'
동적 필드 키 검색: 검색 또는 쿼리 결과에서 동적 필드 키를 반환하려면 필터링과 동일한 JSON 경로 구문을 사용하여 output_fields 파라미터에 명시적으로 지정해야 합니다:
# Example: Include dynamic field keys in search results
results = client.search(
collection_name="my_collection",
data=[[0.1, 0.2, 0.3, 0.4, 0.5]],
filter=filter, # Filter expression defined earlier
limit=10,
output_fields=[
"overview", # Simple dynamic field key
'dynamic_json["varchar"]' # Nested JSON key
]
)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp
MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
.uri("YOUR_CLUSTER_ENDPOINT")
.token("YOUR_CLUSTER_TOKEN")
.build());
FloatVec queryVector = new FloatVec(new float[]{0.1, 0.2, 0.3, 0.4, 0.5});
SearchReq searchReq = SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(queryVector))
.topK(5)
.filter(filter)
.outputFields(Arrays.asList("overview", "dynamic_json['varchar']"))
.build();
SearchResp searchResp = client.search(searchReq);
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "YOUR_CLUSTER_ENDPOINT";
const token = "YOUR_CLUSTER_TOKEN";
const client = new MilvusClient({address, token});
const query_vector = [0.1, 0.2, 0.3, 0.4, 0.5]
const res = await client.search({
collection_name: "my_collection",
data: [query_vector],
limit: 5,
filters: filter,
output_fields: ["overview", "dynamic_json['varchar']"]
})
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "YOUR_CLUSTER_ENDPOINT"
token := "YOUR_CLUSTER_TOKEN"
client, err := client.New(ctx, &client.ClientConfig{
Address: milvusAddr,
APIKey: token,
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
queryVector := []float32{0.1, 0.2, 0.3, 0.4, 0.5}
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection", // collectionName
5, // limit
[]entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClStrong).
WithANNSField("my_vector").
WithFilter(filter).
WithOutputFields("overview", "dynamic_json['varchar']"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
export CLUSTER_ENDPOINT="YOUR_CLUSTER_ENDPOINT"
export TOKEN="YOUR_CLUSTER_TOKEN"
export FILTER='color like "red%" and likes > 50'
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data "{
\"collectionName\": \"my_collection\",
\"data\": [
[0.1, 0.2, 0.3, 0.4, 0.5]
],
\"annsField\": \"my_vector\",
\"filter\": \"${FILTER}\",
\"limit\": 5,
\"outputFields\": [\"overview\", \"dynamic_json.varchar\"]
}"
동적 필드 키는 기본적으로 결과에 포함되지 않으므로 명시적으로 요청해야 합니다.
지원되는 연산자 및 필터 표현식의 전체 목록은 필터링된 검색을 참조하세요.
정리하기
지금까지 동적 필드를 사용하여 스키마에 정의되지 않은 키를 유연하게 저장하고 색인하는 방법을 배웠습니다. 동적 필드 키를 삽입한 후에는 특별한 구문 없이 필터 표현식의 다른 필드처럼 사용할 수 있습니다.
실제 애플리케이션에서 워크플로우를 완료하려면 다음과 같은 작업도 수행해야 합니다:
벡터 필드에 인덱스 생성 (각 컬렉션에 필수)
인덱스 매개변수 설정을 참조하세요.
컬렉션 로드
로드 및 릴리스 참조
JSON 경로 필터를 사용하여 검색 또는 쿼리하기
FAQ
동적 필드 키를 사용하는 대신 스키마에 명시적으로 필드를 정의해야 하는 경우는 언제인가요?
다음과 같은 경우에는 동적 필드 키를 사용하는 대신 스키마에 명시적으로 필드를 정의해야 합니다:
필드가 출력_필드에 자주 포함되는 경우: 명시적으로 정의된 필드만
output_fields을 통해 효율적으로 검색할 수 있도록 보장됩니다. 동적 필드 키는 빈번한 검색에 최적화되어 있지 않으며 성능 오버헤드가 발생할 수 있습니다.필드에 자주 액세스하거나 필터링하는 경우: 동적 필드 키를 인덱싱하면 고정 스키마 필드와 비슷한 필터링 성능을 제공할 수 있지만, 명시적으로 정의된 필드는 더 명확한 구조와 더 나은 유지보수성을 제공합니다.
필드 동작을 완전히 제어할 수 있어야 합니다: 명시적 필드는 스키마 수준의 제약 조건, 유효성 검사, 보다 명확한 타이핑을 지원하므로 데이터 무결성과 일관성을 관리하는 데 유용할 수 있습니다.
인덱싱 불일치를 피하고 싶습니다: 동적 필드 키의 데이터는 유형이나 구조가 불일치하기 쉽습니다. 고정 스키마를 사용하면 특히 인덱싱이나 캐스팅을 사용하려는 경우 데이터 품질을 보장하는 데 도움이 됩니다.
동일한 동적 필드 키에 서로 다른 데이터 유형으로 여러 인덱스를 만들 수 있나요?
아니요, JSON 경로당 하나의 인덱스만 만들 수 있습니다. 동적 필드 키에 혼합 유형 값(예: 일부 문자열과 일부 숫자)이 포함되어 있더라도 해당 경로를 색인할 때는 단일 json_cast_type 을 선택해야 합니다. 현재로서는 동일한 키에 대해 서로 다른 유형의 여러 인덱스가 지원되지 않습니다.
동적 필드 키를 색인할 때 데이터 캐스팅이 실패하면 어떻게 하나요?
동적 필드 키에 인덱스를 만들었는데 데이터 캐스팅이 실패하는 경우(예: double 로 캐스팅되어야 하는 값이 "abc"과 같은 숫자가 아닌 문자열인 경우) 인덱스 생성 중에 이러한 특정 값은 자동으로 건너뛰게 됩니다. 인덱스에 표시되지 않으므로 인덱스에 의존하는 필터 기반 검색이나 쿼리 결과에서 반환되지 않습니다.
여기에는 몇 가지 중요한 의미가 있습니다:
전체 스캔에 대한 폴백이 없습니다: 대부분의 엔티티가 성공적으로 색인되면 필터링 쿼리는 전적으로 색인에 의존하게 됩니다. 캐스팅에 실패한 엔티티는 논리적으로 필터 조건과 일치하더라도 결과 세트에서 제외됩니다.
검색 정확도 위험: 데이터 품질이 일관되지 않은 대규모 데이터 세트(특히 동적 필드 키의 경우)에서는 이러한 동작으로 인해 예기치 않은 결과 누락이 발생할 수 있습니다. 인덱싱하기 전에 일관되고 유효한 데이터 형식을 보장하는 것이 중요합니다.
형변환 함수는 신중하게 사용하세요: 인덱싱 중에
json_cast_function을 사용하여 문자열을 숫자로 변환하는 경우, 문자열 값이 안정적으로 변환 가능한지 확인하세요.json_cast_type과 실제 변환된 유형이 일치하지 않으면 오류가 발생하거나 항목이 건너뛰게 됩니다.
쿼리가 인덱싱된 형 변환 유형과 다른 데이터 유형을 사용하는 경우 어떻게 되나요?
쿼리에서 인덱스에 사용된 것과 다른 데이터 유형을 사용하여 동적 필드 키를 비교하는 경우(예: 인덱스가 double 로 캐스팅된 경우 문자열 비교로 쿼리), 시스템에서 인덱스를 사용하지 않고 가능한 경우에만 전체 스캔으로 돌아갈 수 있습니다. 최상의 성능과 정확성을 위해 쿼리 유형이 인덱스 생성 중에 사용된 json_cast_type 과 일치하는지 확인하세요.