tire 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class BooleanQueriesIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Boolean queries" do
9
+
10
+ should "allow to set multiple queries per condition" do
11
+ s = Tire.search('articles-test') do
12
+ query do
13
+ boolean do
14
+ must { term :tags, 'ruby' }
15
+ must { term :tags, 'python' }
16
+ end
17
+ end
18
+ end
19
+
20
+ assert_equal 1, s.results.size
21
+ assert_equal 'Two', s.results.first.title
22
+ end
23
+
24
+ should "allow to set multiple queries for multiple conditions" do
25
+ s = Tire.search('articles-test') do
26
+ query do
27
+ boolean do
28
+ must { term :tags, 'ruby' }
29
+ should { term :tags, 'python' }
30
+ end
31
+ end
32
+ end
33
+
34
+ assert_equal 2, s.results.size
35
+ assert_equal 'Two', s.results[0].title
36
+ assert_equal 'One', s.results[1].title
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -58,6 +58,24 @@ module Tire
58
58
 
59
59
  end
60
60
 
61
+ context "date ranges" do
62
+
63
+ should "return aggregated values for all results" do
64
+ s = Tire.search('articles-test') do
65
+ query { all }
66
+ facet 'published_on' do
67
+ range :published_on, [{:to => '2010-12-31'}, {:from => '2011-01-01', :to => '2011-01-05'}]
68
+ end
69
+ end
70
+
71
+ facets = s.results.facets['published_on']['ranges']
72
+ assert_equal 2, facets.size, facets.inspect
73
+ assert_equal 0, facets.entries[0]["count"], facets.inspect
74
+ assert_equal 5, facets.entries[1]["count"], facets.inspect
75
+ end
76
+
77
+ end
78
+
61
79
  end
62
80
 
63
81
  end
@@ -1,5 +1,7 @@
1
1
  # Example ActiveModel class with custom index name
2
2
 
3
+ require File.expand_path('../active_model_article', __FILE__)
4
+
3
5
  class ActiveModelArticleWithCustomIndexName < ActiveModelArticle
4
6
  index_name 'custom-index-name'
5
7
  end
data/test/test_helper.rb CHANGED
@@ -1,9 +1,14 @@
1
1
  require 'rubygems'
2
+
3
+ require 'pathname'
2
4
  require 'test/unit'
5
+
6
+ require 'yajl/json_gem'
7
+ require 'sqlite3'
8
+
3
9
  require 'shoulda'
4
- require 'mocha'
5
10
  require 'turn' unless ENV["TM_FILEPATH"]
6
- require 'pathname'
11
+ require 'mocha'
7
12
 
8
13
  require 'tire'
9
14
 
@@ -51,6 +51,16 @@ module Tire
51
51
  assert_nothing_raised { assert @index.refresh }
52
52
  end
53
53
 
54
+ should "open the index" do
55
+ Configuration.client.expects(:post).returns(mock_response('{"ok":true,"_shards":{}}'))
56
+ assert_nothing_raised { assert @index.open }
57
+ end
58
+
59
+ should "close the index" do
60
+ Configuration.client.expects(:post).returns(mock_response('{"ok":true,"_shards":{}}'))
61
+ assert_nothing_raised { assert @index.close }
62
+ end
63
+
54
64
  context "mapping" do
55
65
 
56
66
  should "create index with mapping" do
@@ -118,7 +128,7 @@ module Tire
118
128
 
119
129
  should "store Hash it under its ID property" do
120
130
  Configuration.client.expects(:post).with("#{Configuration.url}/dummy/document/123",
121
- Yajl::Encoder.encode({:id => 123, :title => 'Test'})).
131
+ MultiJson.encode({:id => 123, :title => 'Test'})).
122
132
  returns(mock_response('{"ok":true,"_id":"123"}'))
123
133
  @index.store :id => 123, :title => 'Test'
124
134
  end
@@ -66,7 +66,7 @@ module Tire
66
66
 
67
67
  should "log request in correct format" do
68
68
  log = (<<-"log;").gsub(/^ +/, '')
