【问题标题】:Elasticsearch documents that only have terms intersecting a list of terms but no other terms仅包含与术语列表相交但没有其他术语的术语的 Elasticsearch 文档
【发布时间】:2015-09-24 08:58:19
【问题描述】:

我有包含标签列表的文档:

{ “字段”:{ “标签”: [ “富”, “酒吧”, “巴兹” ], “姓名”: [ 《文件一》 ], "description" : "一个很好的第一个文件", “身份证”:1 } }, { “字段”:{ “标签”: [ “富”, “狗” ], “姓名”: [ 《文件二》 ], "description" : "一个很好的第二个文档", “身份证”:2 } }

我有一个术语列表:

[“foo”、“bar”、“qux”、“zip”、“baz”]

我想要一个查询,该查询将返回在术语列表中具有标签但没有其他术语的文档。

因此,鉴于上面的列表,查询将返回Document One,但 Document Two(因为它的术语dog 不在术语列表中。

我尝试使用not terms 过滤器进行查询,如下所示:

发布 /documents/_search?size=1000 { “领域”:[ “ID”, “姓名”, “标签” ], “筛选”: { “不是”: { “筛选” : { “布尔”:{ “禁止”: { “条款”:{ “标签”: [ “富”, “酒吧”, "qux", “压缩”, “巴兹” ] } } } } } } }

但这没有用。

如果给定一个术语列表,我如何创建一个查询,该查询将匹配仅包含列表中的术语而不包含其他术语的文档?换句话说,所有文档都应该包含一个标签列表,这些标签是所提供术语列表的子集。

【问题讨论】:

    标签: elasticsearch


    【解决方案1】:

    您可以编写过滤器来检查数组术语是否具有文档中标签数组的所有值。我建议您制作一个单独的 groovy 文件或纯 javascript 文件,将其放在 config/scripts/folderToYourScript 中,并在filter: { script : {script_file: file } } 中的查询中使用它

    在脚本文件中你可以使用循环来检查需求

    【讨论】:

    • Rohit,我试过了,它奏效了。对于我的用例来说,它也相当快。我稍后会发布代码。
    • 感谢您恢复... 请发布代码以帮助他人。我本可以自己发布代码,但我没有具体的问题案例。
    【解决方案2】:

    我听从了 Rohit 的建议,实现了Elasticsearch script filter。你需要configure your Elasticsearch server to allow dynamic (inline) Groovy scripts

    这是 Groovy 脚本过滤器的代码:

    def label_map = labels.collectEntries { entry -> [entry, 1] };
    def count = 0;
    
    for (def label : doc['label'].values) {
        if (!label_map.containsKey(label)) {
            return 0
        } else {
            count += 1
        }
    };
    
    return count
    

    要在 Elasticsearch 查询中使用它,您要么需要转义所有换行符,要么将脚本放在一行中,如下所示:

    def label_map = labels.collectEntries { entry -> [entry, 1] }; def count = 0; for (def label : doc['label'].values) { if (!label_map.containsKey(label)) { return 0 } else { count += 1 } }; return count
    

    这是一个与我所做的非常相似的 Elasticsearch 查询,包括脚本过滤器:

    POST /documents/_search
    {
       "fields": [
          "id",
          "name",
          "label",
          "description"
       ],
       "query": {
          "function_score": {
             "query": {
                "filtered": {
                   "query": {
                      "bool": {
                         "minimum_should_match": 1,
                         "should" : {
                            "term" : {
                               "description" : "fine" 
                            }
                         }
                     }
                   },
                   "filter": {
                      "script": {
                         "script": "def label_map = labels.collectEntries { entry -> [entry, 1] }; def count = 0; for (def label : doc['label'].values) { if (!label_map.containsKey(label)) { return 0 } else { count += 1 } }; return count",
                         "lang": "groovy",
                         "params": {
                            "labels": [
                               "foo", 
                               "bar", 
                               "qux", 
                               "zip", 
                               "baz"
                            ]
                         }
                      }
                   }
                }
             },
             "functions": [
                {
                   "filter": {
                      "query": {
                         "match": {
                            "label": "qux"
                         }
                      }
                   },
                   "boost_factor": 25
                }
             ],
             "score_mode": "multiply"
          }
       },
       "size": 10
    }
    

    我的实际查询需要将脚本过滤器与函数分数查询结合起来,这很难弄清楚该怎么做,所以我将其作为示例包含在此处。

    这样做是使用脚本过滤器来选择其标签是查询中传递的标签子集的文档。对于我的用例(数千个文档,而不是数百万个文档),这工作得非常快 - 几十毫秒。

    第一次使用脚本,耗时较长(约1000ms),可能是编译和缓存的原因。但后来的调用速度要快 100 倍。

    几点说明:

    • 我使用Sense console Chrome plugin 来调试 Elasticsearch 查询。比在命令行上使用 curl 好多了! (请注意,Sense 现在是 Marvel 的一部分,因此您也可以在那里获取它。
    • 为了实现 Groovy 脚本,我首先在笔记本电脑上安装了Groovy language,并编写了一些单元测试,并实现了脚本。在确定脚本正常运行后,我将其格式化为适合一行并将其放入 Sense。

    【讨论】:

    • 在将其传递给脚本之前,使用 term/terms 预过滤文档可能是有意义的 - 这样脚本将处理更少的文档
    猜你喜欢
    • 2020-03-19
    • 2017-11-19
    • 2014-10-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多