当某些字段不总是存在时,不确定如何正确过滤

时间:2014-01-08 20:10:30

标签: elasticsearch

我正在尝试根据某些对象上不存在的字段进行过滤。我认为ES会匹配没有该字段的对象。

最终,我试图过滤:

  • 字段A将始终存在,并且应匹配任何标签1,2,3
  • 如果存在,则字段B或C必须与标记5,6,7
  • 中的任何一个匹配
  • 如果存在,则字段B必须匹配标签10,11,12
  • 中的任何一个
  • 如果存在,则字段B或C不得包含任何标记15,16,18。

在这种情况下,我的所有标签都是字符串。另外,字段B和C在另一个字段内。我不确定这是否重要。

基本上,我的目标是:

{ a: ["some", "tags", "here"],
    X : { 
        B: ["more", "tags", "here"],
        C: ["even", "more", "here"]
    }
} 

我正在尝试构建一个白名单和黑名单过滤系统。

但是,当以这种方式过滤时,我没有得到任何不包含该字段的结果。

如何正确格式化此过滤器?

2 个答案:

答案 0 :(得分:4)

Elasticsearch将null或不存在的字段视为与该字段上的查询/过滤器不匹配,因此标准查询/过滤器不会返回这些结果。但是,如果我正确理解您,可以设置一个查询来完成您想要的任务。您可以结合使用"or""not""exist"过滤器。我将通过一些例子向您展示基本想法。

首先,创建一个索引并添加一些文档,各种文档中缺少各种字段:

curl -XPUT "http://localhost:9200/test_index"

curl -XPUT "http://localhost:9200/test_index/docs/1" -d'
{ 
    "a": ["some", "tags", "here"],
    "X" : { 
        "B": ["more", "tags", "here"],
        "C": ["even", "more", "here"]
    }
}'

curl -XPUT "http://localhost:9200/test_index/docs/2" -d'
{ 
    "a": ["some", "tags", "here"]
}'

curl -XPUT "http://localhost:9200/test_index/docs/3" -d'
{ 
    "a": ["some", "tags", "here"],
    "X" : { 
        "B": ["more", "tags", "here"]
    }
}'

curl -XPUT "http://localhost:9200/test_index/docs/4" -d'
{ 
    "a": ["some", "tags", "here"],
    "X" : { 
        "C": ["even", "more", "here"]
    }
}'

如果我想检索包含字段"X.B"(包含任何值)的文档,我可以使用以下查询:

curl -XPOST "http://localhost:9200/test_index/docs/_search" -d'
{
   "query": {
      "filtered": {
         "query": {
            "match_all": {}
         },
         "filter": {
             "exists": {
                "field": "X.B"
             }
         }
      }
   }
}'

这将返回文档"1""3"

另一方面,如果我只想返回没有字段"X.B"的文档,那么我可以使用此查询:

curl -XPOST "http://localhost:9200/test_index/docs/_search" -d'
{
   "query": {
      "filtered": {
         "query": {
            "match_all": {}
         },
         "filter": {
            "not": {
               "filter": {
                  "exists": {
                     "field": "X.B"
                  }
               }
            }
         }
      }
   }
}'

这会返回文档"2""4"

现在,如果我想要返回没有字段"X.B"的文档,或者该字段与字词"here"匹配,则可以使用"or"过滤器,如下所示:< / p>

curl -XPOST "http://localhost:9200/test_index/docs/_search" -d'
{
   "query": {
      "filtered": {
         "query": {
            "match_all": {}
         },
         "filter": {
            "or": [
               {
                  "term": { "X.B" : "here" }
               },
               {
                  "not": {
                     "filter": {
                        "exists": {
                           "field": "X.B"
                        }
                     }
                  }
               }
            ]
         }
      }
   }
}'

在这种情况下,返回所有四个文档,因为它们都匹配两个可能条件中的一个。

这并不能完全满足您的使用案例,但它应该足以让您入门。

这是一个可以使用的可运行示例(您需要在localhost:9200安装并运行ES,或者提供另一个端点): http://sense.qbox.io/gist/f1a644db97c89996f2b44f49793a2c76bae3155c

答案 1 :(得分:1)

问题不在于你的想法。或者至少,还有另一个问题。您看到的差异是由于"relatedProfiles"的分析方式。

看看这个要点:http://sense.qbox.io/gist/eec5dc038167ddd5d845a8ea8413a065f6e63f8f。我已经用两种不同的方式定义了索引,第一种是隐式映射(testfeed1),第二种(testfeed2)使用显式映射。删除除查询的第一部分之外的所有部分并针对每个索引运行它是有益的:

当我以第一种方式构建索引(没有显式映射),然后仅使用"relatedProfiles" : ["LinkedIn/4505"]上的过滤器进行搜索时,我得不到结果:

curl -XPOST "http://localhost:9200/testfeed1/feedItem/_search" -d'
{
   "query": {
      "match_all": {}
   },
   "filter": {
      "terms": {
         "relatedProfiles": [
            "LinkedIn/4505"
         ]
      }
   }
}'

...

{
   "took": 2,
   "timed_out": false,
   "_shards": {
      "total": 2,
      "successful": 2,
      "failed": 0
   },
   "hits": {
      "total": 0,
      "max_score": null,
      "hits": []
   }
}

当我以第二种方式构建索引时,使用显式映射(其中"relatedProfiles"设置为"index": "not_analyzed"),然后执行相同的搜索,我执行得到一个结果:

POST /testfeed2/feedItem/_search
{
   "query": {
      "match_all": {}
   },
   "filter": {
      "terms": {
         "relatedProfiles": [
            "LinkedIn/4505"
         ]
      }
   }
}

...

{
   "took": 1,
   "timed_out": false,
   "_shards": {
      "total": 2,
      "successful": 2,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "testfeed2",
            "_type": "feedItem",
            "_id": "Metabase/16411941826",
            "_score": 1,
            "_source": {
               "source": "Metabase/16411941826",
               "relatedProfiles": [
                  "LinkedIn/4505",
                  "Facebook/113470526913",
                  "CrunchBase/company/scholastic"
               ],
               "timestamp": "2014-01-08T00:49:35-05:00",
               "type": "News",
               "content": {
                  "title": "Opening doors for children, teachers",
                  "description": "class became published authors, chosen to put together the Florida section of “Fifty Great States Scrapbook,” published by Scholastic Books. Their book signing was at Barnes & Noble. “I saw an advertisement in the instructor magazine and decided to",
                  "url": "http://ct.moreover.com/ct?haid=8437fcfcdbaa900a138916017501692f42b5c333349b4&co=f000000011632s-1177024037&u1=SET&u2=56637",
                  "source": "St Augustine Herald"
               },
               "language": "en"
            }
         }
      ]
   }
}

不同之处在于如何分析"relatedProfiles"字段。在第二种情况下,根本不进行分析,因此"LinkedIn/4505"完全匹配,并返回结果。但是,在第一种情况下,由于未指定分析仪,因此使用标准分析仪。所以令牌最终成为"linkedin""4505"(正如你所看到的,如果你搜索那些令牌,就像要点的第34行)。由于我们在上述查询中使用了术语过滤器,因此未对过滤器术语文本进行分析,并且“LinkedIn / 4505”与任何令牌都不匹配,因此不会返回任何结果。

有意义吗?