티스토리 뷰
[Elasticsearch] Section 3. Elasticsearch를 사용한 검색
dev_jun 2022. 11. 23. 01:59Query Lite
검색 본문이 없이도 검색 요청이 가능한 형태.
curl같은 명령어들을 사용할 때 URL 안에 모든 것을 다 집어 넣으면 훨씬 효율적일 것이다.
예를 들면 다음과 같은 형태이다.
/movies/_search?q=title:star
- 영화 제목에 star가 포함되어 있는 검색 결과를 리턴
/movies/_search?q=+year:>2010+title:trek
- 개봉 연도가 2010년 이후이고, 영화 제목이 trek이 포함되는 검색 결과 리턴
?q=
는 쿼리를 하겠다는 뜻으로, 뒤의 내용을 질의하겠다는 의미이다.+
연산자를 사용하여 위의 예시와 같은 boolean 연산을 수행할 수 있으며, 관계형 쿼리도 사용할 수 있다.- 여기서의 관계형은 상대적인 크기를 의미한다. 관계형 데이터베이스에서의 관계가 아니다.
- URL을 사용해 안전하게 인터넷망을 통해 전송할 수 있도록 인코딩해야한다. 이는 결국 가독성을 떨어뜨리게 된다.
- 따라서 URL 사용으로 신속함의 가치를 얻을 수 있지만 가독성은 떨어지는 trade off가 발생한다.
또한 다음의 이유로 중요한 작업에서는 query lite를 사용해서는 안된다.
- 복잡하고 디버깅이 어렵다.
- 변수가 생기기 쉽고 빠르게 변질이 되기도 한다.
- 보안에 취약하다.
- 모든 사용자가 URL에 검색 쿼리 문자열을 쉽게 사용할 수 있다면 이로 인해 클러스터는 다운될 수 있다.
- 취약하다.
- 변수들이 난해해질 수 있고, 하나의 잘못된 문자로 곤경에 빠질 수 있으며, 무슨 일이 일어나는지 알아채기 힘들 때가 있다.
따라서 간단한 로컬 테스트와 같은 작업에서 유용하게 사용할 수 있는 기능이다.
JSON 검색
위에서 사용한 예제를 query lite가 아닌 일반적인 JSON 형태로 검색하려면 다음과 같이 검색할 수 있다.
curl -XGET 127.0.0.1:9200/movies/_search?pretty -d '{
"query": {
"bool": {
"must": {"term": {"title": "trek"}},
"filter": {"range": {"year": {"gte": 2010}}}
}
}
}'
사용할 수 있는 필터는 다양하다.
- Term - 정확한 용어나 값으로 필터링 해야하는 경우
{"term": {"year": 2014}}
- terms - 일치하는 결과값 리스트와 일치하는 항목을 찾고자 하는 경우
{"terms": {"genre": ["Sci-Fi", "Adventure"]}}
- Range - 범위를 필터링
{"range": {"year": {"get": 2010}}}
- Exists - field가 document 안에 존재하는지 찾고자 하는 경우
{"exists": {"field": "tags"}}
- 존재하는지'만' 찾고자 할때는
exist
사용
- Missing - 지정된 field가 없는 document를 찾는 경우
{"missing": {"field": "tags"}}
- Bool - must = AND, must_not = NOT, should = OR
쿼리 또한 다양하다.
- Match_all - 모두 반환되는 기본값. 쿼리를 지정하지 않으면 기본적으로 모든 결과가 반환된다.
{"match_all": {}}
- Match
{"match": {"title": "star"}}
- Multi_match
{"multi_match": {"query": "star", "field": ["title", "synopsis"]}}
- Bool - 관련성 별로 결과를 표시한다는 차이점이 존재한다.
구문 검색
때로는 개별 검색이 아닌 Star Wars처럼 특정 순서로 함께 검색해야 할 수도 있다. 이러한 구절 검색이 Elasticsearch에서 어떻게 작동하는지 알아보자.
Elasticsearch에서는 여러 개의 용어를 같은 순서로 검색하는 것은 쉽다. match_phrase
를 사용하면 되기 때문이다.
curl -XGET 127.0.0.1:9200/movies/_search?pretty -d '{
"query": {
"match_phrase": {
"title": "star wars"
}
}
}'
만약 검색어의 순서가 중요하지만, 바로 붙어있을 필요는 없는 상황이라면 slop
을 사용한다. slop
은 어느 방향으로 얼마나 움직여도 구절 매치를 허용할 것인지를 나타낸다. 만약 'star trek beyond'를 검색하고 싶다면 다음과 같이 작성할 수 있다. 중간에 어떤 단어가 들어와도 검색될 것이다. 또한 reverse 검색도 허용한다. 검색 결과 중 'beyond star'와도 일치한다는 뜻이다.
slop 값을 1로 설정했다고 반드시 중간 사이의 단어가 1개 빈다는 것을 알고 있어야할까? 그것은 아니다. 실제로 slop 값을 100으로 설정해도 모든 검색 결과를 찾는다. 다만 둘 사이가 가까울수록 관련성 점수가 높다.
curl -XGET 127.0.0.1:9200/movies/_search?pretty -d '{
"query": {
"match_phrase": {
"title": {"query": "star beyond", "slop": 1}
}
}
}'
페이징
from, size를 사용하여 페이지네이션을 할 수 있다. 여기서 from은 0부터 시작한다. 데이터베이스 쿼리문의 limit, offset을 떠올리면 이해하기 쉽다.
from - 몇개의 결과만을 받을 것인가
size - 한 페이지에 몇개의 검색 결과를 표시할 것인가
curl -XGET '127.0.0.1:9200/movies/_search?size=2&from=2&pretty'
curl -XGET 127.0.0.1:9200/movies/_search? -d '{
"from": 2,
"size": 2,
"query": {"match": {"genre": "Sci-Fi"}}
}'
일반적으로 페이지네이션에 대한 주의사항은 Elasticsearch에도 적용된다. 바로 깊은 페이징은 성능을 저하할 수 있다는 것이다. 이를 피하기 위해서는 나름대로의 상한선을 적용하는 방법이 있다.
정렬
일반적으로 숫자 field나 검색하기 쉬운 field를 다루는 것은 매우 간단히 sort=
를 사용하면 된다.
curl -XGET '127.0.0.1:9200/movies/_search?sort=year&pretty'
하지만 문자열을 다룰 때는 고려해야할 사항이 있다.
- text field가 있다면 해당 field는 '전체 텍스트 검색'을 위해 분석된다. 따라서 부분 match, purge query 등의 작업을 할 수 있다.
- 역색인이 해당 제목의 용어들을 가지고 있기 때문에 이 기능은 문서를 정렬하는데 사용할 수 없다.
- 영화 제목같은 경우를 예로 들면 '개별 단어'와 '단어 순서'만 역색인에 보관된다.
이를 해결하는 방법은 분석되지 않은 하위 필드를 설정하는 것이다.
curl -XPUT 127.0.0.1:9200/movies -d '{
"mappings": {
"properties": {
"title": {
"type": "text",
"field": {
"raw": {
"type": "keyword"
}
}
}
}
}
}'
- title field가 'text' type임을 확인할 수 있다. 따라서 이는 전체 텍스트 검색을 위해 분석된다.
- 하위에 'raw' 필드가 있으며, 이를 'keyword' 타입으로 지정하여 raw 필드는 분석되지 않도록 하여 제목 그대로를 'raw' 필드에 저장한다.
- 따라서 '전체 텍스트 검색'에 사용할 수 있는 제목 필드 뿐만 아니라 정렬 작업이나 분석되지 않은 필드를 필요로 하는 작업에 사용할 수 있는 'title.raw' 필드도 생성한 것이다.
- 이후
curl -XGET '127.0.0.1:9200/movies/_search?sort=title.raw&pretty'
로 검색 결과를 확인할 수 있다.
- 이후
- 기존 index의 mapping을 변경할 수 없기 때문에 index 생성 시 고려해야하는 사항 중 하나이다.
퍼지(Fuzzy) 쿼리
검색 속 오타를 다루는 쿼리이다. Fuzzy Matches의 기본 개념은 '레벤쉬테인 편집 거리'부터 시작한다. 이는 일반적인 오자와 오타를 정량화 할 수 있다. 여기에는 대체(Substitutions), 삽입(Insertions) 및 삭제(Deletion)의 세 가지 단계가 있다.
- 대체(Substitutions)
- 실수로 잘못된 문자를 입력했을 때 잡아낸다.
- Interstellar -> Intersteller
- 삽입(Insertions)
- 사람의 실수로 있어서는 안되는 철자가 추가된 것
- Interstellar -> Insterstellar
- 삭제(Deletion)
- 누락
- Interstellar -> Interstelar
위의 예시는 모두 레벤쉬테인 편집 거리가 '1'인 상태에서는 하나의 철자만 틀렸기 때문에 검색이 된다. 이 수치는 얼마나 많은 오차를 허용할 것인지를 나타낸다.
이는 다음과 같이 설정할 수 있다.
curl -XGET 127.0.0.1:9200/movies/_search?pretty -d '{
"query": {
"fuzzy": {
"title": {"value": "intrsteller", "fuzziness": 2}
}
}
}'
퍼지를 자동으로 설정해보자. 때로는 문자열 길이에 따라 퍼지를 다르게 설정할 수 있다. 이를 사용하면 1~2개의 문자열에 대해선 어떤 종류의 오타도 허용하지 않는다. 3~5개의 문자열에는 레벤쉬테인 편집 거리 1만 허용하고, 그 외에는 레벤쉬테인 편집 거리 2까지를 허용한다.
부분 매치
검색할 때 접두사 또는 문자열의 일부분을 일치시키는 법을 알아보자.
문자열에 대한 접두사 쿼리는 다음과 같이 작동한다.
curl -XGET '127.0.0.1:9200/movies/_search?pretty' -d '{
"query": {
"prefix": {
"year": "201"
}
}
}'
---
curl -XGET '127.0.0.1:9200/movies/_search?pretty' -d '{
"query": {
"wildcard": {
"year": "1*"
}
}
}'
상기 검색의 첫번째는 영화의 개봉 날짜가 '201'로 시작하는 모든 영화를 검색하여 보여준다. 두번째는 와일드 카드를 이용한 검색 방법을 보여준다. 또한 정규표현식도 지원한다.
Search As You Type
입력하는 동안 검색어가 자동으로 완성되고 몇 가지 검색 제안이 나오는 기능이다. Elasticsearch를 사용하여 이와 유사한 작업을 수행할 수 있다.
가장 쉬운 방법은 'query-time search-as-you-type'이다. 데이터를 특별히 색인화할 필요가 없기 때문이다.
curl -XGET '127.0.0.1:9200/movies/_search?pretty' -d '{
"query": {
"match_phrase_prefix": {
"title": {
"query": "star trek",
"slop": 10
}
}
}
}'
이는 가장 쉬운 방법이지만 가장 효율적인 방법은 아니다.
N-grams
'Query-Time Search-As-You-Type'의 한 가지 단점은 색인 기반 솔루션에 비해 리소스가 많이 사용된다는 것이다. 만약 대규모로 사용해야한다면 'index-time'솔루션이 필요하다. 'index-time' 솔루션에서는 N-grams 라는 간단한 개념을 사용한다. 예를 들어 'star'라는 단어를 살펴보면 다음과 같이 분류할 수 있다.
unigram: [s, t, a, r]
bigram: [st, ta, ar]
trigram: [sta, tar]
4-gram: [star]
이 개념을 'Search-As-You' Type에 적용할 수 있다. 입력값을 N-grams으로 취급하면 index에 매치하여 어떤 단어와 일치하는지 확인할 수 있다. 만약 'star trek'을 찾기 위해서 's'를 입력하면 그 유니그램은 'star'의 유니그램 중 하나인 's'와 일치한다.
Edge N-grams라는 특수한 유형의 N-grams가 존재한다. 예시로 확인했듯이 정말 중요한 것은 주어진 구절 혹은 검색어의 시작 N-grams이다. 따라서 Edge N-grams는 주어진 단어의 앞부분에 대한 N-grams만 계산하는 것이다.
이를 Elasticsearch에 적용하기 위한 단계는 다음과 같다.
- Autocomplete Analyzer를 생성한다.
curl -XPUT '127.0.0.1:9200/movies?pretty' -d '{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
}
}'
- 생성한 autocomplete analyzer를 index-time에 적용한다.
- index화하기 전에 이에 대한 mapping을 설정해주어야 한다.
curl -XPUT '127.0.0.1:9200/movies/_mapping?pretty' -d '{
"properties": {
"title": {
"type": "text",
"analyzer": "autocomplete"
}
}
}'
Completion Suggesters
또 하나의 방법으로는 completion suggesters
를 사용하는 것이다. 이 매커니즘을 사용하여 완료 목록을 미리 업로드 할 수 있다. 최대한 효율적으로 자동 완성 기능을 완벽하게 통제하고 싶다면 이 기능을 사용하는 것을 추천한다.
'Data Engineering > Elasticsearch' 카테고리의 다른 글
[Elasticsearch] Logstash란 (0) | 2022.11.23 |
---|---|
[Elasticsearch] Script 혹은 Library를 사용하여 데이터 import (0) | 2022.11.23 |
[Elasticsearch] 데이터 매핑 및 색인화 - 2. 동시성, Analyzer, Flattened, Mapping Exception (0) | 2022.11.22 |
[Elasticsearch] Data 매핑 및 색인화 - 1 (0) | 2022.11.22 |
Elasticsearch 기본 (0) | 2022.11.15 |
- Total
- Today
- Yesterday
- mahout
- 백준
- CSAPP
- Espher
- DFS
- logstash
- kubernetes
- Algorithm
- CS
- 빅데이터
- 네트워크
- 파이썬
- sqoop
- Elasticsearch
- BOJ
- kafka
- 빅데이터를지탱하는기술
- Python
- OS
- DP
- HDFS
- GROK
- oozie
- Flutter
- cka
- 이코테
- elasticsaerch
- Hadoop
- heapq
- 프로그래머스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |