기본 벡터 검색

벡터 임베딩의 정렬 순서를 기록한 인덱스 파일을 기반으로, 근사 최인접 이웃(ANN) 검색은 수신된 검색 요청에 포함된 쿼리 벡터를 바탕으로 벡터 임베딩의 하위 집합을 찾아내고, 쿼리 벡터를 해당 하위 집합의 벡터들과 비교하여 가장 유사한 결과를 반환합니다. Milvus는 ANN 검색을 통해 효율적인 검색 경험을 제공합니다. 이 페이지에서는 기본적인 ANN 검색을 수행하는 방법을 안내합니다.

컬렉션 생성 후 새로운 필드를 추가하는 경우, 해당 필드를 포함하는 검색에서는 값이 명시적으로 설정되지 않은 엔티티에 대해 정의된 기본값 또는 ‘ NULL ’이 반환됩니다. 자세한 내용은 ‘컬렉션 스키마 변경’을 참조하십시오.

개요

ANN 및 k-최근접 이웃(kNN) 검색은 벡터 유사도 검색에서 일반적으로 사용되는 방법입니다. kNN 검색에서는 가장 유사한 벡터를 파악하기 위해 벡터 공간 내의 모든 벡터를 검색 요청에 포함된 쿼리 벡터와 비교해야 하므로, 시간이 많이 소요되고 리소스를 많이 소모합니다.

kNN 검색과 달리, ANN 검색 알고리즘은 벡터 임베딩의 정렬된 순서를 기록한 인덱스 파일을 요구합니다. 검색 요청이 들어오면 인덱스 파일을 참조하여 쿼리 벡터와 가장 유사한 벡터 임베딩을 포함할 가능성이 높은 하위 그룹을 빠르게 찾을 수 있습니다. 그런 다음 지정된 메트릭 유형을 사용하여 쿼리 벡터와 해당 하위 그룹 내 벡터 간의 유사도를 측정하고, 쿼리 벡터와의 유사도에 따라 그룹 구성원을 정렬한 후 상위 K개 그룹 구성원을 파악할 수 있습니다.

ANN 검색은 미리 구축된 인덱스에 의존하며, 검색 처리량, 메모리 사용량 및 검색 정확도는 선택한 인덱스 유형에 따라 달라질 수 있습니다. 따라서 검색 성능과 정확도 간의 균형을 적절히 맞추어야 합니다.

학습 곡선을 줄이기 위해 Milvus는 AUTOINDEX 기능을 제공합니다. AUTOINDEX를 사용하면 Milvus는 인덱스를 구축하는 동안 컬렉션 내의 데이터 분포를 분석하고, 분석 결과를 바탕으로 검색 성능과 정확성 간의 균형을 맞추기 위해 가장 최적화된 인덱스 매개변수를 설정합니다.

이 섹션에서는 다음 주제에 대한 자세한 정보를 확인할 수 있습니다:

ANN 검색에서 단일 벡터 검색이란 단 하나의 쿼리 벡터만 사용하는 검색을 의미합니다. Milvus는 미리 구축된 인덱스와 검색 요청에 포함된 메트릭 유형을 기반으로, 쿼리 벡터와 가장 유사한 상위 K개 벡터를 찾아냅니다.

이 섹션에서는 단일 벡터 검색을 수행하는 방법을 배웁니다. 검색 요청에는 단일 쿼리 벡터가 포함되며, Milvus는 내적(IP)을 사용하여 쿼리 벡터와 컬렉션 내 벡터 간의 유사도를 계산하고, 가장 유사한 3개를 반환합니다.

from pymilvus import MilvusClient

client = MilvusClient(
    uri="http://localhost:19530",
    token="root:Milvus"
)

# 4. Single vector search
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = client.search(
    collection_name="quick_setup",
    anns_field="vector",
    data=[query_vector],
    limit=3,
    search_params={"metric_type": "IP"}
)

for hits in res:
    for hit in hits:
        print(hit)

# [
#     [
#         {
#             "id": 551,
#             "distance": 0.08821295201778412,
#             "entity": {}
#         },
#         {
#             "id": 296,
#             "distance": 0.0800950899720192,
#             "entity": {}
#         },
#         {
#             "id": 43,
#             "distance": 0.07794742286205292,
#             "entity": {}
#         }
#     ]
# ]
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;

