tire 0.1.16 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -36,7 +36,7 @@ module Tire
36
36
  end
37
37
 
38
38
  assert_equal 1, s.results.count
39
- assert_equal 3, s.results.facets['tags'].size
39
+ assert_equal 3, s.results.facets['tags']['terms'].size
40
40
  end
41
41
 
42
42
  end
@@ -32,7 +32,6 @@ module Tire
32
32
  store :id => 1, :content => content
33
33
  refresh
34
34
  end
35
- sleep(0.5)
36
35
 
37
36
  s = Tire.search('highlight-test') do
38
37
  query { string 'fox' }
@@ -6,7 +6,7 @@ module Tire
6
6
  include Test::Integration
7
7
 
8
8
  context "Default mapping" do
9
- teardown { Tire.index('mapped-index').delete }
9
+ teardown { Tire.index('mapped-index').delete; sleep 0.1 }
10
10
 
11
11
  should "create and return the default mapping" do
12
12
 
@@ -14,8 +14,8 @@ module Tire
14
14
  create
15
15
  store :type => :article, :title => 'One'
16
16
  refresh
17
+ sleep 1
17
18
  end
18
- sleep 1.5
19
19
 
20
20
  assert_equal 'string', index.mapping['article']['properties']['title']['type'], index.mapping.inspect
21
21
  assert_nil index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
@@ -23,14 +23,13 @@ module Tire
23
23
  end
24
24
 
25
25
  context "Creating index with mapping" do
26
- teardown { Tire.index('mapped-index').delete; sleep 1 }
26
+ teardown { Tire.index('mapped-index').delete; sleep 0.1 }
27
27
 
28
28
  should "create the specified mapping" do
29
29
 
30
30
  index = Tire.index 'mapped-index' do
31
31
  create :mappings => { :article => { :properties => { :title => { :type => 'string', :boost => 2.0, :store => 'yes' } } } }
32
32
  end
33
- sleep 1
34
33
 
35
34
  # p index.mapping
36
35
  assert_equal 2.0, index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
@@ -20,7 +20,7 @@ module Tire
20
20
  should "register query as a Hash" do
21
21
  query = { :query => { :query_string => { :query => 'warning' } } }
22
22
  assert @index.register_percolator_query('alert', query)
23
- Tire.index('_percolator').refresh; sleep 0.1
23
+ Tire.index('_percolator').refresh
24
24
 
25
25
  percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
26
26
  assert percolator
@@ -28,7 +28,7 @@ module Tire
28
28
 
29
29
  should "register query as block" do
30
30
  assert @index.register_percolator_query('alert') { string 'warning' }
31
- Tire.index('_percolator').refresh; sleep 0.1
31
+ Tire.index('_percolator').refresh
32
32
 
33
33
  percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
34
34
  assert percolator
@@ -37,11 +37,11 @@ module Tire
37
37
  should "unregister a query" do
38
38
  query = { :query => { :query_string => { :query => 'warning' } } }
39
39
  assert @index.register_percolator_query('alert', query)
40
- Tire.index('_percolator').refresh; sleep 0.1
40
+ Tire.index('_percolator').refresh
41
41
  assert Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
42
42
 
43
43
  assert @index.unregister_percolator_query('alert')
44
- Tire.index('_percolator').refresh; sleep 0.1
44
+ Tire.index('_percolator').refresh
45
45
 
46
46
  assert_raise(RestClient::ResourceNotFound) do
47
47
  Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
@@ -55,7 +55,7 @@ module Tire
55
55
  @index.register_percolator_query('alert') { string 'warning' }
56
56
  @index.register_percolator_query('gantz') { string '"y u no match"' }
57
57
  @index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
58
- Tire.index('_percolator').refresh; sleep 0.1
58
+ Tire.index('_percolator').refresh
59
59
  end
60
60
 
61
61
  should "return an empty array when no query matches" do
@@ -79,7 +79,7 @@ module Tire
79
79
  @index.register_percolator_query('alert') { string 'warning' }
80
80
  @index.register_percolator_query('gantz') { string '"y u no match"' }
