在 elasticsearch 上查找具有空字符串值的文档

Posted

技术标签:

【中文标题】在 elasticsearch 上查找具有空字符串值的文档【英文标题】:Find documents with empty string value on elasticsearch 【发布时间】:2014-08-29 05:03:56 【问题描述】:

我一直在尝试使用 elasticsearch 过滤那些正文中包含空字符串的文档。到目前为止,我没有运气。

在我继续之前,我应该提一下,我已经尝试了 许多 在 Interwebz 和 *** 上传播的“解决方案”。

所以,下面是我尝试运行的查询,然后是对应的查询:


    "query": 
        "filtered":
            "filter": 
                "bool": 
                    "must_not": [
                        
                            "missing":
                                "field":"_textContent"
                            
                        
                    ]
                
            
        
    

我还尝试了以下方法:

 
    "query": 
        "filtered":
            "filter": 
                "bool": 
                    "must_not": [
                        
                            "missing":
                                "field":"_textContent",
                                "existence":true,
                                "null_value":true
                            
                        
                    ]
                
            
        
    

还有以下内容:

   
    "query": 
        "filtered":
            "filter": 
                    "missing": "field": "_textContent"
            
        
    

以上都不起作用。当我确定存在包含空字符串字段的记录时,我得到一个空结果集。

如果有人可以为我提供任何帮助,我将非常感激。

谢谢!

【问题讨论】:

对于 ES,它总是建议提及版本,因为即使是次要版本也有很大差异。 lucene/kql 查询:yourfield.keyword:"" 有效。来自以下答案之一***.com/a/54046098/52074 【参考方案1】:

如果您使用的是默认分析器 (standard),则它无法分析它是否为空字符串。因此,您需要逐字索引该字段(未分析)。这是一个例子:

添加一个映射来索引未标记的字段,如果您还需要索引字段的标记副本,您可以使用Multi Field 类型。

PUT http://localhost:9200/test/_mapping/demo

  "demo": 
    "properties": 
      "_content": 
        "type": "string",
        "index": "not_analyzed"
      
    
  

接下来,索引几个文档。

/POST http://localhost:9200/test/demo/1/

  "_content": ""


/POST http://localhost:9200/test/demo/2

  "_content": "some content"

执行搜索:

POST http://localhost:9200/test/demo/_search

  "query": 
    "filtered": 
      "filter": 
        "term": 
          "_content": ""
        
      
    
  

返回带有空字符串的文档。


    took: 2,
    timed_out: false,
    _shards: 
        total: 5,
        successful: 5,
        failed: 0
    ,
    hits: 
        total: 1,
        max_score: 0.30685282,
        hits: [
            
                _index: test,
                _type: demo,
                _id: 1,
                _score: 0.30685282,
                _source: 
                    _content: ""
                
            
        ]
    

【讨论】:

但是我已经有很多文档已经存储在 elasticsearch 中(大约 50k)。 AFAIK,更新映射信息需要重新索引文档。是这样吗,或者此映射更新将适用于我当前的文档? 如果您更新映射,您将需要重新索引。看看重新索引插件:github.com/karussell/elasticsearch-reindex 此外,此策略要求我存储该字段的两份副本,一份已标记化,另一份作为原始副本。这个 _textContent 字段实际上来自通过 OCR 运行的 PDF 文件,因此它可以变得非常大。我认为存储两份副本可能有点太多了。 我想我现在要使用客户端解决方案。不过谢谢:) 如果字段的映射是关键字怎么办?分析了吗?【参考方案2】:

在这里找到解决方案https://github.com/elastic/elasticsearch/issues/7515 它无需重新索引即可工作。

PUT t/t/1

  "textContent": ""


PUT t/t/2

  "textContent": "foo"


GET t/t/_search

  "query": 
    "bool": 
      "must": [
        
          "exists": 
            "field": "textContent"
          
        
      ],
      "must_not": [
        
          "wildcard": 
            "textContent": "*"
          
        
      ]
    
  

【讨论】:

适用于 ES v. 5.6【参考方案3】:

即使使用默认分析器,您也可以进行这种搜索:使用script filter,它速度较慢但可以处理空字符串:

curl -XPOST 'http://localhost:9200/test/demo/_search' -d '

 "query": 
   "filtered": 
     "filter": 
       "script": 
         "script": "_source._content.length() == 0"
       
     
   
 
'

它会将带有空字符串的文档作为_content返回,而不需要特殊映射

正如@js_gandalf 所指出的,这在 ES>5.0 中已被弃用。相反,您应该使用:query->bool->filter->script as in https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html

【讨论】:

对不起...但这不起作用。 "error":"root_cause":["type":"parsing_exception","re​​ason":"没有为 [filtered] 注册 [query]","line":4,"col":14], "type":"parsing_exception","re​​ason":"no [query] 为 [filtered] 注册","line":4,"col":14,"status":400 我使用的是 elastic 5.2,我认为这会影响正在发生的事情 您好@js_gandalf,这就是 IT 的问题,有时 2 年后 api 中断 :-) 版本 0.90 已回答。我不知道 SO 的用途是什么,我应该删除我的答案吗?无论如何,感谢您注意到这一点。 @VrigileD 没关系。我在这件事上被难住了一段时间。它杀了我!所以我现在必须重新索引我的整个数据库才能让它工作?我正在使用 5.2 最新版本的弹性搜索。 我现在没有使用 ES,但我认为过滤已被弃用,现在你应该使用 query->bool->filter->script,类似于elastic.co/guide/en/elasticsearch/reference/current/… 我刚刚重新索引整个数据库。然后它起作用了!发布答案。对于 ES > 5.2【参考方案4】:

对于那些使用弹性搜索 5.2 或更高版本但仍然卡住的人。最简单的方法是使用关键字类型正确地重新索引您的数据。然后所有对空值的搜索都起作用了。像这样:

"query": 
    "term": "MY_FIELD_TO_SEARCH": ""

实际上,当我重新索引我的数据库并重新运行查询时。成功了=)

问题是我的字段是类型:文本而不是关键字。将索引更改为关键字并重新索引:

curl -X PUT https://username:password@host.io:9200/mycoolindex

curl -X PUT https://user:pass@host.io:9200/mycoolindex/_mapping/mycooltype -d '
  "properties": 
            "MY_FIELD_TO_SEARCH": 
                    "type": "keyword"
                ,
'

curl -X PUT https://username:password@host.io:9200/_reindex -d '
 "source": 
   "index": "oldindex"
 ,
 "dest": 
    "index": "mycoolindex"
 
'

我希望这可以帮助那些像我发现那些空值一样陷入困境的人。

【讨论】:

也适用于巢。需要逐字添加到查询中。【参考方案5】:

为了在您的文档中找到一个字段的空字符串,它与该字段的映射高度相关,换句话说,它的index/analyzer设置。

如果它的索引是not_analyzed,也就是说token就是一个空字符串,你可以直接用term查询来找到它,如下:

"from": 0, "size": 100, "query":"term": "name":""

否则,如果index 设置为analyzed 并且我相信大多数分析器会将空字符串视为空值所以 您可以使用过滤器查找空字符串。

"filter": "missing": "existence": true, "field": "name", "null_value": true, "query": "match_all":

这是您可以参考的 gist 脚本:https://gist.github.com/hxuanji/35b982b86b3601cb5571

顺便说一句,我检查了您提供的命令,看来您不想要空字符串文档。 而我上面的所有命令只是为了找到这些,所以只需将其放入bool 查询的must_not 部分就可以了。 我的 ES 是 1.0.1。


对于 ES 1.3.0,目前我提供的 gist 找不到空字符串。似乎已经报道了:https://github.com/elasticsearch/elasticsearch/issues/7348。让我们拭目以待吧。

不管怎样,它还提供了另一个命令来查找

"查询": “过滤”: “筛选”: “不是”: “筛选”: “范围”: “姓名”:

name 是查找空字符串的字段名称。我已经在 ES 1.3.2 上测试过了。

【讨论】:

不,实际上我想查找在该特定字段中具有空字符串的所有文档。我可能问错了。顺便说一句,在我对该字段进行全文搜索时会分析索引。 好的,如果是这样,第二个命令就可以了。(检查要点)顺便说一句,如果该字段用于全文搜索,我认为not_analyzed设置可能对您没有用. 我已经尝试过您的 gist 查询,但它不起作用,它显然不会将空字符串视为 null,这很奇怪。我想我会在客户端实现这个。我不知道这是否有意义,但在我看来 ES 缺少一个“空”过滤器......无论如何,感谢您的帮助! 嗨,我刚刚对 ES 1.3.2 进行了快速测试。我提供的要点不像你说的那样工作。虽然它适用于 ES 1.0.1,目前我在我的项目中使用它。我不确定这是否是错误。我会做更多的测试。 嗨@PauloVictor 我想你可能知道。这是github.com/elasticsearch/elasticsearch/issues/7348 报告的错误,它报告了 ES 1.3.0。但是官方提供了一些其他的命令来得到你想要的,检查我上面的编辑。【参考方案6】:

我使用的是 Elasticsearch 5.3,但在上面的一些答案中遇到了问题。

以下机构对我有用。

 
    "query": 
        "bool" : 
            "must" : 
                "script" : 
                    "script" : 
                        "inline": "doc['city'].empty",
                        "lang": "painless"
                     
                
            
        
    

