tire 0.3.3 → 0.3.4
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 +34 -3
- data/examples/tire-dsl.rb +1 -1
- data/lib/tire/http/client.rb +5 -5
- data/lib/tire/index.rb +7 -3
- data/lib/tire/model/callbacks.rb +0 -8
- data/lib/tire/model/indexing.rb +3 -0
- data/lib/tire/model/naming.rb +9 -1
- data/lib/tire/model/persistence/finders.rb +3 -3
- data/lib/tire/model/persistence/storage.rb +0 -4
- data/lib/tire/model/search.rb +8 -3
- data/lib/tire/results/pagination.rb +9 -0
- data/lib/tire/search.rb +2 -1
- data/lib/tire/version.rb +5 -25
- data/test/integration/active_record_searchable_test.rb +47 -0
- data/test/integration/mongoid_searchable_test.rb +308 -0
- data/test/integration/persistent_model_test.rb +10 -1
- data/test/models/active_record_models.rb +9 -12
- data/test/models/mongoid_models.rb +98 -0
- data/test/unit/http_client_test.rb +7 -0
- data/test/unit/model_callbacks_test.rb +36 -16
- data/test/unit/model_search_test.rb +43 -0
- data/test/unit/results_collection_test.rb +7 -0
- data/test/unit/search_test.rb +3 -1
- data/tire.gemspec +1 -0
- metadata +228 -172
data/README.markdown
CHANGED
@@ -134,6 +134,17 @@ We can easily manipulate the documents before storing them in the index, by pass
|
|
134
134
|
end
|
135
135
|
```
|
136
136
|
|
137
|
+
If this _declarative_ notation does not fit well in your context,
|
138
|
+
you can use _Tire's_ classes directly, in a more imperative manner:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
index = Tire::Index.new('oldskool')
|
142
|
+
index.delete
|
143
|
+
index.create
|
144
|
+
index.store :title => "Let's do it the old way!"
|
145
|
+
index.refresh
|
146
|
+
```
|
147
|
+
|
137
148
|
OK. Now, let's go search all the data.
|
138
149
|
|
139
150
|
We will be searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
|
@@ -281,7 +292,21 @@ a plain old Ruby `Hash` (or JSON string) with the query declaration to the `sear
|
|
281
292
|
If this sounds like a great idea to you, you are probably able to write your application
|
282
293
|
using just `curl`, `sed` and `awk`.
|
283
294
|
|
284
|
-
|
295
|
+
Do note again, however, that you're not tied to the declarative block-style DSL _Tire_ offers to you.
|
296
|
+
If it makes more sense in your context, you can use its classes directly, in a more imperative style:
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
search = Tire::Search::Search.new('articles')
|
300
|
+
search.query { string('title:T*') }
|
301
|
+
search.filter :terms, :tags => ['ruby']
|
302
|
+
search.sort { by :title, 'desc' }
|
303
|
+
search.facet('global-tags') { terms :tags, :global => true }
|
304
|
+
# ...
|
305
|
+
p search.perform.results
|
306
|
+
```
|
307
|
+
|
308
|
+
To debug the query we have laboriously set up like this,
|
309
|
+
we can display the full query JSON for close inspection:
|
285
310
|
|
286
311
|
```ruby
|
287
312
|
puts s.to_json
|
@@ -692,6 +717,14 @@ This will result in Article instances being stored in an index called 'test_arti
|
|
692
717
|
Please be sure to peruse the [integration test suite](https://github.com/karmi/tire/tree/master/test/integration)
|
693
718
|
for examples of the API and _ActiveModel_ integration usage.
|
694
719
|
|
720
|
+
|
721
|
+
Extensions and Additions
|
722
|
+
------------------------
|
723
|
+
|
724
|
+
The [_tire-contrib_](http://github.com/karmi/tire-contrib/) project contains additions
|
725
|
+
and extensions to the _Tire_ functionality.
|
726
|
+
|
727
|
+
|
695
728
|
Todo, Plans & Ideas
|
696
729
|
-------------------
|
697
730
|
|
@@ -699,8 +732,6 @@ _Tire_ is already used in production by its authors. Nevertheless, it's not cons
|
|
699
732
|
|
700
733
|
There are todos, plans and ideas, some of which are listed below, in the order of importance:
|
701
734
|
|
702
|
-
* Wrap all Tire functionality mixed into a model in a "forwardable" object, and proxy everything via this object. (The immediate problem: [Mongoid](http://mongoid.org/docs/indexing.html))
|
703
|
-
* If we're not stepping on other's toes, bring Tire methods like `index`, `search`, `mapping` also to the class/instance top-level namespace.
|
704
735
|
* Proper RDoc annotations for the source code
|
705
736
|
* [Statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html) facets
|
706
737
|
* [Geo Distance](http://www.elasticsearch.org/guide/reference/api/search/facets/geo-distance-facet.html) facets
|
data/examples/tire-dsl.rb
CHANGED
data/lib/tire/http/client.rb
CHANGED
@@ -8,31 +8,31 @@ module Tire
|
|
8
8
|
|
9
9
|
def self.get(url, data=nil)
|
10
10
|
perform ::RestClient::Request.new(:method => :get, :url => url, :payload => data).execute
|
11
|
-
rescue Exception => e
|
11
|
+
rescue ::RestClient::Exception => e
|
12
12
|
Response.new e.http_body, e.http_code
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.post(url, data)
|
16
16
|
perform ::RestClient.post(url, data)
|
17
|
-
rescue Exception => e
|
17
|
+
rescue ::RestClient::Exception => e
|
18
18
|
Response.new e.http_body, e.http_code
|
19
19
|
end
|
20
20
|
|
21
21
|
def self.put(url, data)
|
22
22
|
perform ::RestClient.put(url, data)
|
23
|
-
rescue Exception => e
|
23
|
+
rescue ::RestClient::Exception => e
|
24
24
|
Response.new e.http_body, e.http_code
|
25
25
|
end
|
26
26
|
|
27
27
|
def self.delete(url)
|
28
28
|
perform ::RestClient.delete(url)
|
29
|
-
rescue Exception => e
|
29
|
+
rescue ::RestClient::Exception => e
|
30
30
|
Response.new e.http_body, e.http_code
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.head(url)
|
34
34
|
perform ::RestClient.head(url)
|
35
|
-
rescue Exception => e
|
35
|
+
rescue ::RestClient::Exception => e
|
36
36
|
Response.new e.http_body, e.http_code
|
37
37
|
end
|
38
38
|
|
data/lib/tire/index.rb
CHANGED
@@ -9,7 +9,11 @@ module Tire
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def exists?
|
12
|
-
Configuration.client.head("#{Configuration.url}/#{@name}")
|
12
|
+
@response = Configuration.client.head("#{Configuration.url}/#{@name}")
|
13
|
+
@response.success?
|
14
|
+
ensure
|
15
|
+
curl = %Q|curl -I "#{Configuration.url}/#{@name}"|
|
16
|
+
logged(@response.body, 'HEAD', curl) if @response
|
13
17
|
end
|
14
18
|
|
15
19
|
def delete
|
@@ -26,7 +30,7 @@ module Tire
|
|
26
30
|
@response.success? ? @response : false
|
27
31
|
ensure
|
28
32
|
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}" -d '#{MultiJson.encode(options)}'|
|
29
|
-
logged(@response.body, 'CREATE', curl)
|
33
|
+
logged(@response.body, 'CREATE', curl) if @response
|
30
34
|
end
|
31
35
|
|
32
36
|
def mapping
|
@@ -230,7 +234,7 @@ module Tire
|
|
230
234
|
if Configuration.logger.level.to_s == 'debug'
|
231
235
|
# FIXME: Depends on RestClient implementation
|
232
236
|
body = if @response
|
233
|
-
defined?(Yajl) ? Yajl::Encoder.encode(@
|
237
|
+
defined?(Yajl) ? Yajl::Encoder.encode(@response.body, :pretty => true) : MultiJson.encode(@response.body)
|
234
238
|
else
|
235
239
|
error.http_body rescue ''
|
236
240
|
end
|
data/lib/tire/model/callbacks.rb
CHANGED
@@ -9,9 +9,6 @@ module Tire
|
|
9
9
|
# The model must respond to `after_save` and `after_destroy` callbacks
|
10
10
|
# (ActiveModel and ActiveRecord models do so, by default).
|
11
11
|
#
|
12
|
-
# By including the model, you will also have the `after_update_elasticsearch_index` and
|
13
|
-
# `before_update_elasticsearch_index` callbacks available.
|
14
|
-
#
|
15
12
|
module Callbacks
|
16
13
|
|
17
14
|
# A hook triggered by the `include Tire::Model::Callbacks` statement in the model.
|
@@ -35,11 +32,6 @@ module Tire
|
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
38
|
-
# Define _Tire's_ callbacks.
|
39
|
-
#
|
40
|
-
base.class_eval do
|
41
|
-
define_model_callbacks(:update_elasticsearch_index, :only => [:after, :before])
|
42
|
-
end if base.respond_to?(:define_model_callbacks)
|
43
35
|
end
|
44
36
|
|
45
37
|
end
|
data/lib/tire/model/indexing.rb
CHANGED
@@ -92,6 +92,9 @@ module Tire
|
|
92
92
|
unless index.exists?
|
93
93
|
index.create :mappings => mapping_to_hash, :settings => settings
|
94
94
|
end
|
95
|
+
rescue Errno::ECONNREFUSED => e
|
96
|
+
STDERR.puts "Skipping index creation, cannot connect to ElasticSearch",
|
97
|
+
"(The original exception was: #{e.inspect})"
|
95
98
|
end
|
96
99
|
|
97
100
|
def store_mapping?
|
data/lib/tire/model/naming.rb
CHANGED
@@ -20,8 +20,16 @@ module Tire
|
|
20
20
|
#
|
21
21
|
# Article.index_name 'my-custom-name'
|
22
22
|
#
|
23
|
-
|
23
|
+
# You can also use a block for defining the index name,
|
24
|
+
# which is evaluated in the class context:
|
25
|
+
#
|
26
|
+
# Article.index_name { "articles-#{Time.now.year}" }
|
27
|
+
#
|
28
|
+
# Article.index_name { "articles-#{Rails.env}" }
|
29
|
+
#
|
30
|
+
def index_name name=nil, &block
|
24
31
|
@index_name = name if name
|
32
|
+
@index_name = block if block_given?
|
25
33
|
@index_name || [index_prefix, klass.model_name.plural].compact.join('_')
|
26
34
|
end
|
27
35
|
|
@@ -22,7 +22,7 @@ module Tire
|
|
22
22
|
else
|
23
23
|
case args = args.pop
|
24
24
|
when Fixnum, String
|
25
|
-
|
25
|
+
index.retrieve document_type, args
|
26
26
|
when :all, :first
|
27
27
|
send(args)
|
28
28
|
else
|
@@ -37,7 +37,7 @@ module Tire
|
|
37
37
|
# TODO: Options like `sort`; Possibly `filters`
|
38
38
|
old_wrapper = Tire::Configuration.wrapper
|
39
39
|
Tire::Configuration.wrapper self
|
40
|
-
s = Tire::Search::Search.new(
|
40
|
+
s = Tire::Search::Search.new(index.name).query { all }
|
41
41
|
s.perform.results
|
42
42
|
ensure
|
43
43
|
Tire::Configuration.wrapper old_wrapper
|
@@ -47,7 +47,7 @@ module Tire
|
|
47
47
|
# TODO: Options like `sort`; Possibly `filters`
|
48
48
|
old_wrapper = Tire::Configuration.wrapper
|
49
49
|
Tire::Configuration.wrapper self
|
50
|
-
s = Tire::Search::Search.new(
|
50
|
+
s = Tire::Search::Search.new(index.name).query { all }.size(1)
|
51
51
|
s.perform.results.first
|
52
52
|
ensure
|
53
53
|
Tire::Configuration.wrapper old_wrapper
|
data/lib/tire/model/search.rb
CHANGED
@@ -99,7 +99,8 @@ module Tire
|
|
99
99
|
# Example usage: `Article.index.refresh`.
|
100
100
|
#
|
101
101
|
def index
|
102
|
-
|
102
|
+
name = index_name.respond_to?(:to_proc) ? klass.instance_eval(&index_name) : index_name
|
103
|
+
@index = Index.new(name)
|
103
104
|
end
|
104
105
|
|
105
106
|
end
|
@@ -120,8 +121,7 @@ module Tire
|
|
120
121
|
#
|
121
122
|
# On model destroy, it will remove the corresponding document from the index.
|
122
123
|
#
|
123
|
-
# It will also
|
124
|
-
# if defined by the model.
|
124
|
+
# It will also execute any `<after|before>_update_elasticsearch_index` callback hooks.
|
125
125
|
#
|
126
126
|
def update_index
|
127
127
|
instance.send :_run_update_elasticsearch_index_callbacks do
|
@@ -237,6 +237,11 @@ module Tire
|
|
237
237
|
@__tire__
|
238
238
|
end
|
239
239
|
|
240
|
+
# Define _Tire's_ callbacks (<after|before>_update_elasticsearch_index).
|
241
|
+
#
|
242
|
+
define_model_callbacks(:update_elasticsearch_index, :only => [:after, :before]) if \
|
243
|
+
respond_to?(:define_model_callbacks)
|
244
|
+
|
240
245
|
# Serialize the model as a Hash.
|
241
246
|
#
|
242
247
|
# Uses `serializable_hash` representation of the model,
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Tire
|
2
2
|
module Results
|
3
3
|
|
4
|
+
# Adds support for WillPaginate and Kaminari
|
5
|
+
#
|
4
6
|
module Pagination
|
5
7
|
|
6
8
|
def total_entries
|
@@ -39,6 +41,13 @@ module Tire
|
|
39
41
|
current_page > total_pages
|
40
42
|
end
|
41
43
|
|
44
|
+
# Kaminari support
|
45
|
+
#
|
46
|
+
alias :limit_value :per_page
|
47
|
+
alias :total_count :total_entries
|
48
|
+
alias :num_pages :total_pages
|
49
|
+
alias :offset_value :offset
|
50
|
+
|
42
51
|
end
|
43
52
|
|
44
53
|
end
|
data/lib/tire/search.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Tire
|
2
2
|
module Search
|
3
|
+
class SearchRequestFailed < StandardError; end
|
3
4
|
|
4
5
|
class Search
|
5
6
|
|
@@ -70,7 +71,7 @@ module Tire
|
|
70
71
|
@response = Configuration.client.get(@url, self.to_json)
|
71
72
|
if @response.failure?
|
72
73
|
STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
|
73
|
-
|
74
|
+
raise SearchRequestFailed
|
74
75
|
end
|
75
76
|
@json = MultiJson.decode(@response.body)
|
76
77
|
@results = Results::Collection.new(@json, @options)
|
data/lib/tire/version.rb
CHANGED
@@ -1,34 +1,14 @@
|
|
1
1
|
module Tire
|
2
|
-
VERSION = "0.3.
|
2
|
+
VERSION = "0.3.4"
|
3
3
|
|
4
4
|
CHANGELOG =<<-END
|
5
5
|
IMPORTANT CHANGES LATELY:
|
6
6
|
|
7
|
-
0.
|
7
|
+
0.3.4
|
8
8
|
---------------------------------------------------------
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Deprecated the dynamic sort methods in favour of the 'sort { by :field_name }' syntax
|
13
|
-
|
14
|
-
0.2.1
|
15
|
-
---------------------------------------------------------
|
16
|
-
# Lighweight check for index presence
|
17
|
-
# Added the 'settings' method for models to define index settings
|
18
|
-
# Fixed errors when importing data with will_paginate vs Kaminari (MongoDB)
|
19
|
-
# Added support for histogram facets [Paco Guzman]
|
20
|
-
|
21
|
-
0.3.0
|
22
|
-
---------------------------------------------------------
|
23
|
-
# Isolated Tire ActiveModel integration into `tire` class and instance method.
|
24
|
-
|
25
|
-
When there's no conflict with existing methods, Tire methods are added
|
26
|
-
to the class namespace, as well, so the change is 100% backwards-compatible.
|
27
|
-
|
28
|
-
0.3.3
|
29
|
-
---------------------------------------------------------
|
30
|
-
# Added proper will_paginate compatibility
|
31
|
-
# Added support for plugging in another HTTP library (see lib/http/clients/curb for an example)
|
9
|
+
# Added documentation (RDoc annotations and README)
|
10
|
+
# Kaminari pagination support
|
11
|
+
# Bugfixes (dependency on ES running, callbacks, bogus exceptions)
|
32
12
|
|
33
13
|
END
|
34
14
|
end
|
@@ -29,6 +29,9 @@ module Tire
|
|
29
29
|
create_table :active_record_class_with_tire_methods do |t|
|
30
30
|
t.string :title
|
31
31
|
end
|
32
|
+
create_table :active_record_class_with_dynamic_index_names do |t|
|
33
|
+
t.string :title
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -72,6 +75,14 @@ module Tire
|
|
72
75
|
assert_equal 'Test', results.first.title
|
73
76
|
end
|
74
77
|
|
78
|
+
should "raise exception on invalid query" do
|
79
|
+
ActiveRecordArticle.create! :title => 'Test'
|
80
|
+
|
81
|
+
assert_raise Search::SearchRequestFailed do
|
82
|
+
ActiveRecordArticle.search '[x'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
75
86
|
context "with eager loading" do
|
76
87
|
setup do
|
77
88
|
ActiveRecordArticle.destroy_all
|
@@ -147,11 +158,20 @@ module Tire
|
|
147
158
|
results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 1
|
148
159
|
assert_equal 5, results.size
|
149
160
|
|
161
|
+
# WillPaginate
|
162
|
+
#
|
150
163
|
assert_equal 2, results.total_pages
|
151
164
|
assert_equal 1, results.current_page
|
152
165
|
assert_equal nil, results.previous_page
|
153
166
|
assert_equal 2, results.next_page
|
154
167
|
|
168
|
+
# Kaminari
|
169
|
+
#
|
170
|
+
assert_equal 5, results.limit_value
|
171
|
+
assert_equal 9, results.total_count
|
172
|
+
assert_equal 2, results.num_pages
|
173
|
+
assert_equal 0, results.offset_value
|
174
|
+
|
155
175
|
assert_equal 'Test1', results.first.title
|
156
176
|
end
|
157
177
|
|
@@ -164,6 +184,12 @@ module Tire
|
|
164
184
|
assert_equal 1, results.previous_page
|
165
185
|
assert_equal nil, results.next_page
|
166
186
|
|
187
|
+
#kaminari
|
188
|
+
assert_equal 5, results.limit_value
|
189
|
+
assert_equal 9, results.total_count
|
190
|
+
assert_equal 2, results.num_pages
|
191
|
+
assert_equal 5, results.offset_value
|
192
|
+
|
167
193
|
assert_equal 'Test6', results.first.title
|
168
194
|
end
|
169
195
|
|
@@ -176,6 +202,12 @@ module Tire
|
|
176
202
|
assert_equal 2, results.previous_page
|
177
203
|
assert_equal nil, results.next_page
|
178
204
|
|
205
|
+
#kaminari
|
206
|
+
assert_equal 5, results.limit_value
|
207
|
+
assert_equal 9, results.total_count
|
208
|
+
assert_equal 2, results.num_pages
|
209
|
+
assert_equal 10, results.offset_value
|
210
|
+
|
179
211
|
assert_nil results.first
|
180
212
|
end
|
181
213
|
|
@@ -267,6 +299,21 @@ module Tire
|
|
267
299
|
|
268
300
|
end
|
269
301
|
|
302
|
+
context "with dynamic index name" do
|
303
|
+
setup do
|
304
|
+
@a = ActiveRecordClassWithDynamicIndexName.create! :title => 'Test'
|
305
|
+
@a.index.refresh
|
306
|
+
end
|
307
|
+
|
308
|
+
should "search in proper index" do
|
309
|
+
assert_equal 'dynamic_index', ActiveRecordClassWithDynamicIndexName.index.name
|
310
|
+
assert_equal 'dynamic_index', @a.index.name
|
311
|
+
|
312
|
+
results = ActiveRecordClassWithDynamicIndexName.search 'test'
|
313
|
+
assert_equal 'dynamic_index', results.first._index
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
270
317
|
context "within Rails" do
|
271
318
|
|
272
319
|
setup do
|