tire 0.1.16 → 0.2.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.
@@ -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