注意:您可能需要为文本字段启用 fielddata,默认情况下它是禁用的。虽然我会在这样做之前阅读:https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html。

为字段启用 fielddata,例如索引“企业”上的“城市”,您需要类型名称“记录”:

PUT business/_mapping/record

    "properties": 
        "city": 
          "type": "text",
          "fielddata": true
        
      

【讨论】:

【参考方案7】:

OR 使用 lucene 查询字符串语法

q=yourfield.keyword:""

请参阅弹性搜索参考https://www.elastic.co/guide/en/elasticsearch/reference/6.5/query-dsl-query-string-query.html#query-string-syntax

【讨论】:

【参考方案8】:

如果您不想或无法重新索引,还有另一种方法。 :-)

您可以使用否定运算符和通配符来匹配任何非空字符串 *

GET /my_index/_search?q=!(fieldToLookFor:*)

【讨论】:

但它只返回此字段为空的文档。等于"query":"bool":"must_not":"exists":"field":"address"【参考方案9】:

对于嵌套字段使用:

curl -XGET "http://localhost:9200/city/_search?pretty=true" -d '
     "query" : 
         "nested" : 
             "path" : "country",
             "score_mode" : "avg",
             "query" : 
                 "bool": 
                    "must_not": 
                        "exists": 
                            "field": "country.name" 
                        
                    
                 
             
         
     
'

注意:路径和字段共同构成搜索。根据需要进行更改以使您工作。

对于常规字段:

curl -XGET 'http://localhost:9200/city/_search?pretty=true' -d'
    "query": 
        "bool": 
            "must_not": 
                "exists": 
                    "field": "name"
                 
             
         
     
'

【讨论】:

他们要求一个确实存在但包含一个空字符串的字段。 must_not exists 仅适用于空数组和空值,因为从技术上讲它们没有被索引。空字符串是。【参考方案10】:

我没有设法在文本字段中搜索空字符串。但是,它似乎适用于类型关键字的字段。所以我建议如下:

    delete /test_idx

    put test_idx
    
      "mappings" : 
        "testMapping": 
          "properties" : 
            "tag" : "type":"text",
            "content" : "type":"text",
                         "fields" : 
                           "x" : "type" : "keyword"
                         
            
          
        
      
    

put /test_idx/testMapping/1

  "tag": "null"


put /test_idx/testMapping/2

  "tag": "empty",
  "content": ""


GET /test_idx/testMapping/_search

   "query" : 
     "match" : "content.x" : ""
             

【讨论】:

【参考方案11】:

您需要通过将 .content 添加到您的字段名称来触发关键字索引器。根据原始索引的设置方式,以下使用 AWS ElasticSearch v6.x 对我“有效”。

GET /my_idx/_search?q=my_field.content:""

【讨论】:

【参考方案12】:

我正在尝试查找空字段(在具有动态映射的索引中)并将它们设置为默认值,以下对我有用

注意这是在 elastic 7.x 中

POST <index_name|pattern>/_update_by_query

  "script": 
    "lang": "painless",
    "source": """
      if (ctx._source.<field name>== "") 
        ctx._source.<field_name>= "0";
       else 
        ctx.op = "noop";
      
    """
  

我关注了线程中的一个响应,并在下面提出了它会做同样的事情

GET index_pattern*/_update_by_query

  "script": 
    "source": "ctx._source.field_name='0'",
    "lang": "painless"
  ,
  "query": 
    "bool": 
      "must": [
        
          "exists": 
            "field": "field_name"
          
        
      ],
      "must_not": [
        
          "wildcard": 
            "field_name": "*"
          
        
      ]
    
    

我也在尝试在索引中找到没有该字段的文档并为它们添加一个值

这个帖子的一个回复帮助我想出了下面

GET index_pattern*/_update_by_query

  "script": 
    "source": "ctx._source.field_name='0'",
    "lang": "painless"
  ,
  "query": 
    "bool": 
      "must_not": [
        
          "exists": 
            "field": "field_name"
          
        
      ]
    
  

感谢为这个帖子做出贡献的每一个人,我能够解决我的问题

【讨论】:

以上是关于在 elasticsearch 上查找具有空字符串值的文档的主要内容,如果未能解决你的问题,请参考以下文章

如何判断具有空字符串值的 python 字符串变量在初始化时是使用单引号还是双引号?

添加具有空字符串作为默认值且非空约束的列会导致 oracle 数据库的行为不一致

Elasticsearch:查找子字符串匹配

ElasticSearch之CURL操作(有空再去整理)

linux grep 找字符串有空格的

如何在 Elasticsearch 中查找包含给定点的多边形