import java.util.*;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .token("root:Milvus")
        .build());
    
FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
        .collectionName("quick_setup")
        .data(Collections.singletonList(queryVector))
        .annsField("vector")
        .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
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.95944905, id=5)
// SearchResp.SearchResult(entity={}, score=0.8689616, id=1)
// SearchResp.SearchResult(entity={}, score=0.866088, id=7)
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 := "localhost:19530"
token := "root:Milvus"

client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
    Address: milvusAddr,
    APIKey:  token,
})
if err != nil {
    fmt.Println(err.Error())
    // handle error
}
defer client.Close(ctx)

queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "quick_setup", // collectionName
    3,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithANNSField("vector"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
}

import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});

// 4. Single vector search
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = await client.search({
    collection_name: "quick_setup",
    data: query_vector,
    limit: 3, // The number of results to return
})

console.log(res.results)

// [
//   { score: 0.08821295201778412, id: '551' },
//   { score: 0.0800950899720192, id: '296' },
//   { score: 0.07794742286205292, id: '43' }
// ]
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "quick_setup",
    "data": [
        [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
    ],
    "annsField": "vector",
    "limit": 3
}'

# {
#     "code": 0,
#     "data": [
#         {
#             "distance": 0.08821295201778412,
#             "id": 551
#         },
#         {
#             "distance": 0.0800950899720192,
#             "id": 296
#         },
#         {
#             "distance": 0.07794742286205292,
#             "id": 43
#         }
#     ]
# }

Milvus는 쿼리 벡터와의 유사도 점수에 따라 검색 결과를 내림차순으로 정렬합니다. 유사도 점수는 쿼리 벡터와의 거리라고도 하며, 그 값의 범위는 사용 중인 메트릭 유형에 따라 달라집니다.

다음 표에는 적용 가능한 메트릭 유형과 해당 거리 범위가 나열되어 있습니다.

메트릭 유형

특징

거리 범위

L2

값이 작을수록 유사성이 높음을 나타냅니다.

[0, ∞)

IP

값이 클수록 유사성이 더 높음을 나타냅니다.

[-1, 1]

COSINE

값이 클수록 유사성이 더 높음을 나타냅니다.

[-1, 1]

JACCARD

값이 작을수록 유사성이 더 높음을 나타냅니다.

[0, 1]

HAMMING

값이 작을수록 유사도가 더 높음을 나타냅니다.

[0, 벡터의 차원]

마찬가지로, 검색 요청에 여러 개의 쿼리 벡터를 포함할 수 있습니다. Milvus는 쿼리 벡터에 대해 ANN 검색을 병렬로 수행하고 두 세트의 결과를 반환합니다.

# 7. Search with multiple vectors
# 7.1. Prepare query vectors
query_vectors = [
    [0.041732933, 0.013779674, -0.027564144, -0.013061441, 0.009748648],
    [0.0039737443, 0.003020432, -0.0006188639, 0.03913546, -0.00089768134]
]

# 7.2. Start search
res = client.search(
    collection_name="quick_setup",
    data=query_vectors,
    limit=3,
)

for hits in res:
    print("TopK results:")
    for hit in hits:
        print(hit)

# Output
#
# [
#     [
#         {
#             "id": 551,
#             "distance": 0.08821295201778412,
#             "entity": {}
#         },
#         {
#             "id": 296,
#             "distance": 0.0800950899720192,
#             "entity": {}
#         },
#         {
#             "id": 43,
#             "distance": 0.07794742286205292,
#             "entity": {}
#         }
#     ],
#     [
#         {
#             "id": 730,
#             "distance": 0.04431751370429993,
#             "entity": {}
#         },
#         {
#             "id": 333,
#             "distance": 0.04231833666563034,
#             "entity": {}
#         },
#         {
#             "id": 232,
#             "distance": 0.04221535101532936,
#             "entity": {}
#         }
#     ]
# ]

import io.milvus.v2.service.vector.request.SearchReq
import io.milvus.v2.service.vector.request.data.BaseVector;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp

List<BaseVector> queryVectors = Arrays.asList(
        new FloatVec(new float[]{0.041732933f, 0.013779674f, -0.027564144f, -0.013061441f, 0.009748648f}),
        new FloatVec(new float[]{0.0039737443f, 0.003020432f, -0.0006188639f, 0.03913546f, -0.00089768134f})
);
SearchReq searchReq = SearchReq.builder()
        .collectionName("quick_setup")
        .data(queryVectors)
        .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
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.49548206, id=1)
// SearchResp.SearchResult(entity={}, score=0.320147, id=3)
// SearchResp.SearchResult(entity={}, score=0.107413776, id=6)
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.5678123, id=6)
// SearchResp.SearchResult(entity={}, score=0.32368967, id=2)
// SearchResp.SearchResult(entity={}, score=0.24108477, id=3)
queryVectors := []entity.Vector{
    entity.FloatVector([]float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}),
    entity.FloatVector([]float32{0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104}),
}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "quick_setup", // collectionName
    3,               // limit
    queryVectors,
).WithConsistencyLevel(entity.ClStrong).
    WithANNSField("vector"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
}
// 7. Search with multiple vectors
const query_vectors = [
    [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592], 
    [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]
]

res = await client.search({
    collection_name: "quick_setup",
    vectors: query_vectors,
    limit: 3,
})

console.log(res.results)

// Output
// 
// [
//   [
//     { score: 0.08821295201778412, id: '551' },
//     { score: 0.0800950899720192, id: '296' },
//     { score: 0.07794742286205292, id: '43' }
//   ],
//   [
//     { score: 0.04431751370429993, id: '730' },
//     { score: 0.04231833666563034, id: '333' },
//     { score: 0.04221535101532936, id: '232' },
//   ]
// ]
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "quick_setup",
    "data": [
        [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],
        [0.19886812562848388, 0.06023560599112088, 0.6976963061752597, 0.2614474506242501, 0.838729485096104]
    ],
    "annsField": "vector",
    "limit": 3
}'

# {
#     "code": 0,
#     "data": [
#         [
#           {
#               "distance": 0.08821295201778412,
#               "id": 551
#           },
#           {
#               "distance": 0.0800950899720192,
#               "id": 296
#           },
#           {
#               "distance": 0.07794742286205292,
#               "id": 43
#           }
#         ],
#         [
#           {
#               "distance": 0.04431751370429993,
#               "id": 730
#           },
#           {
#               "distance": 0.04231833666563034,
#               "id": 333
#           },
#           {
#               "distance": 0.04221535101532936,
#               "id": 232
#           }
#        ]
#     ],
#     "topks":[3]
# }

기본 키 검색Compatible with Milvus 2.6.9+

쿼리 벡터를 직접 설정하는 대신, 대상 컬렉션에 쿼리 벡터가 이미 존재하는 경우 기본 키를 사용할 수 있습니다.

res = client.search(
    collection_name="quick_setup",
    anns_field="vector",
    ids=[551, 296, 43],
    limit=3,
    search_params={"metric_type": "IP"}
)

for hits in res:
    for hit in hits:
        print(hit)
// java
// node.js
// go
# restful
curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
  -H "Content-Type: application/json" \
  -H "Request-Timeout: 10" \
  -H "Authorization: Bearer root:Milvus" \
  -d '{
    "collectionName": "quick_setup",
    "annsField": "vector",
    "ids": [551, 296, 43],
    "limit": 3,
    "searchParams": {
      "metric_type": "IP"
    }
  }'

파티션 내 ANN 검색

컬렉션에 여러 파티션을 생성한 경우, 검색 범위를 특정 수의 파티션으로 좁힐 수 있습니다. 이 경우, 검색 요청에 대상 파티션 이름을 포함시켜 지정된 파티션 내에서만 검색 범위를 제한할 수 있습니다. 검색에 포함되는 파티션 수를 줄이면 검색 성능이 향상됩니다.

다음 코드 예제는 컬렉션에 PartitionA라는 파티션이 있다고 가정합니다.

# 4. Single vector search
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
res = client.search(
    collection_name="quick_setup",
    partition_names=["partitionA"],
    data=[query_vector],
    limit=3,
)

