《相关性搜索:利用solr与ElasticSearch创建智能应用》的实践

可能会持续更新

目标:根据书中的方法,完善搜索,并尽力提高搜索相关性。

使用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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值