我在ElasticSearch中实现了一个自动完成索引,并遇到了排序/评分问题。假设我在索引中有以下字符串:
apple banana coconut donut
apple banana donut durian
apple donut coconut durian
donut banana coconut durian
当我搜索“甜甜圈”时,我希望结果按照这样的术语位置排序:
donut banana coconut durian
apple donut coconut durian
apple banana donut durian
apple banana coconut donut
我无法弄清楚如何实现这一目标。术语位置没有考虑到默认评分逻辑中,我找不到在那里获得它的方法。看起来像一个简单的问题,但其他人之前必须遇到这个问题。还有人想出来吗?
谢谢!
答案 0 :(得分:5)
您可以进行自定义排序,如下所示:
{
"query": {
"match": {
"content": "donut"
}
},
"sort": {
"_script": {
"script": "termInfo=_index['content'].get('donut',_OFFSETS);for(pos in termInfo){return _score+pos.startOffset};",
"type": "number",
"order": "asc"
}
}
}
在那里,我刚刚返回startOffset
。如果您需要其他东西,请使用这些值和原始评分,并根据您的需求提供舒适的价值。
或者你可以这样做:
{
"query": {
"function_score": {
"query": {
"match": {
"content": "donut"
}
},
"script_score": {
"script": "termInfo=_index['content'].get('donut',_OFFSETS);for(pos in termInfo){return pos.startOffset};"
},
"boost_mode": "replace"
}
},
"sort": [
{
"_score": "asc"
}
]
}
在任何一种情况下,您需要在映射中为该特定字段提供此信息:
"content": {
"type": "string",
"index_options": "offsets"
}
意味着index_options
需要设置为offsets
。 Here有关此内容的详细信息。
答案 1 :(得分:0)
以下是基于Andrei的答案我最终得到的解决方案,并扩展为支持多个搜索字词和基于结果中第一个字词长度的额外评分:
首先,定义以下自定义分析器(它将整个字符串保留为单个标记并将其缩小):
"raw_analyzer": {
"type": "custom",
"filter": [
"lowercase"
],
"tokenizer": "keyword"
}
其次,定义你的搜索字段映射(我的名字"名称"):
"name": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index_analyzer": "raw_analyzer",
"search_analyzer": "standard"
}
}
},
"_nameFirstWordLength": {
"type": "long"
}
第三,填充索引时,使用以下逻辑(我在C#中)填充:
_nameFirstWordLength = fi.Name.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries)[0].Length
最后,按以下方式进行搜索:
{
"query":{
"bool":{
"must":{
"match_phrase_prefix":{
"name":{
"query":"apple"
}
}
},
"should":{
"function_score":{
"query":{
"query_string":{
"fields":[
"name.raw"
],
"query":"apple*"
}
},
"script_score":{
"script":"100/doc['_nameFirstWordLength'].value"
},
"boost_mode":"replace"
}
}
}
}
}
我使用match_phrase_prefix以支持部分匹配,例如" ap"匹配" apple"。 bool必须/应该使用针对name.raw的第二个query_string查询为名称以搜索词之一开头的结果提供更高的分数(在我的代码中我预先处理搜索字符串,仅用于第二个查询,在每个单词后添加" *")。最后,将第二个查询包装在使用_nameFirstWordLength值的function_score脚本中会导致第二个查询的得分结果进一步按其第一个单词的长度排序(导致Apple在Applebee'之前显示,例子)。