for hits in res:
    print("TopK results:")
    for hit in hits:
        print(hit)

# [
#     [
#         {
#             "id": 551,
#             "distance": 0.08821295201778412,
#             "entity": {}
#         },
#         {
#             "id": 296,
#             "distance": 0.0800950899720192,
#             "entity": {}
#         },
#         {
#             "id": 43,
#             "distance": 0.07794742286205292,
#             "entity": {}
#         }
#     ]
# ]
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

FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
        .collectionName("quick_setup")
        .partitionNames(Collections.singletonList("partitionA"))
        .data(Collections.singletonList(queryVector))
        .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
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.6395302, id=13)
// SearchResp.SearchResult(entity={}, score=0.5408028, id=12)
// SearchResp.SearchResult(entity={}, score=0.49696884, id=17)
queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "quick_setup", // collectionName
    3,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClStrong).
    WithPartitions("partitionA").
    WithANNSField("vector"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
}
// 4. Single vector search
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = await client.search({
    collection_name: "quick_setup",
    partition_names: ["partitionA"],
    data: query_vector,
    limit: 3, // The number of results to return
})

console.log(res.results)

// [
//   { score: 0.08821295201778412, id: '551' },
//   { score: 0.0800950899720192, id: '296' },
//   { score: 0.07794742286205292, id: '43' }
// ]
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "quick_setup",
    "partitionNames": ["partitionA"],
    "data": [
        [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
    ],
    "annsField": "vector",
    "limit": 3
}'

# {
#     "code": 0,
#     "data": [
#         {
#             "distance": 0.08821295201778412,
#             "id": 551
#         },
#         {
#             "distance": 0.0800950899720192,
#             "id": 296
#         },
#         {
#             "distance": 0.07794742286205292,
#             "id": 43
#         }
#     ],
#     "topks":[3]
# }

출력 필드 사용

검색 결과에서 Milvus는 기본적으로 상위 K개 벡터 임베딩을 포함하는 엔티티의 주 필드 값과 유사도 거리/점수를 포함합니다. 검색 요청에 벡터 필드와 스칼라 필드를 포함한 대상 필드 이름을 출력 필드로 지정하면, 검색 결과에 해당 엔티티의 다른 필드 값도 함께 포함될 수 있습니다.

# 4. Single vector search
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = client.search(
    collection_name="quick_setup",
    data=[query_vector],
    limit=3, # The number of results to return
    search_params={"metric_type": "IP"},
    output_fields=["color"]
)

print(res)

# [
#     [
#         {
#             "id": 551,
#             "distance": 0.08821295201778412,
#             "entity": {
#                 "color": "orange_6781"
#             }
#         },
#         {
#             "id": 296,
#             "distance": 0.0800950899720192,
#             "entity": {
#                 "color": "red_4794"
#             }
#         },
#         {
#             "id": 43,
#             "distance": 0.07794742286205292,
#             "entity": {
#                 "color": "grey_8510"
#             }
#         }
#     ]
# ]
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

FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
        .collectionName("quick_setup")
        .data(Collections.singletonList(queryVector))
        .topK(3)
        .outputFields(Collections.singletonList("color"))
        .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
// TopK results:
// SearchResp.SearchResult(entity={color=black_9955}, score=0.95944905, id=5)
// SearchResp.SearchResult(entity={color=red_7319}, score=0.8689616, id=1)
// SearchResp.SearchResult(entity={color=white_5015}, score=0.866088, id=7)
queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "quick_setup", // collectionName
    3,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClStrong).
    WithANNSField("vector").
    WithOutputFields("color"))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
    fmt.Println("color: ", resultSet.GetColumn("color").FieldData().GetScalars())
}
// 4. Single vector search
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = await client.search({
    collection_name: "quick_setup",
    data: query_vector,
    limit: 3, // The number of results to return
    output_fields: ["color"]
})

console.log(res.results)

// [
//   { score: 0.08821295201778412, id: '551', entity: {"color": "orange_6781"}},
//   { score: 0.0800950899720192, id: '296' entity: {"color": "red_4794"}},
//   { score: 0.07794742286205292, id: '43' entity: {"color": "grey_8510"}}
// ]
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "quick_setup",
    "data": [
        [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
    ],
    "annsField": "vector",
    "limit": 3,
    "outputFields": ["color"]
}'

# {
#     "code": 0,
#     "data": [
#         {
#             "distance": 0.08821295201778412,
#             "id": 551,
#             "color": "orange_6781"
#         },
#         {
#             "distance": 0.0800950899720192,
#             "id": 296,
#             "color": "red_4794"
#         },
#         {
#             "distance": 0.07794742286205292,
#             "id": 43
#             "color": "grey_8510"
#         }
#     ],
#     "topks":[3]
# }

스칼라 필드 기준으로 검색 결과 정렬Compatible with Milvus 3.0.x

기본적으로 Milvus는 쿼리 벡터와의 유사도 점수에 따라 검색 결과를 정렬합니다. 반환된 엔티티가 스칼라 필드 순서를 따르도록 하려면 검색 요청에 ` order_by_fields `를 추가하십시오.

order_by_fields 의 각 항목은 스칼라 필드와 정렬 방향을 지정합니다. 오름차순 정렬을 원하면 "asc" 를, 내림차순 정렬을 원하면 "desc" 를 사용하십시오. order 를 생략하면 Milvus는 해당 필드를 오름차순으로 정렬합니다.

다음 예제는 price 를 기준으로 검색 결과를 낮은 값에서 높은 값 순으로 정렬합니다. 응답에서 필드 값을 확인하려면 output_fields 에 정렬 필드를 포함시키십시오.

res = client.search(
    collection_name="product_catalog",
    data=query_vectors,
    anns_field="embedding",
    limit=20,
    output_fields=["id", "price", "rating", "category"],
    order_by_fields=[
        {"field": "price", "order": "asc"}
    ],
)
// java
// nodejs
// go
# restful

여러 스칼라 필드를 기준으로 정렬할 수도 있습니다. Milvus는 사용자가 지정한 순서대로 필드를 적용합니다. 다음 예제에서 Milvus는 price 필드를 기준으로 결과를 오름차순으로 정렬합니다. price 값이 동일한 엔티티의 경우, Milvus는 rating 필드를 기준으로 내림차순으로 정렬합니다.

res = client.search(
    collection_name="product_catalog",
    data=query_vectors,
    anns_field="embedding",
    limit=20,
    output_fields=["id", "price", "rating", "category"],
    order_by_fields=[
        {"field": "price", "order": "asc"},
        {"field": "rating", "order": "desc"},
    ],
)
// java
// nodejs
// go
# restful

지정된 모든 정렬 기준 필드의 값이 동일한 엔티티의 경우, Milvus는 원래의 유사도 점수 순서를 유지합니다.

Limit 및 Offset 사용

검색 요청에 포함된 limit 매개변수가 검색 결과에 포함될 엔티티의 수를 결정한다는 점을 알 수 있습니다. 이 매개변수는 단일 검색에서 반환할 엔티티의 최대 수를 지정하며, 일반적으로 top-K라고 합니다.

페이지 단위 쿼리를 수행하려면 루프를 사용하여 여러 개의 Search 요청을 전송하고, 각 쿼리 요청에 LimitOffset 매개변수를 포함시킬 수 있습니다. 구체적으로, Limit 매개변수를 현재 쿼리 결과에 포함할 엔티티 수로 설정하고, Offset을 이미 반환된 엔티티의 총 수로 설정할 수 있습니다.

아래 표는 한 번에 100개의 엔티티를 반환할 때 페이지 단위 쿼리를 위해 LimitOffset 매개변수를 설정하는 방법을 요약한 것입니다.

쿼리

쿼리당 반환할 엔티티 수

이미 반환된 엔티티의 총 수

첫 번째 쿼리

100

0

두 번째 쿼리

100

100

세 번째 쿼리

100

200

n번째 쿼리

100

100 × (n-1)

단, 단일 ANN 검색에서 limitoffset 의 합은 16,384 미만이어야 합니다.

# 4. Single vector search
query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = client.search(
    collection_name="quick_setup",
    data=[query_vector],
    limit=3, # The number of results to return
    search_params={
        "metric_type": "IP", 
        "offset": 10 # The records to skip
    }
)
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

FloatVec queryVector = new FloatVec(new float[]{0.3580376395471989f, -0.6023495712049978f, 0.18414012509913835f, -0.26286205330961354f, 0.9029438446296592f});
SearchReq searchReq = SearchReq.builder()
        .collectionName("quick_setup")
        .data(Collections.singletonList(queryVector))
        .topK(3)
        .offset(10)
        .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
// TopK results:
// SearchResp.SearchResult(entity={}, score=0.24120237, id=16)
// SearchResp.SearchResult(entity={}, score=0.22559784, id=9)
// SearchResp.SearchResult(entity={}, score=-0.09906838, id=2)
queryVector := []float32{0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592}

resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
    "quick_setup", // collectionName
    3,               // limit
    []entity.Vector{entity.FloatVector(queryVector)},
).WithConsistencyLevel(entity.ClStrong).
    WithANNSField("vector").
    WithOffset(10))
if err != nil {
    fmt.Println(err.Error())
    // handle error
}

for _, resultSet := range resultSets {
    fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
    fmt.Println("Scores: ", resultSet.Scores)
}
// 4. Single vector search
var query_vector = [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592],

res = await client.search({
    collection_name: "quick_setup",
    data: query_vector,
    limit: 3, // The number of results to return,
    offset: 10 // The record to skip.
})
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
    "collectionName": "quick_setup",
    "data": [
        [0.3580376395471989, -0.6023495712049978, 0.18414012509913835, -0.26286205330961354, 0.9029438446296592]
    ],
    "annsField": "vector",
    "limit": 3,
    "offset": 10
}'

컬렉션에 TIMESTAMPTZ 필드가 있는 경우, 검색 호출 시 timezone 매개변수를 설정하여 단일 작업에 대해 데이터베이스 또는 컬렉션의 기본 시간대를 일시적으로 재정의할 수 있습니다. 이는 작업 중에 TIMESTAMPTZ 값이 표시되고 비교되는 방식을 제어합니다.

timezone 의 값은 유효한 IANA 시간대 식별자 (예: Asia/Shanghai, America/Chicago 또는 UTC)여야 합니다. TIMESTAMPTZ 필드 사용 방법에 대한 자세한 내용은 TIMESTAMPTZ 필드를 참조하십시오.

다음 예제는 검색 작업에 대해 시간대를 임시로 설정하는 방법을 보여줍니다:

res = client.search(
    collection_name="quick_setup",
    anns_field="vector",
    data=[query_vector],
    limit=3,
    search_params={"metric_type": "IP"},
    timezone="America/Havana",
)
// java
// js
// go
# restful
export QUERY_VECTOR='[0.1, 0.2, 0.3, 0.4]'

curl -X POST "http://localhost:19530/v2/vectordb/entities/search" \
-H "Content-Type: application/json" \
-H "Request-Timeout: 10" \
-d '{
  "collectionName": "quick_setup",
  "annsField": "vector",
  "data": ['"$QUERY_VECTOR"'],
  "limit": 3,
  "searchParams": {
    "metric_type": "IP",
    "timezone": "America/Havana"
  }
}'

