load_balanced_tire 0.1
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/.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
|