69
- # 2011-03-19 11:00:00:L [_search] (["articles", "users"])
69
+ # 2011-03-19 11:00:00:#{RUBY_VERSION < "1.9" ? "L" : "000"} [_search] (["articles", "users"])
70
70
  #
71
71
  curl -X GET http://...
72
72
 
@@ -98,7 +98,7 @@ module Tire
98
98
  }
99
99
  json;
100
100
  log = (<<-"log;").gsub(/^\s*/, '')
101
- # 2011-03-19 11:00:00:L [200 OK] (4 msec)
101
+ # 2011-03-19 11:00:00:#{RUBY_VERSION < "1.9" ? "L" : "000"} [200 OK] (4 msec)
102
102
  #
103
103
  log;
104
104
  # log += json.split.map { |line| "# #{line}" }.join("\n")
@@ -39,9 +39,9 @@ module Tire
39
39
  context "Finders" do
40
40
 
41
41
  setup do
42
- @first = { '_id' => 1, '_source' => { :title => 'First' } }
43
- @second = { '_id' => 2, '_source' => { :title => 'Second' } }
44
- @third = { '_id' => 3, '_source' => { :title => 'Third' } }
42
+ @first = { '_id' => 1, '_version' => 1, '_index' => 'persistent_articles', '_type' => 'persistent_article', '_source' => { :title => 'First' } }
43
+ @second = { '_id' => 2, '_index' => 'persistent_articles', '_type' => 'persistent_article', '_source' => { :title => 'Second' } }
44
+ @third = { '_id' => 3, '_index' => 'persistent_articles', '_type' => 'persistent_article', '_source' => { :title => 'Third' } }
45
45
  @find_all = { 'hits' => { 'hits' => [
46
46
  @first,
47
47
  @second,
@@ -62,6 +62,18 @@ module Tire
62
62
  assert_equal 'First', document.title
63
63
  end
64
64
 
65
+ should "have _type, _index, _id, _version attributes" do
66
+ Configuration.client.expects(:get).returns(mock_response(@first.to_json))
67
+ document = PersistentArticle.find 1
68
+
69
+ assert_instance_of PersistentArticle, document
70
+ assert_equal 1, document.id
71
+ assert_equal 1, document.attributes['id']
72
+ assert_equal 'persistent_articles', document._index
73
+ assert_equal 'persistent_article', document._type
74
+ assert_equal 1, document._version
75
+ end
76
+
65
77
  should "find document by string ID" do
66
78
  Configuration.client.expects(:get).returns(mock_response(@first.to_json))
67
79
  document = PersistentArticle.find '1'
@@ -184,7 +196,7 @@ module Tire
184
196
  assert_nothing_raised { article.published_on? }
185
197
  assert ! article.published_on?
186
198
  end
187
-
199
+
188
200
  should "return true for respond_to? calls for set attributes" do
189
201
  article = PersistentArticle.new :title => 'Test'
190
202
  assert article.respond_to?(:title)
@@ -235,9 +247,15 @@ module Tire
235
247
 
236
248
  context "when creating" do
237
249
 
250
+ # TODO: Find a way to mock JSON paylod for Mocha with disregard to Hash entries ordering.
251
+ # Ruby 1.9 brings ordered Hashes, so tests were failing.
252
+
238
253
  should "save the document with generated ID in the database" do
239
254
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/",
240
- '{"title":"Test","tags":["one","two"],"published_on":null}').
255
+ RUBY_VERSION < "1.9" ?
256
+ '{"title":"Test","tags":["one","two"],"published_on":null}' :
257
+ '{"published_on":null,"tags":["one","two"],"title":"Test"}'
258
+ ).
241
259
  returns(mock_response('{"ok":true,"_id":"abc123"}'))
242
260
  article = PersistentArticle.create :title => 'Test', :tags => [:one, :two]
243
261
 
@@ -247,7 +265,10 @@ module Tire
247
265
 
248
266
  should "save the document with custom ID in the database" do
249
267
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/r2d2",
250
- '{"title":"Test","id":"r2d2","tags":null,"published_on":null}').
268
+ RUBY_VERSION < "1.9" ?
269
+ '{"title":"Test","id":"r2d2","tags":null,"published_on":null}' :
270
+ '{"id":"r2d2","published_on":null,"tags":null,"title":"Test"}'
271
+ ).
251
272
  returns(mock_response('{"ok":true,"_id":"r2d2"}'))
