是时候用 ES 拯救发际线啦

如何将以关键字为开头的结果排在最前?

Elasticsearch | 作者 zhangdi | 发布于2021年02月23日 | 阅读数:1600

案例:
现在有两条文档,id=1,id=2:
{
"id": 1,
"name": "营口中北工程有限公司"
}
{
"id": 2,
"name": "中北工程设计咨询有限公司"
}
使用ik中文分词,ik_max_word分词后:
id1的name分词结果为:

{
    "tokens": [
        {
            "token": "营口",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 0
        },
        {
            "token": "口中",
            "start_offset": 1,
            "end_offset": 3,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "中北",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "工程",
            "start_offset": 4,
            "end_offset": 6,
            "type": "CN_WORD",
            "position": 3
        },
        {
            "token": "有限公司",
            "start_offset": 6,
            "end_offset": 10,
            "type": "CN_WORD",
            "position": 4
        },
        {
            "token": "有限",
            "start_offset": 6,
            "end_offset": 8,
            "type": "CN_WORD",
            "position": 5
        },
        {
            "token": "公司",
            "start_offset": 8,
            "end_offset": 10,
            "type": "CN_WORD",
            "position": 6
        }
    ]
}
id=2的name分词结果为:

{
    "tokens": [
        {
            "token": "中北",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 0
        },
        {
            "token": "工程设计",
            "start_offset": 2,
            "end_offset": 6,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "工程",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "设计",
            "start_offset": 4,
            "end_offset": 6,
            "type": "CN_WORD",
            "position": 3
        },
        {
            "token": "咨询",
            "start_offset": 6,
            "end_offset": 8,
            "type": "CN_WORD",
            "position": 4
        },
        {
            "token": "有限公司",
            "start_offset": 8,
            "end_offset": 12,
            "type": "CN_WORD",
            "position": 5
        },
        {
            "token": "有限",
            "start_offset": 8,
            "end_offset": 10,
            "type": "CN_WORD",
            "position": 6
        },
        {
            "token": "公司",
            "start_offset": 10,
            "end_offset": 12,
            "type": "CN_WORD",
            "position": 7
        }
    ]
}
现在用户搜索 “中北工程”,结果id1的评分比id2高,期望达到的结果是 以“中北工程”开头的排在最前。
尝试关闭词频统计和归一化都没啥效果,或者是我没用好。麻烦各位帮帮我,或者提供点思路。谢谢了!!难不成要通过机器人训练分词还是相关性?
已邀请:

God_lockin

赞同来自:

加上哥prefix或者match_phrase之类的should条件试试

zhangdi - d_vv

赞同来自:

版本是ES7.6

Morry

赞同来自:

prefix是针对term的prefix,所以要加个name.keyword子字段,然后再should prefix name.keyword。
 
另外,如果前缀加分是你业务场景下的固定逻辑,那应该直接在index前把前缀抽出来,es里加个额外字段 name_prefix,查询时直接加权,name_prefix^10,这样性能最好,而且这个字段是在外部直接抽出来的,不依赖于ik分词,流程上可控性更高

zhangdi - d_vv

赞同来自:

查询请求参数:
{
"query": {
"bool": {
"must": [
{
"match": {
"name": {
"query": "中北工程",
"boost": 0.1
}
}
}
],
"should": [
{
"prefix": {
"sname.keyword": {
"value": "中北工程",
"boost": 10
}
}
}
]
}
}
}
结果只返回了id2,而且should的分数没有被计算上。
 
setting和mapping
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 20,
"token_chars": [
"letter",
"digit"
]
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text",
"analyzer": "my_analyzer"
},
"sname": {
"type": "keyword"
}
}
}
}

zhangdi - d_vv

赞同来自:

是因为bm25得分不同导致的,
营口中北工程有限公司 和 中北工程设计咨询有限公司

tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from
只因为dl(长度)分数分别为 7 和 8 ,所以怎么计算怎么分词都是id1排名靠前。。。很头大
 

CurryQin

赞同来自:

可以加一个关键字keyword,如果keyword也包含搜索的关键字,该文档分数提高就好了

yanglz3

赞同来自:

试试重评分

zhangdi - d_vv

赞同来自:

不知道最佳方案是什么了。用function script可以满足需求,但是性能就不如意了。
其它的词,如:陕西公路XXX 也会被排在 陕西XXX公路XXXX 后面,真是奇怪

zhangdi - d_vv

赞同来自:

现在能想到的方法:
1、手工打标签,但是工作量太难,也很难有用户给反馈哪个搜不到或者期望达不到。
2、function scrore,但是性能不理想,延迟直接升高10倍,60ms+ 直接变 700ms+
3、昨天妄图在源码里找到查询值的地方然后做字符串匹配,最后还是自己天真忘了这都是通过索引出来的各种压缩的二进制值和分词提高的查询性能,所以不太可能在打分前截获到完整的文档的值

weicai - 90后

赞同来自:

可以修改计算文档的分数的方式,下面是一个示例,利用 Math.pow(position, -1) 这个方程计算得分。
这里只是简单的使用 关键词 的位置的倒数计算分数,您可以根据需要修改。
这里无法直接添加连接,你可以参考 csdn 的 wei_bo_cai的专栏,或是直接查阅官方文档
 
POST weibo/_search
{
"query": {
"function_score": {
"query": {
"match": {
"text": {
"query": "中北工程",
"operator": "and"
}
}
},
"script_score": {
"script": {
"source": "Math.pow(params['_source']['text'].indexOf('中北工程')+1, -1)"
}
},
"boost_mode": "replace"
}
}
}

要回复问题请先登录注册