tire 0.1.8 → 0.1.9

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.
data/examples/tire-dsl.rb CHANGED
@@ -704,6 +704,103 @@ s = Tire.search 'articles' do
704
704
  highlight :title, :body, :options => { :tag => '<strong class="highlight">' }
705
705
  end
706
706
 
707
+ #### Percolation
708
+
709
+ # _ElasticSearch_ comes with one very interesting, and rather unique feature:
710
+ # [_percolation_](http://www.elasticsearch.org/guide/reference/api/percolate.html).
711
+
712
+ # It works in a „reverse search“ manner to regular search workflow of adding
713
+ # documents to the index and then querying them.
714
+ # Percolation allows us to register a query, and ask if a specific document
715
+ # matches it, either on demand, or immediately as the document is being indexed.
716
+
717
+ # Let's review an example for an index named _weather_.
718
+ # We will register three queries for percolation against this index.
719
+ #
720
+ index = Tire.index('weather') do
721
+ delete
722
+ create
723
+
724
+ # First, a query named _warning_,
725
+ #
726
+ register_percolator_query('warning', :tags => ['warning']) { string 'warning OR severe OR extreme' }
727
+
728
+ # a query named _tsunami_,
729
+ #
730
+ register_percolator_query('tsunami', :tags => ['tsunami']) { string 'tsunami' }
731
+
732
+ # and a query named _floods_.
733
+ #
734
+ register_percolator_query('floods', :tags => ['floods']) { string 'flood*' }
735
+
736
+ end
737
+
738
+ # Notice, that we have added a _tags_ field to the query document, because it behaves
739
+ # just like any other document in _ElasticSearch_.
740
+
741
+ # We will refresh the `_percolator` index for immediate access.
742
+ #
743
+ Tire.index('_percolator').refresh
744
+
745
+ # Now, let's _percolate_ a document containing some trigger words against all registered queries.
746
+ #
747
+ matches = index.percolate(:message => '[Warning] Extreme flooding expected after tsunami wave.')
748
+
749
+ # The result will contain, unsurprisingly, names of all the three registered queries:
750
+ #
751
+ # Matching queries: ["floods", "tsunami", "warning"]
752
+ #
753
+ puts "Matching queries: " + matches.inspect
754
+
755
+ # We can filter the executed queries with a regular _ElasticSearch_ query passed as a block to
756
+ # the `percolate` method.
757
+ #
758
+ matches = index.percolate(:message => '[Warning] Extreme flooding expected after tsunami wave.') do
759
+ # Let's use a _terms_ query against the `tags` field.
760
+ term :tags, 'tsunami'
761
+ end
762
+
763
+ # In this case, the result will contain only the name of the “tsunami” query.
764
+ #
765
+ # Matching queries: ["tsunami"]
766
+ #
767
+ puts "Matching queries: " + matches.inspect
768
+
769
+ # What if we percolate another document, without the “tsunami” trigger word?
770
+ #
771
+ matches = index.percolate(:message => '[Warning] Extreme temperatures expected.') { term :tags, 'tsunami' }
772
+
773
+ # As expected, we will get an empty array:
774
+ #
775
+ # Matching queries: []
776
+ #
777
+ puts "Matching queries: " + matches.inspect
778
+
779
+ # Well, that's of course immensely useful for real-time search systems. But, there's more.
780
+ # We can _percolate_ a document _at the same time_ it is being stored in the index,
781
+ # getting back a list of matching queries.
782
+
783
+ # Let's store a document with some trigger words in the index, and mark it for percolation.
784
+ #
785
+ response = index.store :message => '[Warning] Severe floods expected after tsunami wave.', :percolate => true
786
+
787
+ # We will get the names of all matching queries in response.
788
+ #
789
+ # Matching queries: ["floods", "tsunami", "warning"]
790
+ #
791
+ puts "Matching queries: " + response['matches'].inspect
792
+
793
+ # As with the _percolate_ example, we can filter the executed queries.
794
+ #
795
+ response = index.store :message => '[Warning] Severe floods expected after tsunami wave.',
796
+ # Let's use a simple string query for the “tsunami” tag.
797
+ :percolate => 'tags:tsunami'
798
+
799
+ # Unsurprisingly, the response will contain just the name of the “tsunami” query.
800
+ #
801
+ # Matching queries: ["tsunami"]
802
+ #
803
+ puts "Matching queries: " + response['matches'].inspect
707
804
 
