Multi-Search API

Multi-Search API

1.0에서 도입

이름 그대로 multi-search 작업은 여러 검색 요청을 하나의 요청으로 묶어주는 기능이에요. 그러면 OpenSearch가 검색을 병렬로 실행하므로, 검색마다 요청을 하나씩 보내는 것보다 응답을 더 빨리 받을 수 있어요. OpenSearch는 각 검색을 독립적으로 실행하므로, 하나가 실패해도 다른 것에는 영향을 주지 않아요.

출처: 문서

본문

엔드포인트 (Endpoints)

GET  /_msearch
POST /_msearch
GET  /{index}/_msearch
POST /{index}/_msearch

경로에 인덱스를 지정하면, 메타데이터 줄에 index 필드가 없는 검색들의 기본 대상이 그 인덱스가 돼요. 경로 매개변수를 생략하고 검색의 메타데이터 줄에도 index를 지정하지 않으면, 그 검색은 모든 인덱스를 대상으로 실행돼요.

쿼리 매개변수 및 메타데이터 옵션 (Query parameters and metadata options)

모든 매개변수는 선택 사항이에요. 일부는 각 메타데이터 줄의 일부로 검색별로 적용할 수도 있어요.

매개변수 타입 설명 메타데이터 줄 지원
allow_no_indices Boolean 어떤 인덱스와도 일치하지 않는 와일드카드를 무시할지 여부예요. 기본값은 true예요. 예
cancel_after_time_interval Time 이 시간이 지나면 검색 요청이 취소돼요. 상위·하위 요청 수준 모두 지원돼요. 우선순위는 다음과 같아요: 예
1. 하위 수준 매개변수
2. 상위 수준 매개변수
3. 클러스터 설정
기본값은 -1이에요.
ccs_minimize_roundtrips Boolean OpenSearch가 조정 노드와 원격 클러스터 사이의 네트워크 왕복을 최소화하려고 하는지 여부예요(교차 클러스터 검색 요청에만 적용). 기본값은 true예요. 아니요
expand_wildcards Enum 와일드카드 표현식을 실제 인덱스로 확장해요. 여러 값을 쉼표로 조합해요. 지원 값은 all, open, closed, hidden, none이에요. 기본값은 open이에요. 예
ignore_unavailable Boolean 인덱스 목록의 인덱스나 샤드가 없을 때 쿼리를 실패시키지 않고 무시할지 여부예요. 기본값은 false예요. 예
include_named_queries_score Boolean 명명된 쿼리의 점수를 반환할지 여부예요. 기본값은 false예요. 아니요
max_concurrent_searches Integer 최대 동시 검색 수예요. 기본값은 노드 수와 검색 스레드 풀 크기에 따라 달라져요. 값이 클수록 성능이 좋아질 수 있지만 클러스터가 과부하될 위험이 있어요. 아니요
max_concurrent_shard_requests Integer 각 검색이 노드당 실행하는 최대 동시 샤드 요청 수예요. 기본값은 5예요. 값이 클수록 성능이 좋아질 수 있지만 클러스터가 과부하될 위험이 있어요. 아니요
pre_filter_shard_size Integer 쿼리와 일치할 수 없는 샤드를 제거하기 위한 사전 필터(pre-filter) 왕복을 트리거하는 임계값이에요(예: 날짜 범위 필터가 샤드 범위 밖인 경우). 지정하지 않으면 요청이 128개 이상의 샤드를 대상으로 하거나, 읽기 전용 인덱스를 대상으로 하거나, 색인된 필드로 정렬할 때 사전 필터 단계가 실행돼요. 기본값은 128이에요. 아니요
rest_total_hits_as_int String hits.total 속성을 정수(true)로 반환할지 객체(false)로 반환할지 여부예요. 기본값은 false예요. 아니요
routing String 요청의 모든 검색을 특정 샤드로 라우팅하는 데 사용하는 쉼표로 구분된 사용자 지정 라우팅 값이에요. 개별 검색의 라우팅을 설정하려면 메타데이터 줄의 routing 옵션을 사용해요. 아니요
search_type String 관련성 점수에 영향을 줘요. 유효한 옵션은 query_then_fetch와 dfs_query_then_fetch예요. query_then_fetch는 샤드의 term·document 빈도로 문서에 점수를 매기고(더 빠르고 덜 정확), dfs_query_then_fetch는 모든 샤드의 term·document 빈도를 사용해요(더 느리고 더 정확). 기본값은 query_then_fetch예요. 예
typed_keys Boolean 응답에서 집계 이름 앞에 내부 타입을 붙일지 여부예요. 기본값은 false예요. 아니요