81
81
  @index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
82
- Tire.index('_percolator').refresh; sleep 0.1
82
+ Tire.index('_percolator').refresh
83
83
  end
84
84
 
85
85
  should "return an empty array when no query matches" do
@@ -22,13 +22,31 @@ module Tire
22
22
  two = PersistentArticle.create :id => 2, :title => 'Two'
23
23
 
24
24
  PersistentArticle.index.refresh
25
- sleep(1.5)
26
25
 
27
26
  results = PersistentArticle.find [1, 2]
28
27
 
29
28
  assert_equal 2, results.size
30
29
 
31
30
  end
31
+
32
+ context "with pagination" do
33
+
34
+ setup do
35
+ 1.upto(9) { |number| PersistentArticle.create :title => "Test#{number}" }
36
+ PersistentArticle.elasticsearch_index.refresh
37
+ end
38
+
39
+ should "find first page with five results" do
40
+ results = PersistentArticle.search( :per_page => 5, :page => 1 ) { query { all } }
41
+ assert_equal 5, results.size
42
+
43
+ assert_equal 2, results.total_pages
44
+ assert_equal 1, results.current_page
45
+ assert_equal nil, results.previous_page
46
+ assert_equal 2, results.next_page
47
+ end
48
+ end
49
+
32
50
  end
33
51
 
34
52
  end
@@ -30,6 +30,15 @@ module Tire
30
30
  assert_equal 4, search(q).results.count
31
31
  end
32
32
 
33
+ should "pass options to query definition" do
34
+ s = Tire.search 'articles-test' do
35
+ query do
36
+ string 'ruby python', :default_operator => 'AND'
37
+ end
38
+ end
39
+ assert_equal 1, s.results.count
40
+ end
41
+
33
42
  end
34
43
 
35
44
  private
@@ -21,6 +21,17 @@ module Tire
21
21
  assert_nil s.results.first.tags
22
22
  end
23
23
 
24
+ should "allow to retrieve multiple fields" do
25
+ q = 'title:one'
26
+ s = Tire.search('articles-test') do
27
+ query { string q }
28
+ fields 'title', 'tags'
29
+ end
30
+ assert_equal 'One', s.results.first.title
31
+ assert_equal 'ruby', s.results.first.tags[0]
32
+ assert_nil s.results.first.published_on
33
+ end
34
+
24
35
  end
25
36
 
26
37
  end
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+
4
+ class ActiveRecordArticle < ActiveRecord::Base
5
+ has_many :comments, :class_name => "ActiveRecordComment", :foreign_key => "article_id"
6
+ has_many :stats, :class_name => "ActiveRecordStat", :foreign_key => "article_id"
7
+
8
+ include Tire::Model::Search
9
+ include Tire::Model::Callbacks
10
+
11
+ mapping do
12
+ indexes :title, :type => 'string', :boost => 10, :analyzer => 'snowball'
13
+ indexes :created_at, :type => 'date'
14
+
15
+ indexes :comments do
16
+ indexes :author
17
+ indexes :body
18
+ end
19
+ end
20
+
21
+ def to_indexed_json
22
+ {
23
+ :title => title,
24
+ :length => length,
25
+
26
+ :comments => comments.map { |c| { :_type => 'active_record_comment',
27
+ :_id => c.id,
28
+ :author => c.author,
29
+ :body => c.body } },
30
+ :stats => stats.map { |s| { :pageviews => s.pageviews } }
31
+ }.to_json
32
+ end
33
+
34
+ def length
35
+ title.length
36
+ end
37
+
38
+ def comment_authors
39
+ comments.map(&:author).to_sentence
40
+ end
41
+ end
42
+
43
+ class ActiveRecordComment < ActiveRecord::Base
44
+ belongs_to :article, :class_name => "ActiveRecordArticle", :foreign_key => "article_id"
45
+ end
46
+
47
+ class ActiveRecordStat < ActiveRecord::Base
48
+ belongs_to :article, :class_name => "ActiveRecordArticle", :foreign_key => "article_id"
49
+ end
@@ -34,6 +34,8 @@ module Test::Integration
34
34
  URL = "http://localhost:9200"