708
805
  ### ActiveModel Integration
709
806
 
data/lib/tire/client.rb CHANGED
@@ -3,7 +3,7 @@ module Tire
3
3
  module Client
4
4
 
5
5
  class Base
6
- def get(url)
6
+ def get(url, data=nil)
7
7
  raise_no_method_error
8
8
  end
9
9
  def post(url, data)
@@ -21,8 +21,8 @@ module Tire
21
21
  end
22
22
 
23
23
  class RestClient < Base
24
- def self.get(url)
25
- ::RestClient.get url
24
+ def self.get(url, data=nil)
25
+ ::RestClient::Request.new(:method => :get, :url => url, :payload => data).execute
26
26
  end
27
27
  def self.post(url, data)
28
28
  ::RestClient.post url, data
data/lib/tire/index.rb CHANGED
@@ -42,6 +42,7 @@ module Tire
42
42
 
43
43
  def store(*args)
44
44
  # TODO: Infer type from the document (hash property, method)
45
+ # TODO: Refactor common logic for getting id, JSON, into private methods
45
46
 
46
47
  if args.size > 1
47
48
  (type, document = args)
@@ -49,6 +50,11 @@ module Tire
49
50
  (document = args.pop; type = :document)
50
51
  end
51
52
 
53
+ if document.is_a?(Hash)
54
+ percolate = document.delete(:percolate)
55
+ percolate = "*" if percolate === true
56
+ end
57
+
52
58
  old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
53
59
  id = case true
54
60
  when document.is_a?(Hash) then document[:id] || document['id']
@@ -62,7 +68,8 @@ module Tire
62
68
  else raise ArgumentError, "Please pass a JSON string or object with a 'to_indexed_json' method"
63
69
  end
64
70
 
65
- url = id ? "#{Configuration.url}/#{@name}/#{type}/#{id}" : "#{Configuration.url}/#{@name}/#{type}/"
71
+ url = id ? "#{Configuration.url}/#{@name}/#{type}/#{id}" : "#{Configuration.url}/#{@name}/#{type}/"
72
+ url += "?percolate=#{percolate}" if percolate
66
73
 
67
74
  @response = Configuration.client.post url, document
68
75
  MultiJson.decode(@response.body)
@@ -75,8 +82,6 @@ module Tire
75
82
  end
76
83
 
77
84
  def bulk_store documents
78
- create unless exists?
79
-
80
85
  payload = documents.map do |document|
81
86
  old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
82
87
  id = case
@@ -209,6 +214,48 @@ module Tire
209
214
  logged(error, '_close', curl)
210
215
  end
211
216
 
217
+ def register_percolator_query(name, options={}, &block)
218
+ options[:query] = Search::Query.new(&block).to_hash if block_given?
219
+
220
+ @response = Configuration.client.put "#{Configuration.url}/_percolator/#{@name}/#{name}", MultiJson.encode(options)
221
+ MultiJson.decode(@response.body)['ok']
222
+ rescue Exception => error
223
+ raise
224
+ ensure
225
+ curl = %Q|curl -X POST "#{Configuration.url}/_percolator/#{@name}/"|
226
+ logged(error, '_percolator', curl)
227
+ end
228
+
229
+ def percolate(*args, &block)
230
+ # TODO: Infer type from the document (hash property, method)
231
+
232
+ if args.size > 1
233
+ (type, document = args)
234
+ else
235
+ (document = args.pop; type = :document)
236
+ end
237
+
238
+ document = case true
239
+ when document.is_a?(String) then document
240
+ when document.respond_to?(:to_hash) then document.to_hash
241
+ else raise ArgumentError, "Please pass a JSON string or object with a 'to_hash' method"
242
+ end
243
+
244
+ query = Search::Query.new(&block).to_hash if block_given?
245
+
246
+ payload = { :doc => document }
247
+ payload.update( :query => query ) if query
248
+
249
+ @response = Configuration.client.get "#{Configuration.url}/#{@name}/#{type}/_percolate", payload.to_json
250
+ MultiJson.decode(@response.body)['matches']
251
+
252
+ rescue Exception => error
253
+ # raise
254
+ ensure
255
+ curl = %Q|curl -X POST "#{Configuration.url}/#{@name}/#{type}/_percolate"|
256
+ logged(error, '_percolate', curl)
257
+ end
258
+
212
259
  def logged(error=nil, endpoint='/', curl='')
