Radial search
출처: 문서
본문
방사형 검색
방사형 검색은 근사 top-k 검색을 넘어 벡터 검색 기능을 확장해요. 방사형 검색을 사용하면 벡터 공간 안에서 쿼리 포인트로부터 지정한 최대 거리 이내에 있거나 최소 점수 임계값 이상인 모든 포인트를 검색할 수 있어요. 이를 통해 검색 작업의 유연성과 활용도가 한층 높아져요.
방사형 검색은 Lucene 엔진이나 Faiss 엔진 중 하나를 사용해 수행할 수 있어요. 두 엔진 모두 중첩 필드에 대한 방사형 검색을 지원해요.
파라미터
방사형 검색은 다음 파라미터를 지원해요:
max_distance: 벡터 공간 안의 물리적 거리를 지정해요. 쿼리 포인트에서 이 거리 이내에 있는 모든 포인트를 식별하죠. 이 방식은 공간적 근접성이나 절대 거리 측정이 필요한 애플리케이션에 특히 유용해요.
min_score: 유사도 점수를 지정해요. 쿼리 포인트와 비교했을 때 이 점수 이상을 충족하는 포인트를 손쉽게 찾아낼 수 있죠. 이 방식은 물리적 근접성보다 특정 메트릭에 기반한 상대적 유사성이 더 중요한 시나리오에 적합해요.
방사형 검색을 수행할 때는 k, max_distance, min_score 중에서 쿼리 변수 하나만 지정하면 돼요.
공간
지원되는 공간에 대해서는 공간 문서를 참고하세요.
예제
다음 예제를 참고하면 방사형 검색을 시작하는 데 도움이 될 거예요.
사전 준비 사항
방사형 검색에서 벡터 인덱스를 사용하려면 index.knn을 true로 설정해서 벡터 인덱스를 생성해요. 다음 예제와 같이 knn_vector 데이터 타입의 필드를 하나 이상 지정해요:
PUT knn-index-test
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.knn": true
},
"mappings": {
"properties": {
"my_vector": {
"type": "knn_vector",
"dimension": 2,
"space_type": "l2",
"method": {
"name": "hnsw",
"engine": "faiss",
"parameters": {
"ef_construction": 100,
"m": 16,
"ef_search": 100
}
}
}
}
}
}
{% include copy-curl.html %}
인덱스를 생성한 다음에는 다음과 유사한 데이터를 추가해요:
PUT _bulk?refresh=true
{"index": {"_index": "knn-index-test", "_id": "1"}}
{"my_vector": [7.0, 8.2], "price": 4.4}
{"index": {"_index": "knn-index-test", "_id": "2"}}
{"my_vector": [7.1, 7.4], "price": 14.2}
{"index": {"_index": "knn-index-test", "_id": "3"}}
{"my_vector": [7.3, 8.3], "price": 19.1}
{"index": {"_index": "knn-index-test", "_id": "4"}}
{"my_vector": [6.5, 8.8], "price": 1.2}
{"index": {"_index": "knn-index-test", "_id": "5"}}
{"my_vector": [5.7, 7.9], "price": 16.5}
{% include copy-curl.html %}
예제: max_distance를 사용한 방사형 검색
다음 예제는 max_distance를 사용해 수행한 방사형 검색이에요:
GET knn-index-test/_search
{
"query": {
"knn": {
"my_vector": {
"vector": [
7.1,
8.3
],
"max_distance": 2
}
}
}
}
{% include copy-curl.html %}
제곱 유클리드 거리(l2^2)가 2 이내에 해당하는 모든 문서가 다음 응답과 같이 반환돼요:
결과
{: .text-delta}{
"took": 6,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4,
"relation": "eq"
},
"max_score": 0.98039204,
"hits": [
{
"_index": "knn-index-test",
"_id": "1",
"_score": 0.98039204,
"_source": {
"my_vector": [
7.0,
8.2
],
"price": 4.4
}
},
{
"_index": "knn-index-test",
"_id": "3",
"_score": 0.9615384,
"_source": {
"my_vector": [
7.3,
8.3
],
"price": 19.1
}
},
{
"_index": "knn-index-test",
"_id": "4",
"_score": 0.62111807,
"_source": {
"my_vector": [
6.5,
8.8
],
"price": 1.2
}
},
{
"_index": "knn-index-test",
"_id": "2",
"_score": 0.5524861,
"_source": {
"my_vector": [
7.1,
7.4
],
"price": 14.2
}
}
]
}
}
예제: max_distance와 필터를 사용한 방사형 검색
다음 예제는 max_distance와 응답 필터를 사용해 수행한 방사형 검색이에요:
GET knn-index-test/_search
{
"query": {
"knn": {
"my_vector": {
"vector": [7.1, 8.3],
"max_distance": 2,
"filter": {
"range": {
"price": {
"gte": 1,
"lte": 5
}
}
}
}
}
}
}
{% include copy-curl.html %}
제곱 유클리드 거리(l2^2)가 2 이내이면서 가격이 1부터 5 사이에 있는 모든 문서가 다음 응답과 같이 반환돼요:
결과
{: .text-delta}{
"took": 4,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0.98039204,
"hits": [
{
"_index": "knn-index-test",
"_id": "1",
"_score": 0.98039204,
"_source": {
"my_vector": [
7.0,
8.2
],
"price": 4.4
}
},
{
"_index": "knn-index-test",
"_id": "4",
"_score": 0.62111807,
"_source": {
"my_vector": [
6.5,
8.8
],
"price": 1.2
}
}
]
}
}
예제: min_score를 사용한 방사형 검색
다음 예제는 min_score를 사용해 수행한 방사형 검색이에요:
GET knn-index-test/_search
{
"query": {
"knn": {
"my_vector": {
"vector": [7.1, 8.3],
"min_score": 0.95
}
}
}
}
{% include copy-curl.html %}
점수가 0.9 이상인 모든 문서가 다음 응답과 같이 반환돼요:
결과
{: .text-delta}{
"took": 3,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0.98039204,
"hits": [
{
"_index": "knn-index-test",
"_id": "1",
"_score": 0.98039204,
"_source": {
"my_vector": [
7.0,
8.2
],
"price": 4.4
}
},
{
"_index": "knn-index-test",
"_id": "3",
"_score": 0.9615384,
"_source": {
"my_vector": [
7.3,
8.3
],
"price": 19.1
}
}
]
}
}
예제: min_score와 필터를 사용한 방사형 검색
다음 예제는 min_score와 응답 필터를 사용해 수행한 방사형 검색이에요:
GET knn-index-test/_search
{
"query": {
"knn": {
"my_vector": {
"vector": [
7.1,
8.3
],
"min_score": 0.95,
"filter": {
"range": {
"price": {
"gte": 1,
"lte": 5
}
}
}
}
}
}
}
{% include copy-curl.html %}
점수가 0.9 이상이면서 가격이 1부터 5 사이에 있는 모든 문서가 다음 예제와 같이 반환돼요:
결과
{: .text-delta}{
"took": 4,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.98039204,
"hits": [
{
"_index": "knn-index-test",
"_id": "1",
"_score": 0.98039204,
"_source": {
"my_vector": [
7.0,
8.2
],
"price": 4.4
}
}
]
}
}
예제: 중첩 필드에 대한 방사형 검색
다음 예제는 중첩 벡터 필드에 방사형 검색을 수행하는 방법을 보여줘요. 먼저 중첩된 knn_vector 필드를 포함하는 인덱스를 생성해요:
PUT nested-knn-index
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.knn": true
},
"mappings": {
"properties": {
"my_embeddings": {
"type": "nested",
"properties": {
"embedding": {
"type": "knn_vector",
"dimension": 3,
"method": {
"engine": "faiss",
"space_type": "innerproduct",
"name": "hnsw",
"parameters": {
"ef_construction": 100,
"m": 16
}
}
}
}
}
}
}
}
{% include copy-curl.html %}
인덱스에 샘플 데이터를 추가해요:
PUT _bulk?refresh=true
{"index": {"_index": "nested-knn-index", "_id": "1"}}
{"my_embeddings": [{"embedding": [0.1, 0.2, 0.3]}]}
{"index": {"_index": "nested-knn-index", "_id": "2"}}
{"my_embeddings": [{"embedding": [0.4, 0.5, 0.6]}]}
{"index": {"_index": "nested-knn-index", "_id": "3"}}
{"my_embeddings": [{"embedding": [0.7, 0.8, 0.9]}]}
{% include copy-curl.html %}
my_embeddings.embedding 중첩 필드에 방사형 검색을 수행해서, 쿼리 벡터와 유사하면서 유사도 점수가 0.7 이상인 모든 임베딩을 찾아 보세요:
GET nested-knn-index/_search
{
"query": {
"nested": {
"path": "my_embeddings",
"query": {
"knn": {
"my_embeddings.embedding": {
"vector": [0.2, 0.3, 0.4],
"min_score": 0.7
}
}
},
"score_mode": "max"
}
}
}
{% include copy-curl.html %}
이 쿼리는 Lucene 엔진과 Faiss 엔진 모두에서 동작하고, 중첩 벡터 임베딩이 최소 유사도 점수 임계값을 충족하는 문서를 다음 응답과 같이 반환해요:
결과
{: .text-delta}{
"took": 43,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1.74,
"hits": [
{
"_index": "nested-knn-index",
"_id": "3",
"_score": 1.74,
"_source": {
"my_embeddings": [
{
"embedding": [0.7, 0.8, 0.9]
}
]
}
},
{
"_index": "nested-knn-index",
"_id": "2",
"_score": 1.47,
"_source": {
"my_embeddings": [
{
"embedding": [0.4, 0.5, 0.6]
}
]
}
}
]
}
}