[015] ドキュメントの AND による絞り込み検索 by Ruby elasticsearch 7.14

|シリーズトップページ|
|:--|
|https://qiita.com/robozushi10/items/2c8b6951ee342b013974|

# 概要

Ruby の Elasticsearch Client を使って Elasticsearch 7.14 で REST API の操作する.
また、比較のために、Kibana DevTool でのクエリも合せて記しておく.

今回はインデックス「shakespeare」から次の「**ドキュメントの検索**」を行う.
・**フィルード「text_entry」の値に「ACT」かつ「V」という "単語" を含むドキュメントを取り出す.**

ただし、ここでも Elasticsearch デフォルト挙動の 10件しか取り出さないものとする.
なお、全件取り出しを行いたい場合は [[035](https://qiita.com/robozushi10/items/57f6cb087d757a003be7)] を参照すること.

# 検証環境

下記の要領で検証用データ「Shakespeare」が登録された
・Elasticsearch + Kibana (7.14)
を使用した

[[00] Ruby の elasticsearch client パッケージを使って Elasticsearch 7.14 を操作してみる ... 検証環境構築編](https://qiita.com/robozushi10/items/8bd936fe6fbab080149e)



# 参考にした情報

|URL|
|:--|
|https://www.elastic.co/guide/en/elasticsearch/reference/7.14/indices-get-mapping.html|


# 実践

## Kibana DevTool の場合

### コード

```
GET shakespeare/_search
{
  "query": {
    "query_string": { 
      "query": "text_entry:ACT AND text_entry:V"
    }
  }
}
```

### 結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/05190c58-4d33-53a3-d1c8-897664f74f9c.png)



## Ruby の場合

### コード

重要な部分は「`if __FILE__ == $0 以降`」である.

class MySimpleClient は丸々コピーで良い. (が、192.168.10.115 のみ、適宜読み替えること)



```ruby
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-
require 'multi_json'
require 'faraday'
require 'elasticsearch/api'
require 'active_support/core_ext' #! note_0004
require 'active_support' #! note_0005
 
class MySimpleClient
# note_0001
  include Elasticsearch::API
  CONNECTION = ::Faraday::Connection.new url: 'http://192.168.10.115:29200'
  def perform_request(method, path, params, body, headers = nil)
    #! note_0003
    CONNECTION.run_request \
      method.downcase.to_sym,
      path_with_params(path, params),
      (body ? MultiJson.dump(body): nil),
      {'Content-Type' => 'application/json'}
  end
 
  private
 
  def path_with_params(path, params)
    return path if params.blank?
 
    case params
    when String
      "#{path}?#{params}"
    when Hash
      "#{path}?#{params.to_query}"
    else
      raise ArgumentError, "Cannot parse params: '#{params}'"
    end
  end
end
 
if __FILE__ == $0
 
  client = MySimpleClient.new
 
  q = {
    "query": {
      # note_0006
      "query_string": { 
        "query": "text_entry:ACT AND text_entry:V"
      }
    }
  }
  res = client.search index: 'shakespeare', body: q
  h = JSON.parse(res)
  pp h
end
 
 
# note_0001: https://rubydoc.info/gems/elasticsearch-api
# note_0002: https://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Cluster/Actions#health-instance_method
# note_0003: client.cluster.health から呼び出されるので実装が必要である
# note_0004: 'active_support' を 'active_support/core_ext' に変更する.
#            APIドキュメントにある 'active_support' 指定だと次のエラーが発生してしまうためである.
#            tutorial.rb:26:in `path_with_params': undefined method `blank?' for {}:Hash (NoMethodError)
# note_0005: require 'active_support' が存在しないと次のエラーが発生してしまう.
#            /usr/local/bundle/gems/activesupport-6.0.4/lib/active_support/core_ext/object/json.rb:42:
#              in `to_json': uninitialized constant ActiveSupport::JSON (NameError)
# note_0006: mapping は次の通り
# {"shakespeare"=>
#   {"mappings"=>
#     {"properties"=>
#       {"line_id"=>{"type"=>"long"},
#        "line_number"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}},
#        "play_name"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}},
#        "speaker"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}},
#        "speech_number"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}},
#        "text_entry"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}},
#        "type"=>
#         {"type"=>"text",
#          "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}}}}}}
```

### 結果

```ruby
{"took"=>1,
 "timed_out"=>false,
 "_shards"=>{"total"=>1, "successful"=>1, "skipped"=>0, "failed"=>0},
 "hits"=>
  {"total"=>{"value"=>36, "relation"=>"eq"},
   "max_score"=>18.431282,
   "hits"=>
    [{"_index"=>"shakespeare",
      "_type"=>"_doc",
      "_id"=>"2633",
略
     {"_index"=>"shakespeare",
      "_type"=>"_doc",
      "_id"=>"27743",
      "_score"=>18.431282,
      "_source"=>
       {"type"=>"act",
        "line_id"=>27744,
        "play_name"=>"Coriolanus",
        "speech_number"=>7,
        "line_number"=>"",
        "speaker"=>"AUFIDIUS",
        "text_entry"=>"ACT V"}},
     {"_index"=>"shakespeare",
      "_type"=>"_doc",
      "_id"=>"31443",
      "_score"=>18.431282,
      "_source"=>
       {"type"=>"act",
        "line_id"=>31444,
        "play_name"=>"Cymbeline",
        "speech_number"=>13,
        "line_number"=>"",
        "speaker"=>"BELARIUS",
        "text_entry"=>"ACT V"}}]}}
```

## 検証

### 結果比較 DevTool vs Ruby が一致しているか？

🆗一致している模様

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/6b33faae-0ad8-deaf-eff4-159d21b24d0d.png)