tire 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +18 -2
- data/README.markdown +2 -2
- data/examples/tire-dsl.rb +1 -1
- data/lib/tire.rb +1 -0
- data/lib/tire/http/clients/faraday.rb +71 -0
- data/lib/tire/index.rb +30 -7
- data/lib/tire/logger.rb +1 -1
- data/lib/tire/model/persistence.rb +2 -3
- data/lib/tire/model/persistence/finders.rb +2 -2
- data/lib/tire/model/persistence/storage.rb +3 -5
- data/lib/tire/model/search.rb +2 -0
- data/lib/tire/results/collection.rb +8 -8
- data/lib/tire/results/item.rb +4 -1
- data/lib/tire/search.rb +30 -2
- data/lib/tire/search/facet.rb +5 -1
- data/lib/tire/search/query.rb +36 -1
- data/lib/tire/search/script_field.rb +23 -0
- data/lib/tire/tasks.rb +1 -1
- data/lib/tire/version.rb +8 -15
- data/test/integration/dis_max_queries_test.rb +68 -0
- data/test/integration/facets_test.rb +27 -0
- data/test/integration/index_store_test.rb +17 -1
- data/test/integration/index_update_document_test.rb +111 -0
- data/test/integration/persistent_model_test.rb +13 -0
- data/test/integration/prefix_query_test.rb +43 -0
- data/test/integration/reindex_test.rb +10 -0
- data/test/integration/script_fields_test.rb +38 -0
- data/test/test_helper.rb +6 -1
- data/test/unit/index_test.rb +58 -5
- data/test/unit/model_persistence_test.rb +14 -2
- data/test/unit/model_search_test.rb +14 -0
- data/test/unit/results_item_test.rb +9 -2
- data/test/unit/rubyext_test.rb +6 -0
- data/test/unit/search_query_test.rb +61 -2
- data/test/unit/search_script_field_test.rb +26 -0
- data/test/unit/search_test.rb +46 -2
- data/tire.gemspec +1 -1
- metadata +62 -53
data/.travis.yml
CHANGED
@@ -2,12 +2,28 @@
|
|
2
2
|
# Configuration file for http://travis-ci.org/#!/karmi/tire
|
3
3
|
# ---------------------------------------------------------
|
4
4
|
|
5
|
-
|
5
|
+
language: ruby
|
6
6
|
|
7
7
|
rvm:
|
8
|
-
- 1.8.7
|
9
8
|
- 1.9.3
|
9
|
+
- 1.8.7
|
10
10
|
- ree
|
11
11
|
|
12
|
+
env:
|
13
|
+
- TEST_COMMAND="rake test:unit"
|
14
|
+
- TEST_COMMAND="rake test:integration"
|
15
|
+
|
16
|
+
script: "bundle exec $TEST_COMMAND"
|
17
|
+
|
18
|
+
before_install:
|
19
|
+
- sudo service elasticsearch start
|
20
|
+
|
21
|
+
matrix:
|
22
|
+
exclude:
|
23
|
+
- rvm: 1.8.7
|
24
|
+
env: TEST_COMMAND="rake test:integration"
|
25
|
+
- rvm: ree
|
26
|
+
env: TEST_COMMAND="rake test:integration"
|
27
|
+
|
12
28
|
notifications:
|
13
29
|
disable: true
|
data/README.markdown
CHANGED
@@ -675,7 +675,7 @@ OK. All this time we have been talking about `ActiveRecord` models, since
|
|
675
675
|
it is a reasonable _Rails_' default for the storage layer.
|
676
676
|
|
677
677
|
But what if you use another database such as [MongoDB](http://www.mongodb.org/),
|
678
|
-
another object mapping library, such as [Mongoid](http://mongoid.org/)?
|
678
|
+
another object mapping library, such as [Mongoid](http://mongoid.org/) or [MongoMapper](http://mongomapper.com/)?
|
679
679
|
|
680
680
|
Well, things stay mostly the same:
|
681
681
|
|
@@ -691,7 +691,7 @@ Well, things stay mostly the same:
|
|
691
691
|
# These Mongo guys sure do get funky with their IDs in +serializable_hash+, let's fix it.
|
692
692
|
#
|
693
693
|
def to_indexed_json
|
694
|
-
self.
|
694
|
+
self.to_json
|
695
695
|
end
|
696
696
|
|
697
697
|
end
|
data/examples/tire-dsl.rb
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# from <https://github.com/karmi/tire>.
|
13
13
|
#
|
14
14
|
# By following these instructions you should have the search running
|
15
|
-
# on a sane
|
15
|
+
# on a sane operating system in less then 10 minutes.
|
16
16
|
|
17
17
|
# Note, that this file can be executed directly:
|
18
18
|
#
|
data/lib/tire.rb
CHANGED
@@ -25,6 +25,7 @@ require 'tire/search/facet'
|
|
25
25
|
require 'tire/search/filter'
|
26
26
|
require 'tire/search/highlight'
|
27
27
|
require 'tire/search/scan'
|
28
|
+
require 'tire/search/script_field'
|
28
29
|
require 'tire/results/pagination'
|
29
30
|
require 'tire/results/collection'
|
30
31
|
require 'tire/results/item'
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
# A Faraday-based HTTP client, which allows you to choose a HTTP client.
|
4
|
+
#
|
5
|
+
# See <https://github.com/technoweenie/faraday/tree/master/lib/faraday/adapter>
|
6
|
+
#
|
7
|
+
# NOTE: Tire will switch to Faraday for the HTTP abstraction layer. This client is a temporary solution.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# --------
|
11
|
+
#
|
12
|
+
# require 'typhoeus'
|
13
|
+
# require 'tire/http/clients/faraday'
|
14
|
+
#
|
15
|
+
# Tire.configure do |config|
|
16
|
+
#
|
17
|
+
# # Unless specified, tire will use Faraday.default_adapter and no middleware
|
18
|
+
# Tire::HTTP::Client::Faraday.faraday_middleware = Proc.new do |builder|
|
19
|
+
# builder.adapter :typhoeus
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# config.client(Tire::HTTP::Client::Faraday)
|
23
|
+
#
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
#
|
27
|
+
module Tire
|
28
|
+
module HTTP
|
29
|
+
module Client
|
30
|
+
class Faraday
|
31
|
+
|
32
|
+
# Default middleware stack.
|
33
|
+
DEFAULT_MIDDLEWARE = Proc.new do |builder|
|
34
|
+
builder.adapter ::Faraday.default_adapter
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# A customized stack of Faraday middleware that will be used to make each request.
|
39
|
+
attr_accessor :faraday_middleware
|
40
|
+
|
41
|
+
def get(url, data = nil)
|
42
|
+
request(:get, url, data)
|
43
|
+
end
|
44
|
+
|
45
|
+
def post(url, data)
|
46
|
+
request(:post, url, data)
|
47
|
+
end
|
48
|
+
|
49
|
+
def put(url, data)
|
50
|
+
request(:put, url, data)
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete(url, data = nil)
|
54
|
+
request(:delete, url, data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def head(url)
|
58
|
+
request(:head, url)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def request(method, url, data = nil)
|
63
|
+
conn = ::Faraday.new( &(faraday_middleware || DEFAULT_MIDDLEWARE) )
|
64
|
+
response = conn.run_request(method, url, data, nil)
|
65
|
+
Response.new(response.body, response.status, response.headers)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/tire/index.rb
CHANGED
@@ -103,9 +103,9 @@ module Tire
|
|
103
103
|
count = 0
|
104
104
|
|
105
105
|
begin
|
106
|
-
response = Configuration.client.post("#{url}/_bulk", payload.join("\n"))
|
107
|
-
raise RuntimeError, "#{response.code} > #{response.body}" if response.failure?
|
108
|
-
response
|
106
|
+
@response = Configuration.client.post("#{url}/_bulk", payload.join("\n"))
|
107
|
+
raise RuntimeError, "#{@response.code} > #{@response.body}" if @response && @response.failure?
|
108
|
+
@response
|
109
109
|
rescue StandardError => error
|
110
110
|
if count < tries
|
111
111
|
count += 1
|
@@ -149,10 +149,17 @@ module Tire
|
|
149
149
|
new_index = Index.new(name)
|
150
150
|
new_index.create(options) unless new_index.exists?
|
151
151
|
|
152
|
+
transform = options.delete(:transform)
|
153
|
+
|
152
154
|
Search::Scan.new(self.name, &block).each do |results|
|
153
|
-
|
154
|
-
|
155
|
+
|
156
|
+
documents = results.map do |document|
|
157
|
+
document = document.to_hash.except(:type, :_index, :_explanation, :_score, :_version, :highlight, :sort)
|
158
|
+
document = transform.call(document) if transform
|
159
|
+
document
|
155
160
|
end
|
161
|
+
|
162
|
+
new_index.bulk_store documents
|
156
163
|
end
|
157
164
|
end
|
158
165
|
|
@@ -198,6 +205,22 @@ module Tire
|
|
198
205
|
logged(id, curl)
|
199
206
|
end
|
200
207
|
|
208
|
+
def update(type, id, payload={}, options={})
|
209
|
+
raise ArgumentError, "Please pass a document type" unless type
|
210
|
+
raise ArgumentError, "Please pass a document ID" unless id
|
211
|
+
raise ArgumentError, "Please pass a script in the payload hash" unless payload[:script]
|
212
|
+
|
213
|
+
type = Utils.escape(type)
|
214
|
+
url = "#{self.url}/#{type}/#{id}/_update"
|
215
|
+
url += "?#{options.to_param}" unless options.keys.empty?
|
216
|
+
@response = Configuration.client.post url, MultiJson.encode(payload)
|
217
|
+
MultiJson.decode(@response.body)
|
218
|
+
|
219
|
+
ensure
|
220
|
+
curl = %Q|curl -X POST "#{url}" -d '#{MultiJson.encode(payload)}'|
|
221
|
+
logged(id, curl)
|
222
|
+
end
|
223
|
+
|
201
224
|
def refresh
|
202
225
|
@response = Configuration.client.post "#{url}/_refresh", ''
|
203
226
|
|
@@ -243,7 +266,7 @@ module Tire
|
|
243
266
|
MultiJson.decode(@response.body)['ok']
|
244
267
|
|
245
268
|
ensure
|
246
|
-
curl = %Q|curl -X PUT "#{Configuration.url}/_percolator/#{@name}
|
269
|
+
curl = %Q|curl -X PUT "#{Configuration.url}/_percolator/#{@name}/#{name}?pretty=1" -d '#{MultiJson.encode(options)}'|
|
247
270
|
logged('_percolator', curl)
|
248
271
|
end
|
249
272
|
|
@@ -281,7 +304,7 @@ module Tire
|
|
281
304
|
|
282
305
|
Configuration.logger.log_request endpoint, @name, curl
|
283
306
|
|
284
|
-
code = @response ? @response.code : error.class rescue
|
307
|
+
code = @response ? @response.code : error.class rescue 'N/A'
|
285
308
|
|
286
309
|
if Configuration.logger.level.to_s == 'debug'
|
287
310
|
body = if @response
|
data/lib/tire/logger.rb
CHANGED
@@ -9,7 +9,7 @@ module Tire
|
|
9
9
|
end
|
10
10
|
@device.sync = true if @device.respond_to?(:sync)
|
11
11
|
@options = options
|
12
|
-
at_exit { @device.close unless @device.closed? } if @device.respond_to?(:closed?) && @device.respond_to?(:close)
|
12
|
+
# at_exit { @device.close unless @device.closed? } if @device.respond_to?(:closed?) && @device.respond_to?(:close)
|
13
13
|
end
|
14
14
|
|
15
15
|
def level
|
@@ -51,9 +51,8 @@ module Tire
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def self.search(*args, &block)
|
54
|
-
|
55
|
-
args
|
56
|
-
args << { :wrapper => self } unless args.any? { |a| a.is_a? Hash }
|
54
|
+
args.last.update(:wrapper => self, :version => true) if args.last.is_a? Hash
|
55
|
+
args << { :wrapper => self, :version => true } unless args.any? { |a| a.is_a? Hash }
|
57
56
|
|
58
57
|
self.__search_without_persistence(*args, &block)
|
59
58
|
end
|
@@ -41,7 +41,7 @@ module Tire
|
|
41
41
|
old_wrapper = Tire::Configuration.wrapper
|
42
42
|
Tire::Configuration.wrapper self
|
43
43
|
s = Tire::Search::Search.new(index.name).query { all }
|
44
|
-
s.results
|
44
|
+
s.version(true).results
|
45
45
|
ensure
|
46
46
|
Tire::Configuration.wrapper old_wrapper
|
47
47
|
end
|
@@ -51,7 +51,7 @@ module Tire
|
|
51
51
|
old_wrapper = Tire::Configuration.wrapper
|
52
52
|
Tire::Configuration.wrapper self
|
53
53
|
s = Tire::Search::Search.new(index.name).query { all }.size(1)
|
54
|
-
s.results.first
|
54
|
+
s.version(true).results.first
|
55
55
|
ensure
|
56
56
|
Tire::Configuration.wrapper old_wrapper
|
57
57
|
end
|
@@ -55,11 +55,9 @@ module Tire
|
|
55
55
|
self.freeze
|
56
56
|
end
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
def
|
61
|
-
|
62
|
-
def persisted?; !!id; end
|
58
|
+
def destroyed? ; !!@destroyed; end
|
59
|
+
def persisted? ; !!id && !!_version; end
|
60
|
+
def new_record? ; !persisted?; end
|
63
61
|
|
64
62
|
end
|
65
63
|
|
data/lib/tire/model/search.rb
CHANGED
@@ -25,17 +25,17 @@ module Tire
|
|
25
25
|
hits
|
26
26
|
else
|
27
27
|
hits.map do |h|
|
28
|
-
|
28
|
+
document = {}
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
# Update the document with content and ID
|
31
|
+
document = h['_source'] ? document.update( h['_source'] || {} ) : document.update( __parse_fields__(h['fields']) )
|
32
|
+
document.update( {'id' => h['_id']} )
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
# Update the document with meta information
|
35
|
+
['_score', '_type', '_index', '_version', 'sort', 'highlight', '_explanation'].each { |key| document.update( {key => h[key]} || {} ) }
|
36
36
|
|
37
|
-
|
38
|
-
|
37
|
+
# Return an instance of the "wrapper" class
|
38
|
+
@wrapper.new(document)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
data/lib/tire/results/item.rb
CHANGED
data/lib/tire/search.rb
CHANGED
@@ -4,10 +4,15 @@ module Tire
|
|
4
4
|
|
5
5
|
class Search
|
6
6
|
|
7
|
-
attr_reader :indices, :
|
7
|
+
attr_reader :indices, :query, :facets, :filters, :options, :explain, :script_fields
|
8
8
|
|
9
9
|
def initialize(indices=nil, options={}, &block)
|
10
|
-
|
10
|
+
if indices.is_a?(Hash)
|
11
|
+
set_indices_options(indices)
|
12
|
+
@indices = indices.keys
|
13
|
+
else
|
14
|
+
@indices = Array(indices)
|
15
|
+
end
|
11
16
|
@types = Array(options.delete(:type)).map { |type| Utils.escape(type) }
|
12
17
|
@options = options
|
13
18
|
|
@@ -16,6 +21,16 @@ module Tire
|
|
16
21
|
block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
|
17
22
|
end
|
18
23
|
|
24
|
+
|
25
|
+
def set_indices_options(indices)
|
26
|
+
indices.each do |index, index_options|
|
27
|
+
if index_options[:boost]
|
28
|
+
@indices_boost ||= {}
|
29
|
+
@indices_boost[index] = index_options[:boost]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
19
34
|
def results
|
20
35
|
@results || (perform; @results)
|
21
36
|
end
|
@@ -24,6 +39,10 @@ module Tire
|
|
24
39
|
@response || (perform; @response)
|
25
40
|
end
|
26
41
|
|
42
|
+
def json
|
43
|
+
@json || (perform; @json)
|
44
|
+
end
|
45
|
+
|
27
46
|
def url
|
28
47
|
Configuration.url + @path
|
29
48
|
end
|
@@ -55,6 +74,12 @@ module Tire
|
|
55
74
|
self
|
56
75
|
end
|
57
76
|
|
77
|
+
def script_field(name, options={})
|
78
|
+
@script_fields ||= {}
|
79
|
+
@script_fields.merge! ScriptField.new(name, options).to_hash
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
58
83
|
def highlight(*args)
|
59
84
|
unless args.empty?
|
60
85
|
@highlight = Highlight.new(*args)
|
@@ -88,6 +113,7 @@ module Tire
|
|
88
113
|
|
89
114
|
def version(value)
|
90
115
|
@version = value
|
116
|
+
self
|
91
117
|
end
|
92
118
|
|
93
119
|
def perform
|
@@ -110,6 +136,7 @@ module Tire
|
|
110
136
|
def to_hash
|
111
137
|
@options.delete(:payload) || begin
|
112
138
|
request = {}
|
139
|
+
request.update( { :indices_boost => @indices_boost } ) if @indices_boost
|
113
140
|
request.update( { :query => @query.to_hash } ) if @query
|
114
141
|
request.update( { :sort => @sort.to_ary } ) if @sort
|
115
142
|
request.update( { :facets => @facets.to_hash } ) if @facets
|
@@ -119,6 +146,7 @@ module Tire
|
|
119
146
|
request.update( { :size => @size } ) if @size
|
120
147
|
request.update( { :from => @from } ) if @from
|
121
148
|
request.update( { :fields => @fields } ) if @fields
|
149
|
+
request.update( { :script_fields => @script_fields } ) if @script_fields
|
122
150
|
request.update( { :version => @version } ) if @version
|
123
151
|
request.update( { :explain => @explain } ) if @explain
|
124
152
|
request
|
data/lib/tire/search/facet.rb
CHANGED
@@ -17,7 +17,11 @@ module Tire
|
|
17
17
|
def terms(field, options={})
|
18
18
|
size = options.delete(:size) || 10
|
19
19
|
all_terms = options.delete(:all_terms) || false
|
20
|
-
@value =
|
20
|
+
@value = if field.is_a?(Enumerable) and not field.is_a?(String)
|
21
|
+
{ :terms => { :fields => field }.update({ :size => size, :all_terms => all_terms }).update(options) }
|
22
|
+
else
|
23
|
+
{ :terms => { :field => field }.update({ :size => size, :all_terms => all_terms }).update(options) }
|
24
|
+
end
|
21
25
|
self
|
22
26
|
end
|
23
27
|
|
data/lib/tire/search/query.rb
CHANGED
@@ -34,6 +34,14 @@ module Tire
|
|
34
34
|
@value
|
35
35
|
end
|
36
36
|
|
37
|
+
def prefix(field, value, options={})
|
38
|
+
if options[:boost]
|
39
|
+
@value = { :prefix => { field => { :prefix => value, :boost => options[:boost] } } }
|
40
|
+
else
|
41
|
+
@value = { :prefix => { field => value } }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
37
45
|
def custom_score(options={}, &block)
|
38
46
|
@custom_score ||= Query.new(&block)
|
39
47
|
@value[:custom_score] = options
|
@@ -60,6 +68,13 @@ module Tire
|
|
60
68
|
@value
|
61
69
|
end
|
62
70
|
|
71
|
+
def dis_max(options={}, &block)
|
72
|
+
@dis_max ||= DisMaxQuery.new(options)
|
73
|
+
block.arity < 1 ? @dis_max.instance_eval(&block) : block.call(@dis_max) if block_given?
|
74
|
+
@value[:dis_max] = @dis_max.to_hash
|
75
|
+
@value
|
76
|
+
end
|
77
|
+
|
63
78
|
def all
|
64
79
|
@value = { :match_all => {} }
|
65
80
|
@value
|
@@ -119,7 +134,6 @@ module Tire
|
|
119
134
|
end
|
120
135
|
end
|
121
136
|
|
122
|
-
|
123
137
|
class FilteredQuery
|
124
138
|
def initialize(&block)
|
125
139
|
@value = {}
|
@@ -147,5 +161,26 @@ module Tire
|
|
147
161
|
end
|
148
162
|
end
|
149
163
|
|
164
|
+
class DisMaxQuery
|
165
|
+
def initialize(options={}, &block)
|
166
|
+
@options = options
|
167
|
+
@value = {}
|
168
|
+
block.arity < 1 ? self.instance_eval(&block) : block.call(self) if block_given?
|
169
|
+
end
|
170
|
+
|
171
|
+
def query(&block)
|
172
|
+
(@value[:queries] ||= []) << Query.new(&block).to_hash
|
173
|
+
@value
|
174
|
+
end
|
175
|
+
|
176
|
+
def to_hash
|
177
|
+
@value.update(@options)
|
178
|
+
end
|
179
|
+
|
180
|
+
def to_json
|
181
|
+
to_hash.to_json
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
150
185
|
end
|
151
186
|
end
|