stretcher 1.12.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -10,11 +10,12 @@ A concise, fast ElasticSearch client designed to reflect the actual elastic sear
10
10
  # Features
11
11
 
12
12
  * Cleanly matches up to elastic search's JSON api
13
- * Efficiently re-uses connections on a per-server object basis (via net/http/persistent)
13
+ * Efficiently re-uses connections on a per-server object basis (via excon)
14
14
  * Supports efficient bulk indexing operations
15
15
  * Returns most responses in convenient Hashie::Mash form
16
16
  * Configurable logging
17
17
  * Pure, threadsafe, ruby
18
+ * Easily swap HTTP clients via Faraday
18
19
 
19
20
  ## Installation
20
21
 
@@ -80,15 +81,15 @@ docs = [{"_type" => "tweet", "_id" => 91011, "text" => "Bulked"}]
80
81
  server.index(:foo).bulk_index(docs)
81
82
  ```
82
83
 
84
+ ### Full Documentation
85
+
86
+ This README documents only part of stretcher's API. The full documentation for stretcher is available in its [full rdocs](http://rdoc.info/github/PoseBiz/stretcher/master/frames).
87
+
83
88
  ### Rails Integration
84
89
 
85
90
  Stretcher is a low level-client, but it was built as a part of a full suite of Rails integration tools.
86
91
  While not yet open-sourced, you can read our detailed blog post: [integrating Stretcher with Rails](http://blog.andrewvc.com/elasticsearch-rails-stretcher-at-pose).
87
92
 
88
- ### Full Documentation
89
-
90
- This README documents only part of stretcher's API. The full documentation for stretcher is available in its [full rdocs](http://rdoc.info/github/PoseBiz/stretcher/master/frames).
91
-
92
93
  ## Running Specs
93
94
 
94
95
  Running the specs requires an operational Elastic Search server on http://localhost:9200.
@@ -102,6 +103,7 @@ Specs may be run with `rake spec`
102
103
  * [@aq1018](https://github.com/aq1018)
103
104
  * [@akahn](https://github.com/akahn)
104
105
  * [@psynix](https://github.com/psynix)
106
+ * [@cmaitchison](https://github.com/cmaitchison)
105
107
  * [@fmardini](https://github.com/fmardini)
106
108
  * [@chatgris](https://github.com/chatgris)
107
109
  * [@alpinegizmo](https://github.com/alpinegizmo)
@@ -11,16 +11,30 @@ module Stretcher
11
11
  @logger = options[:logger] || index.logger
12
12
  end
13
13
 
14
- # Retrieves the document by ID
15
- # Normally this returns the contents of _source, however, the 'raw' flag is passed in, it will return the full response hash
16
- # Returns nil if the document does not exist
14
+ # Retrieves the document by ID.
15
+ # Normally this returns the contents of _source, however, if the 'raw' flag is passed in, it will return the full response hash.
16
+ # Returns nil if the document does not exist.
17
+ #
18
+ # The :fields argument can either be a csv String or an Array. e.g. [:field1,'field2] or "field1,field2".
19
+ # If the fields parameter is passed in those fields are returned instead of _source.
20
+ #
21
+ # If, you include _source as a field, along with other fields you MUST set the raw flag to true to
22
+ # receive both fields and _source. Otherwise, only _source will be returned
17
23
  def get(id, options={}, raw=false)
18
24
  if options == true || options == false # Support raw as second argument, legacy API
19
25
  raw = true
20
26
  options = {}
21
27
  end
28
+
29
+ # If fields is passed in as an array, properly encode it
30
+ arg_fields = options[:fields]
31
+ if arg_fields.is_a?(Array)
32
+ # no #merge! we don't want to mutate args
33
+ options = options.merge(:fields => arg_fields.join(","))
34
+ end
35
+
22
36
  res = request(:get, id, options)
23
- raw ? res : res["_source"]
37
+ raw ? res : (res["_source"] || res["fields"])
24
38
  end
25
39
 
26
40
  # Retrieves multiple documents of the index type by ID
@@ -2,6 +2,52 @@ module Stretcher
2
2
  class Server < EsComponent
3
3
  attr_reader :uri, :http, :logger
4
4
 
5
+ # Internal use only.
6
+ # Returns a properly configured HTTP client when initializing an instance
7
+ def self.build_client(uri, options={})
8
+ http = Faraday.new(:url => uri) do |builder|
9
+ builder.response :mashify
10
+ builder.response :json, :content_type => /\bjson$/
11
+
12
+ builder.request :json
13
+
14
+ builder.options[:read_timeout] = 4 || options[:read_timeout]
15
+ builder.options[:open_timeout] = 2 || options[:open_timeout]
16
+
17
+ if faraday_configurator = options[:faraday_configurator]
18
+ faraday_configurator.call(builder)
19
+ else
20
+ builder.adapter :excon
21
+ end
22
+ end
23
+
24
+ uri_components = URI.parse(uri)
25
+ if uri_components.user || uri_components.password
26
+ http.basic_auth(uri_components.user, uri_components.password)
27
+ end
28
+
29
+ http
30
+ end
31
+
32
+ # Internal use only.
33
+ # Builds a logger when initializing an instance
34
+ def self.build_logger(options)
35
+ logger = nil
36
+
37
+ if options[:logger]
38
+ logger = options[:logger]
39
+ else
40
+ logger = Logger.new(STDOUT)
41
+ logger.level = Logger::WARN
42
+ end
43
+
44
+ logger.formatter = proc do |severity, datetime, progname, msg|
45
+ "[Stretcher][#{severity}]: #{msg}\n"
46
+ end
47
+
48
+ logger
49
+ end
50
+
5
51
  # Instantiate a new instance in a manner convenient for using the block syntax.
6
52
  # Can be used interchangably with +Stretcher::Server.new+ but will return the value
7
53
  # of the block if present. See the regular constructor for full options.
@@ -27,38 +73,8 @@ module Stretcher
27
73
  def initialize(uri='http://localhost:9200', options={})
28
74
  @request_mtx = Mutex.new
29
75
  @uri = uri
30
-
31
- @http = Faraday.new(:url => @uri) do |builder|
32
- builder.response :mashify
33
- builder.response :json, :content_type => /\bjson$/
34
-
35
- builder.request :json
36
-
37
- builder.options[:read_timeout] = 4 || options[:read_timeout]
38
- builder.options[:open_timeout] = 2 || options[:open_timeout]
39
-
40
- if faraday_configurator = options[:faraday_configurator]
41
- faraday_configurator.call(builder)
42
- else
43
- builder.adapter :excon
44
- end
45
- end
46
-
47
- uri_components = URI.parse(@uri)
48
- if uri_components.user || uri_components.password
49
- @http.basic_auth(uri_components.user, uri_components.password)
50
- end
51
-
52
- if options[:logger]
53
- @logger = options[:logger]
54
- else
55
- @logger = Logger.new(STDOUT)
56
- @logger.level = Logger::WARN
57
- end
58
-
59
- @logger.formatter = proc do |severity, datetime, progname, msg|
60
- "[Stretcher][#{severity}]: #{msg}\n"
61
- end
76
+ @http = self.class.build_client(@uri, options)
77
+ @logger = self.class.build_logger(options)
62
78
  end
63
79
 
64
80
  # Returns a Stretcher::Index object for the index +name+.
@@ -71,7 +87,9 @@ module Stretcher
71
87
  idx = Index.new(self, name, :logger => logger)
72
88
  block ? block.call(idx) : idx
73
89
  end
74
-
90
+
91
+ # Perform a raw bulk operation. You probably want to use Stretcher::Index#bulk_index
92
+ # which properly formats a bulk index request.
75
93
  def bulk(data)
76
94
  request(:post, path_uri("/_bulk")) do |req|
77
95
  req.body = data
@@ -169,19 +187,22 @@ module Stretcher
169
187
  logger.info { "Stretcher: Issuing Request #{method.to_s.upcase}, #{Util.qurl(url,query_opts)}" }
170
188
 
171
189
  # Our default client is threadsafe, but some others might not be
172
- res = nil
173
- @request_mtx.synchronize {
174
- res = if block
175
- http.send(method, url, query_opts, *args) do |req|
176
- # Elastic search does mostly deal with JSON
177
- req.headers["Content-Type"] = 'application/json'
178
- block.call(req)
179
- end
180
- else
181
- http.send(method, url, query_opts, *args)
182
- end
183
- }
184
-
190
+ check_response(@request_mtx.synchronize {
191
+ if block
192
+ http.send(method, url, query_opts, *args) do |req|
193
+ # Default content type to json, the block can change this of course
194
+ req.headers["Content-Type"] = 'application/json' unless req.headers
195
+ block.call(req)
196
+ end
197
+ else
198
+ http.send(method, url, query_opts, *args)
199
+ end
200
+ })
201
+ end
202
+
203
+ # Internal use only
204
+ # Check response codes from request
205
+ def check_response(res)
185
206
  if res.status >= 200 && res.status <= 299
186
207
  res.body
187
208
  elsif [404, 410].include? res.status
@@ -1,3 +1,3 @@
1
1
  module Stretcher
2
- VERSION = "1.12.0"
2
+ VERSION = "1.13.0"
3
3
  end
@@ -69,55 +69,71 @@ describe Stretcher::IndexType do
69
69
  end
70
70
  end
71
71
 
72
- describe "put/get/delete/explain" do
72
+ describe "ops on individual docs" do
73
73
  before do
74
74
  @doc = {:message => "hello!", :_timestamp => 1366420401}
75
75
  @put_res = type.put(987, @doc)
76
76
  end
77
-
78
- it "should put correctly" do
79
- @put_res.should_not be_nil
80
- end
81
-
82
- it "should post correctly" do
83
- type.post(@doc).should_not be_nil
84
- end
85
-
86
- it "should get individual documents correctly" do
87
- type.get(987).message.should == @doc[:message]
88
- end
89
-
90
- it "should return nil when retrieving non-extant docs" do
91
- lambda {
92
- type.get(898323329)
93
- }.should raise_exception(Stretcher::RequestError::NotFound)
94
- end
95
-
96
- it "should get individual, passing through additional options" do
97
- res = type.get(987, {:fields => ['_timestamp']})
98
- res._timestamp.should == @doc[:_timestamp]
99
- res.message == nil
100
- end
101
-
102
- it "should get individual raw documents correctly" do
103
- res = type.get(987, {}, true)
104
- res["_id"].should == "987"
105
- res["_source"].message.should == @doc[:message]
106
- end
107
-
108
- it "should get individual raw documents correctly with legacy API" do
109
- res = type.get(987, true)
110
- res["_id"].should == "987"
111
- res["_source"].message.should == @doc[:message]
112
- end
113
-
77
+
78
+ describe "put" do
79
+ it "should put correctly" do
80
+ @put_res.should_not be_nil
81
+ end
82
+
83
+ it "should post correctly" do
84
+ type.post(@doc).should_not be_nil
85
+ end
86
+ end
87
+
88
+ describe "get" do
89
+ it "should get individual documents correctly" do
90
+ type.get(987).message.should == @doc[:message]
91
+ end
92
+
93
+ it "should return nil when retrieving non-extant docs" do
94
+ lambda {
95
+ type.get(898323329)
96
+ }.should raise_exception(Stretcher::RequestError::NotFound)
97
+ end
98
+
99
+ it "should get individual fields given a String, passing through additional options" do
100
+ res = type.get(987, {:fields => '_timestamp'})
101
+ res._timestamp.should == @doc[:_timestamp]
102
+ res.message.should == nil
103
+ end
104
+
105
+ it "should get individual fields given an Array, passing through additional options" do
106
+ res = type.get(987, {:fields => ['_timestamp']})
107
+ res._timestamp.should == @doc[:_timestamp]
108
+ res.message.should == nil
109
+ end
110
+
111
+ it "should have source and other fields accessible when raw=true" do
112
+ res = type.get(987, {:fields => ['_timestamp', '_source']}, true)
113
+ res._source.message == @doc[:message]
114
+ res.fields._timestamp.should == @doc[:_timestamp]
115
+ end
116
+
117
+ it "should get individual raw documents correctly" do
118
+ res = type.get(987, {}, true)
119
+ res["_id"].should == "987"
120
+ res["_source"].message.should == @doc[:message]
121
+ end
122
+
123
+ it "should get individual raw documents correctly with legacy API" do
124
+ res = type.get(987, true)
125
+ res["_id"].should == "987"
126
+ res["_source"].message.should == @doc[:message]
127
+ end
128
+ end
129
+
114
130
  it "should explain a query" do
115
131
  type.exists?(987).should be_true
116
132
  index.refresh
117
133
  res = type.explain(987, {:query => {:match_all => {}}})
118
134
  res.should have_key('explanation')
119
135
  end
120
-
136
+
121
137
  it "should update individual docs correctly" do
122
138
  type.update(987, :script => "ctx._source.message = 'Updated!'")
123
139
  type.get(987).message.should == 'Updated!'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stretcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-29 00:00:00.000000000 Z
12
+ date: 2013-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday