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

时间:2017-10-24 20:57:46

标签: elasticsearch

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

{
    "mappings": {
        "article": {
            "properties": {
                "title": { "type": "string" },
                "tags": {
                    "type": "keyword"
                },
        }
    }
}

看起来像这样的数据:

{ "title": "Something about Dogs", "tags": ["articles", "dogs"] },
{ "title": "Something about Cats", "tags": ["articles", "cats"] },
{ "title": "Something about Dog Food", "tags": ["articles", "dogs", "dogfood"] }

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

要做的事情是对tags字段进行排序,以便首先返回最相关的标记,而不会影响文档本身的排序顺序。所以我希望得到这样的结果:

{ "title": "Something about Dog Food", "tags": ["dogs", "dogfood", "articles"] }

而不是我现在得到的东西:

{ "title": "Something about Dog Food", "tags": ["articles", "dogs", "dogfood"] }

sortfunction score上的文档不适用于我的案例。任何帮助赞赏。谢谢!

2 个答案:

答案 0 :(得分:5)

您无法对文件_source(您的标签数组)进行排序,因为其匹配"能力。一种方法是使用嵌套字段和inner_hits,允许您对匹配的嵌套字段进行排序。

我的建议是在tags字段中转换nested(我只是简单地选择了keyword,但你也可以拥有text和你的分析器选择):

PUT test
{
  "mappings": {
    "article": {
      "properties": {
        "title": {
          "type": "string"
        },
        "tags": {
          "type": "nested",
          "properties": {
            "value": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

并使用这种查询:

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"
              }
            }
          }
        }
      ]
    }
  }
}

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

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

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

答案 1 :(得分: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"
                }
            }
        }
    }
}

这将返回类似这样的内容,因为您可以看到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"
          ]
        }
      }
    ]
  }
}