tire 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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