메타데이터 전용 옵션 (Metadata-only options)

일부 옵션은 전체 요청에 매개변수로 적용할 수 없어요. 대신 각 메타데이터 줄의 일부로 검색별로 적용할 수 있어요. 모두 선택 사항이에요.

옵션 타입 설명
index String, 문자열 배열 URL의 일부로 인덱스(또는 여러 인덱스)를 지정하지 않았다면(또는 개별 검색의 URL 값을 덮어쓰려면) 여기에 포함할 수 있어요. 예로 "logs-*"와 ["my-store", "sample_data_ecommerce"]가 있어요.
preference String 검색을 수행할 노드나 샤드예요. 테스트에 유용할 수 있지만 대부분의 상황에서는 기본 동작이 최상의 검색 지연 시간을 제공해요. 옵션에는 _local, _only_local, _prefer_nodes, _only_nodes, _shards가 있어요. 마지막 세 옵션은 노드나 샤드 목록을 받아요. 예로 "_only_nodes:data-node1,data-node2"와 "_shards:0,1이 있어요.
request_cache Boolean 결과를 캐시할지 여부예요. 반복 검색의 지연 시간을 줄일 수 있어요. 기본값은 인덱스의 index.requests.cache.enable 설정을 사용해요(새 인덱스의 기본값은 true).
routing String 쉼표로 구분된 사용자 지정 라우팅 값이에요. 예: "routing": "value1,value2,value3". 요청의 모든 검색에 적용되는 쿼리 매개변수 수준의 routing과 달리, 이 옵션은 개별 검색의 라우팅만 대상으로 해요.

요청 본문 (Request body)

multi-search 요청 본문은 줄바꿈으로 구분된 JSON(NDJSON) 형식을 사용하며, 메타데이터 줄과 쿼리 줄이 번갈아 나와요:

Metadata\n

Query\n

Metadata\n

Query\n

메타데이터 줄에는 검색 대상 인덱스, 검색 유형 같은 옵션이 포함돼요. 검색별 덮어쓰기가 필요 없으면 메타데이터 줄은 비워({}) 둘 수 있어요.

쿼리 줄은 query DSL을 사용해요.

bulk 작업처럼 JSON을 최소화할 필요는 없어요. 공백은 괜찮지만, 한 줄에 있어야 해요. OpenSearch는 줄바꿈 문자로 multi-search 요청을 파싱하며 요청 본문이 줄바꿈 문자로 끝나야 해요.

이 엔드포인트로 요청을 보낼 때는 Content-Type 헤더를 application/x-ndjson으로 설정해요.

쿼리 본문 필드 (Query body fields)

각 쿼리 줄은 Search API 요청 본문과 같은 매개변수를 받아요. 다음 표는 가장 흔히 쓰는 필드예요.

필드 데이터 타입 설명
query Object 실행할 query DSL 표현식이에요. Query DSL을 참고해요.
aggregations Object 검색과 함께 실행할 집계예요. Aggregations을 참고해요.
from Integer 반환할 hits의 시작 오프셋이에요. 기본값은 0이에요.
size Integer 반환할 hits 수예요. 기본값은 10이에요.
sort Array 또는 Object 결과를 정렬할 필드와 순서예요.
_source Boolean, String 또는 Object 각 hit의 _source에 포함할 필드를 제어해요.
highlight Object 일치 필드의 하이라이트 설정이에요.

예시: 여러 인덱스 검색하기 (Searching multiple indexes)

다음 예시는 각 메타데이터 줄에서 대상 인덱스를 지정하면서 여러 인덱스에 쿼리를 실행해요:

GET /_msearch
{ "index": "opensearch_dashboards_sample_data_logs"}
{ "query": { "match_all": {} }, "from": 0, "size": 10}
{ "index": "opensearch_dashboards_sample_data_ecommerce", "search_type": "dfs_query_then_fetch"}
{ "query": { "match_all": {} } }

Python 클라이언트로는 이렇게 호출해요:

response = client.msearch(
body = '''
{ "index": "opensearch_dashboards_sample_data_logs"}
{ "query": { "match_all": {} }, "from": 0, "size": 10}
{ "index": "opensearch_dashboards_sample_data_ecommerce", "search_type": "dfs_query_then_fetch"}
{ "query": { "match_all": {} } }
'''
)

예시: 기본 인덱스 사용하기 (Using a default index)

URL 경로에 인덱스를 지정하면, 메타데이터 줄에 index 필드가 없는 검색들의 기본 대상이 그 인덱스가 돼요. 다음 예시는 각 메타데이터 줄에서 인덱스 이름을 반복하지 않고 products 인덱스에 두 쿼리를 실행해요:

GET /products/_msearch
{}
{"query": {"match": {"product_name": "headphones"}}, "size": 1}
{}
{"query": {"range": {"price": {"gte": 30, "lte": 50}}}}

Python 클라이언트로는 이렇게 호출해요:

response = client.msearch(
index = "products",
body = '''
{}
{"query": {"match": {"product_name": "headphones"}}, "size": 1}
{}
{"query": {"range": {"price": {"gte": 30, "lte": 50}}}}
'''
)

검색 템플릿 사용하기 (Using search templates)

multi-search API는 _msearch/template 엔드포인트를 통해 검색 템플릿을 지원해요. 이렇게 하면 매개변수화된 검색을 실행할 수 있고, 쿼리 구조를 검색 시점에 전달되는 값과 분리해요.

예시: 인라인 템플릿 (Inline templates)

다음 요청은 인라인 템플릿으로 단일 호출에서 두 개의 매개변수화된 검색을 실행해요:

GET _msearch/template
{"index": "products"}
{"source": {"query": {"match": {"product_name": ""}}}, "params": {"search_term": "wireless"}}
{"index": "products"}
{"source": {"query": {"range": {"price": {"lte": ""}}}}, "params": {"max_price": "75"}}

예시: 저장된 템플릿 (Stored templates)

미리 등록된 템플릿을 ID로 참조할 수도 있어요. 먼저 저장된 템플릿을 만들어요:

POST _scripts/product_search_template
{
"script": {
"lang": "mustache",
"source": {
"query": {
"multi_match": {
"query": "",
"fields": ["product_name", "description"]
}
},
"size": ""
}
}
}
POST _scripts/price_range_template
{
"script": {
"lang": "mustache",
"source": {
"query": {
"range": {
"price": {
"gte": "",
"lte": ""
}
}
},
"size": ""
}
}
}

그런 다음 multi-search 요청에서 저장된 템플릿을 사용해요:

GET _msearch/template
{"index": "products"}
{"id": "product_search_template", "params": {"query_text": "bluetooth speaker", "result_count": "5"}}
{"index": "products"}
{"id": "price_range_template", "params": {"min_price": "20", "max_price": "100", "result_count": "3"}}

예시 응답 (Example response)

OpenSearch는 multi-search 요청과 같은 순서로 각 검색의 결과가 담긴 배열을 반환해요.

