load_balanced_tire 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +14 -0
- data/.travis.yml +29 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +760 -0
- data/Rakefile +78 -0
- data/examples/rails-application-template.rb +249 -0
- data/examples/tire-dsl.rb +876 -0
- data/lib/tire.rb +55 -0
- data/lib/tire/alias.rb +296 -0
- data/lib/tire/configuration.rb +30 -0
- data/lib/tire/dsl.rb +43 -0
- data/lib/tire/http/client.rb +62 -0
- data/lib/tire/http/clients/curb.rb +61 -0
- data/lib/tire/http/clients/faraday.rb +71 -0
- data/lib/tire/http/response.rb +27 -0
- data/lib/tire/index.rb +361 -0
- data/lib/tire/logger.rb +60 -0
- data/lib/tire/model/callbacks.rb +40 -0
- data/lib/tire/model/import.rb +26 -0
- data/lib/tire/model/indexing.rb +128 -0
- data/lib/tire/model/naming.rb +100 -0
- data/lib/tire/model/percolate.rb +99 -0
- data/lib/tire/model/persistence.rb +71 -0
- data/lib/tire/model/persistence/attributes.rb +143 -0
- data/lib/tire/model/persistence/finders.rb +66 -0
- data/lib/tire/model/persistence/storage.rb +69 -0
- data/lib/tire/model/search.rb +307 -0
- data/lib/tire/results/collection.rb +114 -0
- data/lib/tire/results/item.rb +86 -0
- data/lib/tire/results/pagination.rb +54 -0
- data/lib/tire/rubyext/hash.rb +8 -0
- data/lib/tire/rubyext/ruby_1_8.rb +7 -0
- data/lib/tire/rubyext/symbol.rb +11 -0
- data/lib/tire/search.rb +188 -0
- data/lib/tire/search/facet.rb +74 -0
- data/lib/tire/search/filter.rb +28 -0
- data/lib/tire/search/highlight.rb +37 -0
- data/lib/tire/search/query.rb +186 -0
- data/lib/tire/search/scan.rb +114 -0
- data/lib/tire/search/script_field.rb +23 -0
- data/lib/tire/search/sort.rb +25 -0
- data/lib/tire/tasks.rb +135 -0
- data/lib/tire/utils.rb +17 -0
- data/lib/tire/version.rb +22 -0
- data/test/fixtures/articles/1.json +1 -0
- data/test/fixtures/articles/2.json +1 -0
- data/test/fixtures/articles/3.json +1 -0
- data/test/fixtures/articles/4.json +1 -0
- data/test/fixtures/articles/5.json +1 -0
- data/test/integration/active_model_indexing_test.rb +51 -0
- data/test/integration/active_model_searchable_test.rb +114 -0
- data/test/integration/active_record_searchable_test.rb +446 -0
- data/test/integration/boolean_queries_test.rb +43 -0
- data/test/integration/count_test.rb +34 -0
- data/test/integration/custom_score_queries_test.rb +88 -0
- data/test/integration/dis_max_queries_test.rb +68 -0
- data/test/integration/dsl_search_test.rb +22 -0
- data/test/integration/explanation_test.rb +44 -0
- data/test/integration/facets_test.rb +259 -0
- data/test/integration/filtered_queries_test.rb +66 -0
- data/test/integration/filters_test.rb +63 -0
- data/test/integration/fuzzy_queries_test.rb +20 -0
- data/test/integration/highlight_test.rb +64 -0
- data/test/integration/index_aliases_test.rb +122 -0
- data/test/integration/index_mapping_test.rb +43 -0
- data/test/integration/index_store_test.rb +96 -0
- data/test/integration/index_update_document_test.rb +111 -0
- data/test/integration/mongoid_searchable_test.rb +309 -0
- data/test/integration/percolator_test.rb +111 -0
- data/test/integration/persistent_model_test.rb +130 -0
- data/test/integration/prefix_query_test.rb +43 -0
- data/test/integration/query_return_version_test.rb +70 -0
- data/test/integration/query_string_test.rb +52 -0
- data/test/integration/range_queries_test.rb +36 -0
- data/test/integration/reindex_test.rb +46 -0
- data/test/integration/results_test.rb +39 -0
- data/test/integration/scan_test.rb +56 -0
- data/test/integration/script_fields_test.rb +38 -0
- data/test/integration/sort_test.rb +36 -0
- data/test/integration/text_query_test.rb +39 -0
- data/test/models/active_model_article.rb +31 -0
- data/test/models/active_model_article_with_callbacks.rb +49 -0
- data/test/models/active_model_article_with_custom_document_type.rb +7 -0
- data/test/models/active_model_article_with_custom_index_name.rb +7 -0
- data/test/models/active_record_models.rb +122 -0
- data/test/models/article.rb +15 -0
- data/test/models/mongoid_models.rb +97 -0
- data/test/models/persistent_article.rb +11 -0
- data/test/models/persistent_article_in_namespace.rb +12 -0
- data/test/models/persistent_article_with_casting.rb +28 -0
- data/test/models/persistent_article_with_defaults.rb +11 -0
- data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
- data/test/models/supermodel_article.rb +17 -0
- data/test/models/validated_model.rb +11 -0
- data/test/test_helper.rb +93 -0
- data/test/unit/active_model_lint_test.rb +17 -0
- data/test/unit/configuration_test.rb +74 -0
- data/test/unit/http_client_test.rb +76 -0
- data/test/unit/http_response_test.rb +49 -0
- data/test/unit/index_alias_test.rb +275 -0
- data/test/unit/index_test.rb +894 -0
- data/test/unit/logger_test.rb +125 -0
- data/test/unit/model_callbacks_test.rb +116 -0
- data/test/unit/model_import_test.rb +71 -0
- data/test/unit/model_persistence_test.rb +528 -0
- data/test/unit/model_search_test.rb +913 -0
- data/test/unit/results_collection_test.rb +281 -0
- data/test/unit/results_item_test.rb +162 -0
- data/test/unit/rubyext_test.rb +66 -0
- data/test/unit/search_facet_test.rb +153 -0
- data/test/unit/search_filter_test.rb +42 -0
- data/test/unit/search_highlight_test.rb +46 -0
- data/test/unit/search_query_test.rb +301 -0
- data/test/unit/search_scan_test.rb +113 -0
- data/test/unit/search_script_field_test.rb +26 -0
- data/test/unit/search_sort_test.rb +50 -0
- data/test/unit/search_test.rb +499 -0
- data/test/unit/tire_test.rb +126 -0
- data/tire.gemspec +90 -0
- metadata +549 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
module Search
|
5
|
+
class ScanTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "Scan" do
|
8
|
+
setup do
|
9
|
+
Configuration.reset
|
10
|
+
@results = {
|
11
|
+
"_scroll_id" => "abc123",
|
12
|
+
"took" => 3,
|
13
|
+
"hits" => {
|
14
|
+
"total" => 10,
|
15
|
+
"hits" => [
|
16
|
+
{ "_id" => "1", "_source" => { "title" => "Test" } }
|
17
|
+
]
|
18
|
+
}
|
19
|
+
}
|
20
|
+
@empty_results = @results.merge('hits' => {'hits' => []})
|
21
|
+
@default_response = mock_response @results.to_json, 200
|
22
|
+
end
|
23
|
+
|
24
|
+
should "initialize the search object with the indices" do
|
25
|
+
s = Scan.new(['index1', 'index2'])
|
26
|
+
assert_instance_of Tire::Search::Search, s.search
|
27
|
+
end
|
28
|
+
|
29
|
+
should "fetch the initial scroll ID" do
|
30
|
+
s = Scan.new('index1')
|
31
|
+
s.search.expects(:perform).
|
32
|
+
returns(stub :json => { '_scroll_id' => 'abc123' })
|
33
|
+
|
34
|
+
assert_equal 'abc123', s.scroll_id
|
35
|
+
end
|
36
|
+
|
37
|
+
should "perform the request lazily" do
|
38
|
+
s = Scan.new('dummy')
|
39
|
+
|
40
|
+
s.expects(:scroll_id).
|
41
|
+
returns('abc123').
|
42
|
+
at_least_once
|
43
|
+
|
44
|
+
Configuration.client.expects(:get).
|
45
|
+
with { |url,id| url =~ %r|_search/scroll.*search_type=scan| && id == 'abc123' }.
|
46
|
+
returns(@default_response).
|
47
|
+
once
|
48
|
+
|
49
|
+
assert_not_nil s.results
|
50
|
+
assert_not_nil s.response
|
51
|
+
assert_not_nil s.json
|
52
|
+
end
|
53
|
+
|
54
|
+
should "set the total and seen variables" do
|
55
|
+
s = Scan.new('dummy')
|
56
|
+
s.expects(:scroll_id).returns('abc123').at_least_once
|
57
|
+
Configuration.client.expects(:get).returns(@default_response).at_least_once
|
58
|
+
|
59
|
+
assert_equal 10, s.total
|
60
|
+
assert_equal 1, s.seen
|
61
|
+
end
|
62
|
+
|
63
|
+
should "log the request and response" do
|
64
|
+
Tire.configure { logger STDERR }
|
65
|
+
|
66
|
+
s = Scan.new('dummy')
|
67
|
+
s.expects(:scroll_id).returns('abc123').at_least_once
|
68
|
+
Configuration.client.expects(:get).returns(@default_response).at_least_once
|
69
|
+
|
70
|
+
Configuration.logger.expects(:log_request).
|
71
|
+
with { |(endpoint, params, curl)| endpoint == 'scroll' }
|
72
|
+
|
73
|
+
Configuration.logger.expects(:log_response).
|
74
|
+
with { |code, took, body| code == 200 && took == 3 && body == '1/10 (10.0%)' }
|
75
|
+
|
76
|
+
s.__perform
|
77
|
+
end
|
78
|
+
|
79
|
+
context "results" do
|
80
|
+
setup do
|
81
|
+
@search = Scan.new('dummy')
|
82
|
+
@search.expects(:results).
|
83
|
+
returns(Results::Collection.new @results).
|
84
|
+
then.
|
85
|
+
returns(Results::Collection.new @empty_results).
|
86
|
+
at_least_once
|
87
|
+
@search.results
|
88
|
+
end
|
89
|
+
|
90
|
+
should "be iterable" do
|
91
|
+
assert_respond_to @search, :each
|
92
|
+
assert_respond_to @search, :size
|
93
|
+
|
94
|
+
assert_nothing_raised do
|
95
|
+
@search.each { |batch| p batch; assert_equal 'Test', batch.first.title }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
should "be iterable by individual documents" do
|
100
|
+
assert_respond_to @search, :each_document
|
101
|
+
|
102
|
+
assert_nothing_raised do
|
103
|
+
@search.each_document { |item| assert_equal 'Test', item.title }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire::Search
|
4
|
+
|
5
|
+
class ScriptFieldTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "ScriptField" do
|
8
|
+
|
9
|
+
should "be serialized to JSON" do
|
10
|
+
assert_respond_to ScriptField.new(:test1, {}), :to_json
|
11
|
+
end
|
12
|
+
|
13
|
+
should "encode simple declarations as JSON" do
|
14
|
+
assert_equal( { :test1 => { :script => "doc['my_field_name'].value * factor",
|
15
|
+
:params => { :factor => 2.2 }, :lang => :js } }.to_json,
|
16
|
+
|
17
|
+
ScriptField.new( :test1,
|
18
|
+
{ :script => "doc['my_field_name'].value * factor",
|
19
|
+
:params => { :factor => 2.2 }, :lang => :js } ).to_json
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire::Search
|
4
|
+
|
5
|
+
class SortTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "Sort" do
|
8
|
+
|
9
|
+
should "be serialized to JSON" do
|
10
|
+
assert_respond_to Sort.new, :to_json
|
11
|
+
end
|
12
|
+
|
13
|
+
should "encode simple strings" do
|
14
|
+
assert_equal [:foo].to_json, Sort.new.by(:foo).to_json
|
15
|
+
end
|
16
|
+
|
17
|
+
should "encode method arguments" do
|
18
|
+
assert_equal [:foo => 'desc'].to_json, Sort.new.by(:foo, 'desc').to_json
|
19
|
+
end
|
20
|
+
|
21
|
+
should "encode hash" do
|
22
|
+
assert_equal [ :foo => { :reverse => true } ].to_json, Sort.new.by(:foo, :reverse => true).to_json
|
23
|
+
end
|
24
|
+
|
25
|
+
should "encode multiple sort fields in chain" do
|
26
|
+
assert_equal [:foo, :bar].to_json, Sort.new.by(:foo).by(:bar).to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
should "encode fields when passed as a block to constructor" do
|
30
|
+
s = Sort.new do
|
31
|
+
by :foo
|
32
|
+
by :bar, 'desc'
|
33
|
+
by :_score
|
34
|
+
end
|
35
|
+
assert_equal [ :foo, {:bar => 'desc'}, :_score ].to_json, s.to_json
|
36
|
+
end
|
37
|
+
|
38
|
+
should "encode fields deeper in json" do
|
39
|
+
s = Sort.new { by 'author.name' }
|
40
|
+
assert_equal [ 'author.name' ].to_json, s.to_json
|
41
|
+
|
42
|
+
s = Sort.new { by 'author.name', 'desc' }
|
43
|
+
assert_equal [ {'author.name' => 'desc'} ].to_json, s.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,499 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tire
|
4
|
+
|
5
|
+
class SearchTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
context "Search" do
|
8
|
+
setup { Configuration.reset }
|
9
|
+
|
10
|
+
should "be initialized with single index" do
|
11
|
+
s = Search::Search.new('index') { query { string 'foo' } }
|
12
|
+
assert_match %r|/index/_search|, s.url
|
13
|
+
end
|
14
|
+
|
15
|
+
should "be initialized with multiple indices" do
|
16
|
+
s = Search::Search.new(['index1','index2']) { query { string 'foo' } }
|
17
|
+
assert_match %r|/index1,index2/_search|, s.url
|
18
|
+
end
|
19
|
+
|
20
|
+
should "be initialized with multiple indices with options" do
|
21
|
+
indices = {'index1' => {:boost => 1},'index2' => {:boost => 2}}
|
22
|
+
s = Search::Search.new(indices) { query { string 'foo' } }
|
23
|
+
assert_match /index1/, s.url
|
24
|
+
assert_match /index2/, s.url
|
25
|
+
assert_equal({'index1' => 1, 'index2' => 2}, s.to_hash[:indices_boost])
|
26
|
+
end
|
27
|
+
|
28
|
+
should "be initialized with multiple indices as string" do
|
29
|
+
s = Search::Search.new(['index1,index2,index3']) { query { string 'foo' } }
|
30
|
+
assert_match %r|/index1,index2,index3/_search|, s.url
|
31
|
+
end
|
32
|
+
|
33
|
+
should "allow to search all indices by leaving index empty" do
|
34
|
+
s = Search::Search.new { query { string 'foo' } }
|
35
|
+
assert_match %r|localhost:9200/_search|, s.url
|
36
|
+
end
|
37
|
+
|
38
|
+
should "allow to limit results with document type" do
|
39
|
+
s = Search::Search.new('index', :type => 'bar') do
|
40
|
+
query { string 'foo' }
|
41
|
+
end
|
42
|
+
|
43
|
+
assert_match %r|index/bar/_search|, s.url
|
44
|
+
end
|
45
|
+
|
46
|
+
should "allow to pass search parameters" do
|
47
|
+
s = Search::Search.new('index', :routing => 123, :timeout => 1) { query { string 'foo' } }
|
48
|
+
|
49
|
+
assert ! s.params.empty?
|
50
|
+
|
51
|
+
assert_match %r|routing=123|, s.params
|
52
|
+
assert_match %r|timeout=1|, s.params
|
53
|
+
end
|
54
|
+
|
55
|
+
should "encode search parameters in the request" do
|
56
|
+
Configuration.client.expects(:get).with do |url, payload|
|
57
|
+
url.include? 'routing=123&timeout=1'
|
58
|
+
end.returns mock_response( { 'hits' => { 'hits' => [ {:_id => 1} ] } }.to_json )
|
59
|
+
|
60
|
+
Search::Search.new('index', :routing => 123, :timeout => 1) { query { string 'foo' } }.perform
|
61
|
+
end
|
62
|
+
|
63
|
+
should "encode missing params as an empty string" do
|
64
|
+
Configuration.client.expects(:get).with do |url, payload|
|
65
|
+
(! url.include? '?') && (! url.include? '&')
|
66
|
+
end.returns mock_response( { 'hits' => { 'hits' => [ {:_id => 1} ] } }.to_json )
|
67
|
+
|
68
|
+
s = Search::Search.new('index') { query { string 'foo' } }
|
69
|
+
s.perform
|
70
|
+
|
71
|
+
assert_equal '', s.params
|
72
|
+
end
|
73
|
+
|
74
|
+
should "properly encode namespaced document type" do
|
75
|
+
Configuration.client.expects(:get).with do |url, payload|
|
76
|
+
url.match %r|index/my_application%2Farticle/_search|
|
77
|
+
end.returns mock_response( { 'hits' => { 'hits' => [ {:_id => 1} ] } }.to_json )
|
78
|
+
|
79
|
+
s = Search::Search.new('index', :type => 'my_application/article') do
|
80
|
+
query { string 'foo' }
|
81
|
+
end
|
82
|
+
s.perform
|
83
|
+
|
84
|
+
assert_match %r|index/my_application%2Farticle/_search|, s.url
|
85
|
+
assert_match %r|index/my_application%2Farticle/_search|, s.to_curl
|
86
|
+
end
|
87
|
+
|
88
|
+
should "allow to pass block to query" do
|
89
|
+
Search::Query.any_instance.expects(:instance_eval)
|
90
|
+
|
91
|
+
Search::Search.new('index') do
|
92
|
+
query { string 'foo' }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
should "allow to pass block with argument to query (use variables from outer scope)" do
|
97
|
+
def foo; 'bar'; end
|
98
|
+
|
99
|
+
Search::Query.any_instance.expects(:instance_eval).never
|
100
|
+
|
101
|
+
Search::Search.new('index') do |search|
|
102
|
+
search.query do |query|
|
103
|
+
query.string foo
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
should "store indices as an array" do
|
109
|
+
s = Search::Search.new('index1') do;end
|
110
|
+
assert_equal ['index1'], s.indices
|
111
|
+
|
112
|
+
s = Search::Search.new(['index1', 'index2']) do;end
|
113
|
+
assert_equal ['index1', 'index2'], s.indices
|
114
|
+
end
|
115
|
+
|
116
|
+
should "return curl snippet for debugging" do
|
117
|
+
s = Search::Search.new('index') do
|
118
|
+
query { string 'title:foo' }
|
119
|
+
end
|
120
|
+
assert_equal %q|curl -X GET "http://localhost:9200/index/_search?pretty=true" -d | +
|
121
|
+
%q|'{"query":{"query_string":{"query":"title:foo"}}}'|,
|
122
|
+
s.to_curl
|
123
|
+
end
|
124
|
+
|
125
|
+
should "return curl snippet with multiple indices for debugging" do
|
126
|
+
s = Search::Search.new(['index_1', 'index_2']) do
|
127
|
+
query { string 'title:foo' }
|
128
|
+
end
|
129
|
+
assert_match /index_1,index_2/, s.to_curl
|
130
|
+
end
|
131
|
+
|
132
|
+
should "return itself as a Hash" do
|
133
|
+
s = Search::Search.new('index') do
|
134
|
+
query { string 'title:foo' }
|
135
|
+
end
|
136
|
+
assert_nothing_raised do
|
137
|
+
assert_instance_of Hash, s.to_hash
|
138
|
+
assert_equal "title:foo", s.to_hash[:query][:query_string][:query]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
should "allow chaining" do
|
143
|
+
assert_nothing_raised do
|
144
|
+
Search::Search.new('index').query { }.
|
145
|
+
sort { by :title, 'desc' }.
|
146
|
+
size(5).
|
147
|
+
sort { by :name, 'asc' }.
|
148
|
+
from(1).
|
149
|
+
version(true)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
should "perform the search lazily" do
|
154
|
+
response = mock_response '{"took":1,"hits":[]}', 200
|
155
|
+
Configuration.client.expects(:get).returns(response)
|
156
|
+
Results::Collection.expects(:new).returns([])
|
157
|
+
|
158
|
+
s = Search::Search.new('index')
|
159
|
+
assert_not_nil s.results
|
160
|
+
assert_not_nil s.response
|
161
|
+
assert_not_nil s.json
|
162
|
+
end
|
163
|
+
|
164
|
+
should "allow the search criteria to be chained" do
|
165
|
+
s = Search::Search.new('index').query { string 'foo' }
|
166
|
+
assert_nil s.filters, "Should NOT have filters"
|
167
|
+
|
168
|
+
s.expects(:perform).once
|
169
|
+
s.filter :term, :other_field => 'bar'
|
170
|
+
assert s.filters.size == 1, "Should have filters"
|
171
|
+
s.results
|
172
|
+
end
|
173
|
+
|
174
|
+
should "print debugging information on exception and return false" do
|
175
|
+
::RestClient::Request.any_instance.
|
176
|
+
expects(:execute).
|
177
|
+
raises(::RestClient::InternalServerError)
|
178
|
+
STDERR.expects(:puts)
|
179
|
+
|
180
|
+
s = Search::Search.new('index')
|
181
|
+
assert_raise Search::SearchRequestFailed do
|
182
|
+
s.perform
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
should "log request, but not response, when logger is set" do
|
187
|
+
Configuration.logger STDERR
|
188
|
+
|
189
|
+
Configuration.client.expects(:get).returns(mock_response( '{"took":1,"hits":[]}', 200 ))
|
190
|
+
|
191
|
+
Results::Collection.expects(:new).returns([])
|
192
|
+
Configuration.logger.expects(:log_request).returns(true)
|
193
|
+
Configuration.logger.expects(:log_response).with(200, 1, '')
|
194
|
+
|
195
|
+
Search::Search.new('index').perform
|
196
|
+
end
|
197
|
+
|
198
|
+
should "log the original exception on failed request" do
|
199
|
+
Configuration.logger STDERR
|
200
|
+
|
201
|
+
Configuration.client.expects(:get).raises(Errno::ECONNREFUSED)
|
202
|
+
Configuration.logger.expects(:log_response).with('N/A', 'N/A', '')
|
203
|
+
|
204
|
+
assert_raise Errno::ECONNREFUSED do
|
205
|
+
Search::Search.new('index').perform
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
should "allow to set the server url" do
|
210
|
+
search = Search::Search.new('indexA')
|
211
|
+
Configuration.url 'http://es1.example.com'
|
212
|
+
|
213
|
+
Configuration.client.
|
214
|
+
expects(:get).
|
215
|
+
with do |url, payload|
|
216
|
+
url == 'http://es1.example.com/indexA/_search'
|
217
|
+
end.
|
218
|
+
returns(mock_response( '{"took":1,"hits":{"total": 0, "hits" : []}}', 200 ))
|
219
|
+
|
220
|
+
search.perform
|
221
|
+
end
|
222
|
+
|
223
|
+
context "sort" do
|
224
|
+
|
225
|
+
should "allow sorting by multiple fields" do
|
226
|
+
s = Search::Search.new('index') do
|
227
|
+
sort do
|
228
|
+
by :title, 'desc'
|
229
|
+
by :_score
|
230
|
+
end
|
231
|
+
end
|
232
|
+
hash = MultiJson.decode( s.to_json )
|
233
|
+
assert_equal [{'title' => 'desc'}, '_score'], hash['sort']
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
context "facets" do
|
239
|
+
|
240
|
+
should "retrieve terms facets" do
|
241
|
+
s = Search::Search.new('index') do
|
242
|
+
facet('foo1') { terms :bar, :global => true }
|
243
|
+
facet('foo2', :global => true) { terms :bar }
|
244
|
+
facet('foo3') { terms :baz }
|
245
|
+
end
|
246
|
+
assert_equal 3, s.facets.keys.size
|
247
|
+
assert_not_nil s.facets['foo1']
|
248
|
+
assert_not_nil s.facets['foo2']
|
249
|
+
assert_not_nil s.facets['foo3']
|
250
|
+
end
|
251
|
+
|
252
|
+
should "retrieve date histogram facets" do
|
253
|
+
s = Search::Search.new('index') do
|
254
|
+
facet('date') { date :published_on }
|
255
|
+
end
|
256
|
+
assert_equal 1, s.facets.keys.size
|
257
|
+
assert_not_nil s.facets['date']
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
context "filter" do
|
263
|
+
|
264
|
+
should "allow to specify filter" do
|
265
|
+
s = Search::Search.new('index') do
|
266
|
+
filter :terms, :tags => ['foo']
|
267
|
+
end
|
268
|
+
|
269
|
+
assert_equal 1, s.filters.size
|
270
|
+
|
271
|
+
assert_not_nil s.filters.first
|
272
|
+
assert_not_nil s.filters.first[:terms]
|
273
|
+
|
274
|
+
assert_equal( {:terms => {:tags => ['foo']}}.to_json,
|
275
|
+
s.to_hash[:filter].to_json )
|
276
|
+
end
|
277
|
+
|
278
|
+
should "allow to add multiple filters" do
|
279
|
+
s = Search::Search.new('index') do
|
280
|
+
filter :terms, :tags => ['foo']
|
281
|
+
filter :term, :words => 125
|
282
|
+
end
|
283
|
+
|
284
|
+
assert_equal 2, s.filters.size
|
285
|
+
|
286
|
+
assert_not_nil s.filters.first[:terms]
|
287
|
+
assert_not_nil s.filters.last[:term]
|
288
|
+
|
289
|
+
assert_equal( { :and => [ {:terms => {:tags => ['foo']}}, {:term => {:words => 125}} ] }.to_json,
|
290
|
+
s.to_hash[:filter].to_json )
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
context "highlight" do
|
296
|
+
|
297
|
+
should "allow to specify highlight for single field" do
|
298
|
+
s = Search::Search.new('index') do
|
299
|
+
highlight :body
|
300
|
+
end
|
301
|
+
|
302
|
+
assert_not_nil s.highlight
|
303
|
+
assert_instance_of Tire::Search::Highlight, s.highlight
|
304
|
+
end
|
305
|
+
|
306
|
+
should "allow to specify highlight for more fields" do
|
307
|
+
s = Search::Search.new('index') do
|
308
|
+
highlight :body, :title
|
309
|
+
end
|
310
|
+
|
311
|
+
assert_not_nil s.highlight
|
312
|
+
assert_instance_of Tire::Search::Highlight, s.highlight
|
313
|
+
end
|
314
|
+
|
315
|
+
should "allow to specify highlight with for more fields with options" do
|
316
|
+
s = Search::Search.new('index') do
|
317
|
+
highlight :body, :title => { :fragment_size => 150, :number_of_fragments => 3 }
|
318
|
+
end
|
319
|
+
|
320
|
+
assert_not_nil s.highlight
|
321
|
+
assert_instance_of Tire::Search::Highlight, s.highlight
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
context "with version" do
|
327
|
+
|
328
|
+
should "set the version" do
|
329
|
+
s = Search::Search.new('index') do
|
330
|
+
version true
|
331
|
+
end
|
332
|
+
hash = MultiJson.decode( s.to_json )
|
333
|
+
assert_equal true, hash['version']
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
context "with from/size" do
|
339
|
+
|
340
|
+
should "set the values in request" do
|
341
|
+
s = Search::Search.new('index') do
|
342
|
+
size 5
|
343
|
+
from 3
|
344
|
+
end
|
345
|
+
hash = MultiJson.decode( s.to_json )
|
346
|
+
assert_equal 5, hash['size']
|
347
|
+
assert_equal 3, hash['from']
|
348
|
+
end
|
349
|
+
|
350
|
+
should "set the size value in options" do
|
351
|
+
Results::Collection.any_instance.stubs(:total).returns(50)
|
352
|
+
s = Search::Search.new('index') do
|
353
|
+
size 5
|
354
|
+
end
|
355
|
+
|
356
|
+
assert_equal 5, s.options[:size]
|
357
|
+
end
|
358
|
+
|
359
|
+
should "set the from value in options" do
|
360
|
+
Results::Collection.any_instance.stubs(:total).returns(50)
|
361
|
+
s = Search::Search.new('index') do
|
362
|
+
from 5
|
363
|
+
end
|
364
|
+
|
365
|
+
assert_equal 5, s.options[:from]
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
|
370
|
+
context "when limiting returned fields" do
|
371
|
+
|
372
|
+
should "set the fields limit in request" do
|
373
|
+
s = Search::Search.new('index') do
|
374
|
+
fields :title
|
375
|
+
end
|
376
|
+
hash = MultiJson.decode( s.to_json )
|
377
|
+
assert_equal ['title'], hash['fields']
|
378
|
+
end
|
379
|
+
|
380
|
+
should "take multiple fields as an Array" do
|
381
|
+
s = Search::Search.new('index') do
|
382
|
+
fields [:title, :tags]
|
383
|
+
end
|
384
|
+
hash = MultiJson.decode( s.to_json )
|
385
|
+
assert_equal ['title', 'tags'], hash['fields']
|
386
|
+
end
|
387
|
+
|
388
|
+
should "take multiple fields as splat argument" do
|
389
|
+
s = Search::Search.new('index') do
|
390
|
+
fields :title, :tags
|
391
|
+
end
|
392
|
+
hash = MultiJson.decode( s.to_json )
|
393
|
+
assert_equal ['title', 'tags'], hash['fields']
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
context "explain" do
|
399
|
+
|
400
|
+
should "default to false" do
|
401
|
+
s = Search::Search.new('index') do
|
402
|
+
end
|
403
|
+
hash = MultiJson.decode( s.to_json )
|
404
|
+
assert_nil hash['explain']
|
405
|
+
end
|
406
|
+
|
407
|
+
should "set the explain field in the request when true" do
|
408
|
+
s = Search::Search.new('index') do
|
409
|
+
explain true
|
410
|
+
end
|
411
|
+
hash = MultiJson.decode( s.to_json )
|
412
|
+
assert_equal true, hash['explain']
|
413
|
+
end
|
414
|
+
|
415
|
+
should "not set the explain field when false" do
|
416
|
+
s = Search::Search.new('index') do
|
417
|
+
explain false
|
418
|
+
end
|
419
|
+
hash = MultiJson.decode( s.to_json )
|
420
|
+
assert_nil hash['explain']
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
context "boolean queries" do
|
426
|
+
|
427
|
+
should "wrap other queries" do
|
428
|
+
# TODO: Try to get rid of the `boolean` method
|
429
|
+
#
|
430
|
+
# TODO: Try to get rid of multiple `should`, `must`, invocations, and wrap queries like this:
|
431
|
+
# boolean do
|
432
|
+
# should do
|
433
|
+
# string 'foo'
|
434
|
+
# string 'bar'
|
435
|
+
# end
|
436
|
+
# end
|
437
|
+
s = Search::Search.new('index') do
|
438
|
+
query do
|
439
|
+
boolean do
|
440
|
+
should { string 'foo' }
|
441
|
+
should { string 'moo' }
|
442
|
+
must { string 'title:bar' }
|
443
|
+
must { terms :tags, ['baz'] }
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
hash = MultiJson.decode(s.to_json)
|
449
|
+
query = hash['query']['bool']
|
450
|
+
# p hash
|
451
|
+
|
452
|
+
assert_equal 2, query['should'].size
|
453
|
+
assert_equal 2, query['must'].size
|
454
|
+
|
455
|
+
assert_equal( { 'query_string' => { 'query' => 'foo' } }, query['should'].first)
|
456
|
+
assert_equal( { 'terms' => { 'tags' => ['baz'] } }, query['must'].last)
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
context "script field" do
|
464
|
+
|
465
|
+
should "allow to specify script field" do
|
466
|
+
s = Search::Search.new('index') do
|
467
|
+
script_field :test1, :script => "doc['my_field_name'].value * 2"
|
468
|
+
end
|
469
|
+
|
470
|
+
assert_equal 1, s.script_fields.size
|
471
|
+
|
472
|
+
assert_not_nil s.script_fields
|
473
|
+
assert_not_nil s.script_fields[:test1]
|
474
|
+
|
475
|
+
assert_equal( {:test1 => { :script => "doc['my_field_name'].value * 2" }}.to_json,
|
476
|
+
s.to_hash[:script_fields].to_json )
|
477
|
+
end
|
478
|
+
|
479
|
+
should "allow to add multiple script fields" do
|
480
|
+
s = Search::Search.new('index') do
|
481
|
+
script_field :field1, :script => "doc['my_field_name'].value * 2"
|
482
|
+
script_field :field2, :script => "doc['other_field_name'].value * 3"
|
483
|
+
end
|
484
|
+
|
485
|
+
assert_equal 2, s.script_fields.size
|
486
|
+
|
487
|
+
assert_not_nil s.script_fields[:field1]
|
488
|
+
assert_not_nil s.script_fields[:field2]
|
489
|
+
|
490
|
+
assert_equal( { :field1 => { :script => "doc['my_field_name'].value * 2" }, :field2 => { :script => "doc['other_field_name'].value * 3" } }.to_json,
|
491
|
+
s.to_hash[:script_fields].to_json )
|
492
|
+
end
|
493
|
+
|
494
|
+
end
|
495
|
+
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
end
|