LoginSignup
0
0

More than 1 year has passed since last update.

[017] ドキュメントの検索結果と該当件数の表示をする by Ruby elasticsearch 7.14

Last updated at Posted at 2021-08-14
シリーズトップページ
https://qiita.com/robozushi10/items/2c8b6951ee342b013974

[017] ドキュメントの検索結果と該当件数の表示をする by Ruby elasticsearch 7.14

概要

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

今回はインデックス「shakespeare」から次の「ドキュメントの検索」と「件数表示」をする.

以下2つを全て満たすドキュメントを抽出し、件数を表示させる.

・フィルード「text_entry」の値に「ACT」という単語を含む
・フィルード「text_entry」の値に「V」という単語を含む

ただし、ここでも Elasticsearch デフォルト挙動の 10件しか取り出さないものとする.

検証環境

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

[00] Ruby の elasticsearch client パッケージを使って Elasticsearch 7.14 を操作してみる ... 検証環境構築編

参考にした情報

URL
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html

実践

Kibana DevTool の場合

コード

agg (aggregation) という検索結果に対して計算をする機能を使う.
・下記「docs_aggs」は任意の名称で良い
・下記「field」の「text_entry」はフィールド名である

GET shakespeare/_search
{
  "query": {
    "query_string": { 
      "query": "text_entry:ACT AND text_entry:V"
    }
  },
  "aggs": {
    "docs_aggs": {
      "terms": {
        "field": "text_entry.keyword",
        "size": 2560
      }
    }
  }
}

結果

image.png

Ruby の場合

コード

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

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

#!/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"
      }
    },
    "aggs": {
      "docs_aggs": {
        "terms": {
          "field": "text_entry.keyword",
          "size": 2560
        }
      }
    }
  }
  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}}}}}}}

結果

下記の 🛑} より、36件と表示されている.


       {"type"=>"act",
        "line_id"=>24007,
        "play_name"=>"A Comedy of Errors",
        "speech_number"=>87,
        "line_number"=>"",
        "speaker"=>"OF SYRACUSE",
        "text_entry"=>"ACT V"}},
     {"_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"}}]},
 "aggregations"=>
  {"docs_aggs"=>
    {"doc_count_error_upper_bound"=>0,
     "sum_other_doc_count"=>0,
     "buckets"=>[{"key"=>"ACT V", "doc_count"=>36🛑}]}}}

検証

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

🆗一致している模様

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0