213
260
  if Configuration.logger
214
261
 
@@ -15,6 +15,10 @@ module Tire
15
15
  def destroyed?; !!@destroyed; end
16
16
  end
17
17
  end
18
+
19
+ base.class_eval do
20
+ define_model_callbacks(:update_elastic_search_index, :only => [:after, :before])
21
+ end if base.respond_to?(:define_model_callbacks)
18
22
  end
19
23
 
20
24
  end
@@ -14,7 +14,7 @@ module Tire
14
14
  extend ClassMethods
15
15
  include InstanceMethods
16
16
 
17
- ['_score', '_type', '_index', '_version', 'sort', 'highlight'].each do |attr|
17
+ ['_score', '_type', '_index', '_version', 'sort', 'highlight', 'matches'].each do |attr|
18
18
  # TODO: Find a sane way to add attributes like _score for ActiveRecord -
19
19
  # `define_attribute_methods [attr]` does not work in AR.
20
20
  define_method("#{attr}=") { |value| @attributes ||= {}; @attributes[attr] = value }
@@ -68,6 +68,7 @@ module Tire
68
68
  module InstanceMethods
69
69
 
70
70
  def score
71
+ STDERR.puts "DEPRECATED! Please use #{self.class}#_score instead."
71
72
  attributes['_score']
72
73
  end
73
74
 
@@ -76,12 +77,15 @@ module Tire
76
77
  end
77
78
 
78
79
  def update_elastic_search_index
79
- if destroyed?
80
- index.remove document_type, self
81
- else
82
- response = index.store document_type, self
83
- self.id ||= response['_id'] if self.respond_to?(:id=)
84
- self
80
+ _run_update_elastic_search_index_callbacks do
81
+ if destroyed?
82
+ index.remove document_type, self
83
+ else
84
+ response = index.store document_type, self
85
+ self.id ||= response['_id'] if self.respond_to?(:id=)
86
+ self.matches = response['matches']
87
+ self
88
+ end
85
89
  end
86
90
  end
87
91
 
data/lib/tire/search.rb CHANGED
@@ -65,7 +65,7 @@ module Tire
65
65
 
66
66
  def perform
67
67
  @url = "#{Configuration.url}/#{indices.join(',')}/_search"
68
- @response = Configuration.client.post(@url, self.to_json)
68
+ @response = Configuration.client.get(@url, self.to_json)
69
69
  @json = MultiJson.decode(@response.body)
70
70
  @results = Results::Collection.new(@json, @options)
71
71
  self
@@ -77,20 +77,24 @@ module Tire
77
77
  end
78
78
 
79
79
  def to_curl
80
- %Q|curl -X POST "#{Configuration.url}/#{indices.join(',')}/_search?pretty=true" -d '#{self.to_json}'|
80
+ %Q|curl -X GET "#{Configuration.url}/#{indices.join(',')}/_search?pretty=true" -d '#{self.to_json}'|
81
81
  end
82
82
 
83
- def to_json
83
+ def to_hash
84
84
  request = {}
85
- request.update( { :query => @query } )
86
- request.update( { :sort => @sort } ) if @sort
87
- request.update( { :facets => @facets } ) if @facets
88
- @filters.each { |filter| request.update( { :filter => filter } ) } if @filters
89
- request.update( { :highlight => @highlight } ) if @highlight
90
- request.update( { :size => @size } ) if @size
91
- request.update( { :from => @from } ) if @from
92
- request.update( { :fields => @fields } ) if @fields
93
- request.to_json
85
+ request.update( { :query => @query.to_hash } ) if @query
86
+ request.update( { :sort => @sort.to_ary } ) if @sort
87
+ request.update( { :facets => @facets.to_hash } ) if @facets
88
+ @filters.each { |filter| request.update( { :filter => filter.to_hash } ) } if @filters
89
+ request.update( { :highlight => @highlight.to_hash } ) if @highlight
90
+ request.update( { :size => @size } ) if @size
91
+ request.update( { :from => @from } ) if @from
92
+ request.update( { :fields => @fields } ) if @fields
93
+ request
94
+ end
95
+
96
+ def to_json
97
+ to_hash.to_json
94
98
  end
95
99
 
96
100
  def logged(error=nil)
data/lib/tire/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tire
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -44,7 +44,7 @@ module Tire
44
44
  assert_equal 1, results.count
45
45
  assert_instance_of SupermodelArticle, results.first
46
46
  assert_equal 'Test', results.first.title
47
- assert_not_nil results.first.score
47
+ assert_not_nil results.first._score
48
48
  assert_equal id, results.first.id
49
49
  end
50
50
 
@@ -0,0 +1,86 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class PercolatorIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ context "Percolator" do
9
+ setup do
10
+ @index = Tire.index('percolator-test')
11
+ @index.create
12
+ end
13
+ teardown { @index.delete }
14
+
15
+ context "when registering a query" do
16
+ should "register query as a Hash" do
17
+ query = { :query => { :query_string => { :query => 'warning' } } }
18
+ assert @index.register_percolator_query('alert', query)
19
+ sleep 0.1
20
+
21
+ percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
22
+ assert percolator
23
+ end
24
+
25
+ should "register query as block" do
26
+ assert @index.register_percolator_query('alert') { string 'warning' }
27
+ sleep 0.1
28
+
29
+ percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
30
+ assert percolator
31
+ end
32
+
33
+ end
34
+
35
+ context "when percolating a document" do
36
+ setup do
37
+ @index.register_percolator_query('alert') { string 'warning' }
38
+ @index.register_percolator_query('gantz') { string '"y u no match"' }
39
+ @index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
40
+ sleep 0.1
41
+ end
42
+
43
+ should "return an empty array when no query matches" do
44
+ matches = @index.percolate :message => 'Situation normal'
45
+ assert_equal [], matches
46
+ end
47
+
48
+ should "return an array of matching query names" do
49
+ matches = @index.percolate :message => 'Severe weather warning'
50
+ assert_equal ['alert','weather'], matches.sort
51
+ end
52
+
53
+ should "return an array of matching query names for specific percolated queries" do
54
+ matches = @index.percolate(:message => 'Severe weather warning') { term :tags, 'weather' }
55
+ assert_equal ['weather'], matches
56
+ end
57
+ end
58
+
59
+ context "when storing document and percolating it" do
60
+ setup do
61
+ @index.register_percolator_query('alert') { string 'warning' }
62
+ @index.register_percolator_query('gantz') { string '"y u no match"' }
63
+ @index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
64
+ sleep 0.1
65
+ end
66
+
67
+ should "return an empty array when no query matches" do
68
+ response = @index.store :message => 'Situation normal', :percolate => true
69
+ assert_equal [], response['matches']
70
+ end
71
+
72
+ should "return an array of matching query names" do
73
+ response = @index.store :message => 'Severe weather warning', :percolate => true
74
+ assert_equal ['alert','weather'], response['matches'].sort
75
+ end
76
+
77
+ should "return an array of matching query names for specific percolated queries" do
78
+ response = @index.store :message => 'Severe weather warning', :percolate => 'tags:weather'
79
+ assert_equal ['weather'], response['matches']
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
@@ -393,6 +393,123 @@ module Tire
393
393
 
394
394
  end
395
395
 
