tire 0.5.8 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +25 -3
- data/lib/tire.rb +1 -0
- data/lib/tire/dsl.rb +16 -7
- data/lib/tire/http/client.rb +4 -0
- data/lib/tire/http/clients/curb.rb +4 -0
- data/lib/tire/http/clients/faraday.rb +7 -3
- data/lib/tire/index.rb +23 -10
- data/lib/tire/model/import.rb +13 -4
- data/lib/tire/model/indexing.rb +2 -1
- data/lib/tire/results/collection.rb +10 -5
- data/lib/tire/results/item.rb +3 -0
- data/lib/tire/search/facet.rb +5 -5
- data/lib/tire/search/queries/custom_filters_score.rb +128 -0
- data/lib/tire/search/query.rb +8 -10
- data/lib/tire/tasks.rb +1 -11
- data/lib/tire/version.rb +13 -7
- data/test/integration/bulk_test.rb +13 -0
- data/test/integration/custom_filters_score_queries_test.rb +105 -0
- data/test/integration/dsl_search_test.rb +32 -0
- data/test/integration/facets_test.rb +47 -0
- data/test/integration/results_test.rb +31 -1
- data/test/unit/http_client_test.rb +8 -0
- data/test/unit/index_test.rb +37 -9
- data/test/unit/model_import_test.rb +17 -0
- data/test/unit/model_initialization_test.rb +43 -4
- data/test/unit/results_collection_test.rb +62 -0
- data/test/unit/results_item_test.rb +39 -1
- data/test/unit/search_facet_test.rb +15 -0
- data/test/unit/search_query_test.rb +129 -14
- data/test/unit/tire_test.rb +13 -0
- data/tire.gemspec +1 -0
- metadata +30 -10
- data/test/integration/text_query_test.rb +0 -39
data/lib/tire/search/query.rb
CHANGED
@@ -28,13 +28,6 @@ module Tire
|
|
28
28
|
@value = { :range => { field => value } }
|
29
29
|
end
|
30
30
|
|
31
|
-
def text(field, value, options={})
|
32
|
-
Tire.warn "The 'text' query has been deprecated, please use a 'match' query."
|
33
|
-
query_options = { :query => value }.update(options)
|
34
|
-
@value = { :text => { field => query_options } }
|
35
|
-
@value
|
36
|
-
end
|
37
|
-
|
38
31
|
def string(value, options={})
|
39
32
|
@value = { :query_string => { :query => value } }
|
40
33
|
@value[:query_string].update(options)
|
@@ -98,8 +91,10 @@ module Tire
|
|
98
91
|
@value
|
99
92
|
end
|
100
93
|
|
101
|
-
def ids(values, type)
|
102
|
-
@value = { :ids => { :values => values
|
94
|
+
def ids(values, type=nil)
|
95
|
+
@value = { :ids => { :values => Array(values) } }
|
96
|
+
@value[:ids].update(:type => type) if type
|
97
|
+
@value
|
103
98
|
end
|
104
99
|
|
105
100
|
def boosting(options={}, &block)
|
@@ -193,7 +188,10 @@ module Tire
|
|
193
188
|
end
|
194
189
|
|
195
190
|
def filter(type, *options)
|
196
|
-
@value
|
191
|
+
@value[:filter] ||= {}
|
192
|
+
@value[:filter][:and] ||= []
|
193
|
+
@value[:filter][:and] << Filter.new(type, *options).to_hash
|
194
|
+
@value
|
197
195
|
end
|
198
196
|
|
199
197
|
def query(&block)
|
data/lib/tire/tasks.rb
CHANGED
@@ -87,11 +87,6 @@ namespace :tire do
|
|
87
87
|
namespace :import do
|
88
88
|
desc import_model_desc
|
89
89
|
task :model do
|
90
|
-
if defined?(Rails)
|
91
|
-
puts "[IMPORT] Rails detected, loading environment..."
|
92
|
-
Rake::Task["environment"].invoke
|
93
|
-
end
|
94
|
-
|
95
90
|
if ENV['CLASS'].to_s == ''
|
96
91
|
puts '='*90, 'USAGE', '='*90, import_model_desc, ""
|
97
92
|
exit(1)
|
@@ -121,11 +116,6 @@ namespace :tire do
|
|
121
116
|
|
122
117
|
desc import_all_desc
|
123
118
|
task :all do
|
124
|
-
if defined?(Rails)
|
125
|
-
puts "[IMPORT] Rails detected, loading environment..."
|
126
|
-
Rake::Task["environment"].invoke
|
127
|
-
end
|
128
|
-
|
129
119
|
dir = ENV['DIR'].to_s != '' ? ENV['DIR'] : Rails.root.join("app/models")
|
130
120
|
params = eval(ENV['PARAMS'].to_s) || {}
|
131
121
|
|
@@ -134,7 +124,7 @@ namespace :tire do
|
|
134
124
|
require path
|
135
125
|
|
136
126
|
model_filename = path[/#{Regexp.escape(dir.to_s)}\/([^\.]+).rb/, 1]
|
137
|
-
klass = model_filename.
|
127
|
+
klass = model_filename.camelize.constantize
|
138
128
|
|
139
129
|
# Skip if the class doesn't have Tire integration
|
140
130
|
next unless klass.respond_to?(:tire)
|
data/lib/tire/version.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
module Tire
|
2
|
-
VERSION = "0.
|
2
|
+
VERSION = "0.6.0"
|
3
3
|
|
4
4
|
CHANGELOG =<<-END
|
5
5
|
IMPORTANT CHANGES LATELY:
|
6
6
|
|
7
|
-
* Fixed
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
* Improved the
|
7
|
+
* Fixed incorrect inflection in the Rake import tasks
|
8
|
+
* Added support for `geo_distance` facets
|
9
|
+
* Added support for the `custom_filters_score` query
|
10
|
+
* Added a custom strategy option to <Model.import>
|
11
|
+
* Allow the `:wrapper` option to be passed to Tire.search consistently
|
12
|
+
* Improved the Mongoid importing strategy
|
13
|
+
* Merge returned `fields` with `_source` if both are returned
|
14
|
+
* Removed the deprecated `text` query
|
15
|
+
* [FIX] Rescue HTTP client specific connection errors in MyModel#create_elasticsearch_index
|
16
|
+
* Added support for passing `version` in Tire::Index#store
|
17
|
+
* Added support for `_version_type` in Tire::Index#bulk
|
18
|
+
* Added ActiveModel::Serializers compatibility
|
13
19
|
END
|
14
20
|
end
|
@@ -79,6 +79,19 @@ module Tire
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
should "take external versioning into account" do
|
84
|
+
# Tire.configure { logger STDERR, level: 'verbose' }
|
85
|
+
index = Tire.index 'bulk-test-external-versioning' do
|
86
|
+
delete
|
87
|
+
create
|
88
|
+
store id: '1', title: 'A', _version: 10, _version_type: 'external'
|
89
|
+
end
|
90
|
+
|
91
|
+
response = index.bulk_store [ { id: '1', title: 'A', _version: 0, _version_type: 'external'} ]
|
92
|
+
|
93
|
+
assert_match /VersionConflictEngineException/, MultiJson.load(response.body)['items'][0]['index']['error']
|
94
|
+
end
|
82
95
|
end
|
83
96
|
|
84
97
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class CustomFiltersScoreQueriesIntegrationTest < Test::Unit::TestCase
|
6
|
+
include Test::Integration
|
7
|
+
|
8
|
+
context "Custom filters score queries" do
|
9
|
+
|
10
|
+
should "score the document based on a matching filter" do
|
11
|
+
s = Tire.search('articles-test') do
|
12
|
+
query do
|
13
|
+
custom_filters_score do
|
14
|
+
query { all }
|
15
|
+
|
16
|
+
# Give documents over 300 words a score of 3
|
17
|
+
filter do
|
18
|
+
filter :range, words: { gt: 300 }
|
19
|
+
boost 3
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
assert_equal 3, s.results[0]._score
|
26
|
+
assert_equal 1, s.results[1]._score
|
27
|
+
end
|
28
|
+
|
29
|
+
should "allow to use a script based boost factor" do
|
30
|
+
s = Tire.search('articles-test') do
|
31
|
+
query do
|
32
|
+
custom_filters_score do
|
33
|
+
query { all }
|
34
|
+
|
35
|
+
# Give documents over 300 words a score of 3
|
36
|
+
filter do
|
37
|
+
filter :range, words: { gt: 300 }
|
38
|
+
script 'doc.words.value * 2'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# p s.results.to_a.map { |r| [r.title, r.words, r._score] }
|
45
|
+
|
46
|
+
assert_equal 750, s.results[0]._score
|
47
|
+
assert_equal 1, s.results[1]._score
|
48
|
+
end
|
49
|
+
|
50
|
+
should "allow to define multiple score factors" do
|
51
|
+
s = Tire.search('articles-test') do
|
52
|
+
query do
|
53
|
+
custom_filters_score do
|
54
|
+
query { all }
|
55
|
+
|
56
|
+
# The more words a document contains, the more its score is boosted
|
57
|
+
|
58
|
+
filter do
|
59
|
+
filter :range, words: { to: 10 }
|
60
|
+
boost 1
|
61
|
+
end
|
62
|
+
|
63
|
+
filter do
|
64
|
+
filter :range, words: { to: 100 }
|
65
|
+
boost 2
|
66
|
+
end
|
67
|
+
|
68
|
+
filter do
|
69
|
+
filter :range, words: { to: 150 }
|
70
|
+
boost 3
|
71
|
+
end
|
72
|
+
|
73
|
+
filter do
|
74
|
+
filter :range, words: { to: 250 }
|
75
|
+
boost 5
|
76
|
+
end
|
77
|
+
|
78
|
+
filter do
|
79
|
+
filter :range, words: { to: 350 }
|
80
|
+
boost 7
|
81
|
+
end
|
82
|
+
|
83
|
+
filter do
|
84
|
+
filter :range, words: { from: 350 }
|
85
|
+
boost 10
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# p s.results.to_a.map { |r| [r.title, r.words, r._score] }
|
92
|
+
|
93
|
+
assert_equal 'Three', s.results[0].title
|
94
|
+
assert_equal 375, s.results[0].words
|
95
|
+
assert_equal 10, s.results[0]._score
|
96
|
+
|
97
|
+
assert_equal 5, s.results[1]._score
|
98
|
+
assert_equal 5, s.results[2]._score
|
99
|
+
assert_equal 3, s.results[3]._score
|
100
|
+
assert_equal 3, s.results[4]._score
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -39,6 +39,38 @@ module Tire
|
|
39
39
|
assert_equal 1, s.results.count
|
40
40
|
end
|
41
41
|
|
42
|
+
context "when passing the wrapper option" do
|
43
|
+
class ::MyCustomWrapper < Tire::Results::Item
|
44
|
+
def title_size
|
45
|
+
self.title.size
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
should "be allowed when passing a block" do
|
50
|
+
s = Tire.search 'articles-test', wrapper: ::MyCustomWrapper do
|
51
|
+
query { match :title, 'one' }
|
52
|
+
end
|
53
|
+
|
54
|
+
assert_equal ::MyCustomWrapper, s.options[:wrapper]
|
55
|
+
|
56
|
+
assert_instance_of ::MyCustomWrapper, s.results.first
|
57
|
+
assert_equal 3, s.results.first.title_size
|
58
|
+
end
|
59
|
+
|
60
|
+
should "be allowed when not passing a block" do
|
61
|
+
s = Tire.search(
|
62
|
+
'articles-test',
|
63
|
+
payload: { query: { match: { title: 'one' } } },
|
64
|
+
wrapper: ::MyCustomWrapper
|
65
|
+
)
|
66
|
+
|
67
|
+
assert_equal ::MyCustomWrapper, s.options[:wrapper]
|
68
|
+
|
69
|
+
assert_instance_of ::MyCustomWrapper, s.results.first
|
70
|
+
assert_equal 3, s.results.first.title_size
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
42
74
|
end
|
43
75
|
|
44
76
|
end
|
@@ -194,6 +194,53 @@ module Tire
|
|
194
194
|
|
195
195
|
end
|
196
196
|
|
197
|
+
context "geo distance" do
|
198
|
+
setup do
|
199
|
+
@index = Tire.index('bars-test') do
|
200
|
+
delete
|
201
|
+
create :mappings => {
|
202
|
+
:bar => {
|
203
|
+
:properties => {
|
204
|
+
:name => { :type => 'string' },
|
205
|
+
:location => { :type => 'geo_point', :lat_lon => true }
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
store :type => 'bar',
|
211
|
+
:name => 'one',
|
212
|
+
:location => {:lat => 53.54412, :lon => 9.94021}
|
213
|
+
store :type => 'bar',
|
214
|
+
:name => 'two',
|
215
|
+
:location => {:lat => 53.54421, :lon => 9.94673}
|
216
|
+
store :type => 'bar',
|
217
|
+
:name => 'three',
|
218
|
+
:location => {:lat => 53.55099, :lon => 10.02527}
|
219
|
+
refresh
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
teardown { @index.delete }
|
224
|
+
|
225
|
+
should "return aggregated values for all results" do
|
226
|
+
s = Tire.search('bars-test') do
|
227
|
+
query { all }
|
228
|
+
facet 'geo' do
|
229
|
+
geo_distance :location,
|
230
|
+
{:lat => 53.54507, :lon => 9.95309},
|
231
|
+
[{:to => 1}, {:from => 1, :to => 10}, {:from => 50}],
|
232
|
+
unit: 'km'
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
facets = s.results.facets['geo']['ranges']
|
237
|
+
assert_equal 3, facets.size, facets.inspect
|
238
|
+
assert_equal 2, facets.entries[0]['total_count'], facets.inspect
|
239
|
+
assert_equal 1, facets.entries[1]['total_count'], facets.inspect
|
240
|
+
assert_equal 0, facets.entries[2]['total_count'], facets.inspect
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
197
244
|
context "statistical" do
|
198
245
|
|
199
246
|
should "return computed statistical data on a numeric field" do
|
@@ -10,13 +10,18 @@ module Tire
|
|
10
10
|
should "allow easy access to returned documents" do
|
11
11
|
q = 'title:one'
|
12
12
|
s = Tire.search('articles-test') { query { string q } }
|
13
|
+
|
13
14
|
assert_equal 'One', s.results.first.title
|
14
15
|
assert_equal 'ruby', s.results.first.tags[0]
|
15
16
|
end
|
16
17
|
|
17
18
|
should "allow easy access to returned documents with limited fields" do
|
18
19
|
q = 'title:one'
|
19
|
-
s = Tire.search('articles-test')
|
20
|
+
s = Tire.search('articles-test') do
|
21
|
+
query { string q }
|
22
|
+
fields :title
|
23
|
+
end
|
24
|
+
|
20
25
|
assert_equal 'One', s.results.first.title
|
21
26
|
assert_nil s.results.first.tags
|
22
27
|
end
|
@@ -27,11 +32,36 @@ module Tire
|
|
27
32
|
query { string q }
|
28
33
|
fields 'title', 'tags'
|
29
34
|
end
|
35
|
+
|
30
36
|
assert_equal 'One', s.results.first.title
|
31
37
|
assert_equal 'ruby', s.results.first.tags[0]
|
32
38
|
assert_nil s.results.first.published_on
|
33
39
|
end
|
34
40
|
|
41
|
+
should "return script fields" do
|
42
|
+
s = Tire.search('articles-test') do
|
43
|
+
query { string 'title:one' }
|
44
|
+
fields :title
|
45
|
+
script_field :words_double, :script => "doc.words.value * 2"
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_equal 'One', s.results.first.title
|
49
|
+
assert_equal 250, s.results.first.words_double
|
50
|
+
end
|
51
|
+
|
52
|
+
should "return specific fields, script fields and _source fields" do
|
53
|
+
# Tire.configure { logger STDERR, level: 'debug' }
|
54
|
+
|
55
|
+
s = Tire.search('articles-test') do
|
56
|
+
query { string 'title:one' }
|
57
|
+
fields :title, :_source
|
58
|
+
script_field :words_double, :script => "doc.words.value * 2"
|
59
|
+
end
|
60
|
+
|
61
|
+
assert_equal 'One', s.results.first.title
|
62
|
+
assert_equal 250, s.results.first.words_double
|
63
|
+
end
|
64
|
+
|
35
65
|
should "iterate results with hits" do
|
36
66
|
s = Tire.search('articles-test') { query { string 'title:one' } }
|
37
67
|
|
@@ -43,6 +43,10 @@ module Tire
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
should "have __host_unreachable_exceptions" do
|
47
|
+
assert_respond_to Client::RestClient, :__host_unreachable_exceptions
|
48
|
+
end
|
49
|
+
|
46
50
|
end
|
47
51
|
|
48
52
|
if defined?(Curl)
|
@@ -81,6 +85,10 @@ module Tire
|
|
81
85
|
threads.each { |t| t.join() }
|
82
86
|
end
|
83
87
|
|
88
|
+
should "have __host_unreachable_exceptions" do
|
89
|
+
assert_respond_to Client::RestClient, :__host_unreachable_exceptions
|
90
|
+
end
|
91
|
+
|
84
92
|
end
|
85
93
|
|
86
94
|
end
|
data/test/unit/index_test.rb
CHANGED
@@ -335,6 +335,16 @@ module Tire
|
|
335
335
|
@index.store({:title => 'Test'}, {:replication => 'async'})
|
336
336
|
end
|
337
337
|
|
338
|
+
should "extract the version from options" do
|
339
|
+
Configuration.client.expects(:post).
|
340
|
+
with do |url, payload|
|
341
|
+
assert_equal "#{Configuration.url}/dummy/document/?version=123", url
|
342
|
+
end.
|
343
|
+
returns(mock_response('{"ok":true,"_id":"test"}'))
|
344
|
+
|
345
|
+
@index.store({:title => 'Test'}, {:version => 123})
|
346
|
+
end
|
347
|
+
|
338
348
|
context "document with ID" do
|
339
349
|
|
340
350
|
should "store a Hash under its ID property" do
|
@@ -671,26 +681,44 @@ module Tire
|
|
671
681
|
@index.bulk :delete, [ {:id => '1', :title => 'One'}, {:id => '2', :title => 'Two'} ]
|
672
682
|
end
|
673
683
|
|
674
|
-
should "serialize meta parameters
|
684
|
+
should "serialize meta parameters into payload header" do
|
675
685
|
Configuration.client.
|
676
686
|
expects(:post).
|
677
687
|
with do |url, payload|
|
678
688
|
# print payload
|
679
689
|
lines = payload.split("\n")
|
680
|
-
|
681
|
-
assert_match /"_routing":"
|
682
|
-
|
683
|
-
|
690
|
+
|
691
|
+
assert_match /"_routing":"A"/, lines[0]
|
692
|
+
|
693
|
+
assert_match /"_ttl":"1d"/, lines[2]
|
694
|
+
assert ! lines[2].include?('"_:parent"')
|
695
|
+
|
696
|
+
assert_match /"_version":"1234"/, lines[4]
|
697
|
+
assert ! lines[4].include?('"_:routing"')
|
698
|
+
|
699
|
+
assert_match /"_version_type":"external"/, lines[6]
|
700
|
+
assert ! lines[6].include?('"_:garbage"')
|
701
|
+
|
702
|
+
assert_match /"_percolate":"color:green"/, lines[8]
|
703
|
+
|
704
|
+
assert_match /"_parent":"5678"/, lines[10]
|
705
|
+
|
706
|
+
assert_match /"_timestamp":"2013-02-15 11:00:33"/, lines[12]
|
707
|
+
|
708
|
+
true
|
684
709
|
end.
|
685
710
|
returns(mock_response('{}'), 200)
|
686
711
|
|
687
712
|
@index.bulk :index,
|
688
713
|
[
|
689
|
-
|
690
|
-
|
691
|
-
|
714
|
+
{:id => '1', :title => 'One', :_routing => 'A'},
|
715
|
+
{:id => '2', :title => 'Two', :_ttl => '1d', :_parent => false},
|
716
|
+
{:id => '3', :title => 'Three', :_version => '1234', :_routing => ""},
|
717
|
+
{:id => '4', :title => 'Four', :_version_type => 'external', :_garbage => "stuff"},
|
718
|
+
{:id => '5', :title => 'Five', :_percolate => 'color:green'},
|
719
|
+
{:id => '6', :title => 'Six', :_parent => '5678'},
|
720
|
+
{:id => '7', :title => 'Seven', :_timestamp => '2013-02-15 11:00:33'},
|
692
721
|
]
|
693
|
-
|
694
722
|
end
|
695
723
|
|
696
724
|
should "pass URL parameters such as refresh or consistency" do
|