[Elasticsearch] 검색 paging

HI·2022년 5월 16일
0

검색 paging


  • from, size로 페이징은 기본값이 10,000이다.
    그 이후로 "from":"10100" 이렇게 사용하려면 max_result_window 을 올려야한다.(페이징을 위해 대략 전체문헌수인 2억으로 잡혀있음)
    elasticsearch가 from,size로 요청한 페이지가지 모든 문서를 메모리에 로드한 다음 정렬하고 요청 내용을 반환한다.
    요청당 필요한 메모리는 from+size의 크기에 비례한다. 즉, 건너뛰는 결과가 많을 수록(1000~1010) 쿼리가 느려져 큰 결과 집합을 페이징하는데 소요 시간이 더 걸릴 수 있다.

  • max_result_window를 100으로 해뒀을때 그 이후 문헌도 페이징 결과로 나올 수 있는지 테스트
    PUT test_k/_settings
    {
    "index": {
    "max_result_window": "100"
    }
    }


1. scroll

[First 쿼리]

1m은 elastic에게 search context를 1m 추가로 유지하라고 말하는것.

GET test_k/_search?scroll=1m
{
  "query": {
    "match_all": {

    }
  }
}

scroll에선 from 사용 불가. size 가능.

<결과>

{
 "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlVLOXBTTkNSUU1xNURLUUhkdVdHZ0EAAAAAACmN9BZjcWRLQ2Q4ZlNIR3MwbEdnbXJ6MDJB",

#### [Next_1 쿼리] 위에서 받은 scroll_id를 넣어 다음 결과를 받게 해준다.
GET _search/scroll
{
  "scroll":"1m",
  "scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlVLOXBTTkNSUU1xNURLUUhkdVdHZ0EAAAAAACmOJBZjcWRLQ2Q4ZlNIR3MwbEdnbXJ6MDJB"
}

<Next_1쿼리 결과>

{
 "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlVLOXBTTkNSUU1xNURLUUhkdVdHZ0EAAAAAACmOJBZjcWRLQ2Q4ZlNIR3MwbEdnbXJ6MDJB",

다음결과를 이어 받기 위한 새로운 scroll_id가 생겼다.

[Next_2 쿼리] 계속해서 scroll_id를 새롭게 넣어 다음 페이지를 넘겨준다.

GET _search/scroll

{
  "scroll":"1m",
  "scroll_id": ""FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFlVLOXBTTkNSUU1xNURLUUhkdVdHZ0EAAAAAACmOJBZjcWRLQ2Q4ZlNIR3MwbEdnbXJ6MDJB"
}

scroll의 시간초과

scroll은 문서를 다 읽은 후에도 search context가 지정한 시간만큼 살아있다.

GET /_nodes/stats/indices/search

open context의 개수를 확인할 수 있다.
사용하지 않는 open context의 개수를 지울 수 있다.

DELETE /_search/scroll
{
    "scroll_id" : [
      "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==",
      "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB"
    ]
}

살아있는 모든 context를 닫을 경우

DELETE /_search/scroll/_all

scroll_id는 지정한 시간이 지나면 사라지는데. 그러면 scroll_id로 조회할때 error를 발생한다.
scroll_id를 1분동안 생성한다고 했을때 사용자가 2분 뒤 페이징 넘김을 실행하면 생성됬던 scroll_id는 없어져 error가 발생되고,
scroll_id 생성 시간을 오래 늘려두면 엔진이 담고 있는 search context가 너무 많아져 문제가 될것 같다.
시간초과의 문제 생각해봐야함.


2. search_after

라이브 커서를 제공하여 다음 페이지를 조회할 수 있다.
검색할때 sort 필드를 통해 정렬 조건을 부여하면 결과 값으로 sort필드를 반환하는데 다음 페이지를 검색하기 위해 search_after 필드 내 기존 결과 값 sort필드를 넣으주면
다음 페이지를 조회할 수 있다.

[First 쿼리] 40~49 까지 결과 보여줌.

GET test_k/_search
{
  "query": {
    "match_all": {

    }
  },
  "from":"40",
  "sort": [
    {
      "SKEY": {
        "order": "desc"
      },
      "TNEWS_KEY": {
        "order": "desc"
      }
    }
  ]
}

search_after 사용시 반드시 unique값 하나는 포함하여 정렬해주어야 한다.(SKEY)

결과 중 마지막인 49번째 결과값

 {
        "_index" : "test_k",
        "_type" : "_doc",
        "_id" : "TKR7520160000011",
        "_score" : null,
        "_source" : {
          "SKEY" : "TKR7520160000011",
          "TNKWS" : "(주)수성에너지",
          "TNEWS" : "GREEN"
        },
        "sort" : [
          "tkr7520160000011",
          "green"
        ]
  }
  

[Next 쿼리] 49번 doc 결과 이후 50~59 문헌 보여줌

GET test_k/_search
 {
 "query": {
   "match_all": {

   }
 },
 "sort": [
   {
     "SKEY": {
       "order": "desc"
     },
     "TNEWS_KEY": {
       "order": "desc"
     }
   }
 ],

첫 검색결과의 마지막 sort값을 넣으면 그 후의 문서를 반환한다.

  "search_after": [ // 하는 반드시 unique한 값이야 함.
      "tkr7520160000011",
      "green"
  ]
}

search_after에 넣은 sort정보가 49번째 doc의 sort값이다.
그래서 위의 쿼리로 50~59번의 doc을 받을 수 있다.

[참고] https://stackoverflow.com/questions/68127892/how-does-search-after-work-in-elastic-search

GET kr/_search
{
  "size": 5,
  "query": {
    "bool": {
      "filter": [
        {
          "terms": {
            "COLP": [
              "1",
              "2",
              "3",
              "4"
            ]
          }
        }
      ],
      "must": [
        {
          "bool": {
            "should": [
              {
                "match_phrase": {
                  "TI": "car"
                }
              },
              {
                "match_phrase": {
                  "TIW": "car"
                }
              }
            ]
          }
        }
      ]
    }
  },
  "from": 0,
  "_source": [
    "SKEY",
    "TIK"
  ],
  "sort": [
    {
      "WRC": {
        "order": "desc"
      }
    },
    {
      "XD.keyword": {
        "order": "desc"
      }
    },
    {
      "SKEY": {
        "order": "asc"
      }
    }
  ],
  "search_after":[
       10,
          "20220503",
          3522191000926
    ],
  "aggs": {
    "topn_skeys": {
      "scripted_metric": {
        "init_script": {
          "id": "topn_skeys_init_script"
        },
        "map_script": {
          "id": "topn_skeys_map_script"
        },
        "combine_script": {
          "id": "topn_skeys_combine_script"
        },
        "params": {
          "sort": [
            {
              "field": "WRC",
              "order": "asc"
            },
            {
              "field": "XD.keyword",
              "order": "desc"
            },
            {
              "field": "SKEY",
              "order": "asc"
            }
          ],
          "topn": 20000
        },
        "reduce_script": {
          "id": "topn_skeys_reduce_script"
        }
      }
    },
    "group_by_colp": {
      "terms": {
        "field": "COLP",
        "size": 100
      }
    }
  },
  "track_total_hits": true
}

지금 wt에서 사용하는 쿼리로 결과에 sort

"sort" : [
        10,
        "20220502",
        3522191000683
      ] 를
 "search_after" : [
        10,
        "20220502",
        3522191000683
      ]

이렇게 사용. unique값 하나만 있으면 필드값이 2개이상이여도 페이징 되는것 같다.

주의점)
sort되는 값이 변동되는 값이면 이전 결과를 보장 받을 수 없다. (색인 중에 페이징시 문제 발생 가능성)

문제)
from 값을 사용할 수 없기 때문에, UI에서 더보기 버튼이나 스크롤 방식이 아닌 페이지 번호 조회인 경우는 사용할 수 없습니다.

3. point in time(pit)

특정 시점의 색인에 대해

색인이 실시간으로 변경되지는 않기에 search after를 사용할때 pit를 함께 사용하지 않아도 될것 같다.

[참고] https://velog.io/@nmrhtn7898/elasticsearch-%EA%B9%8A%EC%9D%80deep-%ED%8E%98%EC%9D%B4%EC%A7%80%EB%84%A4%EC%9D%B4%EC%85%98

profile
hi

0개의 댓글