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.
- data/README.markdown +27 -2
- data/examples/rails-application-template.rb +6 -7
- data/lib/tire.rb +1 -0
- data/lib/tire/delete_by_query.rb +76 -0
- data/lib/tire/dsl.rb +4 -0
- data/lib/tire/http/client.rb +1 -1
- data/lib/tire/http/clients/curb.rb +5 -1
- data/lib/tire/index.rb +30 -3
- data/lib/tire/model/persistence/attributes.rb +10 -2
- data/lib/tire/model/persistence/storage.rb +4 -0
- data/lib/tire/results/pagination.rb +1 -0
- data/lib/tire/search.rb +1 -1
- data/lib/tire/tasks.rb +9 -2
- data/lib/tire/version.rb +8 -13
- data/test/integration/active_model_searchable_test.rb +15 -0
- data/test/integration/bulk_test.rb +18 -0
- data/test/integration/custom_score_queries_test.rb +1 -1
- data/test/integration/delete_by_query_test.rb +47 -0
- data/test/integration/explanation_test.rb +4 -9
- data/test/integration/index_update_document_test.rb +28 -18
- data/test/integration/percolator_test.rb +3 -3
- data/test/integration/persistent_model_test.rb +19 -2
- data/test/unit/index_test.rb +40 -7
- data/test/unit/model_persistence_test.rb +4 -2
- data/test/unit/results_collection_test.rb +1 -1
- data/test/unit/search_test.rb +8 -0
- data/tire.gemspec +2 -2
- metadata +13 -13
data/README.markdown
CHANGED
@@ -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/
|
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/
|
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
|
17
|
-
# *
|
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
|
-
|
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
|
data/lib/tire.rb
CHANGED
@@ -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
|
data/lib/tire/dsl.rb
CHANGED
@@ -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
|
data/lib/tire/http/client.rb
CHANGED
@@ -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.
|
47
|
+
client.send method, data
|
44
48
|
Response.new client.body_str, client.response_code
|
45
49
|
end
|
46
50
|
|
data/lib/tire/index.rb
CHANGED
@@ -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 `
|
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.
|
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
|
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
|
-
|
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))
|
data/lib/tire/search.rb
CHANGED
data/lib/tire/tasks.rb
CHANGED
@@ -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
|
|
data/lib/tire/version.rb
CHANGED
@@ -1,20 +1,15 @@
|
|
1
1
|
module Tire
|
2
|
-
VERSION = "0.6.
|
2
|
+
VERSION = "0.6.1"
|
3
3
|
|
4
4
|
CHANGELOG =<<-END
|
5
5
|
IMPORTANT CHANGES LATELY:
|
6
6
|
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
* Added
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
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
|
-
|
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"
|
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"
|
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',
|
40
|
-
|
41
|
-
|
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',
|
51
|
-
|
52
|
-
|
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',
|
63
|
-
|
64
|
-
|
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',
|
105
|
-
|
106
|
-
|
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
|
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
|
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
|
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
|
data/test/unit/index_test.rb
CHANGED
@@ -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
|
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
|
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,
|
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
|
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
|
data/test/unit/search_test.rb
CHANGED
@@ -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' }
|
data/tire.gemspec
CHANGED
@@ -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/
|
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/
|
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.
|
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-
|
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/
|
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/
|
523
|
-
\ IMPORTANT CHANGES LATELY:\n\n *
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
the
|
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
|