load_balanced_tire 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/.gitignore +14 -0
  2. data/.travis.yml +29 -0
  3. data/Gemfile +4 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.markdown +760 -0
  6. data/Rakefile +78 -0
  7. data/examples/rails-application-template.rb +249 -0
  8. data/examples/tire-dsl.rb +876 -0
  9. data/lib/tire.rb +55 -0
  10. data/lib/tire/alias.rb +296 -0
  11. data/lib/tire/configuration.rb +30 -0
  12. data/lib/tire/dsl.rb +43 -0
  13. data/lib/tire/http/client.rb +62 -0
  14. data/lib/tire/http/clients/curb.rb +61 -0
  15. data/lib/tire/http/clients/faraday.rb +71 -0
  16. data/lib/tire/http/response.rb +27 -0
  17. data/lib/tire/index.rb +361 -0
  18. data/lib/tire/logger.rb +60 -0
  19. data/lib/tire/model/callbacks.rb +40 -0
  20. data/lib/tire/model/import.rb +26 -0
  21. data/lib/tire/model/indexing.rb +128 -0
  22. data/lib/tire/model/naming.rb +100 -0
  23. data/lib/tire/model/percolate.rb +99 -0
  24. data/lib/tire/model/persistence.rb +71 -0
  25. data/lib/tire/model/persistence/attributes.rb +143 -0
  26. data/lib/tire/model/persistence/finders.rb +66 -0
  27. data/lib/tire/model/persistence/storage.rb +69 -0
  28. data/lib/tire/model/search.rb +307 -0
  29. data/lib/tire/results/collection.rb +114 -0
  30. data/lib/tire/results/item.rb +86 -0
  31. data/lib/tire/results/pagination.rb +54 -0
  32. data/lib/tire/rubyext/hash.rb +8 -0
  33. data/lib/tire/rubyext/ruby_1_8.rb +7 -0
  34. data/lib/tire/rubyext/symbol.rb +11 -0
  35. data/lib/tire/search.rb +188 -0
  36. data/lib/tire/search/facet.rb +74 -0
  37. data/lib/tire/search/filter.rb +28 -0
  38. data/lib/tire/search/highlight.rb +37 -0
  39. data/lib/tire/search/query.rb +186 -0
  40. data/lib/tire/search/scan.rb +114 -0
  41. data/lib/tire/search/script_field.rb +23 -0
  42. data/lib/tire/search/sort.rb +25 -0
  43. data/lib/tire/tasks.rb +135 -0
  44. data/lib/tire/utils.rb +17 -0
  45. data/lib/tire/version.rb +22 -0
  46. data/test/fixtures/articles/1.json +1 -0
  47. data/test/fixtures/articles/2.json +1 -0
  48. data/test/fixtures/articles/3.json +1 -0
  49. data/test/fixtures/articles/4.json +1 -0
  50. data/test/fixtures/articles/5.json +1 -0
  51. data/test/integration/active_model_indexing_test.rb +51 -0
  52. data/test/integration/active_model_searchable_test.rb +114 -0
  53. data/test/integration/active_record_searchable_test.rb +446 -0
  54. data/test/integration/boolean_queries_test.rb +43 -0
  55. data/test/integration/count_test.rb +34 -0
  56. data/test/integration/custom_score_queries_test.rb +88 -0
  57. data/test/integration/dis_max_queries_test.rb +68 -0
  58. data/test/integration/dsl_search_test.rb +22 -0
  59. data/test/integration/explanation_test.rb +44 -0
  60. data/test/integration/facets_test.rb +259 -0
  61. data/test/integration/filtered_queries_test.rb +66 -0
  62. data/test/integration/filters_test.rb +63 -0
  63. data/test/integration/fuzzy_queries_test.rb +20 -0
  64. data/test/integration/highlight_test.rb +64 -0
  65. data/test/integration/index_aliases_test.rb +122 -0
  66. data/test/integration/index_mapping_test.rb +43 -0
  67. data/test/integration/index_store_test.rb +96 -0
  68. data/test/integration/index_update_document_test.rb +111 -0
  69. data/test/integration/mongoid_searchable_test.rb +309 -0
  70. data/test/integration/percolator_test.rb +111 -0
  71. data/test/integration/persistent_model_test.rb +130 -0
  72. data/test/integration/prefix_query_test.rb +43 -0
  73. data/test/integration/query_return_version_test.rb +70 -0
  74. data/test/integration/query_string_test.rb +52 -0
  75. data/test/integration/range_queries_test.rb +36 -0
  76. data/test/integration/reindex_test.rb +46 -0
  77. data/test/integration/results_test.rb +39 -0
  78. data/test/integration/scan_test.rb +56 -0
  79. data/test/integration/script_fields_test.rb +38 -0
  80. data/test/integration/sort_test.rb +36 -0
  81. data/test/integration/text_query_test.rb +39 -0
  82. data/test/models/active_model_article.rb +31 -0
  83. data/test/models/active_model_article_with_callbacks.rb +49 -0
  84. data/test/models/active_model_article_with_custom_document_type.rb +7 -0
  85. data/test/models/active_model_article_with_custom_index_name.rb +7 -0
  86. data/test/models/active_record_models.rb +122 -0
  87. data/test/models/article.rb +15 -0
  88. data/test/models/mongoid_models.rb +97 -0
  89. data/test/models/persistent_article.rb +11 -0
  90. data/test/models/persistent_article_in_namespace.rb +12 -0
  91. data/test/models/persistent_article_with_casting.rb +28 -0
  92. data/test/models/persistent_article_with_defaults.rb +11 -0
  93. data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
  94. data/test/models/supermodel_article.rb +17 -0
  95. data/test/models/validated_model.rb +11 -0
  96. data/test/test_helper.rb +93 -0
  97. data/test/unit/active_model_lint_test.rb +17 -0
  98. data/test/unit/configuration_test.rb +74 -0
  99. data/test/unit/http_client_test.rb +76 -0
  100. data/test/unit/http_response_test.rb +49 -0
  101. data/test/unit/index_alias_test.rb +275 -0
  102. data/test/unit/index_test.rb +894 -0
  103. data/test/unit/logger_test.rb +125 -0
  104. data/test/unit/model_callbacks_test.rb +116 -0
  105. data/test/unit/model_import_test.rb +71 -0
  106. data/test/unit/model_persistence_test.rb +528 -0
  107. data/test/unit/model_search_test.rb +913 -0
  108. data/test/unit/results_collection_test.rb +281 -0
  109. data/test/unit/results_item_test.rb +162 -0
  110. data/test/unit/rubyext_test.rb +66 -0
  111. data/test/unit/search_facet_test.rb +153 -0
  112. data/test/unit/search_filter_test.rb +42 -0
  113. data/test/unit/search_highlight_test.rb +46 -0
  114. data/test/unit/search_query_test.rb +301 -0
  115. data/test/unit/search_scan_test.rb +113 -0
  116. data/test/unit/search_script_field_test.rb +26 -0
  117. data/test/unit/search_sort_test.rb +50 -0
  118. data/test/unit/search_test.rb +499 -0
  119. data/test/unit/tire_test.rb +126 -0
  120. data/tire.gemspec +90 -0
  121. 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