35
35
 
36
36
  def setup
37
+ begin; Object.send(:remove_const, :Rails); rescue; end
38
+
37
39
  begin
38
40
  ::RestClient.get URL
39
41
  rescue Errno::ECONNREFUSED
@@ -181,7 +181,10 @@ module Tire
181
181
  setup do
182
182
  Configuration.reset :wrapper
183
183
 
184
- Configuration.client.stubs(:post).with("#{Configuration.url}/dummy/article/", '{"title":"Test"}').
184
+ Configuration.client.stubs(:post).with do |url, payload|
185
+ url == "#{Configuration.url}/dummy/article/" &&
186
+ payload =~ /"title":"Test"/
187
+ end.
185
188
  returns(mock_response('{"ok":true,"_id":"id-1"}'))
186
189
  @index.store :type => 'article', :title => 'Test'
187
190
  end
@@ -319,7 +322,7 @@ module Tire
319
322
  end
320
323
  end
321
324
 
322
- should "raise exception when collection item does not have ID" do
325
+ should "display error message when collection item does not have ID" do
323
326
  Configuration.client.expects(:post).with { |url, json| url == "#{Configuration.url}/_bulk" }
324
327
  STDERR.expects(:puts).once
325
328
 
@@ -487,7 +490,7 @@ module Tire
487
490
  Configuration.client.expects(:put).with do |url, payload|
488
491
  payload = MultiJson.decode(payload)
489
492
  url == "#{Configuration.url}/_percolator/dummy/my-query" &&
490
- payload['query']['query_string']['query'] == 'foo'
493
+ payload['query']['query_string']['query'] == 'foo' &&
491
494
  payload['tags'] == ['alert']
492
495
  end.