252
273
  article = PersistentArticle.create :id => 'r2d2', :title => 'Test'
253
274
 
@@ -256,7 +277,7 @@ module Tire
256
277
 
257
278
  should "perform model validations" do
258
279
  Configuration.client.expects(:post).never
259
-
280
+
260
281
  assert ! ValidatedModel.create(:name => nil)
261
282
  end
262
283
 
@@ -266,7 +287,10 @@ module Tire
266
287
 
267
288
  should "set the id property" do
268
289
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/",
269
- {:title => 'Test', :tags => nil, :published_on => nil}.to_json).
290
+ RUBY_VERSION < "1.9" ?
291
+ {:title => 'Test', :tags => nil, :published_on => nil}.to_json :
292
+ {:published_on => nil, :tags => nil, :title => 'Test'}.to_json
293
+ ).
270
294
  returns(mock_response('{"ok":true,"_id":"1"}'))
271
295
 
272
296
  article = PersistentArticle.create :title => 'Test'
@@ -275,7 +299,10 @@ module Tire
275
299
 
276
300
  should "not set the id property if already set" do
277
301
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/123",
278
- '{"title":"Test","id":"123","tags":null,"published_on":null}').
302
+ RUBY_VERSION < "1.9" ?
303
+ '{"title":"Test","id":"123","tags":null,"published_on":null}' :
304
+ '{"id":"123","published_on":null,"tags":null,"title":"Test"}'
305
+ ).
279
306
  returns(mock_response('{"ok":true, "_id":"XXX"}'))
280
307
 
281
308
  article = PersistentArticle.create :id => '123', :title => 'Test'
@@ -290,13 +317,19 @@ module Tire
290
317
  article = PersistentArticle.new :id => 1, :title => 'Test'
291
318
 
292
319
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/1",
293
- '{"title":"Test","id":1,"tags":null,"published_on":null}').
320
+ RUBY_VERSION < "1.9" ?
321
+ '{"title":"Test","id":1,"tags":null,"published_on":null}' :
322
+ '{"id":1,"published_on":null,"tags":null,"title":"Test"}'
323
+ ).
294
324
  returns(mock_response('{"ok":true,"_id":"1"}'))
295
325
  assert article.save
296
326
 
297
327
  article.title = 'Updated'
298
328
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/1",
299
- '{"title":"Updated","id":1,"tags":null,"published_on":null}').
329
+ RUBY_VERSION < "1.9" ?
330
+ '{"title":"Updated","id":1,"tags":null,"published_on":null}' :
331
+ '{"id":1,"published_on":null,"tags":null,"title":"Updated"}'
332
+ ).
300
333
  returns(mock_response('{"ok":true,"_id":"1"}'))
301
334
  assert article.save
302
335
  end
@@ -323,7 +356,10 @@ module Tire
323
356
  article.title = 'Test'
324
357
 
325
358
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/123",
326
- '{"title":"Test","id":"123","tags":null,"published_on":null}').
359
+ RUBY_VERSION < "1.9" ?
360
+ '{"title":"Test","id":"123","tags":null,"published_on":null}' :
361
+ '{"id":"123","published_on":null,"tags":null,"title":"Test"}'
362
+ ).
327
363
  returns(mock_response('{"ok":true,"_id":"XXX"}'))
328
364
  assert article.save
329
365
  assert_equal '123', article.id
@@ -335,7 +371,10 @@ module Tire
335
371
 
336
372
  should "delete the document from the database" do
