검색엔진 스터디

04장 데이터 검색

막이86 2023. 11. 8. 17:26
728x90

엘라스틱서치 실무 가이드 4장을 요약한 내용입니다.

  • 다양한 검색 조건을 충족시키기 위해 Query DSL이라는 특수한 쿼리 문법을 제공

4.1 검색 API

  • 문장은 색인 시점에 텀으로 분해
  • 색인 시점에 Analyzer를 통해 분석된 텀을 Term, 출현빈도, 문서번호와 같이 연색인 구조로 만들어 내부적으로 저장
  • 검색 시점에는 Keyword 타입과 같은 분석이 불가능한 데이터와 Text 타입과 같은 분석이 가능한 데이터를 구분해 분석을 수행
    • [그림 4.1] 색인 시점과 검색 시점에서의 동작 비교 참고

4.1.1 검색 질의 표현 방식

  • 검색 API는 기본적으로 질의를 기반으로 동작

4.1.2 URI 검색

  • URL에 검색할 칼럼과 검색어를 직접 지정하면 검색이 수행
  • 간단한 URI 검색 쿼리
    • [표 4.1] URI 검색에서 자주사용하는 파라미터 참고
    POST /movie_search/_search?q=movieNm:Family
    
  • 표현의 한계로 복잡한 질의를 작성하는 것은 불가능
  • 다양한 필드를 검색 조건으로 추가해서 검색 요청
  • POST /movie_search/_search?q=movieNmEn:* AND prdtYear:2017&analyze_wildcard=true&from=0&size=5&sort=_score:desc,movieCd:asc&_source_includes=movieCd,movieNm,moiveNmEn,typeNm
  • 검색조건 분석
    • 검색식이 복잡해서 사용하기 불편하고 가독성이 떨어짐
    movieNmEn:* AND prdtYear:2017                      # 필드에 대한 쿼리
    analyze_wildcard=true                              # 와일드카드 옵션 활성화
    from=0                                             # 페이징 시작값
    size=5                                             # 페이징 사이즈
    sort=_score:desc,movieCd:asc                       # 정렬
    _source_includes=movieCd,movieNm,moiveNmEn,typeNm  # _source 필드명
    

4.1.3 Request Body 검색

  • 간단한 검색 조건의 경우 URI 방식이 더 좋아보일 수도 있다
    • JSON 구조를 파악하면 깔끔한 구조가 눈에 더 잘 들어 온다
    POST /movie_search/_search
    {
    	"query": {
    		"query_string": {
    			"default_field": "movieNmEn",
    			"query": "Family"
    		}
    	}
    }
    
  • 복잡한 검색 옵션도 JSON 구조로 표현하는 것이 가능
  • POST /movie_search/_search { "query": { "query_string": { "default_field": "movieNmEn", "query": "movieNmEn:* AND prdtYear:2017" } }, "from": 0, "size": 5, "sort": [ { "_score": { "order": "desc" }, "movieCd": { "order": "asc" }, } ], "_source": [ "movieCd", "movieNm", "movieNmEn", "typeNm" ] }

4.2 Query DSL 이해하기

  • 여러 개의 질의를 조합하거나 질의 결과에 대해 다시 검색을 수행하는 등의 검색이 가능
  • Request Body 검색을 이용할 때 사용하는 JSON 구조를 지원

