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