tire 0.5.8 → 0.6.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.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
|