4.2.1 Query DSL 쿼리의 구조

  • Query DSL 요청을 위한 JSON 구조
  • { "size": // 리턴 받는 결과의 개수, 기본값은 10 "form": // 몇 번째 문서부터 가져올지 지정, 기본값은 0 "timeout": // 결과를 받는 데까지 걸리는 시간 "_source": {} // 검색시 필요한 필드만 요청 "query": {} // 검색 조건문 "aggs": {} // 통계 및 집계 데이터 사용시 "sort": {} // 문서 결과를 어떻게 출력할지 조건 }
  • JSON 응답 구조
  • { "took": // 쿼리 실행 시간 "timed_out": // 시간이 초과한 경우 "_shards": { "total": // 쿼리를 요청한 전체 샤드의 수 "successful": // 검색 요청에 성공한 샤드의 수 "failed" // 검색 요청에 실패한 샤드의 수 }, "hits": { "total": // 검색어에 매칭된 문서의 전체 개수 "max_scour": // 일치하는 문서의 스코어 값 중 가장 높은 값 "hits": [] // 각 문서의 정보와 스코어 값 } }

4.2.2 Query DSL 쿼리와 필터

  • 작은 질의들은 두가지 형태로 나눠서 생각해볼 수 있다.쿼리 컨텍스트 필터 컨텍스트
    용도 전문 검색 시 사용 조건 검색 시 사용
    특징 분석기에 의해 분석이 수행, 연관성 관련 Scoure를 계산, 루씬 레벨에서 분석과정을 거처야 하므로 상대적으로 느림 Yes/No로 단순 판별 가능, 연관성 관련 계산을 하지 않음, 엘라스틱서치 레벨에서 처리가 가능하므로 상대적으로 빠름
    사용 예 “Harry Potter” 같은 문장 분석 “create_year” 필드 값이 2018년인지 여부, “status” 필드에 “use”라는 코드 포함 여부

쿼리 컨텍스트

  • 문서가 쿼리와 얼마나 유사한지를 스코어로 계산
  • 질의가 요청될 때마다 엘라스틱서치에서 내부의 루씬을 이용해 계산을 수행
    • 결과가 캐싱되지 않음
  • 일반적인 전문 검색에 많이 사용
  • 캐싱되지 않고 디스크 연산을 수행하기 때문에 상대적으로 느림
  • POST /movie_search/_search { "query": { "match": { "movieNm": "기묘한 가족" } } }

필터 컨텍스트

  • 쿼리의 조건과 문서가 일치하는지(Yes/No)를 구분
  • 별도로 스코어를 계산하지 않고 단순 매칭 여부를 검사
  • 자주 사용되는 필터의 결관느 엘라스틱서치 내부적으로 캐싱
  • 기본적으로 메모리 연산을 수행하기 때문에 상대적으로 빠르다
  • POST /movie_search/_search { "query": { "bool": { "must": [ { "match_all": {} } ], "filter": { "term": { "repGenreNm": "다큐멘터리" } } } } }

4.2.3 Query DSL의 주요 파라미터

multi Index 검색

  • 모든 검색 요청은 Multi Index 및 Multi Type 검색이 가능
  • POST /movie_search,movie_auto/_search { "query": { "term": { "repGenreNm": "다큐멘터리" } } }
  • 검색 요청 시 인덱스 이름을 지정할 때 “*”를 와일드카드로 사용 가능
  • POST /log-2019-*/_search

쿼리 결과 페이징

  • from, size를 사용하여 페이징 사용 가능
  • 설정된 페이지를 제공하기 위해서는 전체를 읽어서 사이즈만큼 필터링해서 제공하는 구조
    • 페이지 번호가 높아질수록 쿼리 비용은 높아질 수밖에 없다

쿼리 결과 정렬

  • 필드의 이름이나 가격, 날짜 등을 기준으로 재정렬 할 때 사용

_source 필드 필터링

  • 필요에 따라 특정 필드를 검색 결과에서 제고하고 싶을 경우 사용
  • 검색 결과에 특정 칼럼을 제외하면 네트워크 사용량을 줄일 수 있다

범위 검색

  • 숫자나 날짜 데이터의 경우 지정한 값이 아닌 범위를 기준으로 질의 가능

operator 설정

  • 검색 시 문장이 들어올 경우 기본적으로 OR 연산으로 동작
  • AND 연산을 사용해 정확도를 높여야 할 경우 사용
  • POST /movie_search/_search { "query": { "match": { "movieNm": { "query": "자전차왕 엄복동", "operator": "and" } } } }

minimum_should_match 설정

  • operator의 OR 연산의 경우 너무 많은 결과가 나올 수 있음
  • 텀의 개수가 몇 개 이상 매칭될 때만 검색 결과로 나오게 설정

fuzziness 설정

  • 같은 값을 찾는 Match Query를 유사한 값을 찾는 Funzzy Query로 변경
  • “Fly High”를 “Fil High”로 입력할 경우 검색이 가능하도록 설정
  • POST /movie_search/_search { "query": { "match": { "movieNmEn": { "query": "Fli High", "fuzziness": 1 } } } }

boost 설정

  • 관련성이 높은 키워드에 가중치를 더 줄 수 있다.
  • 한글 영화 제목이 일치하게 된다면 스코어에 가중치 값을 3을 곱한다
  • POST /movie_search/_search { "query": { "multi_match": { "query": "Fly", "fields": ["movieNm^3", "movieNmEn"] } } }

4.3 Query DSL의 주요 쿼리

4.3.1 Match All Query

  • 색인에 모든 문서를 검색하는 쿼리
  • 가장 단순한 쿼리로 일반적으로 색인에 저장될 문서를 확인할 때 사용
  • POST /movie_search/_search { "query": { "match_all": {} } }

4.3.2 Match Query

  • 텍스트, 숫자, 날짜 등이 포함된 문장을 형태소 분석을 통해 텀으로 분리한 후 텀들을 이용해 검색 질의를 수행
  • 검색어가 분리되어야 할 경우에 사용
  • POST /movie_search/_search { "query": { "match": { "movieNm": "그대 장미" } } }

4.3.3 Multi Match Query

  • 단일 필드가 아닌 여러 개의 필드를 대상으로 검색해야 할 때 사용
  • POST /movie_search/_search { "query": { "multi_match": { "query": "가족", "fields": ["movieNm", "movieNmEn"] } } }

4.3.4 Term Query

  • 별도의 분석 작업을 수행하지 않고 입력된 텍스트가 존재하는 문서를 찾는다
  • 일반적으로 숫자, Keyword, 날짜 데이터 쿼리에 사용
  • POST /movie_search/_search { "query": { "term": { "genreAlt": "코미디" } } }
  • 필드에 텀이 정확히 존재하지 않는 경우 검색이 되지 않는다
    • 영문의 경우 대소문자가 다를 경우 검색 되지 않음

4.3.5 Bool Query

  • 하나의 쿼리에 여러 개의 쿼리를 조합해서 더 높은 스코어를 가진 쿼리를 조검으로 검색을 수행할 수 있다.
  • POST /movie_search/_search { "query": { "bool": { "must": [ { "term": { "repGenreNm": "코미디" } }, { "match": { "repNationNm": "한국" } }, ], "must_not": [ { "match": { "typeNm": "단편" } } ] } } }

4.3.6 Query String

  • 내장된 쿼리 분석기를 이용하는 질의를 작성할 수 있다.
  • POST /movie_search/_search { "query": { "query_string": { "defaul_field": "movieNm", "query": "(가정) AND (어린이 날)" } } }

4.3.7 Prefix Query

  • 접두어가 있는 모든 문서를 검색
  • POST /movie_search/_search { "query": { "prefix": { "movieNm": "자전차" } } }

4.3.8 Exists Query

  • 실제 값이 존재하는 문서만 찾고 싶을때 사용
  • POST /movie_search/_search { "query": { "exists": { "field": "movieNm" } } }

4.3.9 Wildcard Query

  • 검색어가 와일드카드와 일치하는 구문을 찾는다와일드카드 옵션 설명
    * 문자의 길이와 상관없이 와일드 카드와 일치하는 모든 문서를 찾는다.
    ? 지정된 위치의 한 글자가 다른 경우의 문서를 찾는다
  • 와일드카드를 사용할 경우 단어의 첫 글자는 절대 사용해서는 안된다.
    • 전체 문서를 찾아야하는 문제 발생
    POST /movie_search/_search
    {
    	"query": {
    		"wildcard": {
    			"typeNm": "장?"
    		}
    	}
    }
    

4.3.10 Nested Query

  • 분산 데이터 환경에서도 SQL의 조인과 유사한 기능을 수행
  • 인덱스 생성
  • PUT /movie_nested { "settings": { "number_of_replicas": 1, "number_of_shards": 5 }, "mappings": { "_doc": { "properties": { "repGenreNum": { "type": "keyword" }, "companies": { "type": "nested", "properties": { "companyCd": { "type": "keyword" }, "companyNm": { "type": "keyword" } } } } } } }
  • 생성된 인덱스에 문서 추가
  • PUT /movie_nested/_doc/1 { "movieCd": "20184623", "movieNm": "바람난 아내들2", "movieNmEn": "", "prdtYear": "2018", "openDt": "", "typeNm": "장편", "prdtStatNm": "개봉예정", "nationAlt": "한국", "genreAlt": "멜로/로맨스", "repNationNm": "한국", "repGenreNm": "멜로/로맨스", "companies": [ { "companyCd": "20173401", "companyNm": "(주)케이피에이기획" } ] }
  • Nested Query를 이용해 지정된 문서의 특정 필드를 검색
  • GET /movie_nestd/_search { "query": { "bool": { "must": [ { "term": { "repGenreNm": "멜로/로멘스" } }, { "nested": { "path": "companies", "query": { "bool": { "must": [ { "term": { "companies.companyCd": "20173401" } } ] } } } } ] } } }
  • 성능상의 이유로 Parent 문서와 Child문서를 모두 동일한 샤드에 저장
    • 특정 샤드에 데이터가 많이 저장될 수 있으니 주의

4.4 부가적인 검색 API

4.4.1 효율적인 검색을 위한 환경설정

  • 대량의 데이터를 처리하기 위해 기본적으로 데이터를 분산 처리
  • 검색 요청이 발생하면 모든 샤드에 검색 요청을 브로드캐스팅해서 전달하고 기다린다.
  • 데이터 특성이나 검색 패턴에 따라 검색 설정 값을 적절한 값으로 수정한다면 좀더 효율적으로 운영 가능

동적 분배 방식의 샤드 선택

  • 모든 샤드에 검색을 수행하게 되면 중복된 결과를 전달할 수 있음
    • 동일 데이털르 가지고 있는 샤드 중 하나만 선택해 검색을 수행

글로벌 타임아웃 설정

  • 검색 요청 시 타임아웃 설정은 매우 중요
    • 무거운 쿼리가 타임아웃 없이 동작한다면 시스템 장애가 발생 될 수 있음
  • 모든 검색 쿼리에 동일하게 적용하도록 정책으로 설정하는 것이 좋다

4.4.2 Search Shards API

  • 검색이 수행되는 노드 및 샤드에 대한 정보를 확인 가능
  • 질의를 최적화하거나 질의가 정상적으로 수행되지 않을 때 문제를 해결하는데 유용
  • POST /movie_search/_search_shards

4.4.3 Multi Search API

  • 여러 건의 검색 요청을 통해서 한번에 요청하고 한번에 결과를 종합해서 받을 수 있다
  • POST _msearch {"index": "movie_auto"} {"query": {"match_all": {}}, "from": 0, "size": 10} {"index": "movie_search"} {"query": {"match_all": {}}, "from": 0, "size": 10}

4.4.4 Count API

  • 검색된 문서의 개수가 몇 개인지 숫자만 필요할 때 사용
  • POST /movie_search/_count?q=prdtYear:2017 POST /movie_search/_count { "query": { "query_string": { "default_field": "prdtYear", "query": "2017" } } }

4.4.5 Validate API

  • 쿼리를 실행하기전 쿼리가 유효한지 검증
  • POST /movie_search/_validate/query?q=prdYear:2017 POST /movie_search/_validate/query { "query": { "match": { "prdYear": "2017" } } }
  • 쿼리의 실패한 자세한 이유를 알고 싶으면 rewrite=true를 추가
  • POST /movie_search/_validate/query?rewrite=true { "query": { "match": { "prdYear": "2017" } } }

4.4.6 Explain API

  • _score값이 어떻게 계산된 것인지 자세한 정보를 제공
  • POST /movie_search/_search { "query": { "term": { "prdtYear": 2017 } } } # 조회 결과의 문서 ID를 추가해 호출 POST /movie_search/_doc/8/_explain { "query": { "term": { "prdtYear": 2017 } } }
  • 특정 문서가 검색되지 않을 때 디버깅 정보로 유용

4.4.7 Profile API

  • 쿼리에 대한 상세한 수행 계획과 각 수행 계획별로 수행된 시간 정보를 반환
    • 성능 튜닝하거나 디버깅시 유용
    • 샤드별로 프로파일 정보를 제공
    POST /movie_search/_search
    {
    	"profile": true,
    	"query": {
    		"match_all": {}
    	}
    }
    
  • “내 질의 결과에 대한 스코어를 알고 싶을 때”
    • Explain API 사용
  • “내 질의를 실행하는 정보를 알고 싶을 때”
    • Profile API 사용
728x90

'검색엔진 스터디' 카테고리의 다른 글

07장 한글 검색 확장 기능  (1) 2023.11.08
05장 데이터 집계  (3) 2023.11.08
03장 데이터 모델링  (1) 2023.11.08
02장 엘라스틱서치 살펴보기  (4) 2023.11.08
01장 검색 시스템 이해하기  (0) 2023.11.08