tire 0.4.3 → 0.5.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.
Files changed (57) hide show
  1. data/.gitignore +1 -1
  2. data/.yardopts +1 -0
  3. data/README.markdown +2 -2
  4. data/examples/rails-application-template.rb +20 -6
  5. data/lib/tire.rb +2 -0
  6. data/lib/tire/alias.rb +1 -1
  7. data/lib/tire/configuration.rb +8 -0
  8. data/lib/tire/dsl.rb +69 -2
  9. data/lib/tire/index.rb +33 -20
  10. data/lib/tire/model/indexing.rb +7 -1
  11. data/lib/tire/model/persistence.rb +7 -4
  12. data/lib/tire/model/persistence/attributes.rb +1 -1
  13. data/lib/tire/model/persistence/finders.rb +4 -16
  14. data/lib/tire/model/search.rb +21 -8
  15. data/lib/tire/multi_search.rb +263 -0
  16. data/lib/tire/results/collection.rb +78 -49
  17. data/lib/tire/results/item.rb +6 -3
  18. data/lib/tire/results/pagination.rb +15 -1
  19. data/lib/tire/rubyext/ruby_1_8.rb +1 -7
  20. data/lib/tire/rubyext/uri_escape.rb +74 -0
  21. data/lib/tire/search.rb +33 -11
  22. data/lib/tire/search/facet.rb +8 -3
  23. data/lib/tire/search/filter.rb +1 -1
  24. data/lib/tire/search/highlight.rb +1 -1
  25. data/lib/tire/search/queries/match.rb +40 -0
  26. data/lib/tire/search/query.rb +42 -6
  27. data/lib/tire/search/scan.rb +1 -1
  28. data/lib/tire/search/script_field.rb +1 -1
  29. data/lib/tire/search/sort.rb +1 -1
  30. data/lib/tire/tasks.rb +17 -14
  31. data/lib/tire/version.rb +26 -8
  32. data/test/integration/active_record_searchable_test.rb +248 -129
  33. data/test/integration/boosting_queries_test.rb +32 -0
  34. data/test/integration/custom_score_queries_test.rb +1 -0
  35. data/test/integration/dsl_search_test.rb +9 -1
  36. data/test/integration/facets_test.rb +19 -6
  37. data/test/integration/match_query_test.rb +79 -0
  38. data/test/integration/multi_search_test.rb +114 -0
  39. data/test/integration/persistent_model_test.rb +58 -0
  40. data/test/models/article.rb +1 -1
  41. data/test/models/persistent_article_in_index.rb +16 -0
  42. data/test/models/persistent_article_with_defaults.rb +4 -3
  43. data/test/test_helper.rb +3 -1
  44. data/test/unit/configuration_test.rb +10 -0
  45. data/test/unit/index_test.rb +69 -27
  46. data/test/unit/model_initialization_test.rb +31 -0
  47. data/test/unit/model_persistence_test.rb +21 -7
  48. data/test/unit/model_search_test.rb +56 -5
  49. data/test/unit/multi_search_test.rb +304 -0
  50. data/test/unit/results_collection_test.rb +42 -2
  51. data/test/unit/results_item_test.rb +4 -0
  52. data/test/unit/search_facet_test.rb +35 -11
  53. data/test/unit/search_query_test.rb +96 -0
  54. data/test/unit/search_test.rb +60 -3
  55. data/test/unit/tire_test.rb +14 -0
  56. data/tire.gemspec +0 -1
  57. metadata +75 -44
@@ -126,21 +126,26 @@ module Tire
126
126
  assert_equal 1, response['tokens'].size
127
127
  end
128
128
 
129
- should "properly encode parameters" do
129
+ should "properly encode parameters for analyzer" do
130
130
  Configuration.client.expects(:get).with do |url, payload|
131
- url == "#{@index.url}/_analyze?analyzer=whitespace&pretty=true"
131
+ assert_equal "#{@index.url}/_analyze?analyzer=whitespace&pretty=true", url
132
132
  end.returns(mock_response(@mock_analyze_response))
133
133
 
134
134
  @index.analyze("foo bar", :analyzer => 'whitespace')
135
135
 
136
+ end
137
+
138
+ should "properly encode parameters for field" do
136
139
  Configuration.client.expects(:get).with do |url, payload|
137
- url == "#{@index.url}/_analyze?field=title&pretty=true"
140
+ assert_equal "#{@index.url}/_analyze?field=title&pretty=true", url
138
141
  end.returns(mock_response(@mock_analyze_response))
139
142
 
140
143
  @index.analyze("foo bar", :field => 'title')
144
+ end
141
145
 
146
+ should "properly encode format parameter" do
142
147
  Configuration.client.expects(:get).with do |url, payload|
143
- url == "#{@index.url}/_analyze?analyzer=keyword&format=text&pretty=true"
148
+ assert_equal "#{@index.url}/_analyze?analyzer=keyword&format=text&pretty=true", url
144
149
  end.returns(mock_response(@mock_analyze_response))
145
150
 
146
151
  @index.analyze("foo bar", :analyzer => 'keyword', :format => 'text')
@@ -210,21 +215,21 @@ module Tire
210
215
 
211
216
  should "set type from Hash :type property" do
212
217
  Configuration.client.expects(:post).with do |url,document|
213
- url == "#{@index.url}/article/"
218
+ assert_equal "#{@index.url}/article/", url
214
219
  end.returns(mock_response('{"ok":true,"_id":"test"}'))
215
220
  @index.store :type => 'article', :title => 'Test'
216
221
  end
217
222
 
218
223
  should "set type from Hash :_type property" do
219
224
  Configuration.client.expects(:post).with do |url,document|
220
- url == "#{@index.url}/article/"
225
+ assert_equal "#{@index.url}/article/", url
221
226
  end.returns(mock_response('{"ok":true,"_id":"test"}'))
222
227
  @index.store :_type => 'article', :title => 'Test'
223
228
  end
224
229
 
225
230
  should "set type from Object _type method" do
226
231
  Configuration.client.expects(:post).with do |url,document|
227
- url == "#{@index.url}/article/"
232
+ assert_equal "#{@index.url}/article/", url
228
233
  end.returns(mock_response('{"ok":true,"_id":"test"}'))
229
234
 
230
235
  article = Class.new do
@@ -236,7 +241,7 @@ module Tire
236
241
 
237
242
  should "set type from Object type method" do
238
243
  Configuration.client.expects(:post).with do |url,document|
239
- url == "#{@index.url}/article/"
244
+ assert_equal "#{@index.url}/article/", url
240
245
  end.returns(mock_response('{"ok":true,"_id":"test"}'))
241
246
 
242
247
  article = Class.new do
@@ -248,7 +253,7 @@ module Tire
248
253
 
249
254
  should "properly encode namespaced document types" do
250
255
  Configuration.client.expects(:post).with do |url,document|
251
- url == "#{@index.url}/my_namespace%2Fmy_model/"
256
+ assert_equal "#{@index.url}/my_namespace%2Fmy_model/", url
252
257
  end.returns(mock_response('{"ok":true,"_id":"123"}'))
253
258
 
254
259
  module MyNamespace
@@ -363,6 +368,29 @@ module Tire
363
368
  article = @index.retrieve 'my_namespace/my_model', 'id-1'
364
369
  end
365
370
 
371
+ should "allow to set routing" do
372
+ Configuration.client.expects(:get).with("#{@index.url}/article/id-1?routing=foo").
373
+ returns(mock_response('{"_id":"id-1"'))
374
+ article = @index.retrieve :article, 'id-1', :routing => 'foo'
375
+ end
376
+
377
+ should "allow to set routing and fields" do
378
+ Configuration.client.expects(:get).with do |url|
379
+ assert url.include?('routing=foo'), url
380
+ assert url.include?('fields=name'), url
381
+ end.returns(mock_response('{"_id":"id-1"'))
382
+
383
+ article = @index.retrieve :article, 'id-1', :routing => 'foo', :fields => 'name'
384
+ end
385
+
386
+ should "allow to set preference" do
387
+ Configuration.client.expects(:get).with do |url|
388
+ assert url.include?('preference=foo'), url
389
+ end.returns(mock_response('{"_id":"id-1"'))
390
+
391
+ article = @index.retrieve :article, 'id-1', :preference => 'foo'
392
+ end
393
+
366
394
  end
367
395
 
368
396
  context "when removing" do
@@ -694,8 +722,9 @@ module Tire
694
722
  query = { :query => { :query_string => { :query => 'foo' } } }
695
723
  Configuration.client.expects(:put).with do |url, payload|
696
724
  payload = MultiJson.decode(payload)
697
- url == "#{Configuration.url}/_percolator/dummy/my-query" &&
698
- payload['query']['query_string']['query'] == 'foo'
725
+ assert_equal "#{Configuration.url}/_percolator/dummy/my-query",
726
+ url
727
+ assert_equal 'foo', payload['query']['query_string']['query']
699
728
  end.
700
729
  returns(mock_response('{
701
730
  "ok" : true,
@@ -711,8 +740,9 @@ module Tire
711
740
  should "register percolator query as a block" do
712
741
  Configuration.client.expects(:put).with do |url, payload|
713
742
  payload = MultiJson.decode(payload)
714
- url == "#{Configuration.url}/_percolator/dummy/my-query" &&
715
- payload['query']['query_string']['query'] == 'foo'
743
+ assert_equal "#{Configuration.url}/_percolator/dummy/my-query",
744
+ url
745
+ assert_equal 'foo', payload['query']['query_string']['query']
716
746
  end.
717
747
  returns(mock_response('{
718
748
  "ok" : true,
@@ -733,9 +763,10 @@ module Tire
733
763
 
734
764
  Configuration.client.expects(:put).with do |url, payload|
735
765
  payload = MultiJson.decode(payload)
736
- url == "#{Configuration.url}/_percolator/dummy/my-query" &&
737
- payload['query']['query_string']['query'] == 'foo' &&
738
- payload['tags'] == ['alert']
766
+ assert_equal "#{Configuration.url}/_percolator/dummy/my-query",
767
+ url
768
+ assert_equal 'foo', payload['query']['query_string']['query']
769
+ assert_equal ['alert'], payload['tags']
739
770
  end.
740
771
  returns(mock_response('{
741
772
  "ok" : true,
@@ -757,8 +788,8 @@ module Tire
757
788
  should "percolate document against all registered queries" do
758
789
  Configuration.client.expects(:get).with do |url,payload|
759
790
  payload = MultiJson.decode(payload)
760
- url == "#{@index.url}/document/_percolate" &&
761
- payload['doc']['title'] == 'Test'
791
+ assert_equal "#{@index.url}/document/_percolate", url
792
+ assert_equal 'Test', payload['doc']['title']
762
793
  end.
763
794
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
764
795
 
@@ -769,8 +800,8 @@ module Tire
769
800
  should "percolate a typed document against all registered queries" do
770
801
  Configuration.client.expects(:get).with do |url,payload|
771
802
  payload = MultiJson.decode(payload)
772
- url == "#{@index.url}/article/_percolate" &&
773
- payload['doc']['title'] == 'Test'
803
+ assert_equal "#{@index.url}/article/_percolate", url
804
+ assert_equal 'Test', payload['doc']['title']
774
805
  end.
775
806
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
776
807
 
@@ -782,9 +813,9 @@ module Tire
782
813
  Configuration.client.expects(:get).with do |url,payload|
783
814
  payload = MultiJson.decode(payload)
784
815
  # p [url, payload]
785
- url == "#{@index.url}/document/_percolate" &&
786
- payload['doc']['title'] == 'Test' &&
787
- payload['query']['query_string']['query'] == 'tag:alerts'
816
+ assert_equal "#{@index.url}/document/_percolate", url
817
+ assert_equal 'Test', payload['doc']['title']
818
+ assert_equal 'tag:alerts', payload['query']['query_string']['query']
788
819
  end.
789
820
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
790
821
 
@@ -797,8 +828,7 @@ module Tire
797
828
  should "percolate document against all registered queries" do
798
829
  Configuration.client.expects(:post).
799
830
  with do |url, payload|
800
- url == "#{@index.url}/article/?percolate=*" &&
801
- payload =~ /"title":"Test"/
831
+ assert_equal "#{@index.url}/article/?percolate=%2A", url
802
832
  end.
803
833
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
804
834
  @index.store( {:type => 'article', :title => 'Test'}, {:percolate => true} )
@@ -807,8 +837,7 @@ module Tire
807
837
  should "percolate document against specific queries" do
808
838
  Configuration.client.expects(:post).
809
839
  with do |url, payload|
810
- url == "#{@index.url}/article/?percolate=tag:alerts" &&
811
- payload =~ /"title":"Test"/
840
+ assert_equal "#{@index.url}/article/?percolate=tag%3Aalerts", url
812
841
  end.
813
842
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
814
843
  response = @index.store( {:type => 'article', :title => 'Test'}, {:percolate => 'tag:alerts'} )
@@ -819,6 +848,19 @@ module Tire
819
848
 
820
849
  end
821
850
 
851
+ context "when passing parent document ID" do
852
+
853
+ should "set the :parent option in the request parameters" do
854
+ Configuration.client.expects(:post).
855
+ with do |url, payload|
856
+ assert_equal "#{Configuration.url}/dummy/document/?parent=1234", url
857
+ end.
858
+ returns(mock_response('{"ok":true,"_id":"test"}'))
859
+
860
+ @index.store({:title => 'Test'}, {:parent => 1234})
861
+ end
862
+ end
863
+
822
864
  context "reindexing" do
823
865
  setup do
824
866
  @results = {
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ class ModelWithIncorrectMapping
4
+ extend ActiveModel::Naming
5
+ include Tire::Model::Search
6
+ include Tire::Model::Callbacks
7
+
8
+ tire do
9
+ mapping do
10
+ indexes :title, :type => 'boo'
11
+ end
12
+ end
13
+ end
14
+
15
+ module Tire
16
+ module Model
17
+
18
+ class ModelInitializationTest < Test::Unit::TestCase
19
+
20
+ context "Model initialization" do
21
+
22
+ should "display a warning when creating the index fails" do
23
+ STDERR.expects(:puts)
24
+ result = ModelWithIncorrectMapping.create_elasticsearch_index
25
+ assert ! result, result.inspect
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -24,16 +24,16 @@ module Tire
24
24
  setup do
25
25
  Model::Search.index_prefix 'prefix'
26
26
  end
27
-
27
+
28
28
  teardown do
29
29
  Model::Search.index_prefix nil
30
30
  end
31
-
31
+
32
32
  should "have configured prefix in index_name" do
33
33
  assert_equal 'prefix_persistent_articles', PersistentArticle.index_name
34
34
  assert_equal 'prefix_persistent_articles', PersistentArticle.new(:title => 'Test').index_name
35
35
  end
36
-
36
+
37
37
  end
38
38
 
39
39
  should "have document_type" do
@@ -125,8 +125,13 @@ module Tire
125
125
  assert_equal ids.size, documents.count
126
126
  end
127
127
 
128
- should "find all documents" do
129
- Configuration.client.stubs(:get).returns(mock_response(@find_all.to_json))
128
+ should "find all documents with correct type" do
129
+ Configuration.client.expects(:get).
130
+ with do |url,payload|
131
+ assert_equal "#{Configuration.url}/persistent_articles/persistent_article/_search", url
132
+ end.
133
+ times(3).
134
+ returns(mock_response(@find_all.to_json))
130
135
  documents = PersistentArticle.all
131
136
 
132
137
  assert_equal 3, documents.count
@@ -134,8 +139,12 @@ module Tire
134
139
  assert_equal PersistentArticle.find(:all).map { |e| e.id }, PersistentArticle.all.map { |e| e.id }
135
140
  end
136
141
 
137
- should "find first document" do
138
- Configuration.client.expects(:get).returns(mock_response(@find_first.to_json))
142
+ should "find first document with correct type" do
143
+ Configuration.client.expects(:get).
144
+ with do |url,payload|
145
+ assert_equal "#{Configuration.url}/persistent_articles/persistent_article/_search?size=1", url
146
+ end.
147
+ returns(mock_response(@find_first.to_json))
139
148
  document = PersistentArticle.first
140
149
 
141
150
  assert_equal 'First', document.attributes['title']
@@ -201,6 +210,11 @@ module Tire
201
210
  assert_equal false, article.hidden
202
211
  end
203
212
 
213
+ should "evaluate lambdas as default values" do
214
+ article = PersistentArticleWithDefaults.new
215
+ assert_equal Time.now.year, article.created_at.year
216
+ end
217
+
204
218
  should "not affect default value" do
205
219
  article = PersistentArticleWithDefaults.new :title => 'Test'
206
220
  article.tags << "ruby"
@@ -19,7 +19,7 @@ module Tire
19
19
  context "Model::Search" do
20
20
 
21
21
  setup do
22
- @stub = stub('search') { stubs(:query).returns(self); stubs(:perform).returns(self); stubs(:results).returns([]) }
22
+ @stub = stub('search') { stubs(:query).returns(self); stubs(:perform).returns(self); stubs(:results).returns([]); stubs(:size).returns(true) }
23
23
  Tire::Index.any_instance.stubs(:exists?).returns(false)
24
24
  end
25
25
 
@@ -39,7 +39,7 @@ module Tire
39
39
  should "limit searching in index for documents matching the model 'document_type'" do
40
40
  Tire::Search::Search.
41
41
  expects(:new).
42
- with(ActiveModelArticle.index_name, { :type => ActiveModelArticle.document_type }).
42
+ with('active_model_articles', { :type => 'active_model_article' }).
43
43
  returns(@stub).
44
44
  twice
45
45
 
@@ -121,6 +121,16 @@ module Tire
121
121
  assert_equal 'Article', document.title
122
122
  end
123
123
 
124
+ should "not pass the search option as URL parameter" do
125
+ Configuration.client.
126
+ expects(:get).with do |url, payload|
127
+ assert ! url.include?('sort')
128
+ end.
129
+ returns( mock_response({ 'hits' => { 'hits' => [] } }.to_json) )
130
+
131
+ ActiveModelArticle.search(@q, :sort => 'title:DESC').results
132
+ end
133
+
124
134
  context "searching with a block" do
125
135
  setup do
126
136
  Tire::Search::Search.any_instance.expects(:perform).returns(@stub)
@@ -191,14 +201,14 @@ module Tire
191
201
  should "allow to specify sort direction" do
192
202
  Tire::Search::Sort.any_instance.expects(:by).with('title', 'DESC')
193
203
 
194
- ActiveModelArticle.search @q, :order => 'title DESC'
204
+ ActiveModelArticle.search @q, :order => 'title:DESC'
195
205
  end
196
206
 
197
207
  should "allow to specify more fields to sort on" do
198
208
  Tire::Search::Sort.any_instance.expects(:by).with('title', 'DESC')
199
209
  Tire::Search::Sort.any_instance.expects(:by).with('author.name', nil)
200
210
 
201
- ActiveModelArticle.search @q, :order => ['title DESC', 'author.name']
211
+ ActiveModelArticle.search @q, :order => ['title:DESC', 'author.name']
202
212
  end
203
213
 
204
214
  should "allow to specify number of results per page" do
@@ -237,6 +247,25 @@ module Tire
237
247
 
238
248
  end
239
249
 
250
+ context "multi search" do
251
+
252
+ should "perform search request within corresponding index and type" do
253
+ Tire::Search::Multi::Search.
254
+ expects(:new).
255
+ with do |index, options, block|
256
+ assert_equal 'active_model_articles', index
257
+ assert_equal 'active_model_article', options[:type]
258
+ end.
259
+ returns( mock(:results => []) )
260
+
261
+ ActiveModelArticle.multi_search do
262
+ search 'foo'
263
+ search 'xoo'
264
+ end
265
+ end
266
+
267
+ end
268
+
240
269
  should "not set callback when hooks are missing" do
241
270
  @model = ActiveModelArticle.new
242
271
  @model.expects(:update_elasticsearch_index).never
@@ -610,7 +639,7 @@ module Tire
610
639
 
611
640
  end
612
641
 
613
- should "serialize mapped properties when mapping procs are set" do
642
+ should "evaluate :as mapping options passed as strings or procs" do
614
643
  class ::ModelWithMappingProcs
615
644
  extend ActiveModel::Naming
616
645
  extend ActiveModel::Callbacks
@@ -646,6 +675,28 @@ module Tire
646
675
  assert_equal 3, document['three']
647
676
  end
648
677
 
678
+ should "index :as mapping options passed as arbitrary objects" do
679
+ class ::ModelWithMappingOptionAsObject
680
+ extend ActiveModel::Naming
681
+ extend ActiveModel::Callbacks
682
+ include ActiveModel::Serialization
683
+ include Tire::Model::Search
684
+
685
+ mapping do
686
+ indexes :one, :as => [1, 2, 3]
687
+ end
688
+
689
+ attr_reader :attributes
690
+
691
+ def initialize(attributes = {}); @attributes = attributes; end
692
+ end
693
+
694
+ model = ::ModelWithMappingOptionAsObject.new
695
+ document = MultiJson.decode(model.to_indexed_json)
696
+
697
+ assert_equal [1, 2, 3], document['one']
698
+ end
699
+
649
700
  end
650
701
 
651
702
  context "with percolation" do
@@ -0,0 +1,304 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+ class MultiSearchTest < Test::Unit::TestCase
5
+
6
+ context "Multi::Search" do
7
+ setup { Configuration.reset }
8
+
9
+ should "be initialized with index" do
10
+ @search = Tire::Search::Multi::Search.new 'foo'
11
+ assert_equal ['foo'], @search.indices
12
+ end
13
+
14
+ context "search definition" do
15
+ setup do
16
+ @search_definitions = Tire::Search::Multi::SearchDefinitions.new
17
+ end
18
+
19
+ should "be enumerable" do
20
+ assert_respond_to @search_definitions, :each
21
+ assert_respond_to @search_definitions, :size
22
+ end
23
+
24
+ should "allow to add definition" do
25
+ @search_definitions << { :name => 'foo', :search => { 'query' => 'bar' } }
26
+ assert_equal 1, @search_definitions.size
27
+ end
28
+
29
+ should "return definition" do
30
+ @search_definitions << { :name => 'foo', :search => { 'query' => 'bar' } }
31
+ assert_equal 'bar', @search_definitions['foo']['query']
32
+ end
33
+
34
+ should "have names" do
35
+ @search_definitions << { :name => 'foo', :search => { 'query' => 'bar' } }
36
+ assert_equal ['foo'], @search_definitions.names
37
+ end
38
+ end
39
+
40
+ context "search results" do
41
+ setup do
42
+ @search_definitions = Tire::Search::Multi::SearchDefinitions.new
43
+ @search_definitions << { :name => 'foo', :search => Tire::Search::Search.new }
44
+ @responses = [{ 'hits' => { 'hits' => [{'_id' => 1},
45
+ {'_id' => 2},
46
+ {'_id' => 3}] }
47
+ }]
48
+
49
+ @results = Tire::Search::Multi::Results.new @search_definitions, @responses
50
+ end
51
+
52
+ should "be enumerable" do
53
+ assert_respond_to @results, :each
54
+ assert_respond_to @results, :each_pair
55
+ assert_respond_to @results, :each_with_index
56
+ assert_respond_to @results, :size
57
+ end
58
+
59
+ should "return named results" do
60
+ assert_instance_of Tire::Results::Collection, @results['foo']
61
+ assert_equal 1, @results['foo'].first.id
62
+ end
63
+
64
+ should "return nil for incorrect index" do
65
+ assert_nil @results['moo']
66
+ assert_nil @results[999]
67
+ end
68
+
69
+ should "iterate over results" do
70
+ @results.each do |results|
71
+ assert_instance_of Tire::Results::Collection, results
72
+ end
73
+ end
74
+
75
+ should "iterate over named results" do
76
+ @results.each_pair do |name, results|
77
+ assert_equal 'foo', name
78
+ assert_instance_of Tire::Results::Collection, results
79
+ end
80
+ end
81
+
82
+ should "be serializable to Hash" do
83
+ assert_instance_of Tire::Results::Collection, @results.to_hash['foo']
84
+ end
85
+
86
+ should "pass search options to collection" do
87
+ @search_definitions = Tire::Search::Multi::SearchDefinitions.new
88
+ @search_definitions << { :name => 'foo', :search => Tire::Search::Search.new(nil, :foo => 'bar') }
89
+ @responses = [{ 'hits' => { 'hits' => [{'_id' => 1}] } }]
90
+
91
+ @results = Tire::Search::Multi::Results.new @search_definitions, @responses
92
+
93
+ assert_equal 'bar', @results['foo'].options[:foo]
94
+ end
95
+
96
+ context "error responses" do
97
+ setup do
98
+ @search_definitions = Tire::Search::Multi::SearchDefinitions.new
99
+ @search_definitions << { :name => 'foo', :search => Tire::Search::Search.new }
100
+ @search_definitions << { :name => 'xoo', :search => Tire::Search::Search.new }
101
+ @responses = [{ 'hits' => { 'hits' => [{'_id' => 1},
102
+ {'_id' => 2},
103
+ {'_id' => 3}] }
104
+ },
105
+ {'error' => 'SearchPhaseExecutionException ...'}
106
+ ]
107
+
108
+ @results = Tire::Search::Multi::Results.new @search_definitions, @responses
109
+ end
110
+
111
+ should "return success/failure state" do
112
+ assert_equal true, @results['foo'].success?
113
+ assert_equal 3, @results['foo'].size
114
+ assert_equal true, @results['xoo'].failure?
115
+ end
116
+ end
117
+ end
118
+
119
+ context "URL" do
120
+
121
+ should "have no index and type by default" do
122
+ @search = Tire::Search::Multi::Search.new
123
+ assert_equal '/_msearch', @search.path
124
+ end
125
+
126
+ should "have an index" do
127
+ @search = Tire::Search::Multi::Search.new 'foo'
128
+ assert_equal '/foo/_msearch', @search.path
129
+ end
130
+
131
+ should "have multiple indices" do
132
+ @search = Tire::Search::Multi::Search.new ['foo', 'bar']
133
+ assert_equal '/foo,bar/_msearch', @search.path
134
+ end
135
+
136
+ should "have index and type" do
137
+ @search = Tire::Search::Multi::Search.new 'foo', :type => 'bar'
138
+ assert_equal '/foo/bar/_msearch', @search.path
139
+ end
140
+
141
+ should "have index and multiple types" do
142
+ @search = Tire::Search::Multi::Search.new 'foo', :type => ['bar', 'bam']
143
+ assert_equal '/foo/bar,bam/_msearch', @search.path
144
+ end
145
+
146
+ should "contain host" do
147
+ @search = Tire::Search::Multi::Search.new
148
+ assert_equal 'http://localhost:9200/_msearch', @search.url
149
+ end
150
+
151
+ end
152
+
153
+ context "URL params" do
154
+
155
+ should "be empty when no params passed" do
156
+ @search = Tire::Search::Multi::Search.new 'foo'
157
+ assert_equal '', @search.params
158
+ end
159
+
160
+ should "serialize parameters" do
161
+ @search = Tire::Search::Multi::Search.new 'foo', :search_type => 'count'
162
+ assert_equal '?search_type=count', @search.params
163
+ end
164
+
165
+ end
166
+
167
+ context "search request" do
168
+ setup do
169
+ @search = Tire::Search::Multi::Search.new
170
+ end
171
+
172
+ should "have a collection of searches" do
173
+ @search.search :one
174
+ assert_instance_of Tire::Search::Multi::SearchDefinitions, @search.searches
175
+ end
176
+
177
+ should "be initialized with name" do
178
+ @search.search :one
179
+ assert_instance_of Tire::Search::Search, @search.searches[:one]
180
+ end
181
+
182
+ should "be initialized with options" do
183
+ @search.search :foo => 'bar'
184
+ assert_equal 'bar', @search.searches[0].options[:foo]
185
+ end
186
+
187
+ should "be initialized with name and options" do
188
+ @search.search :one, :foo => 'bar'
189
+ assert_instance_of Tire::Search::Search, @search.searches[:one]
190
+ assert_equal 'bar', @search.searches[:one].options[:foo]
191
+ assert_equal 'bar', @search.searches(:one).options[:foo]
192
+ end
193
+
194
+ should "pass the index name and options to the search object" do
195
+ @search.search :index => 'foo', :type => 'bar'
196
+ assert_equal ['foo'], @search.searches[0].indices
197
+ assert_equal ['bar'], @search.searches[0].types
198
+ assert_equal ['foo'], @search.searches(0).indices
199
+ end
200
+
201
+ should "pass options to Search object" do
202
+ @search.search :search_type => 'count'
203
+ assert_equal 'count', @search.searches[0].options[:search_type]
204
+ end
205
+
206
+ end
207
+
208
+ context "payload" do
209
+
210
+ should "serialize search payload header and body as Array" do
211
+ @search = Tire::Search::Multi::Search.new do
212
+ search :index => 'foo' do
213
+ query { all }
214
+ size 100
215
+ end
216
+ end
217
+
218
+ assert_equal 1, @search.searches.size
219
+ assert_equal 2, @search.to_array.size
220
+
221
+ assert_equal( 'foo', @search.to_array[0][:index] )
222
+ assert_equal( {:match_all => {}}, @search.to_array[1][:query] )
223
+ assert_equal( 100, @search.to_array[1][:size] )
224
+ end
225
+
226
+ should "serialize search payload header and body for multiple searches" do
227
+ @search = Tire::Search::Multi::Search.new do
228
+ search(:index => 'foo') { query { all } }
229
+ search(:index => 'ooo') { query { term :foo, 'bar' } }
230
+ end
231
+
232
+ assert_equal 2, @search.searches.size
233
+ assert_equal 4, @search.to_array.size
234
+
235
+ assert_equal 'foo', @search.to_array[0][:index]
236
+ assert_equal 'ooo', @search.to_array[2][:index]
237
+ assert_equal 'match_all', @search.to_array[1][:query].keys.first.to_s
238
+ assert_equal 'term', @search.to_array[3][:query].keys.first.to_s
239
+ end
240
+
241
+ should "serialize search parameters" do
242
+ @search = Tire::Search::Multi::Search.new do
243
+ search(:search_type => 'count') { query { all } }
244
+ end
245
+
246
+ assert_equal 'count', @search.to_array[0][:search_type]
247
+ end
248
+
249
+ should "serialize search payload as a string" do
250
+ @search = Tire::Search::Multi::Search.new do
251
+ search(:index => 'foo') { query { all } }
252
+ end
253
+
254
+ assert_equal 2, @search.to_payload.split("\n").size
255
+ end
256
+
257
+ should "end with a new line" do
258
+ @search = Tire::Search::Multi::Search.new { search(:index => 'foo') { query { all } } }
259
+ assert_match /.*\n$/, @search.to_payload
260
+ end
261
+
262
+ should "leave header empty when no index is passed" do
263
+ @search = Tire::Search::Multi::Search.new do
264
+ search() { query { all } }
265
+ end
266
+
267
+ assert_equal( {}, @search.to_array.first )
268
+ end
269
+
270
+ end
271
+
272
+ context "perform" do
273
+ setup do
274
+ @search = Tire::Search::Multi::Search.new do
275
+ search(:index => 'foo') do
276
+ query { all }
277
+ size 100
278
+ end
279
+ end
280
+ @response = mock_response '{ "responses" : [{"took":1,"hits":{"total":0,"hits":[]}}] }', 200
281
+ end
282
+
283
+ should "perform the request" do
284
+ Configuration.client.expects(:get).
285
+ with do |url, payload|
286
+ assert_equal 'http://localhost:9200/_msearch', url
287
+ assert payload.include?('match_all')
288
+ end.
289
+ returns(@response)
290
+ @search.perform
291
+ end
292
+
293
+ should "log the request" do
294
+ Configuration.client.expects(:get).returns(@response)
295
+ @search.expects(:logged)
296
+ @search.perform
297
+ end
298
+
299
+ end
300
+
301
+ end
302
+
303
+ end
304
+ end