|
| 1 | +[[api]] |
| 2 | +=== Elasticsearch API |
| 3 | + |
| 4 | +The `elasticsearch-api` library provides a Ruby implementation of the https://www.elastic.co/elastic-stack/[Elasticsearch] REST API. |
| 5 | + |
| 6 | +[discrete] |
| 7 | +[[api-install]] |
| 8 | +==== Installation |
| 9 | + |
| 10 | +Install the package from https://rubygems.org[Rubygems]: |
| 11 | + |
| 12 | +[source,bash] |
| 13 | +---------------------------- |
| 14 | +gem install elasticsearch-api |
| 15 | +---------------------------- |
| 16 | + |
| 17 | +To use an unreleased version, either add it to your `Gemfile` for https://bundler.io/[Bundler]: |
| 18 | + |
| 19 | +[source,bash] |
| 20 | +---------------------------- |
| 21 | +gem 'elasticsearch-api', git: 'git://github.com/elasticsearch/elasticsearch-ruby.git' |
| 22 | +---------------------------- |
| 23 | + |
| 24 | +or install it from a source code checkout: |
| 25 | + |
| 26 | +[source,bash] |
| 27 | +---------------------------- |
| 28 | +git clone https://github.com/elasticsearch/elasticsearch-ruby.git |
| 29 | +cd elasticsearch-ruby/elasticsearch-api |
| 30 | +bundle install |
| 31 | +rake install |
| 32 | +---------------------------- |
| 33 | + |
| 34 | +[discrete] |
| 35 | +[[api-example-usage]] |
| 36 | +==== Example usage |
| 37 | + |
| 38 | +The library is designed as a group of standalone Ruby modules, which can be mixed into a class |
| 39 | +providing connection to Elasticsearch -- an Elasticsearch client. |
| 40 | + |
| 41 | +[discrete] |
| 42 | +===== Usage with the `elasticsearch` gem |
| 43 | + |
| 44 | +**When you use the client from the https://github.com/elasticsearch/elasticsearch-ruby[`elasticsearch-ruby`] client, the library modules have been already included**, so you just call the API methods. |
| 45 | + |
| 46 | +The response will be an `Elasticsearch::API::Response` object which wraps an `Elasticsearch::Transport::Transport::Response` object. It provides `body`, `status` and `headers` methods, but you can treat is as a hash and access the keys directly. |
| 47 | + |
| 48 | +[source,rb] |
| 49 | +---------------------------- |
| 50 | +require 'elasticsearch' |
| 51 | +
|
| 52 | +client = Elasticsearch::Client.new |
| 53 | +
|
| 54 | +>response = client.index(index: 'myindex', id: 1, body: { title: 'Test' }) |
| 55 | +=> #<Elasticsearch::API::Response:0x00007fc9564b4980 |
| 56 | + @response= |
| 57 | + #<Elastic::Transport::Transport::Response:0x00007fc9564b4ac0 |
| 58 | + @body= |
| 59 | + {"_index"=>"myindex", |
| 60 | + "_id"=>"1", |
| 61 | + "_version"=>2, |
| 62 | + "result"=>"updated", |
| 63 | + "_shards"=>{"total"=>1, "successful"=>1, "failed"=>0}, |
| 64 | + "_seq_no"=>1, |
| 65 | + "_primary_term"=>1}, |
| 66 | + @headers= |
| 67 | + {"x-elastic-product"=>"Elasticsearch", |
| 68 | + "content-type"=>"application/json", |
| 69 | + "content-encoding"=>"gzip", |
| 70 | + "content-length"=>"130"}, |
| 71 | + @status=200>> |
| 72 | +
|
| 73 | +> response['result'] |
| 74 | +=> "created" |
| 75 | +
|
| 76 | +client.search(index: 'myindex', body: { query: { match: { title: 'test' } } }) |
| 77 | +# => => #<Elasticsearch::API::Response:0x00007fc95674a550 |
| 78 | + @response= |
| 79 | + #<Elastic::Transport::Transport::Response:0x00007fc95674a5c8 |
| 80 | + @body= |
| 81 | + {"took"=>223, |
| 82 | + "timed_out"=>false, |
| 83 | + "_shards"=>{"total"=>2, "successful"=>2, "skipped"=>0, "failed"=>0}, |
| 84 | + "hits"=> |
| 85 | + {"total"=>{"value"=>1, "relation"=>"eq"}, |
| 86 | + "max_score"=>0.2876821, |
| 87 | + "hits"=>[{"_index"=>"myindex", "_id"=>"1", "_score"=>0.2876821, "_source"=>{"title"=>"Test"}}]}}, |
| 88 | + @headers= |
| 89 | + {"x-elastic-product"=>"Elasticsearch", |
| 90 | + "content-type"=>"application/json", |
| 91 | + "content-encoding"=>"gzip", |
| 92 | + "content-length"=>"188"}, |
| 93 | + @status=200>> |
| 94 | +---------------------------- |
| 95 | + |
| 96 | +Full documentation and examples are included as RDoc annotations in the source code and available online at <http://rubydoc.info/gems/elasticsearch-api>. |
| 97 | + |
| 98 | +[discrete] |
| 99 | +===== Usage with a custom client |
| 100 | + |
| 101 | +When you want to mix the library with your own client, it must conform to the following _contract_: |
| 102 | + |
| 103 | +* It responds to a `perform_request(method, path, params, body, headers)` method, |
| 104 | +* the method returns an object with `status`, `body` and `headers` methods. |
| 105 | + |
| 106 | +A simple client could look like this (_with a dependency on `active_support` to parse the query params_): |
| 107 | + |
| 108 | +[source,rb] |
| 109 | +---------------------------- |
| 110 | +require 'multi_json' |
| 111 | +require 'faraday' |
| 112 | +require 'elasticsearch/api' |
| 113 | +
|
| 114 | +class MySimpleClient |
| 115 | + include Elasticsearch::API |
| 116 | +
|
| 117 | + CONNECTION = ::Faraday::Connection.new(url: 'http://localhost:9200') |
| 118 | +
|
| 119 | + def perform_request(method, path, params, body, headers = nil) |
| 120 | + puts "--> #{method.upcase} #{path} #{params} #{body} #{headers}" |
| 121 | +
|
| 122 | + CONNECTION.run_request \ |
| 123 | + method.downcase.to_sym, |
| 124 | + path_with_params(path, params), |
| 125 | + ( body ? MultiJson.dump(body): nil ), |
| 126 | + {'Content-Type' => 'application/json'} |
| 127 | + end |
| 128 | +
|
| 129 | + private |
| 130 | +
|
| 131 | + def path_with_params(path, params) |
| 132 | + return path if params.blank? |
| 133 | +
|
| 134 | + case params |
| 135 | + when String |
| 136 | + "#{path}?#{params}" |
| 137 | + when Hash |
| 138 | + "#{path}?#{URI.encode_www_form(params)}" |
| 139 | + else |
| 140 | + raise ArgumentError, "Cannot parse params: '#{params}'" |
| 141 | + end |
| 142 | + end |
| 143 | +end |
| 144 | +
|
| 145 | +client = MySimpleClient.new |
| 146 | +
|
| 147 | +p client.cluster.health |
| 148 | +# --> GET _cluster/health {} |
| 149 | +# => "{"cluster_name":"elasticsearch" ... }" |
| 150 | +
|
| 151 | +p client.index(index: 'myindex', id: 'custom', body: { title: "Indexing from my client" }) |
| 152 | +# --> PUT myindex/mytype/custom {} {:title=>"Indexing from my client"} |
| 153 | +# => "{"ok":true, ... }" |
| 154 | +---------------------------- |
| 155 | + |
| 156 | +[discrete] |
| 157 | +===== Using JSON Builders |
| 158 | + |
| 159 | +Instead of passing the `:body` argument as a Ruby _Hash_, you can pass it as a _String_, potentially |
| 160 | +taking advantage of JSON builders such as https://github.com/rails/jbuilder[JBuilder] or |
| 161 | +https://github.com/bsiggelkow/jsonify[Jsonify]: |
| 162 | + |
| 163 | +[source,rb] |
| 164 | +---------------------------- |
| 165 | +require 'jbuilder' |
| 166 | +
|
| 167 | +query = Jbuilder.encode do |json| |
| 168 | + json.query do |
| 169 | + json.match do |
| 170 | + json.title do |
| 171 | + json.query 'test 1' |
| 172 | + json.operator 'and' |
| 173 | + end |
| 174 | + end |
| 175 | + end |
| 176 | +end |
| 177 | +
|
| 178 | +client.search(index: 'myindex', body: query) |
| 179 | +
|
| 180 | +# 2013-06-25 09:56:05 +0200: GET http://localhost:9200/myindex/_search [status:200, request:0.015s, query:0.011s] |
| 181 | +# 2013-06-25 09:56:05 +0200: > {"query":{"match":{"title":{"query":"test 1","operator":"and"}}}} |
| 182 | +# ... |
| 183 | +# => {"took"=>21, ..., "hits"=>{"total"=>1, "hits"=>[{ "_source"=>{"title"=>"Test 1", ...}}]}} |
| 184 | +---------------------------- |
| 185 | + |
| 186 | +[discrete] |
| 187 | +===== Using Hash Wrappers |
| 188 | + |
| 189 | +For a more comfortable access to response properties, you may wrap it in one of the _Hash_ "object access" |
| 190 | +wrappers, such as https://github.com/intridea/hashie[`Hashie::Mash`]: |
| 191 | + |
| 192 | +[source,rb] |
| 193 | +---------------------------- |
| 194 | +require 'hashie' |
| 195 | +
|
| 196 | +response = client.search( |
| 197 | + index: 'myindex', |
| 198 | + body: { |
| 199 | + query: { match: { title: 'test' } }, |
| 200 | + aggregations: { tags: { terms: { field: 'tags' } } } |
| 201 | + } |
| 202 | +) |
| 203 | +
|
| 204 | +mash = Hashie::Mash.new(response) |
| 205 | +
|
| 206 | +mash.hits.hits.first._source.title |
| 207 | +# => 'Test' |
| 208 | +---------------------------- |
| 209 | + |
| 210 | +[discrete] |
| 211 | +===== Using a Custom JSON Serializer |
| 212 | + |
| 213 | +The library uses the https://rubygems.org/gems/multi_json/[MultiJson] gem by default but allows you to set a custom JSON library, provided it uses the standard `load/dump` interface: |
| 214 | + |
| 215 | +[source,rb] |
| 216 | +---------------------------- |
| 217 | +Elasticsearch::API.settings[:serializer] = JrJackson::Json |
| 218 | +Elasticsearch::API.serializer.dump({foo: 'bar'}) |
| 219 | +# => {"foo":"bar"} |
| 220 | +---------------------------- |
0 commit comments