About
이번 블로그에서는 이전 블로그(Elasticsearch 기본 개념과 python 패키지 elasticsearch-dsl 기본 사용법)에서 설명하지 못한 regex
를 이용한 Search방법과 Aggregation
에 대해서 보충 설명하고자 한다. Elasticsearch가 처음이시거나 기본 개념을 아시고자 하시는 분은 위 링크에서 확인하고 오는 것을 추천한다.
Implementation
먼저 아래와 같이 모듈들을 임포트하고, es
에는 elasticsearch 객체를 생성하는 것을 아래의 코드들에서도 공통되게 사용할 것이다.
import elasticsearch
from elasticsearch_dsl import Search, Q, Index
es = elasticsearch.Elasticsearch('localhost:9200')
Regexp filter
q = Q(
'bool',
should=[
Q('regexp', name='.*수{1}'),
Q('regexp', name='.*맛집.*'),
],
minimum_should_match=1)
s = Search(using=es, index='index_name').query(q)
res = s.execute()
Q 모듈을 이용하여 regexp search가 가능하다. 가장 기본적인 syntax들만 살펴보자. 아래 내용은 Elasticsearch Regexp-syntax에서 소개한 일부를 번역한 것이다. 자세하게 알고싶으신 분은 위의 링크로 들어가서 확인하도록 하자.
1. .
하나의 어떤 글자든 될 수 있다.
예) ab.
# matches ‘abc’, ‘abd’, ‘abs’ … etc
2. ?
앞글자가 0번 혹은 1번 등장하는 경우를 찾아온다.
예) abc?
라면 c가 나오거나 안나오거나 둘 다 가져온다. # matches ‘ab’, ‘abc’
3. +
앞글자가 1번이상으로 등장하는 경우를 찾아온다.
예) abc+
# matches ‘abc’, ‘abccccc’ … etc
4. *
앞글자가 0번 혹은 1번이상 등장하는 경우를 찾아온다.
예) abc*
# matches ‘ab’, ‘abccc’ … etc
5. {}
앞글자가 등장할 수 있는 범위를 설정할 수 있다.
예) c{2}
# matches cc
c{2, 4}
# matches cc
, ccc
, cccc
이제 위의 내용으로 정리해보자면,
'.*수{1}'
는 앞에 어떤 글자들이 나오거나 안나와도 관계없으나 “수”는 한번만 나오는 text를 반환하는 코드다.
'.*맛집.*'
은 앞과 뒤에 어떤 글자든 있거나 없어도 관계없고, “맛집”이 등장한 text를 반환하는 코드다.
하지만 elasticsearch에서 사용할 때는 일반적인 regexp와 차이점이 있다. Inverted index에 저장된 단어들을 하나하나 확인하기 때문에 문장 시작을 의미하는 ^
라던가 끝을 의미하는 $
와 같은 표현법은 사용하지 못하게 되어있음을 명심하자.
2. Aggregation
s = Search(index='index_name', using=es)
a = A('terms', field='user_name', size=s.count())
a.pipeline('작성자가 답한 문항 정보', 'terms', field='question_id')
s.aggs.bucket('user history', a)
res = s.execute()
buckets = res.aggregations['user history'].to_dict()['buckets']
나중에 다른 통계기법을 사용하게되면 다시 블로그를 작성하도록하고, 이번 블로그에서는 간단한 통계 정보인 count하는 법에 대해서만 알아보도록 하겠다.
참고로 이번 Index는 사용자들의 설문조사 답변들이 등록된 것이라고 가정하고 설명하도록 하겠다.
{
'user_name': '홍길동',
'item_id': 1,
'question_id': 1,
'question': '해당 아이템의 전체적인 만족도는 몇 점인가요?',
'answer': 5.0
},
{
'user_name': '홍길동',
'item_id': 1,
'question_id': 2,
'question': '해당 아이템의 가격 만족도는 몇 점인가요?',
'answer': 5.0
}
예를 들면 위와 같은 형식의 데이터들이 있다.
이제 해석을 해보자. 먼저
a = A('terms', field='user_name', size=s.count())
는 user_name
필드에서 unique한 user들이 몇번 등장했는지 return한다. size는 return하는 총 데이터 개수이다. 여기서는 전체를 반환해달라는 의미로 s.count()
를 사용했다.
반환하는 값은
{
'key': '홍길동',
'doc_count': 1275
}
과 같은 방식이다. 이는 user_name 필드가 홍길동
인 데이터 row가 총 1275번 등장했다는 의미다.
a.pipeline('작성자가 답한 문항 정보', 'terms', field='question_id')
pipeline
을 이용하게되면 위에서 가져온 정보내에서 다시 한번 aggregation을 진행하게 된다. 위의 코드는 홍길동
이 답한 question_id
들의 통계를 반환한다.
s.aggs.bucket('user history', a)
이는 위에서 실행한 모든 통계에 대한 정보를 user history
라는 key로 감싸서 값들을 return해달라는 의미다.
그럼 반환하는 값은 아래와 같은 형식이다.
{
'key': '홍길동',
'doc_count': 1275,
'uname2uid': {
'doc_count_error_upper_bound': 0,
'sum_other_doc_count': 0,
'buckets': [
{'key': 1, 'doc_count': 32},
{'key': 2, 'doc_count': 13},
]
}
}
Conculusion
필자가 회사에 적용하면서 배운점들을 작성해봤다. 만약 조금 더 난이도 있는 작업들을 하게된다면 관련해서 또 작성하도록 하겠다. 만약 도움이 되었다면 널리 퍼뜨려주시길!