[ElasticSearch] 검색엔진 만들기 3 – Python 검색 모듈 만들기
IMS 데이터는 앞서 Elasticsearch에 인덱싱 해두었고 이를 검색하는 모듈을 Python API 를 이용해서 만들어본다. Python Flask로 만들면 웹서비스를 직접 할 수도 있고 다른 프레임워크에 이식하기 위한 HTTP API 를 만들어내기도 간편한 것 같다.
쿼리 템플릿
먼저 검색에 사용할 쿼리를 만들어야하는데 kibana dev tools 에서 SQL 툴처럼 쿼리를 만들어볼 수 있다. IMS 검색엔진에서는 다음과 같은 쿼리를 사용한다.
full-text 검색이 필요한 Issue Details
, Subject
, Action Log
필드를 중심으로 검색이 이뤄지도록 할 것이므로, 세 필드에 match_phrase
검색 조건을 두고 Product
, Category
필드에는 필터 조건을 설정한다. _source
에는 검색 결과 화면에서 보여줄 요약 정보에 필요한 정보들을 모두 선택한다.
vi body.json
{
"_source": [
"Issue Number",
"Subject",
"Issue Details",
"Reporter",
"Customer",
"Project",
"Registered date",
"Product",
"Version",
"Category",
"Module"
],
"size":10,
"from":0,
"query": {
"bool": {
"must": [
{
"bool": {
"should": [
{
"multi_match": {
"query": "",
"fields": [
"Issue Details",
"Subject",
"Action Log"
],
"operator": "and"
}
},
{
"match_phrase":{
"Issue Details": {
"query":"",
"boost": 10
}
}
},
{
"match_phrase":{
"Action Log": {
"query":"",
"boost": 10
}
}
},
{
"match_phrase":{
"Subject": {
"query":"",
"boost": 10
}
}
}
]
}
}
],
"filter":[
{
"match": {
"Product": ""
}
},
{
"terms": {
"Category": []
}
}
]
}
}
}
이슈 검색 함수 issue
위에서 만들어놓은 json 쿼리 템플릿을 기반으로, 검색 키워드 s
를 대입해 elasticsearch(localhost:9200
) 클러스터에 결과를 조회하는 함수를 작성. 제품명, 카테고리 그리고 정렬 방식과 같은 옵션 설정을 위한 인자도 설정한다.
vi search.py
from elasticsearch import Elasticsearch
import json
def issue(s,page=1,product=None,op=None,sort=None,ctgr=None):
if product == 'All' : product = None
if page == None or "": doc_from,doc_to = 0,10
else: doc_from,doc_to = (int(page)-1)*10,(int(page))*10
es_client = Elasticsearch("localhost:9200",timeout=30)
with open('./body.json',mode='rt') as f:
body = json.load(f)
f.close()
body['query']['bool']['must'][0]['bool']['should'][0]['multi_match'].update({"query":s})
body['query']['bool']['must'][0]['bool']['should'][1]['match_phrase']['Issue Details'].update({"query":s})
body['query']['bool']['must'][0]['bool']['should'][2]['match_phrase']['Action Log'].update({"query":s})
body['query']['bool']['must'][0]['bool']['should'][3]['match_phrase']['Subject'].update({"query":s})
body.update({"from":doc_from})
if op == 'or':
body['query']['bool']['must'][0]['bool']['should'][0]['multi_match'].update({"operator":op})
if sort == 'latest':
body.update({"sort":[{"Registered date": {"order": "desc"}}]})
body['query']['bool']['filter'][0]['match'].update({"Product":product})
body['query']['bool']['filter'][1]['terms'].update({"Category":ctgr})
if product == None:
body['query']['bool']['filter'].pop(0)
response = es_client.search(index="issue-v0.1.4",body=body)
hits = response['hits']['hits']
total = response['hits']['total']['value']
return hits,total
6버전까지는 타입이라는 개념이 있었기때문에 search 메소드에서 doc_type
속성을 지정했어야했는데 7버전부터 확실히 없어졌기때문에 지정하면 에러가 발생한다. 그리고 6버전에서는 검색 결과가 ['hits']['total']
에서 바로 숫자로 반환되었는데, 7버전부터는 정보가 추가되면서 한 단계 아래로 내려가 ['hits']['total']['value']
값에서 받아볼 수 있다.