동적 필드 활성화 (Enable Dynamic Field)
이 페이지는 유연한 데이터 삽입과 검색을 위해 컬렉션에서 동적 필드를 사용하는 방법을 설명해요.
출처: Milvus 문서
본문
개요 (Overview)
Milvus는 각 특정 필드의 이름과 데이터 타입을 설정해 컬렉션의 스키마를 정의할 수 있게 해줘요. 이렇게 하면 이러한 필드에 인덱스를 만들어 검색 성능을 향상시킬 수 있어요.
필드가 정의되면 데이터를 삽입할 때 이 필드를 포함해야 해요. 그런데 일부 필드가 데이터 항목마다 항상 존재하지 않는다면 어떨까요? 이때 동적 필드(dynamic field)가 사용돼요.
컬렉션의 동적 필드는 $meta라는 이름의 예약된 JSON 필드예요. 스키마에 정의되지 않은 필드와 그 값을 키-값 쌍으로 보관할 수 있어요. 동적 필드를 사용하면 스키마에 정의된 필드와 정의되지 않은 필드를 모두 검색·쿼리할 수 있어요.
동적 필드 활성화 (Enable dynamic field)
컬렉션의 스키마를 정의할 때 enable_dynamic_field를 True로 설정해 예약된 동적 필드를 활성화할 수 있어요. 이는 나중에 삽입되는 스키마에 정의되지 않은 필드와 값이 예약된 동적 필드에 키-값 쌍으로 저장됨을 나타내요.
다음 코드 조각은 id와 vector라는 두 개의 스키마 정의 필드를 가진 컬렉션을 만들고 동적 필드를 활성화해요.
파라미터에 대한 자세한 내용은 SDK 참조의 create_collection()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 createCollection()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 createCollection()을 참고하세요.
import random, time
from pymilvus import connections, MilvusClient, DataType
SERVER_ADDR = "http://localhost:19530"
# 1. Set up a Milvus client
client = MilvusClient(
uri=SERVER_ADDR
)
# 2. Create a collection
schema = MilvusClient.create_schema(
auto_id=False,
enable_dynamic_field=True,
)
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(field_name="vector", datatype=DataType.FLOAT_VECTOR, dim=5)
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="id",
index_type="STL_SORT"
)
index_params.add_index(
field_name="vector",
index_type="IVF_FLAT",
metric_type="L2",
params={"nlist": 1024}
)
client.create_collection(
collection_name="test_collection",
schema=schema,
index_params=index_params
)
res = client.get_load_state(
collection_name="test_collection"
)
print(res)
# Output
#
# {
# "state": "<LoadState: Loaded>"
# }
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.*;
import io.milvus.v2.service.vector.request.*;
import io.milvus.v2.service.vector.request.data.*;
import io.milvus.v2.service.vector.response.*;
import java.util.*;
String CLUSTER_ENDPOINT = "http://localhost:19530";
// 1. Connect to Milvus server
ConnectConfig connectConfig = ConnectConfig.builder()
.uri(CLUSTER_ENDPOINT)
.build();
MilvusClientV2 client = new MilvusClientV2(connectConfig);
// 2. Create a collection in customized setup mode
// 2.1 Create schema
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);
// 2.2 Add fields to schema
schema.addField(AddFieldReq.builder().fieldName("id").dataType(DataType.Int64).isPrimaryKey(true).autoID(false).build());
schema.addField(AddFieldReq.builder().fieldName("vector").dataType(DataType.FloatVector).dimension(5).build());
// 2.3 Prepare index parameters
IndexParam indexParamForIdField = IndexParam.builder()
.fieldName("id")
.indexType(IndexParam.IndexType.STL_SORT)
.build();
Map<String, Object> params = new HashMap<>();
params.put("nlist", 1024);
IndexParam indexParamForVectorField = IndexParam.builder()
.fieldName("vector")
.indexType(IndexParam.IndexType.IVF_FLAT)
.metricType(IndexParam.MetricType.IP)
.extraParams(params)
.build();
List<IndexParam> indexParams = new ArrayList<>();
indexParams.add(indexParamForIdField);
indexParams.add(indexParamForVectorField);
// 2.4 Create a collection with schema and index parameters
CreateCollectionReq customizedSetupReq = CreateCollectionReq.builder()
.collectionName("customized_setup")
.collectionSchema(schema)
.indexParams(indexParams)
.build();
client.createCollection(customizedSetupReq);
Thread.sleep(5000);
// 2.5 Get load state of the collection
GetLoadStateReq customSetupLoadStateReq1 = GetLoadStateReq.builder()
.collectionName("customized_setup")
.build();
boolean res = client.getLoadState(customSetupLoadStateReq1);
System.out.println(res);
// Output:
// true
const { MilvusClient, DataType, sleep } = require("@zilliz/milvus2-sdk-node")
const address = "http://localhost:19530"
async function main() {
// 1. Set up a Milvus Client
client = new MilvusClient({address});
// 2. Create a collection
// 2.1 Define fields
const fields = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
auto_id: false
},
{
name: "vector",
data_type: DataType.FloatVector,
dim: 5
},
]
// 2.2 Prepare index parameters
const index_params = [{
field_name: "id",
index_type: "STL_SORT"
},{
field_name: "vector",
index_type: "IVF_FLAT",
metric_type: "IP",
params: { nlist: 1024}
}]
// 2.3 Create a collection with fields and index parameters
res = await client.createCollection({
collection_name: "test_collection",
fields: fields,
index_params: index_params,
enable_dynamic_field: true
})
console.log(res.error_code)
// Output
//
// Success
//
res = await client.getLoadState({
collection_name: "test_collection",
})
console.log(res.state)
// Output
//
// LoadStateLoaded
//
}
동적 데이터 삽입 (Insert dynamic data)
컬렉션이 생성되면 동적 데이터를 포함한 데이터를 컬렉션에 삽입하기 시작할 수 있어요.
데이터 준비 (Prepare data)
이 섹션에서는 나중에 삽입할 몇 가지 무작위 생성 데이터를 준비해야 해요.
colors = ["green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey"]
data = []
for i in range(1000):
current_color = random.choice(colors)
current_tag = random.randint(1000, 9999)
data.append({
"id": i,
"vector": [ random.uniform(-1, 1) for _ in range(5) ],
"color": current_color,
"tag": current_tag,
"color_tag": f"{current_color}_{str(current_tag)}"
})
print(data[0])
List<String> colors = Arrays.asList("green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey");
List<JsonObject> data = new ArrayList<>();
Gson gson = new Gson();
Random rand = new Random();
for (int i=0; i<1000; i++) {
String current_color = colors.get(rand.nextInt(colors.size()-1));
int current_tag = rand.nextInt(8999) + 1000;
JsonObject row = new JsonObject();
row.addProperty("id", (long) i);
row.add("vector", gson.toJsonTree(Arrays.asList(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), rand.nextFloat())));
row.addProperty("color", current_color);
row.addProperty("tag", current_tag);
row.addProperty("color_tag", current_color + "_" + (rand.nextInt(8999) + 1000));
data.add(row);
}
System.out.println(data.get(0).toString());
const colors = ["green", "blue", "yellow", "red", "black", "white", "purple", "pink", "orange", "brown", "grey"]
var data = []
for (let i = 0; i < 1000; i++) {
const current_color = colors[Math.floor(Math.random() * colors.length)]
const current_tag = Math.floor(Math.random() * 8999 + 1000)
data.push({
id: i,
vector: [Math.random(), Math.random(), Math.random(), Math.random(), Math.random()],
color: current_color,
tag: current_tag,
color_tag: `${current_color}_${current_tag}`
})
}
console.log(data[0])
첫 번째 항목을 확인해 생성된 데이터의 구조를 볼 수 있어요.
{
id: 0,
vector: [
0.1275656405044483,
0.47417858592773277,
0.13858264437643286,
0.2390904907020377,
0.8447862593689635
],
color: 'blue',
tag: 2064,
color_tag: 'blue_2064'
}
데이터 삽입 (Insert data)
그런 다음 데이터를 안전하게 컬렉션에 삽입할 수 있어요.
파라미터에 대한 자세한 내용은 SDK 참조의 insert()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 insert()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 insert()을 참고하세요.
res = client.insert(
collection_name="test_collection",
data=data,
)
print(res)
# Output
#
# {
# "insert_count": 1000,
# "ids": [
# 0,
# 1,
# 2,
# 3,
# 4,
# 5,
# 6,
# 7,
# 8,
# 9,
# "(990 more items hidden)"
# ]
# }
time.sleep(5)
// 3.1 Insert data into the collection
InsertReq insertReq = InsertReq.builder()
.collectionName("customized_setup")
.data(data)
.build();
InsertResp insertResp = client.insert(insertReq);
System.out.println(insertResp.getInsertCnt());
// Output:
// 1000
Thread.sleep(5000);
res = await client.insert({
collection_name: "test_collection",
data: data,
})
console.log(res.insert_cnt)
// Output
//
// 1000
//
await sleep(5000)
동적 필드로 검색 (Search with dynamic fields)
동적 필드를 활성화한 컬렉션을 만들고 스키마에 정의되지 않은 필드를 삽입했다면, 이 필드들을 다음과 같이 검색 또는 쿼리의 필터 표현식에서 사용할 수 있어요.
파라미터에 대한 자세한 내용은 SDK 참조의 search()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 search()을 참고하세요.
파라미터에 대한 자세한 내용은 SDK 참조의 search()을 참고하세요.
# 4. Search with dynamic fields
query_vectors = [[0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]]
res = client.search(
collection_name="test_collection",
data=query_vectors,
filter="color in [\"red\", \"green\"]",
search_params={"metric_type": "L2", "params": {"nprobe": 10}},
limit=3
)
print(res)
# Output
#
# [
# [
# {
# "id": 863,
# "distance": 0.188413605093956,
# "entity": {
# "id": 863,
# "color_tag": "red_2371"
# }
# },
# {
# "id": 799,
# "distance": 0.29188022017478943,
# "entity": {
# "id": 799,
# "color_tag": "red_2235"
# }
# },
# {
# "id": 564,
# "distance": 0.3492690920829773,
# "entity": {
# "id": 564,
# "color_tag": "red_9186"
# }
# }
# ]
# ]
// 4. Search with non-schema-defined fields
List<BaseVector> queryVectors = Collections.singletonList(new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f}));
SearchReq searchReq = SearchReq.builder()
.collectionName("customized_setup")
.data(queryVectors)
.filter("$meta[\"color\"] in [\"red\", \"green\"]")
.outputFields(Arrays.asList("id", "color_tag"))
.topK(3)
.build();
SearchResp searchResp = client.search(searchReq);
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
for (List<SearchResp.SearchResult> results : searchResults) {
System.out.println("TopK results:");
for (SearchResp.SearchResult result : results) {
System.out.println(result);
}
}
// Output:
// SearchResp.SearchResult(entity={color_tag=green_2205, id=556}, score=1.134007, id=556)
// SearchResp.SearchResult(entity={color_tag=red_2786, id=310}, score=0.9072295, id=310)
// SearchResp.SearchResult(entity={color_tag=red_9493, id=215}, score=0.8819287, id=215)
// 4. Search with non-schema-defined fields
const query_vectors = [[0.1, 0.2, 0.3, 0.4, 0.5]]
res = await client.search({
collection_name: "test_collection",
data: query_vectors,
filter: "color in [\"red\", \"green\"]",
output_fields: ["color_tag"],
limit: 3
})
console.log(res.results)
// Output
//
// [
// { score: 1.2284551858901978, id: '301', color_tag: 'red_1270' },
// { score: 1.2195171117782593, id: '205', color_tag: 'red_2780' },
// { score: 1.2055039405822754, id: '487', color_tag: 'red_6653' }
// ]
//
요약 (Recaps)
color, tag, color_tag는 컬렉션 스키마를 정의할 때 존재하지 않지만, 검색과 쿼리를 수행할 때 스키마 정의 필드처럼 사용할 수 있다는 점을 기억할 가치가 있어요.
스키마에 정의되지 않은 필드의 이름이 숫자, 글자, 밑줄 외의 문자(예: 더하기 기호(+), 별표(*), 달러 기호($))를 포함한다면, 그 키를 불리언 표현식에서 사용하거나 출력 필드에 포함할 때 다음 코드 조각처럼 $meta[] 안에 넣어야 해요.
...
filter='$meta["$key"] in ["a", "b", "c"]',
output_fields='$meta["$key"]'
...
더 알아보기 (Learn more)
- create_collection() — SDK 참조
- insert() — SDK 참조
- search() — SDK 참조
- Milvus 공식 문서 — 컬렉션·스키마 관련 자료