stretcher 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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