Object配列内のデータ検索

ElasticSearchでの開発を始めたばかりの初心者です。

以下のマッピングとデータで、"class"="B"の条件で検索し、
結果として、"value"内の"string"が"two"と"three"のデータだけを抽出したいのですが、
方法がわからず、困っております。

マッピングの方法やクエリの組み方について、どのような方法が考えられるのか、
ご教示いただけないでしょうか。

よろしくお願いいたします。

■マッピング
{
"mappings": {
"testtype": {
"properties": {
"name": {
"type": "keyword"
},
"value": {
"type": "object",
"properties": {
"string": {
"type": "keyword"
},
"class": {
"type": "keyword"
}
}
}
}
}
}
}

■データ
{
"name": "TEST",
"value": [
{
"string": "one",
"class": "A",
},
{
"string": "two",
"class": "B",
},
{
"string": "three",
"class": "B",
},
{
"string": "four",
"class": "C",
}
]
}

たとえば、次のように1件のindexを作成して、class=Bのものを指定して検索したとき、作成したレコードが1件ヒットします。
このインデックスの中のvalue配列の中からstringがtwo, threeの要素だけを取り出したい、ということでしょうか?

検索対象レコードの作成

POST forum1019/testtype/5
{
  "name": "TEST",
  "value": [
    {
      "string": "one",
      "class": "A"
    },
    {
      "string": "two",
      "class": "B"
    },
    {
      "string": "three",
      "class": "B"
    },
    {
      "string": "four",
      "class": "C"
    }
  ]
}

検索クエリ

これで検索すれば、testtype/5がヒットしますが、value配列の中からclass=Bでない要素を除いておきたい、ということでしょうか?

GET forum1019/_search
{
  "query": {
    "match": {
      "value.class": "B"
    }
  }
}

単純に、class と stringの組み合わせで、class=Bのもののstringだけが欲しいということであれば、
データを4件として作成すれば良いのではないでしょうか。

POST forum1019/testtype/1
{
  "name": "TEST",
  "value": {
    "string": "one",
    "class": "A"
  }
}
POST forum1019/testtype/2
{
  "name": "TEST",
  "value": {
    "string": "two",
    "class": "B"
  }
}
POST forum1019/testtype/3
{
  "name": "TEST",
  "value": {
    "string": "three",
    "class": "B"
  }
}
POST forum1019/testtype/4
{
  "name": "TEST",
  "value": {
    "string": "four",
    "class": "C"
  }
}

早々に返信頂きありがとうございます。

やりたいこととしては、返信いただいた冒頭の部分そのものでございまして、
ヒットしたvalue配列の中から、stringがtwo, threeの要素だけを取り出したいです。

サンプルとしてデータは1件のみ記載しましたが、このインデックス内には複数件のレコードが存在し、
かつ、value配列内のデータ数も4つとは限らないというデータ構成になっています。

以上を考慮しまして、良い方法がありましたら、引き続きご教示お願いいたします。

こちらのNested datatypeでお望みの挙動になるのではないでしょうか。
https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html

こちらで、Mappingの変更、クエリを変更して試した内容を書いておきますので
期待する内容となっているか確認してみてください。

なお、確認にはkibanaのDev ToolsのConsoleを使っています。

Mappingの変更

valueのtypeをobjectからnestedに変更します。

PUT forum1019
{
  "mappings": {
    "testtype": {
      "properties": {
        "name": {
          "type": "keyword"
        },
        "value": {
          "type": "nested"
        }
      }
    }
  }
}

データの作成

前と変更なし

POST forum1019/testtype/5
{
  "name": "TEST",
  "value": [
    {
      "string": "one",
      "class": "A"
    },
    {
      "string": "two",
      "class": "B"
    },
    {
      "string": "three",
      "class": "B"
    },
    {
      "string": "four",
      "class": "C"
    }
  ]
}

クエリの変更

nestedを使ったクエリに変更します。
(_sourceをfalseにしているのは、便宜的に結果を見やすくするためです)

inner_hitsをつけておくことで、ヒットした部分だけが抽出されます。

GET forum1019/_search
{
  "_source": false,
  "query": {
    "nested": {
      "path": "value",
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "value.class": "B"
              }
            }
          ]
        }
      },
      "inner_hits": {}
    }
  }
}

結果

inner_hists内を見てください。twoとthreeしか返されていないことが確認できました。

{
  "took": 14,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.6931472,
    "hits": [
      {
        "_index": "forum1019",
        "_type": "testtype",
        "_id": "5",
        "_score": 0.6931472,
        "inner_hits": {
          "value": {
            "hits": {
              "total": 2,
              "max_score": 0.6931472,
              "hits": [
                {
                  "_nested": {
                    "field": "value",
                    "offset": 2
                  },
                  "_score": 0.6931472,
                  "_source": {
                    "value": {
                      "string": "three",
                      "class": "B"
                    }
                  }
                },
                {
                  "_nested": {
                    "field": "value",
                    "offset": 1
                  },
                  "_score": 0.6931472,
                  "_source": {
                    "value": {
                      "string": "two",
                      "class": "B"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}
1 Like

早々の返信とクエリをご提示頂きありがとうございます。
返信いただいた内容で、目的の検索が可能になりました。
本当にありがとうございました!

解決した懸案ではありますが、もう1点、ご教示いただきたく思います。

上記の結果を"inner_hits"を使用せずに取得する方法はあるのでしょうか?

最終的に"inner_hits"の件数を集計することを考えおりまして、”aggregation”を使ったクエリを組んでみたのですが、
"[nested] query does not support [aggs]"のエラーで実行できないでおります。

何か方法があるのか、教えていただけるとうれしいです。
よろしくお願いいたします。

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-nested-aggregation.html

こちらではないでしょうか?

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.