web-dev-qa-db-ja.com

配列に特定の値のいずれかが含まれるアイテムをフィルター処理する

私のようなドキュメントのセットがあります

{
    tags:['a','b','c']
    // ... a bunch properties
}

タイトルで述べたように:Nestを使用して、指定されたタグのいずれかを含むすべてのドキュメントをフィルターする方法はありますか?

たとえば、上記のレコードは['c'、 'd']と一致します

または、複数の「OR」を手動で構築する必要がありますか?

53
Olivier

編集:以下のビットセットは興味深い読み物かもしれませんが、答え自体は少し古くなっています。この機能の一部は2.xで変更されています。また、Slawekはtermsクエリがこの場合の検索をDRYする簡単な方法であると別の答えで指摘しています。現在のベストプラクティスのために最後にリファクタリングされました。 —nz

おそらく、 Bool Query (または、より多くの場合 Filter を別のクエリと一緒に)と、should句が必要になるでしょう。

Boolクエリには、mustshould、およびmust_notの3つの主要なプロパティがあります。これらはそれぞれ、別のクエリまたはクエリの配列を受け入れます。句の名前はかなり自明です。あなたの場合、should句はリストフィルターを指定できます。リストフィルターのいずれかと一致すると、探しているドキュメントが返されます。

ドキュメントから:

must句のないブールクエリでは、1つ以上のshould句がドキュメントと一致する必要があります。一致するべきshould句の最小数は、minimum_should_matchパラメーターを使用して設定できます。

Boolクエリが単独でどのように見えるかの例を次に示します。

{
  "bool": {
    "should": [
      { "term": { "tag": "c" }},
      { "term": { "tag": "d" }}
    ]
  }
}

そして、より汎用的な Filtered Query 内のフィルターとしてのBoolクエリの別の例を次に示します。

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "bool": {
        "should": [
          { "term": { "tag": "c" }},
          { "term": { "tag": "d" }}
        ]
      }
    }
  }
}

Boolをクエリとして(たとえば、一致のスコアに影響を与えるために)使用するか、フィルターとして(たとえば、スコアリングまたはポストフィルター処理されるヒットを減らすために)使用するかは、要件によって異なります。

And/Or/Notを使用する理由がない限り(そのような理由が存在する場合を除き)、 Or Filter を優先してBoolを使用することをお勧めします。 Elasticsearchブログには、それぞれの異なる実装に関する詳細情報があり、And/Or/NotよりもBoolを好む場合の良い例があります。逆もまた同様です。

Elasticsearchブログ: Elasticsearchフィルタービットセットのすべて

リファクタリングされたクエリで更新...

さて、すべてのthatが邪魔にならないため、termsクエリは上記のすべてのDRYerバージョンです。内部のクエリのタイプに関して正しいことを行い、minimum_should_matchオプションを使用してbool + shouldと同じように動作し、全体が少し簡潔になります。

次に、最後のクエリを少しリファクタリングしました。

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "terms": {
        "tag": [ "c", "d" ],
        "minimum_should_match": 1
      }
    }
  }
}
43
Nick Zadrozny

terms query もあり、作業を節約できます。ここにドキュメントからの例:

{
  "terms" : {
      "tags" : [ "blue", "pill" ],
      "minimum_should_match" : 1
  }
}

ボンネットの下では、booleanを構築します。したがって、基本的には上記と同じですが短くなります。

対応する terms filter もあります。

クエリを要約すると、次のようになります。

{
  "filtered": {
    "query": {
      "match": { "title": "hello world" }
    },
    "filter": {
      "terms": {
        "tags": ["c", "d"]
      }
    }
  }
}

タグの数が多いと、これにより長さが大幅に異なる可能性があります。

53
slawek

これは古い質問でしたが、私は最近この問題にぶつかりました。ここでの回答のいくつかは非推奨になりました(コメントが指摘しているように)。ここでつまずいたかもしれない他の人のために:

termクエリを使用して、逆インデックスで指定された正確な用語を見つけることができます。

{
  "query": {
   "term" : { "tags" : "a" }
} 

ドキュメントから https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html

または、termsクエリを使用して、指定された配列で指定されたアイテムのいずれかとすべてのドキュメントを照合できます。

{
  "query": {
   "terms" : { "tags" : ["a", "c"]}
} 

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html

気を付けなければならないことが1つあります。これは、ドキュメントの定義方法にも違いがあります。検索しているフィールドがテキストタイプとしてインデックス付けされている場合、Elasticsearchは全文検索を実行します(つまり、analyzed文字列を使用)。

フィールドをキーワードとしてインデックス付けした場合、「分析されていない」文字列を使用したキーワード検索が実行されます。これは、分析された文字列が前処理される(小文字、句読点の削除など)ため、実用上大きな影響を与える可能性があります。( https://www.elastic.co/guide/en/elasticsearch/guide/master/term- vs-full-text.html

これらの問題を回避するために、文字列フィールドは、フルテキスト検索に使用するテキストと、キーワード検索に使用するキーワードの2つの新しいタイプに分割されました。 ( https://www.elastic.co/blog/strings-are-dead-long-live-strings

6
mdmjsh