按相关性对ElasticSearch文档中的关键字字段数组进行排序

Jos*_*tey 5 elasticsearch

我有一个看起来像这样的ElasticSearch索引:

{
    "mappings": {
        "article": {
            "properties": {
                "title": { "type": "string" },
                "tags": {
                    "type": "keyword"
                },
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

和看起来像这样的数据:

{ "title": "Something about Dogs", "tags": ["articles", "dogs"] },
{ "title": "Something about Cats", "tags": ["articles", "cats"] },
{ "title": "Something about Dog Food", "tags": ["articles", "dogs", "dogfood"] }
Run Code Online (Sandbox Code Playgroud)

如果我搜索dog,我会得到第一个和第三个文件,正如我所期望的那样.而且我可以按照自己喜欢的方式对搜索文档进行加权(实际上,我正在使用一个function_score查询来衡量一堆与此问题无关的字段).

什么我喜欢做的排序是tags场让最相关的标签首先返回,而不会影响文件本身的排序顺序.所以我希望得到这样的结果:

{ "title": "Something about Dog Food", "tags": ["dogs", "dogfood", "articles"] }
Run Code Online (Sandbox Code Playgroud)

而不是我现在得到的:

{ "title": "Something about Dog Food", "tags": ["articles", "dogs", "dogfood"] }
Run Code Online (Sandbox Code Playgroud)

关于排序功能评分的文档不包括我的案例.任何帮助赞赏.谢谢!

And*_*fan 5

_source鉴于其"匹配"功能,您无法对文档(您的标记数组)进行排序.一种方法是使用嵌套字段,并inner_hits允许您对匹配的嵌套字段进行排序.

我的建议是tags在一个nested领域进行改造(我选择keyword那里只是简单,但你也可以text和你选择的分析仪):

PUT test
{
  "mappings": {
    "article": {
      "properties": {
        "title": {
          "type": "string"
        },
        "tags": {
          "type": "nested",
          "properties": {
            "value": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

并使用这种查询:

GET test/_search
{
  "_source": {
    "exclude": "tags"
  },
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "dogs"
          }
        },
        {
          "nested": {
            "path": "tags",
            "query": {
              "bool": {
                "should": [
                  {
                    "match_all": {}
                  },
                  {
                    "match": {
                      "tags.value": "dogs"
                    }
                  }
                ]
              }
            },
            "inner_hits": {
              "sort": {
                "_score": "desc"
              }
            }
          }
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试匹配标记的嵌套字段值,则尝试在标题上匹配.然后,使用inner_hits排序,您可以实际根据内部评分对嵌套值进行排序.

@ Val的建议非常好,但只要你的"相关标签"你只需要一个简单的文本匹配作为子串(i1.indexOf(params.search))就可以了.他的解决方案的最大优势是您不必更改映射.

我的解决方案的最大优势是您实际上使用Elasticsearch真正的搜索功能来确定"相关"标签.但缺点是你需要nested字段而不是常规的简单keyword.


Val*_*Val 2

您从搜索电话中获得的是源文档。响应中的文档以与索引它们时完全相同的形式返回,这意味着如果您索引["articles", "dogs", "dogfood"],您将始终以未更改的形式获得该数组。

解决这个问题的一种方法是声明一个script_field应用一个小脚本来对数组进行排序并返回该排序的结果。

该脚本所做的只是将包含搜索词的词移动到列表的前面

{
    "_source": ["title"],
    "query" : {
        "match_all": {}
    },
    "script_fields" : {
        "sorted_tags" : {
            "script" : {
                "lang": "painless",
                "source": "return params._source.tags.stream().sorted((i1, i2) -> i1.indexOf(params.search) > -1 ? -1 : 1).collect(Collectors.toList())",
                "params" : {
                    "search": "dog"
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将返回类似这样的内容,因为您可以看到该sorted_tags数组包含您期望的术语。

{
  "took": 18,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "tests",
        "_type": "article",
        "_id": "1",
        "_score": 1,
        "_source": {
          "title": "Something about Dog Food"
        },
        "fields": {
          "sorted_tags": [
            "dogfood",
            "dogs",
            "articles"
          ]
        }
      }
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)