当前位置: 首页 > 知识库问答 >
问题:

Elasticsearch文档中只有与术语列表相交的术语,而没有其他术语

拓拔骁
2023-03-14

我有一个带有标签列表的文档:


    {
       "fields": {
          "label": [
               "foo",
               "bar",
               "baz"
          ],
          "name": [
             "Document One"
          ],
          "description" : "A fine first document",
          "id" : 1
       }
    },
    {
       "fields": {
          "label": [
               "foo",
               "dog"
          ],
          "name": [
             "Document Two"
          ],
          "description" : "A fine second document",
          "id" : 2
       }
    }

我有一个术语清单:


    [ "foo", "bar", "qux", "zip", "baz"]

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

因此,给定上面的列表,查询将返回文档一,但不返回文档二(因为它有一个术语dog不在术语列表中)。

我尝试使用术语过滤器执行查询,如下所示:


    POST /documents/_search?size=1000
    {
       "fields": [
          "id",
          "name",
          "label"
       ],
       "filter": {
           "not": {
               "filter" : {
                   "bool" : {
                       "must_not": {
                          "terms": {
                             "label": [
                                "foo",
                                "bar",
                                "qux",
                                "zip",
                                "baz"
                             ]
                          }
                       }
                   }
               }
           }
       }
    }

但那没用。

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

共有1个答案

郁明诚
2023-03-14

我遵循了Rohit的建议,实现了一个Elasticsearch脚本过滤器。您需要配置Elasticsearch服务器以允许动态(内联)Groovy脚本。

下面是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
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
}

我的实际查询需要将脚本过滤器与函数得分查询相结合,这很难理解如何实现,所以我在这里将其作为一个示例。

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

第一次使用脚本需要很长时间(大约1000 ms),这可能是由于编译和缓存的原因。但是以后的调用速度要快100倍。

几个注意事项:

  • 我使用Sense console Chrome插件来调试Elasticsearch查询。比在命令行上使用curl好多了!(请注意,Sense现在是Marvel的一部分,所以您也可以在那里获得它。
  • 为了实现Groovy脚本,我首先在笔记本电脑上安装了Groovy语言,并编写了一些单元测试,实现了脚本。一旦我确定脚本正常工作,我就对它进行格式化,使其适合一行,并使其具有意义。
 类似资料:
  • 问题内容: 我有带有标签列表的文档: 我有一个术语列表: 我想要一个查询,该查询将返回在术语列表中具有标签的文档-但没有其他术语。 因此,鉴于上述列表,查询将返回,但 不会 返回(因为查询中的术语不在术语列表中。) 我尝试使用 过滤器进行查询,如下所示: 但这没有用。 在给定的术语列表的情况下,如何创建一个查询,该查询将匹配仅包含列表中的术语而没有其他术语的文档?换句话说,所有文档都应包含标签列表

  • 另外,类型中的所有文档都存储了上面提到的community的值。感谢帮助。

  • 问题内容: 我是相当新的elasticsearch,使用6.5版。我的数据库包含网站页面及其内容,如下所示: 我已经能够执行一个简单的查询,该查询返回所有内容中包含“汽车”一词的文档(使用Python): 结果看起来像这样: “ _id”指的是一个域,所以我基本上回来了: abc.com def.com jkl.com 但我现在想知道如何往往是搜索关键词(“汽车”)出现 在 每个文档,如: abc

  • 请注意,本术语表中的定义简短而简单,旨在传达核心思想,而不是术语的完整细微之处。 有关更多详细信息,请参阅正文中的参考资料。 [TOC] 异步(asynchronous) 不等待某些事情完成(例如,将数据发送到网络中的另一个节点),并且不会假设要花多长时间。请参阅第153页上的“同步与异步复制”,第284页上的“同步与异步网络”,以及第306页上的“系统模型与现实”。 原子(atomic) 1.在

  • 下面是一系列的 Ansible 文档中的术语。 在主界面查看文档,了解这些术语的上下文环境,不过这里是一个好的资源测试你对 Ansible 组件的了解和更好的理解他们是如何组合起来的。你可能在想回顾 Ansible 知识的时候看看这里,在邮件中出现一些术语的时候参考一下这个文档。 动作(Action) 一个动作属于一个任务的一部分,指定运行的模块,然后传递参数给此模块。每个任务之一一个动作,但是它

  • Spring Batch术语表 Batch 批 An accumulation of business transactions over time. 随着时间累积而形成的一批业务事务。 Batch Application Style (批处理程序风格) Term used to designate batch as an application style in its own right si