tire 0.6.0 → 0.6.1

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.
@@ -1,6 +1,17 @@
1
1
  Tire
2
2
  =========
3
3
 
4
+ ---------------------------------------------------------------------------------------------------
5
+
6
+ NOTICE: This library has been renamed and retired in September 2013
7
+ ([read the explanation](https://github.com/karmi/retire/wiki/Tire-Retire)).
8
+
9
+ Have a look at the **<http://github.com/elasticsearch/elasticsearch-ruby>**
10
+ suite of gems, which will contain similar set of features for
11
+ ActiveRecord and Rails integration as Tire.
12
+
13
+ ---------------------------------------------------------------------------------------------------
14
+
4
15
  _Tire_ is a Ruby (1.8 or 1.9) client for the [Elasticsearch](http://www.elasticsearch.org/)
5
16
  search engine/database.
6
17
 
@@ -9,7 +20,7 @@ full-text search engine and database with
9
20
  [powerful aggregation features](http://www.elasticsearch.org/guide/reference/api/search/facets/),
10
21
  communicating by JSON over RESTful HTTP, based on [Lucene](http://lucene.apache.org/), written in Java.
11
22
 
12
- This Readme provides a brief overview of _Tire's_ features. The more detailed documentation is at <http://karmi.github.com/tire/>.
23
+ This Readme provides a brief overview of _Tire's_ features. The more detailed documentation is at <http://karmi.github.com/retire/>.
13
24
 
14
25
  Both of these documents contain a lot of information. Please set aside some time to read them thoroughly, before you blindly dive into „somehow making it work“. Just skimming through it **won't work** for you. For more information, please see the project [Wiki](https://github.com/karmi/tire/wiki/_pages), search the [issues](https://github.com/karmi/tire/issues), and refer to the [integration test suite](https://github.com/karmi/tire/tree/master/test/integration).
15
26
 
@@ -52,7 +63,7 @@ To test-drive the core _Elasticsearch_ functionality, let's require the gem:
52
63
  ```
53
64
 
54
65
  Please note that you can copy these snippets from the much more extensive and heavily annotated file
55
- in [examples/tire-dsl.rb](http://karmi.github.com/tire/).
66
+ in [examples/tire-dsl.rb](http://karmi.github.com/retire/).
56
67
 
57
68
  Also, note that we're doing some heavy JSON lifting here. _Tire_ uses the
58
69
  [_multi_json_](https://github.com/intridea/multi_json) gem as a generic JSON wrapper,
@@ -595,6 +606,20 @@ just drop down one layer and use the `Tire::Index#store` and `Tire::Index#remove
595
606
  end
596
607
  ```
597
608
 
609
+ Of course, in this way, you're still performing an HTTP request during your database transaction,
610
+ which is not optimal for large-scale applications. In these situations, a better option would be processing
611
+ the index operations in background, with something like [Resque](https://github.com/resque/resque) or
612
+ [Sidekiq](https://github.com/mperham/sidekiq):
613
+
614
+ ```ruby
615
+ class Article < ActiveRecord::Base
616
+ include Tire::Model::Search
617
+
618
+ after_save { Indexer::Index.perform_async(document) }
619
+ after_destroy { Indexer::Remove.perform_async(document) }
620
+ end
621
+ ```
622
+
598
623
  When you're integrating _Tire_ with ActiveRecord models, you should use the `after_commit`
599
624
  and `after_rollback` hooks to keep the index in sync with your database.
600
625
 
@@ -13,8 +13,8 @@
13
13
  # * Git
14
14
  # * Ruby >= 1.8.7
15
15
  # * Rubygems
16
- # * Rails >= 3.0.7
17
- # * Sun Java 6 (for Elasticsearch)
16
+ # * Rails >= 3
17
+ # * Java 6 or 7 (for Elasticsearch)
18
18
  #
19
19
  #
20
20
  # Usage
@@ -181,14 +181,13 @@ file 'app/models/article.rb', <<-CODE
181
181
  class Article < ActiveRecord::Base
182
182
  include Tire::Model::Search
183
183
  include Tire::Model::Callbacks
184
-
185
- attr_accessible :title, :content, :published_on
186
184
  end
187
185
  CODE
188
186
 
189
187
  initializer 'tire.rb', <<-CODE
190
188
  Tire.configure do
191
- logger STDERR
189
+ # url 'http://localhost:9200'
190
+ # logger STDERR
192
191
  end
193
192
  CODE
194
193
 
@@ -201,7 +200,7 @@ puts '-'*80, ''; sleep 1
201
200
  gsub_file 'app/controllers/articles_controller.rb', %r{# GET /articles/1$}, <<-CODE
202
201
  # GET /articles/search
203
202
  def search
204
- @articles = Article.search params[:q]
203
+ @articles = Article.tire.search params[:q]
205
204
 
206
205
  render :action => "index"
207
206
  end
@@ -238,7 +237,7 @@ puts
238
237
  say_status "Index", "Indexing the database...", :yellow
239
238
  puts '-'*80, ''; sleep 0.5
240
239
 
241
- rake "environment tire:import CLASS='Article' FORCE=true"
240
+ rake "environment tire:import:model CLASS='Article' FORCE=true"
242
241
 
243
242
  puts
244
243
  say_status "Git", "Details about the application:", :yellow
@@ -30,6 +30,7 @@ require 'tire/search/filter'
30
30
  require 'tire/search/highlight'
31
31
  require 'tire/search/scan'
32
32
  require 'tire/search/script_field'
33
+ require 'tire/delete_by_query'
33
34
  require 'tire/multi_search'
34
35
  require 'tire/count'
35
36
  require 'tire/results/pagination'
@@ -0,0 +1,76 @@
1
+ module Tire
2
+ class DeleteByQuery
3
+ class DeleteByQueryRequestFailed < StandardError; end
4
+
5
+ attr_reader :indices, :types, :query, :response, :json
6
+
7
+ def initialize(indices=nil, options={}, &block)
8
+ @indices = Array(indices)
9
+ @types = Array(options[:type]).flatten
10
+ @options = options
11
+
12
+ if block_given?
13
+ @query = Search::Query.new
14
+ block.arity < 1 ? @query.instance_eval(&block) : block.call(@query)
15
+ else
16
+ raise "no query given for #{self.class}"
17
+ end
18
+ end
19
+
20
+ def perform
21
+ @response = Configuration.client.delete url
22
+ if @response.failure?
23
+ STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
24
+ raise DeleteByQueryRequestFailed, @response.to_s
25
+ end
26
+ @json = MultiJson.decode(@response.body)
27
+ true
28
+ ensure
29
+ logged
30
+ end
31
+
32
+ private
33
+
34
+ def path
35
+ [
36
+ '/',
37
+ indices.join(','),
38
+ types.map { |type| Utils.escape(type) }.join(','),
39
+ '_query',
40
+ ].compact.join('/')
41
+ end
42
+
43
+ def url
44
+ "#{Configuration.url}#{path}/?source=#{Utils.escape(to_json)}"
45
+ end
46
+
47
+ def to_json(options={})
48
+ query.to_json
49
+ end
50
+
51
+ def to_curl
52
+ %Q|curl -X DELETE '#{url}'|
53
+ end
54
+
55
+ def logged(endpoint='_query')
56
+ if Configuration.logger
57
+
58
+ Configuration.logger.log_request endpoint, indices, to_curl
59
+
60
+ code = response.code rescue nil
61
+
62
+ if Configuration.logger.level.to_s == 'debug'
63
+ body = if json
64
+ MultiJson.encode(json, :pretty => Configuration.pretty)
65
+ else
66
+ MultiJson.encode(MultiJson.load(response.body), :pretty => Configuration.pretty) rescue ''
67
+ end
68
+ else
69
+ body = ''
70
+ end
71
+
72
+ Configuration.logger.log_response code || 'N/A', 'N/A', body || 'N/A'
73
+ end
74
+ end
75
+ end
76
+ end
@@ -105,6 +105,10 @@ module Tire
105
105
  Search::Count.new(indices, options, &block).value
106
106
  end
107
107
 
108
+ def delete(indices=nil, options={}, &block)
109
+ DeleteByQuery.new(indices, options, &block).perform
110
+ end
111
+
108
112
  def index(name, &block)
109
113
  Index.new(name, &block)
110
114
  end
@@ -48,7 +48,7 @@ module Tire
48
48
  end
49
49
 
50
50
  def self.__host_unreachable_exceptions
51
- [Errno::ECONNREFUSED, ::RestClient::ServerBrokeConnection, ::RestClient::RequestTimeout]
51
+ [Errno::ECONNREFUSED, Errno::ETIMEDOUT, ::RestClient::ServerBrokeConnection, ::RestClient::RequestTimeout, SocketError]
52
52
  end
53
53
 
54
54
  private
@@ -38,9 +38,13 @@ module Tire
38
38
  Response.new client.body_str, client.response_code
39
39
  end
40
40
 
41
+ # NOTE: newrelic_rpm breaks Curl::Easy#http_put
42
+ # https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/curb.rb#L49
43
+ #
41
44
  def self.put(url, data)
45
+ method = client.respond_to?(:http_put_without_newrelic) ? :http_put_without_newrelic : :http_put
42
46
  client.url = url
43
- client.http_put data
47
+ client.send method, data
44
48
  Response.new client.body_str, client.response_code
45
49
  end
46
50
 
@@ -154,13 +154,13 @@ module Tire
154
154
  #
155
155
  # @myindex.bulk :index, [ {id: 1, title: 'One'}, { id: 2, title: 'Two', _version: 3 } ], refresh: true
156
156
  #
157
- # Pass the action (`index`, `create`, `delete`) as the first argument, the collection of documents as
157
+ # Pass the action (`index`, `create`, `delete`, `update`) as the first argument, the collection of documents as
158
158
  # the second argument, and URL parameters as the last option.
159
159
  #
160
160
  # Any _meta_ information contained in documents (such as `_routing` or `_parent`) is extracted
161
161
  # and added to the "header" line.
162
162
  #
163
- # Shortcut methods `bulk_store`, `bulk_delete` and `bulk_create` are available.
163
+ # Shortcut methods `bulk_store`, `bulk_delete`, `bulk_create`, and `bulk_update` are available.
164
164
  #
165
165
  def bulk(action, documents, options={})
166
166
  return false if documents.empty?
@@ -181,12 +181,22 @@ module Tire
181
181
  STDERR.puts "[ERROR] Document #{document.inspect} does not have ID" unless id
182
182
  end
183
183
 
184
+ if action.to_sym == :update
185
+ raise ArgumentError, "Cannot update without document type" unless type
186
+ raise ArgumentError, "Cannot update without document ID" unless id
187
+ raise ArgumentError, "Update requires a hash document" unless document.respond_to?(:to_hash)
188
+ document = document.to_hash
189
+ raise ArgumentError, "Update requires either a script or a partial document" unless document[:script] || document[:doc]
190
+ end
191
+
184
192
  header = { action.to_sym => { :_index => name, :_type => type, :_id => id } }
193
+ header[action.to_sym].update({:_retry_on_conflict => options[:retry_on_conflict]}) if options[:retry_on_conflict]
185
194
 
186
195
  if document.respond_to?(:to_hash) && doc_hash = document.to_hash
187
196
  meta = doc_hash.select do |k,v|
188
197
  [ :_parent,
189
198
  :_percolate,
199
+ :_retry_on_conflict,
190
200
  :_routing,
191
201
  :_timestamp,
192
202
  :_ttl,
@@ -204,6 +214,19 @@ module Tire
204
214
  header[action.to_sym].update(meta)
205
215
  end
206
216
 
217
+ if action.to_sym == :update
218
+ document.keep_if do |k,_|
219
+ [
220
+ :doc,
221
+ :upsert,
222
+ :doc_as_upsert,
223
+ :script,
224
+ :params,
225
+ :lang
226
+ ].include?(k)
227
+ end
228
+ end
229
+
207
230
  output = []
208
231
  output << MultiJson.encode(header)
209
232
  output << convert_document_to_json(document) unless action.to_s == 'delete'
@@ -254,6 +277,10 @@ module Tire
254
277
  bulk :delete, documents, options
255
278
  end
256
279
 
280
+ def bulk_update(documents, options={})
281
+ bulk :update, documents, options
282
+ end
283
+
257
284
  def import(klass_or_collection, options={})
258
285
  case
259
286
  when method = options.delete(:method)
@@ -487,7 +514,7 @@ module Tire
487
514
  when document.is_a?(Hash)
488
515
  document[:_id] || document['_id'] || document[:id] || document['id']
489
516
  when document.respond_to?(:id) && document.id != document.object_id
490
- document.id.as_json
517
+ document.id.to_s
491
518
  end
492
519
  $VERBOSE = old_verbose
493
520
  id
@@ -46,7 +46,7 @@ module Tire
46
46
 
47
47
  # Save property default value (when relevant):
48
48
  unless (default_value = options.delete(:default)).nil?
49
- property_defaults[name.to_sym] = default_value.respond_to?(:call) ? default_value.call : default_value
49
+ property_defaults[name.to_sym] = default_value
50
50
  end
51
51
 
52
52
  # Save property casting (when relevant):
@@ -92,7 +92,15 @@ module Tire
92
92
  #
93
93
  property_defaults = self.class.property_defaults.inject({}) do |hash, item|
94
94
  key, value = item
95
- hash[key.to_sym] = value.class.respond_to?(:new) ? value.clone : value
95
+
96
+ hash[key.to_sym] = if value.respond_to?(:call)
97
+ value.call
98
+ elsif value.class.respond_to?(:new)
99
+ value.clone
100
+ else
101
+ value
102
+ end
103
+
96
104
  hash
97
105
  end
98
106
  __update_attributes(property_defaults.merge(attributes))
@@ -23,6 +23,10 @@ module Tire
23
23
  result
24
24
  end
25
25
  end
26
+
27
+ def delete(&block)
28
+ DeleteByQuery.new(index_name, {:type => document_type}, &block).perform
29
+ end
26
30
  end
27
31
 
28
32
  module InstanceMethods
@@ -52,6 +52,7 @@ module Tire
52
52
  alias :total_count :total_entries
53
53
  alias :num_pages :total_pages
54
54
  alias :offset_value :offset
55
+ alias :out_of_range? :out_of_bounds?
55
56
 
56
57
  def first_page?
57
58
  current_page == 1
@@ -48,7 +48,7 @@ module Tire
48
48
  end
49
49
 
50
50
  def params
51
- options = @options.except(:wrapper, :payload)
51
+ options = @options.except(:wrapper, :payload, :load)
52
52
  options.empty? ? '' : '?' + options.to_param
53
53
  end
54
54
 
@@ -121,11 +121,18 @@ namespace :tire do
121
121
 
122
122
  puts "[IMPORT] Loading models from: #{dir}"
123
123
  Dir.glob(File.join("#{dir}/**/*.rb")).each do |path|
124
- require path
125
-
126
124
  model_filename = path[/#{Regexp.escape(dir.to_s)}\/([^\.]+).rb/, 1]
125
+
126
+ next if model_filename.match(/^concerns\//i) # Skip concerns/ folder
127
+
127
128
  klass = model_filename.camelize.constantize
128
129
 
130
+ begin
131
+ klass = model_filename.camelize.constantize
132
+ rescue NameError
133
+ require(path) ? retry : raise
134
+ end
135
+
129
136
  # Skip if the class doesn't have Tire integration
130
137
  next unless klass.respond_to?(:tire)
131
138
 
@@ -1,20 +1,15 @@
1
1
  module Tire
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
6
6
 
7
- * Fixed incorrect inflection in the Rake import tasks
8
- * Added support for `geo_distance` facets
9
- * Added support for the `custom_filters_score` query
10
- * Added a custom strategy option to <Model.import>
11
- * Allow the `:wrapper` option to be passed to Tire.search consistently
12
- * Improved the Mongoid importing strategy
13
- * Merge returned `fields` with `_source` if both are returned
14
- * Removed the deprecated `text` query
15
- * [FIX] Rescue HTTP client specific connection errors in MyModel#create_elasticsearch_index
16
- * Added support for passing `version` in Tire::Index#store
17
- * Added support for `_version_type` in Tire::Index#bulk
18
- * Added ActiveModel::Serializers compatibility
7
+ * Added support for bulk update
8
+ * Improved Kaminari support
9
+ * Improved the behaviour of `default` properties in Tire::Persistence
10
+ * Added the information about the gem "retirement" and other documentation improvements
11
+ * Fixed errors due to NewRelic's patching of Curl
12
+ * [ACTIVEMODEL] Use Object#id#to_s in `get_id_from_document`
13
+ * Added support for "Delete By Query" API
19
14
  END
20
15
  end
@@ -86,6 +86,21 @@ module Tire
86
86
  assert_equal 'abc123', results.first.id
87
87
  end
88
88
 
89
+ should "return facets" do
90
+ a = SupermodelArticle.new :title => 'Test'
91
+ a.save
92
+ a.index.refresh
93
+
94
+ s = SupermodelArticle.search do
95
+ query { match :title, 'test' }
96
+ facet 'title' do
97
+ terms :title
98
+ end
99
+ end
100
+
101
+ assert_equal 1, s.facets['title']['terms'][0]['count']
102
+ end
103
+
89
104
  context "within Rails" do
90
105
 
91
106
  setup do
@@ -49,6 +49,24 @@ module Tire
49
49
  assert_equal 0, Tire.search('bulk-test') { query {all} }.results.size
50
50
  end
51
51
 
52
+ should 'update documents in bulk' do
53
+ @index.bulk_store @articles, refresh: true
54
+
55
+ documents = @articles.map do |a|
56
+ {
57
+ id: a[:id],
58
+ type: a[:type],
59
+ doc: { title: "#{a[:title]}-updated" }
60
+ }
61
+ end
62
+ @index.bulk_update documents, refresh: true
63
+
64
+ documents = Tire.search('bulk-test') { query {all} }.results.to_a
65
+ assert_equal 'one-updated', documents[0][:title]
66
+ assert_equal 'two-updated', documents[1][:title]
67
+ assert_equal 'three-updated', documents[2][:title]
68
+ end
69
+
52
70
  should "allow to feed search results to bulk API" do
53
71
  (1..10).to_a.each { |i| @index.store id: i }
54
72
  @index.refresh
@@ -68,7 +68,7 @@ module Tire
68
68
  query do
69
69
  # Replace documents score with parameterized computation
70
70
  #
71
- custom_score :script => "doc['words'].doubleValue / max(a, b)",
71
+ custom_score :script => "doc['words'].value.doubleValue() / max(a, b)",
72
72
  :params => { :a => 1, :b => 2 } do
73
73
  string "title:T*"
74
74
  end
@@ -0,0 +1,47 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+ class DeleteByQueryIntegrationTest < Test::Unit::TestCase
5
+ include Test::Integration
6
+
7
+ should "delete documents matching a query" do
8
+ assert_python_size(1)
9
+ delete_by_query
10
+ assert_python_size(0)
11
+ end
12
+
13
+ should "leave documents not matching a query" do
14
+ assert_python_size(1)
15
+ delete_by_query('article', 'go')
16
+ assert_python_size(1)
17
+ end
18
+
19
+ should "not delete documents with different types" do
20
+ assert_python_size(1)
21
+ delete_by_query('different_type')
22
+ assert_python_size(1)
23
+ end
24
+
25
+ context "DSL" do
26
+ should "delete documents matching a query" do
27
+ assert_python_size(1)
28
+ Tire.delete('articles-test') { term :tags, 'python' }
29
+ assert_python_size(0)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def delete_by_query(type='article', token='python')
36
+ Tire::DeleteByQuery.new('articles-test', :type => type) do
37
+ term :tags, token
38
+ end.perform
39
+ end
40
+
41
+ def assert_python_size(size)
42
+ Tire.index('articles-test').refresh
43
+ search = Tire.search('articles-test') { query { term :tags, 'python' } }
44
+ assert_equal size, search.results.size
45
+ end
46
+ end
47
+ end
@@ -20,7 +20,6 @@ module Tire
20
20
  end
21
21
 
22
22
  should "add '_explanation' field to the result item" do
23
- # Tire::Configuration.logger STDERR, :level => 'debug'
24
23
  s = Tire.search 'explanation-test', :explain => true do
25
24
  query do
26
25
  boolean do
@@ -30,15 +29,11 @@ module Tire
30
29
  end
31
30
 
32
31
  doc = s.results.first
32
+ d = doc._explanation.details.first
33
33
 
34
- explanation = doc._explanation
35
-
36
- assert explanation.description.include?("product of:")
37
- assert explanation.value < 0.6
38
- assert_not_nil explanation.details
39
- end
40
-
34
+ assert d.description.include?("product of:")
35
+ assert_not_nil d.details
41
36
  end
42
-
43
37
  end
44
38
  end
39
+ end
@@ -23,12 +23,12 @@ module Tire
23
23
  teardown { Tire.index('articles-with-tags').delete }
24
24
 
25
25
  should "increment a counter" do
26
- Tire.index('articles-with-tags') { update( 'article', '1', :script => "ctx._source.views += 1" ) and refresh }
26
+ Tire.index('articles-with-tags') { update( 'article', '1', {:script => "ctx._source.views += 1"}, :refresh => true) }
27
27
 
28
28
  document = Tire.search('articles-with-tags') { query { string 'title:one' } }.results.first
29
29
  assert_equal 1, document.views, document.inspect
30
30
 
31
- Tire.index('articles-with-tags') { update( 'article', '2', :script => "ctx._source.views += 1" ) and refresh }
31
+ Tire.index('articles-with-tags') { update( 'article', '2', {:script => "ctx._source.views += 1"}, :refresh => true) }
32
32
 
33
33
  document = Tire.search('articles-with-tags') { query { string 'title:two' } }.results.first
34
34
  assert_equal 11, document.views, document.inspect
@@ -36,9 +36,13 @@ module Tire
36
36
 
37
37
  should "add a tag to document" do
38
38
  Tire.index('articles-with-tags') do
39
- update 'article', '1', :script => "ctx._source.tags += tag",
40
- :params => { :tag => 'new' }
41
- refresh
39
+ update 'article', '1', {
40
+ :script => "ctx._source.tags += tag",
41
+ :params => { :tag => 'new' }
42
+ },
43
+ {
44
+ :refresh => true
45
+ }
42
46
  end
43
47
 
44
48
  document = Tire.search('articles-with-tags') { query { string 'title:one' } }.results.first
@@ -47,9 +51,12 @@ module Tire
47
51
 
48
52
  should "remove a tag from document" do
49
53
  Tire.index('articles-with-tags') do
50
- update 'article', '1', :script => "ctx._source.tags = tags",
51
- :params => { :tags => [] }
52
- refresh
54
+ update 'article', '1', {
55
+ :script => "ctx._source.tags = tags",
56
+ :params => { :tags => [] }
57
+ }, {
58
+ :refresh => true
59
+ }
53
60
  end
54
61
 
55
62
  document = Tire.index('articles-with-tags').retrieve 'article', '1'
@@ -59,9 +66,12 @@ module Tire
59
66
  should "remove the entire document if specific condition is met" do
60
67
  Tire.index('articles-with-tags') do
61
68
  # Remove document when it contains tag 'foobar'
62
- update 'article', '3', :script => "ctx._source.tags.contains(tag) ? ctx.op = 'delete' : 'none'",
63
- :params => { :tag => 'foobar' }
64
- refresh
69
+ update 'article', '3', {
70
+ :script => "ctx._source.tags.contains(tag) ? ctx.op = 'delete' : 'none'",
71
+ :params => { :tag => 'foobar' }
72
+ }, {
73
+ :refresh => true
74
+ }
65
75
  end
66
76
 
67
77
  assert_nil Tire.index('articles-with-tags').retrieve 'article', '3'
@@ -81,8 +91,7 @@ module Tire
81
91
 
82
92
  should "update the document with a partial one" do
83
93
  Tire.index('articles-with-tags') do
84
- update( 'article', '1', :doc => { :title => 'One UPDATED' } )
85
- refresh
94
+ update( 'article', '1', {:doc => { :title => 'One UPDATED' }}, :refresh => true )
86
95
  end
87
96
 
88
97
  document = Tire.search('articles-with-tags') { query { string 'title:one' } }.results.first
@@ -101,9 +110,12 @@ module Tire
101
110
  Tire.index('articles-with-tags') do |index|
102
111
  $t.assert_not_nil @tags
103
112
 
104
- index.update 'article', '3', :script => "ctx._source.tags = tags",
105
- :params => { :tags => @tags }
106
- index.refresh
113
+ index.update 'article', '3', {
114
+ :script => "ctx._source.tags = tags",
115
+ :params => { :tags => @tags }
116
+ }, {
117
+ :refresh => true
118
+ }
107
119
  end
108
120
  end
109
121
  end
@@ -115,7 +127,5 @@ module Tire
115
127
  end
116
128
 
117
129
  end
118
-
119
130
  end
120
-
121
131
  end
@@ -8,13 +8,13 @@ module Tire
8
8
  context "Percolator" do
9
9
  setup do
10
10
  delete_registered_queries
11
- delete_percolator_index if ENV['TRAVIS']
11
+ delete_percolator_index
12
12
  @index = Tire.index('percolator-test')
13
13
  @index.create
14
14
  end
15
15
  teardown do
16
16
  delete_registered_queries
17
- delete_percolator_index if ENV['TRAVIS']
17
+ delete_percolator_index
18
18
  @index.delete
19
19
  end
20
20
 
@@ -98,7 +98,7 @@ module Tire
98
98
  end
99
99
  end
100
100
 
101
- end
101
+ end if ENV['TRAVIS']
102
102
 
103
103
  private
104
104
 
@@ -78,6 +78,23 @@ module Tire
78
78
  assert_equal [], results.first.tags
79
79
  end
80
80
 
81
+ context "with deleting" do
82
+ should "search with simple query" do
83
+ PersistentArticle.create :id => 1, :title => 'One'
84
+ PersistentArticle.index.refresh
85
+
86
+ results = PersistentArticle.search 'one'
87
+ assert_equal 1, results.size
88
+
89
+ PersistentArticle.delete do
90
+ term :title, 'one'
91
+ end
92
+
93
+ results = PersistentArticle.search 'one'
94
+ assert_equal 0, results.size
95
+ end
96
+ end
97
+
81
98
  context "with pagination" do
82
99
 
83
100
  setup do
@@ -188,7 +205,7 @@ module Tire
188
205
  context "percolated search" do
189
206
  setup do
190
207
  delete_registered_queries
191
- delete_percolator_index if ENV['TRAVIS']
208
+ delete_percolator_index
192
209
  PersistentArticleWithPercolation.index.register_percolator_query('alert') { string 'warning' }
193
210
  Tire.index('_percolator').refresh
194
211
  end
@@ -206,7 +223,7 @@ module Tire
206
223
  a = PersistentArticleWithPercolation.create :title => 'Warning!'
207
224
  assert_contains a.matches, 'alert'
208
225
  end
209
- end
226
+ end if ENV['TRAVIS']
210
227
 
211
228
  context "with strict mapping" do
212
229
  should "successfuly save valid model" do
@@ -363,13 +363,11 @@ module Tire
363
363
  @index.store Article.new(:id => 123, :title => 'Test', :body => 'Lorem')
364
364
  end
365
365
 
366
- should "convert document ID to string or number" do
367
- # This is related to issues #529, #535:
368
- # When using Mongoid and the Yajl gem, document IDs from Mongo (Moped::BSON::ObjectId)
369
- # are incorrectly serialized to JSON, and documents are stored with incorrect, auto-generated IDs.
366
+ should "convert document ID to string" do
367
+ # This is related to issues #529, #535, #775
370
368
  class Document1; def id; "one"; end; end
371
369
  class Document2; def id; 1; end; end
372
- class Document3; class ID; def as_json; 'c'; end; end
370
+ class Document3; class ID; def to_s; 'c'; end; end
373
371
  def id; ID.new; end
374
372
  end
375
373
 
@@ -378,7 +376,7 @@ module Tire
378
376
  document_3 = Document3.new
379
377
 
380
378
  assert_equal 'one', @index.get_id_from_document(document_1)
381
- assert_equal 1, @index.get_id_from_document(document_2)
379
+ assert_equal '1', @index.get_id_from_document(document_2)
382
380
  assert_equal 'c', @index.get_id_from_document(document_3)
383
381
  end
384
382
 
@@ -616,7 +614,7 @@ module Tire
616
614
  end
617
615
 
618
616
  context "when performing a bulk api action" do
619
- # Possible Bulk API actions are `index`, `create`, `delete`
617
+ # Possible Bulk API actions are `index`, `create`, `delete`, `update`
620
618
  #
621
619
  # The expected JSON looks like this:
622
620
  #
@@ -625,6 +623,8 @@ module Tire
625
623
  # {"create":{"_index":"dummy","_type":"document","_id":"2"}}
626
624
  # {"id":"2","title":"Two"}
627
625
  # {"delete":{"_index":"dummy","_type":"document","_id":"2"}}
626
+ # {"update":{"_index":"dummy","_type":"document","_id":"1","_retry_on_conflict": 3}}
627
+ # {"doc":{"title":"Updated One"}}
628
628
  #
629
629
  # See http://www.elasticsearch.org/guide/reference/api/bulk.html
630
630
 
@@ -681,6 +681,39 @@ module Tire
681
681
  @index.bulk :delete, [ {:id => '1', :title => 'One'}, {:id => '2', :title => 'Two'} ]
682
682
  end
683
683
 
684
+ should "serialize payload for update action" do
685
+ Configuration.client.
686
+ expects(:post).
687
+ with do |url, payload|
688
+ assert_equal "#{@index.url}/_bulk", url
689
+ assert_match /"update"/, payload
690
+ assert_match /"_index":"dummy"/, payload
691
+ assert_match /"_type":"document"/, payload
692
+ assert_match /"_id":"1"/, payload
693
+ assert_match /"_id":"2"/, payload
694
+ lines = payload.split("\n")
695
+ assert_equal 'One', MultiJson.decode(lines[1])['doc']['title']
696
+ assert_equal 'Two', MultiJson.decode(lines[3])['doc']['title']
697
+ assert_equal true, MultiJson.decode(lines[3])['doc_as_upsert']
698
+ end.
699
+ returns(mock_response('{}'), 200)
700
+
701
+ @index.bulk :update, [ {:id => '1', :doc => {:title => 'One'}}, {:id => '2', :doc => {:title => 'Two'}, :doc_as_upsert => true} ]
702
+ end
703
+
704
+ should 'serialize _retry_on_conflict parameter into payload header' do
705
+ Configuration.client.
706
+ expects(:post).
707
+ with do |url, payload|
708
+ lines = payload.split("\n")
709
+ assert_equal 3, MultiJson.decode(lines[0])['update']['_retry_on_conflict']
710
+ assert_equal 3, MultiJson.decode(lines[2])['update']['_retry_on_conflict']
711
+ end.
712
+ returns(mock_response('{}'), 200)
713
+
714
+ @index.bulk :update, [ {:id => '1', :doc => {:title => 'One'}}, {:id => '2', :doc => {:title => 'Two'}} ], :retry_on_conflict => 3
715
+ end
716
+
684
717
  should "serialize meta parameters into payload header" do
685
718
  Configuration.client.
686
719
  expects(:post).
@@ -214,9 +214,11 @@ module Tire
214
214
  assert_equal false, article.hidden
215
215
  end
216
216
 
217
- should "evaluate lambdas as default values" do
217
+ should "evaluate lambdas as default values at time of initialization" do
218
+ now = Time.now
219
+ Time.stubs(:now).returns(now)
218
220
  article = PersistentArticleWithDefaults.new
219
- assert_equal Time.now.year, article.created_at.year
221
+ assert_equal now, article.created_at
220
222
  end
221
223
 
222
224
  should "not affect default value" do
@@ -69,7 +69,7 @@ module Tire
69
69
 
70
70
  should "be kaminari compatible" do
71
71
  collection = Results::Collection.new(@default_response)
72
- %w(limit_value total_count num_pages offset_value first_page? last_page?).each do |method|
72
+ %w(limit_value total_count num_pages offset_value first_page? last_page? out_of_range?).each do |method|
73
73
  assert_respond_to collection, method
74
74
  end
75
75
  end
@@ -128,6 +128,14 @@ module Tire
128
128
  assert_match /index_1,index_2/, s.to_curl
129
129
  end
130
130
 
131
+ should 'not include the load option in queries' do
132
+ s = Search::Search.new(:load => { :includes => [:author, {:nested => :relation}] }) do
133
+ query { string 'title:foo' }
134
+ end
135
+
136
+ assert_nil s.to_hash[:load], 'Make sure to ignore load in URL params'
137
+ end
138
+
131
139
  should "return itself as a Hash" do
132
140
  s = Search::Search.new('index') do
133
141
  query { string 'title:foo' }
@@ -68,13 +68,13 @@ Gem::Specification.new do |s|
68
68
  your models (incrementally upon saving, or in bulk), searching and
69
69
  paginating the results.
70
70
 
71
- Please check the documentation at <http://karmi.github.com/tire/>.
71
+ Please check the documentation at <http://karmi.github.com/retire/>.
72
72
  DESC
73
73
 
74
74
  s.post_install_message =<<-CHANGELOG.gsub(/^ /, '')
75
75
  ================================================================================
76
76
 
77
- Please check the documentation at <http://karmi.github.com/tire/>.
77
+ Please check the documentation at <http://karmi.github.com/retire/>.
78
78
 
79
79
  --------------------------------------------------------------------------------
80
80
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tire
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-08 00:00:00.000000000 Z
12
+ date: 2013-10-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -370,7 +370,7 @@ description: ! " Tire is a Ruby client for the Elasticsearch search engine/dat
370
370
  bulk API, and presents an easy-to-use DSL for constructing your queries.\n\n It
371
371
  has full ActiveRecord/ActiveModel compatibility, allowing you to index\n your
372
372
  models (incrementally upon saving, or in bulk), searching and\n paginating the
373
- results.\n\n Please check the documentation at <http://karmi.github.com/tire/>.\n"
373
+ results.\n\n Please check the documentation at <http://karmi.github.com/retire/>.\n"
374
374
  email: karmi@karmi.cz
375
375
  executables: []
376
376
  extensions: []
@@ -391,6 +391,7 @@ files:
391
391
  - lib/tire/alias.rb
392
392
  - lib/tire/configuration.rb
393
393
  - lib/tire/count.rb
394
+ - lib/tire/delete_by_query.rb
394
395
  - lib/tire/dsl.rb
395
396
  - lib/tire/http/client.rb
396
397
  - lib/tire/http/clients/curb.rb
@@ -444,6 +445,7 @@ files:
444
445
  - test/integration/count_test.rb
445
446
  - test/integration/custom_filters_score_queries_test.rb
446
447
  - test/integration/custom_score_queries_test.rb
448
+ - test/integration/delete_by_query_test.rb
447
449
  - test/integration/dis_max_queries_test.rb
448
450
  - test/integration/dsl_search_test.rb
449
451
  - test/integration/explanation_test.rb
@@ -519,16 +521,13 @@ files:
519
521
  homepage: http://github.com/karmi/tire
520
522
  licenses: []
521
523
  post_install_message: ! "================================================================================\n\n
522
- \ Please check the documentation at <http://karmi.github.com/tire/>.\n\n--------------------------------------------------------------------------------\n\n
523
- \ IMPORTANT CHANGES LATELY:\n\n * Fixed incorrect inflection in the Rake import
524
- tasks\n * Added support for `geo_distance` facets\n * Added support for the `custom_filters_score`
525
- query\n * Added a custom strategy option to <Model.import>\n * Allow the `:wrapper`
526
- option to be passed to Tire.search consistently\n * Improved the Mongoid importing
527
- strategy\n * Merge returned `fields` with `_source` if both are returned\n * Removed
528
- the deprecated `text` query\n * [FIX] Rescue HTTP client specific connection errors
529
- in MyModel#create_elasticsearch_index\n * Added support for passing `version` in
530
- Tire::Index#store\n * Added support for `_version_type` in Tire::Index#bulk\n *
531
- Added ActiveModel::Serializers compatibility\n\n See the full changelog at <http://github.com/karmi/tire/commits/v0.6.0>.\n\n--------------------------------------------------------------------------------\n"
524
+ \ Please check the documentation at <http://karmi.github.com/retire/>.\n\n--------------------------------------------------------------------------------\n\n
525
+ \ IMPORTANT CHANGES LATELY:\n\n * Added support for bulk update\n * Improved Kaminari
526
+ support\n * Improved the behaviour of `default` properties in Tire::Persistence\n
527
+ \ * Added the information about the gem \"retirement\" and other documentation improvements\n
528
+ \ * Fixed errors due to NewRelic's patching of Curl\n * [ACTIVEMODEL] Use Object#id#to_s
529
+ in `get_id_from_document`\n * Added support for \"Delete By Query\" API\n\n See
530
+ the full changelog at <http://github.com/karmi/tire/commits/v0.6.1>.\n\n--------------------------------------------------------------------------------\n"
532
531
  rdoc_options:
533
532
  - --charset=UTF-8
534
533
  require_paths:
@@ -567,6 +566,7 @@ test_files:
567
566
  - test/integration/count_test.rb
568
567
  - test/integration/custom_filters_score_queries_test.rb
569
568
  - test/integration/custom_score_queries_test.rb
569
+ - test/integration/delete_by_query_test.rb
570
570
  - test/integration/dis_max_queries_test.rb
571
571
  - test/integration/dsl_search_test.rb
572
572
  - test/integration/explanation_test.rb