-
이 포스트는 elasticsearch 7.17 버전에 대한 이야기 입니다.
운영 중인 7.17 버전 elasticsearch의 한 인덱스의 모든 도큐먼트를 S3로 옮겨야 하는 상황이 발생했다.
elasticsearch 에서는 _search API로 한 인덱스의 전체 도큐먼트를 가지고 올 수 없다.
elasticsearch의 _search API의 경우, 아래 처럼 size를 지정해 가져오고 싶은 도큐먼트의 갯수를 지정할 수 있다.
curl -X POST "localhost:9200/my-index-000001/_search?pretty" -H 'Content-Type: application/json' -d' { "size": 100, "query": { "match_all": {} } } '
그런데 이 size의 경우 기본값은 10이고, 최대 10000까지만 설정이 가능하다.(이상으로 지정해도 최대 1만개 까지만 적용된 결과를 얻음)
한 인덱스의 전체 데이터를 가져오기 위해서는 페이징 방식과 유사한 기능을 제공하는 _scroll API를 사용할 수 있다.
_scroll API
curl -X POST "localhost:9200/my-index-000001/_search?scroll=1m&pretty" -H 'Content-Type: application/json' -d' { "size": 100, "query": { "match_all": {} } } '
쿼리 파라미터의 scroll=1m 이부분은 search context를 1분 동안만 유지 하겠다는 것이다.
위 요청을 처음 보내면 아래와 같은 결과를 받는데 _scroll_id를 통해 다음 페이지의 데이터를 가지고 올 수 있다.
{ "_scroll_id" : "FGluY2x1ZGVf...", "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 280881, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { 다큐먼트 내용 }, . . . ] }
다음 페이지의 정보를 가지고 오기 위해서 보내는 두번째 요청
POST /_search/scroll { "scroll" : "1m", "scroll_id" : "FGluY2x1ZGVf..." }
제너레이터 함수와 for wait 문법으로 페이지네이션 방식의 API 멋지게 가져오기
제너레이터 함수와 for wait 문법을 사용하면, 최초 한번 요청 후 _scroll_id 결과가 응답에 있을 때 까지 요청을 반복적으로 보내는 로직을 깔끔하게 작성할 수 있다.
(공식 문서 설명 : Another cool usage of the scroll API can be done with Node.js ≥ 10, by using async iteration!)
import { Client, ApiResponse, } from '@elastic/elasticsearch' import 'dotenv/config' const INDEX: string = process.env.ES_INDEX_PATTERN || ''; type ESDocument = { _index: string _type: string _id: string _score: number, _source: object } type QueryResults = ESDocument[]; type SearchParam = { index: string, size?: number, body: object | string scroll?: string } const client: Client = new Client({ node: process.env.ES_CLOUD_NODE || '', auth: { username: process.env.ES_CLOUD_USERNAME || '', password: process.env.ES_CLOUD_PASSWORD || '' } }) async function* scrollSearch (params: SearchParam) { let response: ApiResponse = await client.search(params) while (true) { const sourceHits: QueryResults = response.body.hits.hits if (sourceHits.length === 0) { break } for (const hit of sourceHits) { yield hit // 제너레이터 함수: yield로 리턴 후 이 함수가 다시 실행될 때는 이 밑부터 실행 됨 } if (!response.body._scroll_id) { break } console.log(response); response = await client.scroll({ scroll_id: response.body._scroll_id, scroll: params.scroll }) } } const runScroll = async (index: string): Promise<QueryResults> => { try { // new definitions const params = { index, scroll: '30s', // size: 10000, // max 10000 body: { query: { match_all: {} } } } let results: ESDocument[] = []; for await (const hit of scrollSearch(params)) { results.push(hit); } return results; } catch (error: unknown) { throw error; } } const main = async () => { const res: QueryResults = await runScroll(INDEX); console.log([res[0]], res.length); } main();
search context란 무엇일까?
elasticsearch는 한 샤드 검색 작업이 일어나는 동안 검색에 필요한 상태를 유지하는데 이것을 search context라고 한다.
동시에 많은 양의 검색작업이 생기면 그만큼 search context의 갯수도 많아지고 메모리를 차지하게 된다.
What is search context?
What is search context and how and when is it created ? Is there a good number beyond which if the number of search contexts go, it can be alarming ?
discuss.elastic.co
현재 search context 확인하기
GET /_nodes/stats/
stats API로 현재 노드에 대한 여러 정보를 확인 할 수 있다.
GET /_nodes/stats/indices/search
그 중에서 indices/search 부분에서 현재 활성 중인 search context를 확인 할 수 있다.
참고
Paginate search results | Elasticsearch Guide [7.17] | Elastic
All PIT search requests add an implicit sort tiebreaker field called _shard_doc, which can also be provided explicitly. If you cannot use a PIT, we recommend that you include a tiebreaker field in your sort. This tiebreaker field should contain a unique va
www.elastic.co
https://soyoung-new-challenge.tistory.com/96
[Elasticsearch] Scroll API 사용하기 (전체 문서 읽어오기)
이번 포스팅은 엘라스틱 서치에서 전체 문서를 가져오기 위해, 스크롤 API를 사용하는 방법에 관한 포스팅입니다. Elasticsearch Scoll API란, Scroll API search 요청이 하나의 페이지를 결과로 리턴하는 동
soyoung-new-challenge.tistory.com
https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/master/scroll_examples.html
Scroll | Elasticsearch JavaScript Client [master] | Elastic
The results that are returned from a scroll request reflect the state of the index at the time that the initial search request was made, like a snapshot in time. Subsequent changes to documents (index, update or delete) will only affect later search reques
www.elastic.co
'🏢 업무 리서치 기록' 카테고리의 다른 글
Clinic.js로 Node.js 서버 프로파일링 하기 (0) 2023.01.17 로그인 기능 구현의 과정 (feat. OAuth2.0 와 JWT) (0) 2022.12.16 OLTP VS OLAP (feat. 트랜잭션 ACID 특성) (0) 2022.12.15 ssh 포트포워딩(터널링) 뚫기 (feat. 로컬에서 AWS VPC내 RDS 접속하기) (3) 2022.12.15 [BigQuery] Streaming Insert VS Insert Into DML Query (1) (0) 2022.12.01