396
+ context "when percolating" do
397
+
398
+ should "register percolator query as a Hash" do
399
+ query = { :query => { :query_string => { :query => 'foo' } } }
400
+ Configuration.client.expects(:post).with do |url, payload|
401
+ payload = MultiJson.decode(payload)
402
+ url == "#{Configuration.url}/_percolator/dummy/my-query" &&
403
+ payload['query']['query_string']['query'] == 'foo'
404
+ end.
405
+ returns(mock_response('{
406
+ "ok" : true,
407
+ "_index" : "_percolator",
408
+ "_type" : "dummy",
409
+ "_id" : "my-query",
410
+ "_version" : 1
411
+ }'))
412
+
413
+ @index.register_percolator_query 'my-query', query
414
+ end
415
+
416
+ should "register percolator query as a block" do
417
+ Configuration.client.expects(:post).with do |url, payload|
418
+ payload = MultiJson.decode(payload)
419
+ url == "#{Configuration.url}/_percolator/dummy/my-query" &&
420
+ payload['query']['query_string']['query'] == 'foo'
421
+ end.
422
+ returns(mock_response('{
423
+ "ok" : true,
424
+ "_index" : "_percolator",
425
+ "_type" : "dummy",
426
+ "_id" : "my-query",
427
+ "_version" : 1
428
+ }'))
429
+
430
+ @index.register_percolator_query 'my-query' do
431
+ string 'foo'
432
+ end
433
+ end
434
+
435
+ should "register percolator query with a key" do
436
+ query = { :query => { :query_string => { :query => 'foo' } },
437
+ :tags => ['alert'] }
438
+
439
+ Configuration.client.expects(:post).with do |url, payload|
440
+ payload = MultiJson.decode(payload)
441
+ url == "#{Configuration.url}/_percolator/dummy/my-query" &&
442
+ payload['query']['query_string']['query'] == 'foo'
443
+ payload['tags'] == ['alert']
444
+ end.
445
+ returns(mock_response('{
446
+ "ok" : true,
447
+ "_index" : "_percolator",
448
+ "_type" : "dummy",
449
+ "_id" : "my-query",
450
+ "_version" : 1
451
+ }'))
452
+
453
+ assert @index.register_percolator_query('my-query', query)
454
+ end
455
+
456
+ should "percolate document against all registered queries" do
457
+ Configuration.client.expects(:post).with do |url,payload|
458
+ payload = MultiJson.decode(payload)
459
+ url == "#{Configuration.url}/dummy/document/_percolate" &&
460
+ payload['doc']['title'] == 'Test'
461
+ end.
462
+ returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
463
+
464
+ matches = @index.percolate :title => 'Test'
465
+ assert_equal ["alerts"], matches
466
+ end
467
+
468
+ should "percolate a typed document against all registered queries" do
469
+ Configuration.client.expects(:post).with do |url,payload|
470
+ payload = MultiJson.decode(payload)
471
+ url == "#{Configuration.url}/dummy/article/_percolate" &&
472
+ payload['doc']['title'] == 'Test'
473
+ end.
474
+ returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
475
+
476
+ matches = @index.percolate :article, :title => 'Test'
477
+ assert_equal ["alerts"], matches
478
+ end
479
+
480
+ should "percolate document against specific queries" do
481
+ Configuration.client.expects(:post).with do |url,payload|
482
+ payload = MultiJson.decode(payload)
483
+ # p [url, payload]
484
+ url == "#{Configuration.url}/dummy/document/_percolate" &&
485
+ payload['doc']['title'] == 'Test' &&
486
+ payload['query']['query_string']['query'] == 'tag:alerts'
487
+ end.
488
+ returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
489
+
490
+ matches = @index.percolate(:title => 'Test') { string 'tag:alerts' }
491
+ assert_equal ["alerts"], matches
492
+ end
493
+
494
+ context "when storing document" do
495
+
496
+ should "percolate document against all registered queries" do
497
+ Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=*", '{"title":"Test"}').
498
+ returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
499
+ @index.store :article, :title => 'Test', :percolate => true
500
+ end
501
+
502
+ should "percolate document against specific queries" do
503
+ Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=tag:alerts", '{"title":"Test"}').
504
+ returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
505
+ response = @index.store :article, :title => 'Test', :percolate => 'tag:alerts'
506
+ assert_equal response['matches'], ['alerts']
507
+ end
508
+
509
+ end
510
+
511
+ end
512
+
396
513
  end
397
514
 
398
515
  end
@@ -86,21 +86,21 @@ module Tire
86
86
  end
87
87
 
88
88
  should "find document by list of IDs" do
89
- Configuration.client.expects(:post).returns(mock_response(@find_last_two.to_json))
89
+ Configuration.client.expects(:get).returns(mock_response(@find_last_two.to_json))
90
90
  documents = PersistentArticle.find 2, 3
91
91
 
92
92
  assert_equal 2, documents.count
93
93
  end
94
94
 
95
95
  should "find document by array of IDs" do
96
- Configuration.client.expects(:post).returns(mock_response(@find_last_two.to_json))
96
+ Configuration.client.expects(:get).returns(mock_response(@find_last_two.to_json))
97
97
  documents = PersistentArticle.find [2, 3]
98
98
 
99
99
  assert_equal 2, documents.count
100
100
  end
101
101
 
102
102
  should "find all documents" do
103
- Configuration.client.stubs(:post).returns(mock_response(@find_all.to_json))
103
+ Configuration.client.stubs(:get).returns(mock_response(@find_all.to_json))
104
104
  documents = PersistentArticle.all
105
105
 
106
106
  assert_equal 3, documents.count
@@ -109,7 +109,7 @@ module Tire
109
109
  end
110
110
 
111
111
  should "find first document" do
112
- Configuration.client.expects(:post).returns(mock_response(@find_first.to_json))
112
+ Configuration.client.expects(:get).returns(mock_response(@find_first.to_json))
113
113
  document = PersistentArticle.first
114
114
 
115
115
  assert_equal 'First', document.attributes['title']
@@ -1,5 +1,16 @@
1
1
  require 'test_helper'
2
2
 
3
+ class ModelWithIndexCallbacks
4
+ extend ActiveModel::Naming
5
+ extend ActiveModel::Callbacks
6
+
7
+ include Tire::Model::Search
8
+ include Tire::Model::Callbacks
9
+
10
+ def destroyed?; false; end
11
+ def serializable_hash; {:one => 1}; end
12
+ end
13
+
3
14
  module Tire
4
15
  module Model
5
16
 
@@ -20,6 +31,11 @@ module Tire
20
31
  assert_respond_to ActiveModelArticle, :search
21
32
  end
22
33
 
34
+ should "have the `update_elastic_search_index` callback methods defined" do
35
+ assert_respond_to ::ModelWithIndexCallbacks, :before_update_elastic_search_index
36
+ assert_respond_to ::ModelWithIndexCallbacks, :after_update_elastic_search_index
37
+ end
38
+
23
39
  should_eventually "contain all Tire class/instance methods in a proxy object" do
24
40
  end
25
41
 
@@ -70,7 +86,7 @@ module Tire
70
86
 
71
87
  should "wrap results in proper class with ID and score and not change the original wrapper" do
72
88
  response = { 'hits' => { 'hits' => [{'_id' => 1, '_score' => 0.8, '_source' => { 'title' => 'Article' }}] } }
73
- Configuration.client.expects(:post).returns(mock_response(response.to_json))
89
+ Configuration.client.expects(:get).returns(mock_response(response.to_json))
74
90
 
75
91
  collection = ActiveModelArticle.search 'foo'
76
92
  assert_instance_of Results::Collection, collection
@@ -200,7 +216,7 @@ module Tire
200
216
 
201
217
  should "store the record in index on :update_elastic_search_index when saved" do
202
218
  @model = ActiveModelArticleWithCallbacks.new
203
- Tire::Index.any_instance.expects(:store)
219
+ Tire::Index.any_instance.expects(:store).returns({})
204
220
 
205
221
  @model.save
206
222
  end
@@ -226,6 +242,7 @@ module Tire
226
242
 
227
243
  class ::ModelWithCustomMapping
228
244
  extend ActiveModel::Naming
245
+ extend ActiveModel::Callbacks
229
246
 
230
247
  include Tire::Model::Search
231
248
  include Tire::Model::Callbacks
@@ -241,6 +258,52 @@ module Tire
241
258
 
242
259
  end
243
260
 
261
+ context "with index update callbacks" do
262
+ setup do
263
+ class ::ModelWithIndexCallbacks
264
+ _update_elastic_search_index_callbacks.clear
265
+ def notify; end
266
+ end
267
+
268
+ response = { 'ok' => true,
269
+ '_id' => 1,
270
+ 'matches' => ['foo'] }
271
+ Configuration.client.expects(:post).returns(mock_response(response.to_json))
272
+ end
273
+
274
+ should "run the callback defined as block" do
275
+ class ::ModelWithIndexCallbacks
276
+ after_update_elastic_search_index { self.go! }
277
+ end
278
+
279
+ @model = ::ModelWithIndexCallbacks.new
280
+ @model.expects(:go!)
281
+
282
+ @model.update_elastic_search_index
283
+ end
284
+
285
+ should "run the callback defined as symbol" do
286
+ class ::ModelWithIndexCallbacks
287
+ after_update_elastic_search_index :notify
288
+
289
+ def notify; self.go!; end
290
+ end
291
+
292
+ @model = ::ModelWithIndexCallbacks.new
293
+ @model.expects(:go!)
294
+
295
+ @model.update_elastic_search_index
296
+ end
297
+
298
+ should "set the 'matches' property from percolated response" do
299
+ @model = ::ModelWithIndexCallbacks.new
300
+ @model.update_elastic_search_index
301
+
302
+ assert_equal ['foo'], @model.matches
303
+ end
304
+
305
+ end
306
+
244
307
  context "serialization" do
245
308
  setup { Tire::Index.any_instance.stubs(:create).returns(true) }
246
309
 
@@ -253,6 +316,7 @@ module Tire
253
316
 
254
317
  class ::ModelWithoutMapping
255
318
  extend ActiveModel::Naming
319
+ extend ActiveModel::Callbacks
256
320
  include ActiveModel::Serialization
257
321
  include Tire::Model::Search
258
322
  include Tire::Model::Callbacks
@@ -279,6 +343,7 @@ module Tire
279
343
 
280
344
  class ::ModelWithMapping
281
345
  extend ActiveModel::Naming
346
+ extend ActiveModel::Callbacks
282
347
  include ActiveModel::Serialization
283
348
  include Tire::Model::Search
284
349
  include Tire::Model::Callbacks
@@ -43,7 +43,7 @@ module Tire
43
43
  s = Search::Search.new('index') do
44
44
  query { string 'title:foo' }
45
45
  end
46
- assert_equal %q|curl -X POST "http://localhost:9200/index/_search?pretty=true" -d | +
46
+ assert_equal %q|curl -X GET "http://localhost:9200/index/_search?pretty=true" -d | +
47
47
  %q|'{"query":{"query_string":{"query":"title:foo"}}}'|,
48
48
  s.to_curl
49
49
  end
@@ -55,6 +55,16 @@ module Tire
55
55
  assert_match /index_1,index_2/, s.to_curl
56
56
  end
57
57
 
58
+ should "return itself as a Hash" do
59
+ s = Search::Search.new('index_1', 'index_2') do
60
+ query { string 'title:foo' }
61
+ end
62
+ assert_nothing_raised do
63
+ assert_instance_of Hash, s.to_hash
64
+ assert_equal "title:foo", s.to_hash[:query][:query_string][:query]
65
+ end
66
+ end
67
+
58
68
  should "allow chaining" do
59
69
  assert_nothing_raised do
60
70
  Search::Search.new('index').query { }.sort { title 'desc' }.size(5).sort { name 'asc' }.from(1)
@@ -63,7 +73,7 @@ module Tire
63
73
 
64
74
  should "perform the search" do
65
75
  response = stub(:body => '{"took":1,"hits":[]}', :code => 200)
66
- Configuration.client.expects(:post).returns(response)
76
+ Configuration.client.expects(:get).returns(response)
67
77
  Results::Collection.expects(:new).returns([])
68
78
 
69
79
  s = Search::Search.new('index')
@@ -73,7 +83,7 @@ module Tire
73
83
  end
74
84
 
75
85
  should "print debugging information on exception and re-raise it" do
76
- Configuration.client.expects(:post).raises(RestClient::InternalServerError)
86
+ Configuration.client.expects(:get).raises(RestClient::InternalServerError)
77
87
  STDERR.expects(:puts)
78
88
 
79
89
  s = Search::Search.new('index')
@@ -84,7 +94,7 @@ module Tire
84
94
  Configuration.logger STDERR
85
95
 
86
96
  response = stub(:body => '{"took":1,"hits":[]}', :code => 200)
87
- Configuration.client.expects(:post).returns(response)
97
+ Configuration.client.expects(:get).returns(response)
88
98
 
89
99
  Results::Collection.expects(:new).returns([])
90
100
  Configuration.logger.expects(:log_request).returns(true)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tire
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 8
10
- version: 0.1.8
9
+ - 9
10
+ version: 0.1.9
11
11
  platform: ruby
12
12
  authors:
13
13
  - Karel Minarik
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-08 00:00:00 +02:00
18
+ date: 2011-06-13 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -300,6 +300,7 @@ files:
300
300
  - test/integration/highlight_test.rb
301
301
  - test/integration/index_mapping_test.rb
302
302
  - test/integration/index_store_test.rb
303
+ - test/integration/percolator_test.rb
303
304
  - test/integration/persistent_model_test.rb
304
305
  - test/integration/query_string_test.rb
305
306
  - test/integration/results_test.rb
@@ -384,6 +385,7 @@ test_files:
384
385
  - test/integration/highlight_test.rb
385
386
  - test/integration/index_mapping_test.rb
386
387
  - test/integration/index_store_test.rb
388
+ - test/integration/percolator_test.rb
387
389
  - test/integration/persistent_model_test.rb
388
390
  - test/integration/query_string_test.rb
389
391
  - test/integration/results_test.rb