337
373
  Configuration.client.expects(:post).with("#{Configuration.url}/persistent_articles/persistent_article/123",
338
- '{"title":"Test","id":"123","tags":null,"published_on":null}').
374
+ RUBY_VERSION < "1.9" ?
375
+ '{"title":"Test","id":"123","tags":null,"published_on":null}' :
376
+ '{"id":"123","published_on":null,"tags":null,"title":"Test"}'
377
+ ).
339
378
  returns(mock_response('{"ok":true,"_id":"123"}'))
340
379
  Configuration.client.expects(:delete).with("#{Configuration.url}/persistent_articles/persistent_article/123")
341
380
 
@@ -25,6 +25,11 @@ module Tire
25
25
  assert_equal 3, Results::Collection.new(@default_response).size
26
26
  end
27
27
 
28
+ should "allow access to items" do
29
+ assert_not_nil Results::Collection.new(@default_response)[1]
30
+ assert_equal 2, Results::Collection.new(@default_response)[1][:id]
31
+ end
32
+
28
33
  should "be initialized with parsed json" do
29
34
  assert_nothing_raised do
30
35
  collection = Results::Collection.new( @default_response )
@@ -10,13 +10,18 @@ module Tire
10
10
  @document = Results::Item.new :title => 'Test', :author => { :name => 'Kafka' }
11
11
  end
12
12
 
13
- should "be initialized with a Hash" do
13
+ should "be initialized with a Hash or Hash like object" do
14
14
  assert_raise(ArgumentError) { Results::Item.new('FUUUUUUU') }
15
15
 
16
16
  assert_nothing_raised do
17
17
  d = Results::Item.new(:id => 1)
18
18
  assert_instance_of Results::Item, d
19
19
  end
20
+
21
+ assert_nothing_raised do
22
+ class AlmostHash < Hash; end
23
+ d = Results::Item.new(AlmostHash.new(:id => 1))
24
+ end
20
25
  end
21
26
 
22
27
  should "respond to :to_indexed_json" do
@@ -6,12 +6,39 @@ module Tire
6
6
 
7
7
  context "Hash" do
8
8
 
9
- should "have to_indexed_json doing the same as to_json" do
9
+ context "with no to_json method provided" do
10
+ setup do
11
+ @hash = { :one => 1}
12
+ # Undefine the `to_json` method...
13
+ class Hash; undef_method(:to_json); end
14
+ # ... and reload the extension, so it's added
15
+ load 'tire/rubyext/hash.rb'
16
+ end
17
+
18
+ should "have its own to_json method" do
19
+ assert_respond_to( @hash, :to_json )
20
+ assert_equal '{"one":1}', @hash.to_json
21
+ end
22
+
23
+ end
24
+
25
+ should "have a to_json method from Yajl" do
26
+ assert defined?(Yajl)
27
+ assert_respond_to( {}, :to_json )
28
+ assert_equal '{"one":1}', { :one => 1}.to_json
29
+ end
30
+
31
+ should "have to_indexed_json method doing the same as to_json" do
10
32
  [{}, { 1 => 2 }, { 3 => 4, 5 => 6 }, { nil => [7,8,9] }].each do |h|
11
- assert_equal Yajl::Parser.parse(h.to_json), Yajl::Parser.parse(h.to_indexed_json)
33
+ assert_equal MultiJson.decode(h.to_json), MultiJson.decode(h.to_indexed_json)
12
34
  end
13
35
  end
14
36
 
37
+ should "properly serialize Time into JSON" do
38
+ json = { :time => Time.mktime(2011, 01, 01, 11, 00).to_json }.to_json
39
+ assert_equal '"2011-01-01T11:00:00+01:00"', MultiJson.decode(json)['time']
40
+ end
41
+
15
42
  end
16
43
 
17
44
  end
@@ -51,7 +51,7 @@ module Tire::Search
51
51
  context "date histogram" do
52
52
 
53
53
  should "encode the JSON with default values" do
54
- f = Facet.new('date') { date :published_on, :interval => 'day' }
54
+ f = Facet.new('date') { date :published_on }
55
55
  assert_equal({ :date => { :date_histogram => { :field => 'published_on', :interval => 'day' } } }.to_json, f.to_json)