{
  "took" : 2150,
  "responses" : [
    {
      "took" : 2149,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 10000,
          "relation" : "gte"
        },
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "opensearch_dashboards_sample_data_logs",
            "_id" : "_fnhBXsBgv2Zxgu9dZ8Y",
            "_score" : 1.0,
            "_source" : {
              "agent" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)",
              "bytes" : 4657,
              "clientip" : "213.116.129.196",
              "extension" : "zip",
              "geo" : {
                "srcdest" : "CN:US",
                "src" : "CN",
                "dest" : "US",
                "coordinates" : {
                  "lat" : 42.35083333,
                  "lon" : -86.25613889
                }
              },
              "host" : "artifacts.opensearch.org",
              "index" : "opensearch_dashboards_sample_data_logs",
              "ip" : "213.116.129.196",
              "machine" : {
                "ram" : 16106127360,
                "os" : "ios"
              },
              "memory" : null,
              "message" : "213.116.129.196 - - [2018-07-30T14:12:11.387Z] \"GET /opensearch_dashboards/opensearch_dashboards-1.0.0-windows-x86_64.zip HTTP/1.1\" 200 4657 \"-\" \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)\"",
              "phpmemory" : null,
              "referer" : "http://twitter.com/success/ellison-onizuka",
              "request" : "/opensearch_dashboards/opensearch_dashboards-1.0.0-windows-x86_64.zip",
              "response" : 200,
              "tags" : [
                "success",
                "info"
              ],
              "timestamp" : "2021-08-02T14:12:11.387Z",
              "url" : "https://artifacts.opensearch.org/downloads/opensearch_dashboards/opensearch_dashboards-1.0.0-windows-x86_64.zip",
              "utc_time" : "2021-08-02T14:12:11.387Z",
              "event" : {
                "dataset" : "sample_web_logs"
              }
            }
          },
          ...
        ]
      },
      "status" : 200
    },
    {
      "took" : 1473,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 4675,
          "relation" : "eq"
        },
        "max_score" : 1.0,
        "hits" : [
          {
            "_index" : "opensearch_dashboards_sample_data_ecommerce",
            "_id" : "efnhBXsBgv2Zxgu9ap7e",
            "_score" : 1.0,
            "_source" : {
              "category" : [
                "Women's Clothing"
              ],
              "currency" : "EUR",
              "customer_first_name" : "Gwen",
              "customer_full_name" : "Gwen Dennis",
              "customer_gender" : "FEMALE",
              "customer_id" : 26,
              "customer_last_name" : "Dennis",
              "customer_phone" : "",
              "day_of_week" : "Tuesday",
              "day_of_week_i" : 1,
              "email" : "[email protected]",
              "manufacturer" : [
                "Tigress Enterprises",
                "Gnomehouse mom"
              ],
              "order_date" : "2021-08-10T16:24:58+00:00",
              "order_id" : 576942,
              "products" : [
                {
                  "base_price" : 32.99,
                  "discount_percentage" : 0,
                  "quantity" : 1,
                  "manufacturer" : "Tigress Enterprises",
                  "tax_amount" : 0,
                  "product_id" : 22182,
                  "category" : "Women's Clothing",
                  "sku" : "ZO0036600366",
                  "taxless_price" : 32.99,
                  "unit_discount_amount" : 0,
                  "min_price" : 14.85,
                  "_id" : "sold_product_576942_22182",
                  "discount_amount" : 0,
                  "created_on" : "2016-12-20T16:24:58+00:00",
                  "product_name" : "Jersey dress - black/red",
                  "price" : 32.99,
                  "taxful_price" : 32.99,
                  "base_unit_price" : 32.99
                },
                {
                  "base_price" : 28.99,
                  "discount_percentage" : 0,
                  "quantity" : 1,
                  "manufacturer" : "Gnomehouse mom",
                  "tax_amount" : 0,
                  "product_id" : 14230,
                  "category" : "Women's Clothing",
                  "sku" : "ZO0234902349",
                  "taxless_price" : 28.99,
                  "unit_discount_amount" : 0,
                  "min_price" : 13.05,
                  "_id" : "sold_product_576942_14230",
                  "discount_amount" : 0,
                  "created_on" : "2016-12-20T16:24:58+00:00",
                  "product_name" : "Blouse - june bug",
                  "price" : 28.99,
                  "taxful_price" : 28.99,
                  "base_unit_price" : 28.99
                }
              ],
              "sku" : [
                "ZO0036600366",
                "ZO0234902349"
              ],
              "taxful_total_price" : 61.98,
              "taxless_total_price" : 61.98,
              "total_quantity" : 2,
              "total_unique_products" : 2,
              "type" : "order",
              "user" : "gwen",
              "geoip" : {
                "country_iso_code" : "US",
                "location" : {
                  "lon" : -118.2,
                  "lat" : 34.1
                },
                "region_name" : "California",
                "continent_name" : "North America",
                "city_name" : "Los Angeles"
              },
              "event" : {
                "dataset" : "sample_ecommerce"
              }
            }
          },
         ...
        ]
      },
      "status" : 200
    }
  ]
}

응답 본문 필드 (Response body fields)

다음 표는 최상위 응답 본문 필드를 정리한 거예요.

필드 데이터 타입 설명
took Integer OpenSearch가 요청의 모든 검색을 처리하는 데 걸린 총 시간(밀리초)이에요.
responses Array 검색 응답 객체 배열이에요. 요청의 해당 검색과 같은 순서로 반환돼요. 특정 검색이 완전히 실패하면 그 검색의 배열 항목에는 일반 검색 응답 대신 error 객체와 status 코드가 들어 있어요.

다음 표는 responses 배열의 각 항목에 있는 필드를 정리한 거예요.

필드 데이터 타입 설명
took Integer OpenSearch가 개별 검색을 처리하는 데 걸린 시간(밀리초)이에요.
timed_out Boolean 검색이 완료되기 전에 타임아웃됐는지 여부예요.
_shards Object 검색에 관련된 샤드 수에 대한 정보예요. total, successful, skipped, failed 개수를 포함해요.
hits Object 검색 결과예요. 총 hit 수, max_score, 일치하는 hits 배열을 포함해요.
status Integer 개별 검색 결과의 HTTP 상태 코드예요. 값 200은 성공을 의미해요.

부분 응답 (Partial responses)

실행 중 하나 이상의 샤드가 실패해도 multi-search API는 성공한 샤드의 결과를 계속 반환해요. responses 배열의 각 개별 검색 응답에는 몇 개의 샤드가 성공하고 실패했는지 보고하는 _shards 객체가 포함되어 있어, 결과가 완전한지 판단할 수 있어요.

필요한 권한 (Required permissions)

Security 플러그인을 사용한다면 적절한 권한이 있는지 확인해야 해요: indices:data/read/msearch.

더 알아보기