我有一个看起来像这样的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"] }
sort和function score上的文档不适用于我的案例。任何帮助赞赏。谢谢!
答案 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"
]
}
}
]
}
}