493
496
  returns(mock_response('{
@@ -548,13 +551,21 @@ module Tire
548
551
  context "while storing document" do
549
552
 
550
553
  should "percolate document against all registered queries" do
551
- Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=*", '{"title":"Test"}').
554
+ Configuration.client.expects(:post).
555
+ with do |url, payload|
556
+ url == "#{Configuration.url}/dummy/article/?percolate=*" &&
557
+ payload =~ /"title":"Test"/
558
+ end.
552
559
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
553
560
  @index.store( {:type => 'article', :title => 'Test'}, {:percolate => true} )
554
561
  end
555
562
 
556
563
  should "percolate document against specific queries" do
557
- Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=tag:alerts", '{"title":"Test"}').
564
+ Configuration.client.expects(:post).
565
+ with do |url, payload|
566
+ url == "#{Configuration.url}/dummy/article/?percolate=tag:alerts" &&
567
+ payload =~ /"title":"Test"/
568
+ end.
558
569
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
559
570
  response = @index.store( {:type => 'article', :title => 'Test'}, {:percolate => 'tag:alerts'} )
560
571
  assert_equal response['matches'], ['alerts']
@@ -90,20 +90,18 @@ module Tire
90
90
  ActiveModelArticle.elasticsearch_index.refresh
91
91
  end
92
92
 
93
- should "wrap results in proper class with ID and score and not change the original wrapper" do
93
+ should "wrap results in instances of the wrapper class" do
94
94
  response = { 'hits' => { 'hits' => [{'_id' => 1, '_score' => 0.8, '_source' => { 'title' => 'Article' }}] } }
95
95
  Configuration.client.expects(:get).returns(mock_response(response.to_json))
96
96
 
97
97
  collection = ActiveModelArticle.search 'foo'
98
98
  assert_instance_of Results::Collection, collection
99
99
 
100
- assert_equal Results::Item, Tire::Configuration.wrapper
101
-
102
100
  document = collection.first
103
101
 
104
- assert_instance_of ActiveModelArticle, document
105
- assert_not_nil document._score
106
- assert_equal 1, document.id
102
+ assert_instance_of Results::Item, document
103
+ assert_not_nil document._score
104
+ assert_equal 1, document.id
107
105
  assert_equal 'Article', document.title
108
106
  end
109
107
 
@@ -129,6 +127,15 @@ module Tire
129
127
  end
130
128
  end
131
129
 
130
+ should "allow to pass :page and :per_page options" do
131
+ Tire::Search::Search.any_instance.expects(:size).with(10)
132
+ Tire::Search::Search.any_instance.expects(:from).with(20)
133
+
134
+ ActiveModelArticle.search :per_page => 10, :page => 3 do
135
+ query { string 'foo' }
136
+ end
137
+ end
138
+
132
139
  end
133
140
 
134
141
  context "searching with query string" do
@@ -551,6 +558,38 @@ module Tire
551
558
 
552
559
  end
553
560
 
561
+ context "Results::Item" do
562
+
563
+ setup do
564
+ module ::Rails
565
+ end
566
+
567
+ class ::FakeRailsModel
568
+ extend ActiveModel::Naming
569
+ include ActiveModel::Conversion
570
+ def self.find(*args); new; end
571
+ end
572
+
573
+ @document = Results::Item.new :id => 1, :_type => 'fake_rails_model', :title => 'Test'
574
+ end
575
+
576
+ should "load the 'real' instance from the corresponding model" do
577
+ assert_respond_to @document, :load
578
+ assert_instance_of FakeRailsModel, @document.load
579
+ end
580
+
581
+ should "pass the ID to the corresponding model's find method" do
582
+ FakeRailsModel.expects(:find).with(1).returns(FakeRailsModel.new)
583
+ @document.load
584
+ end
585
+
586
+ should "pass the options to the corresponding model's find method" do
587
+ FakeRailsModel.expects(:find).with(1, {:include => 'everything'}).returns(FakeRailsModel.new)
588
+ @document.load :include => 'everything'
589
+ end
590
+
591
+ end
592
+
554
593
  end
555
594
 
556
595
  end
@@ -6,7 +6,8 @@ module Tire
6
6
 
7
7
  context "Collection" do
8
8
  setup do
9
- Configuration.reset :wrapper
9
+ begin; Object.send(:remove_const, :Rails); rescue; end
10
+ Configuration.reset
10
11
  @default_response = { 'hits' => { 'hits' => [{'_id' => 1, '_score' => 1, '_source' => {:title => 'Test'}},
11
12
  {'_id' => 2},
12
13
  {'_id' => 3}] } }
@@ -110,7 +111,7 @@ module Tire
110
111
  # Underlying issue: https://github.com/karmi/tire/pull/31#issuecomment-1340967
111
112
  #
112
113
  setup do
113
- Configuration.reset :wrapper
114
+ Configuration.reset
114
115
  @default_response = { 'hits' => { 'hits' =>
115
116
  [ { '_id' => 1, '_score' => 0.5, '_index' => 'testing', '_type' => 'article',
116
117
  'fields' => {
@@ -187,6 +188,54 @@ module Tire
187
188
 
188
189
  end
189
190
 
191
+ context "with eager loading" do
192
+ setup do
193
+ @response = { 'hits' => { 'hits' => [ {'_id' => 1, '_type' => 'active_record_article'},
194
+ {'_id' => 2, '_type' => 'active_record_article'},
195
+ {'_id' => 3, '_type' => 'active_record_article'}] } }
196
+ ActiveRecordArticle.stubs(:inspect).returns("<ActiveRecordArticle>")
197
+ end
198
+
199
+ should "load the records via model find method from database" do
200
+ ActiveRecordArticle.expects(:find).with([1,2,3]).
201
+ returns([ Results::Item.new(:id => 3),
202
+ Results::Item.new(:id => 1),
203
+ Results::Item.new(:id => 2) ])
204
+ Results::Collection.new(@response, :load => true).results
205
+ end
206
+
207
+ should "pass the :load option Hash to model find metod" do
208
+ ActiveRecordArticle.expects(:find).with([1,2,3], :include => 'comments').
209
+ returns([ Results::Item.new(:id => 3),
210
+ Results::Item.new(:id => 1),
211
+ Results::Item.new(:id => 2) ])
212
+ Results::Collection.new(@response, :load => { :include => 'comments' }).results
213
+ end
214
+
215
+ should "preserve the order of records returned from search" do
216
+ ActiveRecordArticle.expects(:find).with([1,2,3]).
217
+ returns([ Results::Item.new(:id => 3),
218
+ Results::Item.new(:id => 1),
219
+ Results::Item.new(:id => 2) ])
220
+ assert_equal [1,2,3], Results::Collection.new(@response, :load => true).results.map(&:id)
221
+ end
222
+
223
+ should "raise error when model class cannot be inferred from _type" do
224
+ assert_raise(NameError) do
225
+ response = { 'hits' => { 'hits' => [ {'_id' => 1, '_type' => 'hic_sunt_leones'}] } }
226
+ Results::Collection.new(response, :load => true).results
227
+ end
228
+ end
229
+
230
+ should "raise error when _type is missing" do
231
+ assert_raise(NoMethodError) do
232
+ response = { 'hits' => { 'hits' => [ {'_id' => 1}] } }
233
+ Results::Collection.new(response, :load => true).results
234
+ end
235
+ end
236
+
237
+ end
238
+
190
239
  end
191
240
 
192
241
  end
@@ -1,9 +1,17 @@
1
1
  require 'test_helper'
2
2
 
3
3
  module Tire
4
-
5
4
  class ResultsItemTest < Test::Unit::TestCase
6
5
 
6
+ # ActiveModel compatibility tests
7
+ #
8
+ def setup
9
+ super
10
+ begin; Object.send(:remove_const, :Rails); rescue; end
11
+ @model = Results::Item.new :title => 'Test'
12
+ end
13
+ include ActiveModel::Lint::Tests
14
+
7
15
  context "Item" do
8
16
 
9
17
  setup do
@@ -57,6 +65,61 @@ module Tire
57
65
  assert_equal 'Kafka', @document.author.name
58
66
  end
59
67
 
68
+ should "wrap arrays" do
69
+ @document = Results::Item.new :stats => [1, 2, 3]
70
+ assert_equal [1, 2, 3], @document.stats
71
+ end
72
+
73
+ should "wrap hashes in arrays" do
74
+ @document = Results::Item.new :comments => [{:title => 'one'}, {:title => 'two'}]
75
+ assert_equal 2, @document.comments.size
76
+ assert_instance_of Results::Item, @document.comments.first
77
+ assert_equal 'one', @document.comments.first.title
78
+ assert_equal 'two', @document.comments.last.title
79
+ end
80
+
81
+ should "be an Item instance" do
82
+ assert_instance_of Tire::Results::Item, @document
83
+ end
84
+
85
+ should "be convertible to hash" do
86
+ assert_instance_of Hash, @document.to_hash
87
+ end
88
+
89
+ should "be inspectable" do
90
+ assert_match /<Item title|Item author/, @document.inspect
91
+ end
92
+
93
+ context "within Rails" do
94
+
95
+ setup do
96
+ module ::Rails
97
+ end
98
+
99
+ class ::FakeRailsModel
100
+ extend ActiveModel::Naming
101
+ include ActiveModel::Conversion
102
+ def self.find(id, options); new; end
103
+ end
104
+
105
+ @document = Results::Item.new :id => 1, :_type => 'fake_rails_model', :title => 'Test'
106
+ end
107
+
108
+ should "be an instance of model, based on _type" do
109
+ assert_equal FakeRailsModel, @document.class
110
+ end
111
+
112
+ should "be inspectable with masquerade" do
113
+ assert_match /<Item \(FakeRailsModel\)/, @document.inspect
114
+ end
115
+
116
+ should "return proper singular and plural forms" do
117
+ assert_equal 'fake_rails_model', ActiveModel::Naming.singular(@document)
118
+ assert_equal 'fake_rails_models', ActiveModel::Naming.plural(@document)
119
+ end
120
+
121
+ end
122
+
60
123
  end
61
124
 
62
125
  end