이 글은 제가 작업했던 내용을 정리하기 위해 수기 형식으로 작성 된 글입니다.
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-1(개요)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-2(데이터 수집, 스크래핑)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-3(셀레니움 최소화)
2022.12.09 - [[신.만.추]] - 신입이 만드는 추천시스템-4(한국어 전처리 및 워드임베딩)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-5(아이템 벡터화)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-6(웹서버 구축)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-7(검색성능향상)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-8(포트포워딩)
2022.12.10 - [[신.만.추]] - 신입이 만드는 추천시스템-9(WSGI)
2022.12.11 - [[신.만.추]] - 신입이 만드는 추천시스템-10(엘라스틱서치에 관하여)
2022.12.11 - [[신.만.추]] - 신입이 만드는 추천시스템-11(인덱스 매핑)
2022.12.11 - [[신.만.추]] - 신입이 만드는 추천시스템-12(데이터 bulk)
2022.12.12 - [[신.만.추]] - 신입이 만드는 추천시스템-13(검색쿼리와 점수산정식)
- 엘라스틱서치에 관하여
- 인덱스 매핑에 관하여
- 엘라스틱서치에 데이터 Bulk
- 검색쿼리와 점수산정식
- 필터 적용
저번에 작성했던 글에서 검색쿼리와 점수산정식에 대한 내용을 다뤘다. 이번 글에서는 저번 글에서 빼먹고 지나갔던 필터 부분을 적어보려한다.
5. 필터 적용
엘라스틱서치에서 필터는 판다스 데이터프레임에서 조건을 정해주는 것과 동일하게 작동한다.
다만 조건을 정해주는 부분이 생각보다 까다로워 적절한 필터를 만드는데 생각보다 시간이 좀 걸렸다.
이전 filter부분의 쿼리는 아래와 같았다.
index = 'test'
body = {
"_source": ["channelLogo", "channelName", "subscribers","simTag", "videoUrl"],
"sort" : ["_score"],
"query": {
"script_score": {
"query": {
"bool": {
"must": [{"multi_match": {"query": search_key,"fields": ["title","moreTtext","simTag"]}}] ,
"should": [{"multi_match": {"query": search_key,"fields": ["title","moreText","simTag"],"type": "phrase","slop": 3}}],
"filter": el_filter,
"must_not": filter_seq_list
}
},
"script": {
"source": "_score*0.8 + cosineSimilarity(params.queryVector, 'youtuberVector')*100",
"params": {"queryVector": query_vector}
}
}
}
}
result = es.search(index=index, body=body, size=200)
filter의 시작은 아래와 같다.
el_filter = []
must와 같이 리스트를 값으로 가지기 때문에, 여러 필터를 적용 할 수 있다.
필터의 경우 다양하게 활용 할 수 있지만, 이전 글에서 사용했던 것은 구독자 수 하나뿐이었다.
실제 서비스에서는 구독자수, 조회수, 카테고리 등 다양하지만 이 글에서는 구독자 구간만 다룰 예정이다.
구독자 구간에 따른 필터 설정은 아래와 같다.
min_range = keyword_dict["min_range"]
max_range = keyword_dict["max_range"]
if max_range !=0:
sub_range = {"range": {"subscribers": {"gte": str(min_range),"lte": str(max_range)}}}
el_filter.append(sub_range)
else:
sub_range = {"range": {"subscribers": {"gte": str(min_range)}}}
el_filter.append(sub_range)
이렇게 만들어주면 리스트 안의 내용으로 필터가 적용된다.
사실 이렇게만 만들면 필터가 복잡하지는 않다.
다만, range의 범위가 다른필드에 각각 다른 값으로 동시 적용되고, 이 조건이 여러 개가 있어 일치하는 내용들만을 필터링 하기 위해서는 복잡해지게 된다.
예를들어 구독자 구간 5000명에서 10000명이면서 영상 업로드 일이 3일 이내인 크리에이터 또는 구독자 구간 100000명에서 150000명이면서 영상 업로드 일이 15일 이내인 크리에이터를 검색해본다고 하자.
위와 같은 조건으로 filter를 구성하려면 el_filter 안에 bool과 should, match의 파티가 벌어진다.
살짝 적어보자면 아래와 같이 만들 수 있다.
el_filter = [{"bool": {"should": []}}]
condition = [[5000,10000,3],[100000,150000,15]]
for i in range(len(condition)):
tmp_filter = {"bool": {"must": []}}
sub_range = {"range": {"subscribers": {"gte": str(condition[i][0]),"lte": str(str(condition[i][1])}}}
avg_hits_range = {"range": {"timeDiff": {"lte": str(condition[i][2])}}}
tmp_filter["bool"]["must"]=[sub_range,avg_hits_range]
el_filter[0]["bool"]["should"].append(tmp_filter)
el_filter
[
{"bool":
{"should":
[
{"bool":
{"must":
[
{"range": {"subscribers": {"gte": str(condition[0][0]),"lte": str(condition[0][1])}}},
{"range": {"timeDiff": {"lte": str(condition[0][2])}}}
]
}
},
{"bool":
{"must":
[
{"range": {"subscribers": {"gte": str(condition[1][0]),"lte": str(condition[1][1])}}},
{"range": {"timeDiff": {"lte": str(condition[1][2])}}}
]
}
}
]
}
}
]
bool과 should, must의 파티가 벌어졌다.
하나씩 뜯어보면 이해하기가 편한데 이걸 직접 생각해서 짜려고 해서 까다로웠다.
구글서치로 어느정도 도움을 받아가면서 하나하나 수정하면서 짰었다.
매치 되는 부분을 하나씩 뜯어보자. 위의 조건에서 필터는 아래와 같이 나눠볼 수 있다.
{구독자 구간 5000명에서 10000명이면서 영상 업로드 일이 3일 이내인 크리에이터 }
또는
{구독자 구간 100000명에서 150000명이면서 영상 업로드 일이 15일 이내인 크리에이터}
커다란 조건 두개가 또는(or)으로 연결되어 있다. 따라서 bool의 should를 사용했다.
{ “bool” :
{”should” :
[
{구독자 구간 5000명에서 10000명이면서 영상 업로드 일이 3일 이내인 크리에이터 },
{구독자 구간 100000명에서 150000명이면서 영상 업로드 일이 15일 이내인 크리에이터}
]
}
}
그리고 안의 조건을 살펴보자. 안의 조건은 그리고(and)로 연결되어있다. 이 부분 역시 bool의 must를 사용하면 된다.
{ "bool" :
{"should" :
[
{"bool":
{"must":
[
{"range": {"subscribers": {"gte": str(condition[0][0]),"lte": str(condition[0][1])}}},
{"range": {"timeDiff": {"lte": str(condition[0][2])}}}
]
}
},
{"bool":
{"must":
[
{"range": {"subscribers": {"gte": str(condition[1][0]),"lte": str(condition[1][1])}}},
{"range": {"timeDiff": {"lte": str(condition[1][2])}}}
]
}
},
]
}
}
이렇게 filter안의 bool을 사용하여 다양한 조건들을 추가 할 수 있게 되었다.
참조링크
https://www.elastic.co/guide/en/elasticsearch/guide/master/combining-filters.html
'[신.만.추]' 카테고리의 다른 글
신입이 만드는 추천시스템-13(검색쿼리와 점수산정식) (0) | 2022.12.12 |
---|---|
신입이 만드는 추천시스템-12(데이터 bulk) (0) | 2022.12.11 |
신입이 만드는 추천시스템-11(인덱스 매핑) (0) | 2022.12.11 |
신입이 만드는 추천시스템-10(엘라스틱서치에 관하여) (0) | 2022.12.11 |
엘라스틱서치 설치 및 환경구성 (0) | 2022.12.10 |