可能会持续更新
目标:根据书中的方法,完善搜索,并尽力提高搜索相关性。
使用ElasticSearch版本:6.8.x
使用elasticsearch-py,是官方低层级封装py包
github地址:https://github.com/elastic/elasticsearch-py
文档地址:https://elasticsearch-py.readthedocs.io/en/6.8.2/index.html
使用数据:Enterprise-Registration-Data,需要数据可以私聊我
中文分词器:https://github.com/medcl/elasticsearch-analysis-ik
三个步骤:收集信息和需求,设计搜索应用,部署监控改进
三个步骤:找出用户要问的问题,找出商业需求,找出必要及可用的信息
设计几个角色代表来代表不同的用户类型,找出不同类型用户的典型需求,设计时避免行为重叠。
理解业务需求,可能有一些公司像排在前排
需求结论:
两个目标:1功能,2体验
目前只能完成功能
对字段定义需要通过迭代的方式完成。
以分类的方式来思考字段,可以简化迭代过程。
书中把示例的字段分为:位置,偏好,内容和业务。这个实践只需要考虑:位置,时间和内容。
字段名 | 信息 | 分析 |
---|---|---|
name | 公司名,全称 | ik_max_word分词 |
code | 代码 | 不分词 |
registrationDay | 注册时间 | date格式 |
legalRepresentative | 法人代表 | ik_max_word分词 |
businessScope | 经营范围 | ik_max_word分词 |
province | 省份 | 不分词 |
city | 城市 | 不分词 |
address | 地址 | ik_max_word分词 |
省份和城市,keyword类型不分词做直接匹配。
用term配合filter列表完成。
注册时间的筛选,直接导入日期格式时es识别类型为date做匹配。
用range范围匹配,gte大于等于,lte小于等于。
分为2个查询语句:
基础查询:用ik_max_word分词器匹配字段,并添加简单权重。
放大查询:使用cross_fields模式加and操作,把目标字段作为同一个字段进行匹配,达到放大匹配到内容的分数。同时提高用户在搜索词不明确时匹配的概率(例如在搜索词分词后不同的词匹配到不同的目标字段,但单个匹配分数低,无法在有效在基础匹配时展现出来足够的分数。视为同一字段匹配后则可以获得更高的分数,放大了匹配的相关性。
bool中查询语句为相加,及两个查询语句分数的和。使用相加更容易控制放大查询对整体分数的影响
def search_company(self, keyword=None, start_date=None, end_date=None, province=None, city=None, query_filed=None):
"""
:param keyword: 搜索关键字
- 搜索字段 :"name^3", "legalRepresentative^3", "businessScope^2", "address"
- 名字和法人 权重最高,设为3倍权重
- 经营范围 其次,2倍权重
- 地址 1倍权重
- keyword_query:用ik_max_word分词器,匹配出分数相加最高的字段
- keyword_query_cross_fields:把目标字段作为同一个字段进行匹配
:param start_date: 注册时间筛选开始时间,大于等于
:param end_date: 注册时间筛选结束时间,小于等于
:param province: 省份匹配
:param city: 城市匹配
:param query_filed: 搜索后返回字段
:return:result_list:搜索结果列表
"""
if query_filed is None:
query_filed = []
# 筛选列表
filter_list = list()
if start_date and end_date:
date_range = {"range": {"registrationDay": {"gte": start_date, "lte": end_date}}}
filter_list.append(date_range)
if province:
province_term = {"term": {"province": province}}
filter_list.append(province_term)
if city:
city_term = {"term": {"city": city}}
filter_list.append(city_term)
# 关键字搜索
if not keyword:
keyword_query = {"match_all": {}}
keyword_query_cross_fields = keyword_query
else:
keyword_query = {"multi_match": {
"fields": ["name^3", "legalRepresentative^3", "businessScope^2", "address"],
"query": keyword,
"analyzer": "ik_max_word",
"type": "most_fields"
}}
keyword_query_cross_fields = deepcopy(keyword_query)
keyword_query_cross_fields["multi_match"]["minimum_should_match"] = "50%"
keyword_query_cross_fields["multi_match"]["type"] = "cross_fields"
keyword_query_cross_fields["multi_match"]["operator"] = "and"
body = {
"query": {
"bool": {
"should": [keyword_query, keyword_query_cross_fields],
"filter": filter_list,
},
}
}
response = self.es.search("company", body)
result_list = list()
for hit in response['hits']['hits']:
result_dict = {
"index": hit.get('_index', None),
"score": hit.get('_score', None)
}
for k in query_filed:
result_dict[k] = hit.get('_source').get(k, None)
result_list.append(result_dict)
result_total = response.get('hits').get('total')
return result_list, result_total
代码地址:https://github.com/wenboyu00/relevant_search_in_action