AUTOINDEX는 ANN 검색의 학습 곡선을 상당히 완화합니다. 그러나 상위 K개 결과가 늘어날수록 검색 결과가 항상 정확하지 않을 수 있습니다. Milvus는 검색 범위를 줄이고, 검색 결과의 관련성을 높이며, 검색 결과를 다양화함으로써 다음과 같은 검색 기능 향상을 실현합니다.

  • 필터링 검색

    검색 요청에 필터링 조건을 포함하면 Milvus가 ANN 검색을 수행하기 전에 메타데이터 필터링을 수행하여, 전체 컬렉션에서 지정된 필터링 조건에 맞는 엔티티로만 검색 범위를 좁힐 수 있습니다.

    메타데이터 필터링 및 필터링 조건에 대한 자세한 내용은 ‘필터링 검색’, ‘필터링 설명’ 및 관련 주제를 참조하십시오.

  • 범위 검색

    반환되는 엔티티의 거리나 점수를 특정 범위로 제한하여 검색 결과의 관련성을 높일 수 있습니다. Milvus에서 범위 검색은 쿼리 벡터와 가장 유사한 벡터 임베딩을 중심으로 두 개의 동심원을 그리는 방식으로 이루어집니다. 검색 요청에서 두 원의 반지름을 지정하면, Milvus는 바깥쪽 원 안에는 포함되지만 안쪽 원 안에는 포함되지 않는 모든 벡터 임베딩을 반환합니다.

    범위 검색에 대한 자세한 내용은 ‘범위 검색’을 참조하십시오.

  • 그룹화 검색

    반환된 엔티티가 특정 필드에서 동일한 값을 가질 경우, 검색 결과가 벡터 공간 내 모든 벡터 임베딩의 분포를 제대로 반영하지 못할 수 있습니다. 검색 결과를 다양화하려면 그룹화 검색을 사용하는 것을 고려해 보십시오.

    그룹화 검색에 대한 자세한 내용은 ‘그룹화 검색’을 참조하십시오.

  • 하이브리드 검색

    컬렉션에는 서로 다른 임베딩 모델을 사용하여 생성된 벡터 임베딩을 저장하기 위해 여러 벡터 필드를 포함할 수 있습니다. 이를 통해 하이브리드 검색을 사용하여 이러한 벡터 필드의 검색 결과를 재순위화함으로써 리콜률을 향상시킬 수 있습니다.

    하이브리드 검색에 대한 자세한 내용은 ‘하이브리드 검색’을 참조하십시오.

  • 검색 이터레이터

    단일 ANN 검색은 최대 16,384개의 엔티티를 반환합니다. 단일 검색에서 더 많은 엔티티를 반환해야 하는 경우 검색 이터레이터 사용을 고려해 보십시오.

    검색 이터레이터에 대한 자세한 내용은 ‘검색 이터레이터’를 참조하십시오.

  • 전체 텍스트 검색

    전체 텍스트 검색은 텍스트 데이터 세트에서 특정 용어 또는 구문이 포함된 문서를 검색한 다음, 관련성에 따라 결과의 순위를 매기는 기능입니다. 이 기능은 정확한 용어를 간과할 수 있는 시맨틱 검색의 한계를 극복하여, 가장 정확하고 문맥적으로 관련성 높은 결과를 얻을 수 있도록 보장합니다. 또한, 원시 텍스트 입력을 허용하여 벡터 검색을 간소화하며, 벡터 임베딩을 수동으로 생성할 필요 없이 텍스트 데이터를 스파스 임베딩으로 자동 변환합니다.

    전체 텍스트 검색에 대한 자세한 내용은 전체 텍스트 검색을 참조하십시오.

  • 텍스트 일치

    Milvus의 키워드 일치 기능은 특정 용어를 기반으로 문서를 정확하게 검색할 수 있게 해줍니다. 이 기능은 주로 특정 조건을 충족하는 필터링 검색에 사용되며, 스칼라 필터링을 통합하여 쿼리 결과를 정교하게 다듬을 수 있어 스칼라 기준을 충족하는 벡터 내에서 유사도 검색을 수행할 수 있습니다.

    키워드 일치에 대한 자세한 내용은 키워드 일치를 참조하십시오.

  • 파티션 키 사용

    메타데이터 필터링에 여러 스칼라 필드를 포함하거나 다소 복잡한 필터링 조건을 사용하면 검색 효율에 영향을 미칠 수 있습니다. 스칼라 필드를 파티션 키로 설정하고 검색 요청에서 해당 파티션 키를 포함한 필터링 조건을 사용하면, 지정된 파티션 키 값에 해당하는 파티션 내에서 검색 범위를 제한하는 데 도움이 될 수 있습니다.

    파티션 키에 대한 자세한 내용은 ‘파티션 키 사용’을 참조하십시오.

  • mmap 사용

    mmap 설정에 대한 자세한 내용은 ‘mmap 사용’을 참조하십시오.

  • 클러스터링 압축

    클러스터링 압축에 대한 자세한 내용은 ‘클러스터링 압축’을 참조하십시오.

  • 재순위 지정 사용

    랭커를 사용하여 검색 결과의 관련성을 높이는 방법에 대한 자세한 내용은 'Decay Ranker 개요''Model Ranker 개요'를 참조하십시오.