56
56
  end
57
57
 
@@ -62,6 +62,13 @@ module Tire::Search
62
62
 
63
63
  end
64
64
 
65
+ context "range facet" do
66
+ should "encode facet options" do
67
+ f = Facet.new('range') { range :published_on, [{:to => '2010-12-31'}, {:from => '2011-01-01', :to => '2011-05-27'}, {:from => '2011-05-28'}]}
68
+ assert_equal({ :range => { :range => { :field => 'published_on', :ranges => [{:to => '2010-12-31'}, {:from => '2011-01-01', :to => '2011-05-27'}, {:from => '2011-05-28'}] } } }.to_json, f.to_json)
69
+ end
70
+ end
71
+
65
72
  end
66
73
 
67
74
  end
@@ -10,6 +10,11 @@ module Tire::Search
10
10
  assert_respond_to Query.new, :to_json
11
11
  end
12
12
 
13
+ should "return itself as a Hash" do
14
+ assert_respond_to Query.new, :to_hash
15
+ assert_equal( { :term => { :foo => 'bar' } }, Query.new.term(:foo, 'bar').to_hash )
16
+ end
17
+
13
18
  should "allow a block to be given" do
14
19
  assert_equal( { :term => { :foo => 'bar' } }.to_json, Query.new do
15
20
  term(:foo, 'bar')
@@ -39,6 +44,16 @@ module Tire::Search
39
44
  Query.new.string('foo', :default_field => 'title') )
40
45
  end
41
46
 
47
+ should "allow set default operator when searching with a query string" do
48
+ assert_equal( { :query_string => { :query => 'foo', :default_operator => 'AND' } },
49
+ Query.new.string('foo', :default_operator => 'AND') )
50
+ end
51
+
52
+ should "allow to set options when searching with a query string" do
53
+ assert_equal( { :query_string => { :query => 'foo', :fields => ['title.*'], :use_dis_max => true } },
54
+ Query.new.string('foo', :fields => ['title.*'], :use_dis_max => true) )
55
+ end
56
+
42
57
  should "search for all documents" do
43
58
  assert_equal( { :match_all => { } }, Query.new.all )
44
59
  end
@@ -50,6 +65,56 @@ module Tire::Search
50
65
 
51
66
  end
52
67
 
68
+ context "BooleanQuery" do
69
+
70
+ should "raise ArgumentError when no block given" do
71
+ assert_raise(ArgumentError) { Query.new.boolean }
72
+ end
73
+
74
+ should "encode options" do
75
+ query = Query.new.boolean(:minimum_number_should_match => 1) do
76
+ must { string 'foo' }
77
+ end
78
+
79
+ assert_equal 1, query[:bool][:minimum_number_should_match]
80
+ end
81
+
82
+ should "wrap single query" do
83
+ assert_equal( { :bool => {:must => [{ :query_string => { :query => 'foo' } }] }},
84
+ Query.new.boolean { must { string 'foo' } } )
85
+ end
86
+
87
+ should "wrap multiple queries for the same condition" do
88
+ query = Query.new.boolean do
89
+ must { string 'foo' }
90
+ must { string 'bar' }
91
+ end
92
+
93
+ assert_equal( 2, query[:bool][:must].size, query[:bool][:must].inspect )
94
+ assert_equal( { :query_string => {:query => 'foo'} }, query[:bool][:must].first )
95
+ assert_equal( { :query_string => {:query => 'bar'} }, query[:bool][:must].last )
96
+ end
97
+
98
+ should "wrap queries for multiple conditions" do
99
+ query = Query.new.boolean do
100
+ should { string 'foo' }
101
+ must { string 'bar' }
102
+ must { string 'baz' }
103
+ must_not { string 'fuu' }
104
+ end
105
+
106
+ assert_equal 2, query[:bool][:must].size
107
+ assert_equal 1, query[:bool][:should].size
108
+ assert_equal 1, query[:bool][:must_not].size
109
+
110
+ assert_equal( { :query_string => {:query => 'foo'} }, query[:bool][:should].first )
111
+ assert_equal( { :query_string => {:query => 'bar'} }, query[:bool][:must].first )
112
+ assert_equal( { :query_string => {:query => 'baz'} }, query[:bool][:must].last )
113
+ assert_equal( { :query_string => {:query => 'fuu'} }, query[:bool][:must_not].first )
114
+ end
115
+
116
+ end
117
+
53
118
  end
54
119
 
55
120
  end
@@ -100,7 +100,7 @@ module Tire
100
100
  _score
101
101
  end
102
102
  end
103
- hash = JSON.load( s.to_json )
103
+ hash = MultiJson.decode( s.to_json )
104
104
  assert_equal [{'title' => 'desc'}, '_score'], hash['sort']
105
105
  end
106
106
 
@@ -182,7 +182,7 @@ module Tire
182
182
  size 5
183
183
  from 3
184
184
  end
185
- hash = JSON.load( s.to_json )
185
+ hash = MultiJson.decode( s.to_json )
186
186
  assert_equal 5, hash['size']
187
187
  assert_equal 3, hash['from']
188
188
  end
@@ -213,12 +213,48 @@ module Tire
213
213
  s = Search::Search.new('index') do
214
214
  fields :title
215
215
  end
216
- hash = JSON.load( s.to_json )
216
+ hash = MultiJson.decode( s.to_json )
217
217
  assert_equal 'title', hash['fields']
218
218
  end
219
219
 
220
220
  end
221
221
 
222
+ context "boolean queries" do
223
+
224
+ should "wrap other queries" do
225
+ # TODO: Try to get rid of the `boolean` method
226
+ #
227
+ # TODO: Try to get rid of multiple `should`, `must`, invocations, and wrap queries like this:
228
+ # boolean do
229
+ # should do
230
+ # string 'foo'
231
+ # string 'bar'
232
+ # end
233
+ # end
234
+ s = Search::Search.new('index') do
235
+ query do
236
+ boolean do
237
+ should { string 'foo' }
238
+ should { string 'moo' }
239
+ must { string 'title:bar' }
240
+ must { terms :tags, ['baz'] }
241
+ end
242
+ end
243
+ end
244
+
245
+ hash = MultiJson.decode(s.to_json)
246
+ query = hash['query']['bool']
247
+ # p hash
248
+
249
+ assert_equal 2, query['should'].size
250
+ assert_equal 2, query['must'].size
251
+
252
+ assert_equal( { 'query_string' => { 'query' => 'foo' } }, query['should'].first)
253
+ assert_equal( { 'terms' => { 'tags' => ['baz'] } }, query['must'].last)
254
+ end
255
+
256
+ end
257
+
222
258
  end
223
259
 
224
260
  end
data/tire.gemspec CHANGED
@@ -27,16 +27,18 @@ Gem::Specification.new do |s|
27
27
  s.add_dependency "rake", "~> 0.8.0"
28
28
  s.add_dependency "bundler", "~> 1.0.0"
29
29
  s.add_dependency "rest-client", "~> 1.6.0"
30
- s.add_dependency "yajl-ruby", "> 0.8.0"
31
- s.add_dependency "activemodel", "> 3.0.0"
30
+ s.add_dependency "multi_json", "~> 1.0"
31
+ s.add_dependency "activemodel", "~> 3.0.7"
32
32
 
33
+ s.add_development_dependency "yajl-ruby", "~> 0.8.0"
33
34
  s.add_development_dependency "turn"
34
35
  s.add_development_dependency "shoulda"
35
36
  s.add_development_dependency "mocha"
36
37
  s.add_development_dependency "sdoc"
37
38
  s.add_development_dependency "rcov"
38
- s.add_development_dependency "activerecord"
39
+ s.add_development_dependency "activerecord", "~> 3.0.7"
39
40
  s.add_development_dependency "supermodel"
41
+ s.add_development_dependency "sqlite3"
40
42
 
41
43
  s.description = <<-DESC
42
44
  Tire is a Ruby client for the ElasticSearch search engine/database.