tire 0.1.4 → 0.1.5
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/.gitignore +1 -0
- data/.travis.yml +12 -0
- data/README.markdown +61 -1
- data/examples/tire-dsl.rb +8 -0
- data/lib/tire/configuration.rb +3 -2
- data/lib/tire/dsl.rb +1 -1
- data/lib/tire/index.rb +30 -8
- data/lib/tire/results/collection.rb +4 -0
- data/lib/tire/results/item.rb +1 -1
- data/lib/tire/rubyext/hash.rb +5 -0
- data/lib/tire/search/facet.rb +4 -0
- data/lib/tire/search/query.rb +53 -2
- data/lib/tire/search.rb +3 -3
- data/lib/tire/tasks.rb +1 -1
- data/lib/tire/version.rb +1 -1
- data/lib/tire.rb +1 -1
- data/test/integration/active_record_searchable_test.rb +2 -4
- data/test/integration/boolean_queries_test.rb +43 -0
- data/test/integration/facets_test.rb +18 -0
- data/test/models/active_model_article_with_custom_index_name.rb +2 -0
- data/test/test_helper.rb +7 -2
- data/test/unit/index_test.rb +11 -1
- data/test/unit/logger_test.rb +2 -2
- data/test/unit/model_persistence_test.rb +52 -13
- data/test/unit/results_collection_test.rb +5 -0
- data/test/unit/results_item_test.rb +6 -1
- data/test/unit/rubyext_hash_test.rb +29 -2
- data/test/unit/search_facet_test.rb +8 -1
- data/test/unit/search_query_test.rb +65 -0
- data/test/unit/search_test.rb +39 -3
- data/tire.gemspec +5 -3
- metadata +59 -25
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# ---------------------------------------------------------
|
2
|
+
# Configuration file for http://travis-ci.org/#!/karmi/tire
|
3
|
+
# ---------------------------------------------------------
|
4
|
+
|
5
|
+
script: "bundle exec rake test:unit"
|
6
|
+
|
7
|
+
rvm:
|
8
|
+
- 1.8.7
|
9
|
+
- 1.9.2
|
10
|
+
|
11
|
+
notifications:
|
12
|
+
disable: true
|
data/README.markdown
CHANGED
@@ -44,14 +44,26 @@ classes for convenient usage in Rails applications.
|
|
44
44
|
|
45
45
|
To test-drive the core _ElasticSearch_ functionality, let's require the gem:
|
46
46
|
|
47
|
+
```ruby
|
47
48
|
require 'rubygems'
|
48
49
|
require 'tire'
|
50
|
+
```
|
49
51
|
|
50
52
|
Please note that you can copy these snippets from the much more extensive and heavily annotated file
|
51
53
|
in [examples/tire-dsl.rb](http://karmi.github.com/tire/).
|
52
54
|
|
55
|
+
Also, note that we're doing some heavy JSON lifting here. _Tire_ uses the
|
56
|
+
[_multi_json_](https://github.com/intridea/multi_json) gem as a generic JSON wrapper,
|
57
|
+
which allows you to use your preferred JSON library. We'll use the
|
58
|
+
[_yajl-ruby_](https://github.com/brianmario/yajl-ruby) gem in the full on mode here:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'yajl/json_gem'
|
62
|
+
```
|
63
|
+
|
53
64
|
OK. Let's create an index named `articles` and store/index some documents:
|
54
65
|
|
66
|
+
```ruby
|
55
67
|
Tire.index 'articles' do
|
56
68
|
delete
|
57
69
|
create
|
@@ -63,11 +75,13 @@ OK. Let's create an index named `articles` and store/index some documents:
|
|
63
75
|
|
64
76
|
refresh
|
65
77
|
end
|
78
|
+
```
|
66
79
|
|
67
80
|
We can also create the index with custom
|
68
81
|
[mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
|
69
82
|
for a specific document type:
|
70
83
|
|
84
|
+
```ruby
|
71
85
|
Tire.index 'articles' do
|
72
86
|
create :mappings => {
|
73
87
|
:article => {
|
@@ -80,10 +94,12 @@ for a specific document type:
|
|
80
94
|
}
|
81
95
|
}
|
82
96
|
end
|
97
|
+
```
|
83
98
|
|
84
99
|
Of course, we may have large amounts of data, and it may be impossible or impractical to add them to the index
|
85
100
|
one by one. We can use _ElasticSearch's_ [bulk storage](http://www.elasticsearch.org/guide/reference/api/bulk.html):
|
86
101
|
|
102
|
+
```ruby
|
87
103
|
articles = [
|
88
104
|
{ :id => '1', :title => 'one' },
|
89
105
|
{ :id => '2', :title => 'two' },
|
@@ -93,16 +109,19 @@ one by one. We can use _ElasticSearch's_ [bulk storage](http://www.elasticsearch
|
|
93
109
|
Tire.index 'bulk' do
|
94
110
|
import articles
|
95
111
|
end
|
112
|
+
```
|
96
113
|
|
97
114
|
We can also easily manipulate the documents before storing them in the index, by passing a block to the
|
98
115
|
`import` method:
|
99
116
|
|
117
|
+
```ruby
|
100
118
|
Tire.index 'bulk' do
|
101
119
|
import articles do |documents|
|
102
120
|
|
103
121
|
documents.each { |document| document[:title].capitalize! }
|
104
122
|
end
|
105
123
|
end
|
124
|
+
```
|
106
125
|
|
107
126
|
OK. Now, let's go search all the data.
|
108
127
|
|
@@ -110,6 +129,7 @@ We will be searching for articles whose `title` begins with letter “T”, sort
|
|
110
129
|
filtering them for ones tagged “ruby”, and also retrieving some [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/)
|
111
130
|
from the database:
|
112
131
|
|
132
|
+
```ruby
|
113
133
|
s = Tire.search 'articles' do
|
114
134
|
query do
|
115
135
|
string 'title:T*'
|
@@ -127,20 +147,24 @@ from the database:
|
|
127
147
|
terms :tags
|
128
148
|
end
|
129
149
|
end
|
150
|
+
```
|
130
151
|
|
131
152
|
(Of course, we may also page the results with `from` and `size` query options, retrieve only specific fields
|
132
153
|
or highlight content matching our query, etc.)
|
133
154
|
|
134
155
|
Let's display the results:
|
135
156
|
|
157
|
+
```ruby
|
136
158
|
s.results.each do |document|
|
137
159
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
138
160
|
end
|
139
161
|
|
140
162
|
# * Two [tags: ruby, python]
|
163
|
+
```
|
141
164
|
|
142
165
|
Let's display the global facets (distribution of tags across the whole database):
|
143
166
|
|
167
|
+
```ruby
|
144
168
|
s.results.facets['global-tags']['terms'].each do |f|
|
145
169
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
146
170
|
end
|
@@ -149,11 +173,13 @@ Let's display the global facets (distribution of tags across the whole database)
|
|
149
173
|
# python 1
|
150
174
|
# php 1
|
151
175
|
# java 1
|
176
|
+
```
|
152
177
|
|
153
178
|
Now, let's display the facets based on current query (notice that count for articles
|
154
179
|
tagged with 'java' is included, even though it's not returned by our query;
|
155
180
|
count for articles tagged 'php' is excluded, since they don't match the current query):
|
156
181
|
|
182
|
+
```ruby
|
157
183
|
s.results.facets['current-tags']['terms'].each do |f|
|
158
184
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
159
185
|
end
|
@@ -161,32 +187,43 @@ count for articles tagged 'php' is excluded, since they don't match the current
|
|
161
187
|
# ruby 1
|
162
188
|
# python 1
|
163
189
|
# java 1
|
190
|
+
```
|
164
191
|
|
165
192
|
If configuring the search payload with a block somehow feels too weak for you, you can simply pass
|
166
193
|
a Ruby `Hash` (or JSON string) with the query declaration to the `search` method:
|
167
194
|
|
195
|
+
```ruby
|
168
196
|
Tire.search 'articles', :query => { :fuzzy => { :title => 'Sour' } }
|
197
|
+
```
|
169
198
|
|
170
199
|
If this sounds like a great idea to you, you are probably able to write your application
|
171
200
|
using just `curl`, `sed` and `awk`.
|
172
201
|
|
173
202
|
We can display the full query JSON for close inspection:
|
174
203
|
|
204
|
+
```ruby
|
175
205
|
puts s.to_json
|
176
206
|
# {"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}
|
207
|
+
```
|
177
208
|
|
178
209
|
Or, better, we can display the corresponding `curl` command to recreate and debug the request in the terminal:
|
179
210
|
|
211
|
+
```ruby
|
180
212
|
puts s.to_curl
|
181
213
|
# curl -X POST "http://localhost:9200/articles/_search?pretty=true" -d '{"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}'
|
214
|
+
```
|
182
215
|
|
183
216
|
However, we can simply log every search query (and other requests) in this `curl`-friendly format:
|
184
217
|
|
218
|
+
```ruby
|
185
219
|
Tire.configure { logger 'elasticsearch.log' }
|
220
|
+
```
|
186
221
|
|
187
222
|
When you set the log level to _debug_:
|
188
223
|
|
224
|
+
```ruby
|
189
225
|
Tire.configure { logger 'elasticsearch.log', :level => 'debug' }
|
226
|
+
```
|
190
227
|
|
191
228
|
the JSON responses are logged as well. This is not a great idea for production environment,
|
192
229
|
but it's priceless when you want to paste a complicated transaction to the mailing list or IRC channel.
|
@@ -217,17 +254,21 @@ example Rails application, with an `ActiveRecord` model and a search form, to pl
|
|
217
254
|
For the rest, let's suppose you have an `Article` class in your Rails application.
|
218
255
|
To make it searchable with _Tire_, you just `include` it:
|
219
256
|
|
257
|
+
```ruby
|
220
258
|
class Article < ActiveRecord::Base
|
221
259
|
include Tire::Model::Search
|
222
260
|
include Tire::Model::Callbacks
|
223
261
|
end
|
262
|
+
```
|
224
263
|
|
225
264
|
When you now save a record:
|
226
265
|
|
266
|
+
```ruby
|
227
267
|
Article.create :title => "I Love ElasticSearch",
|
228
268
|
:content => "...",
|
229
269
|
:author => "Captain Nemo",
|
230
270
|
:published_on => Time.now
|
271
|
+
```
|
231
272
|
|
232
273
|
it is automatically added into the index, because of the included callbacks.
|
233
274
|
(You may want to skip them in special cases, like when your records are indexed via some external
|
@@ -238,23 +279,28 @@ The document attributes are indexed exactly as when you call the `Article#to_jso
|
|
238
279
|
|
239
280
|
Now you can search the records:
|
240
281
|
|
282
|
+
```ruby
|
241
283
|
Article.search 'love'
|
284
|
+
```
|
242
285
|
|
243
286
|
OK. This is where the game stops, often. Not here.
|
244
287
|
|
245
288
|
First of all, you may use the full query DSL, as explained above, with filters, sorting,
|
246
289
|
advanced facet aggregation, highlighting, etc:
|
247
290
|
|
291
|
+
```ruby
|
248
292
|
q = 'love'
|
249
293
|
Article.search do
|
250
294
|
query { string q }
|
251
295
|
facet('timeline') { date :published_on, :interval => 'month' }
|
252
296
|
sort { published_on 'desc' }
|
253
297
|
end
|
298
|
+
```
|
254
299
|
|
255
300
|
Dynamic mapping is a godsend when you're prototyping.
|
256
301
|
For serious usage, though, you'll definitely want to define a custom mapping for your model:
|
257
302
|
|
303
|
+
```ruby
|
258
304
|
class Article < ActiveRecord::Base
|
259
305
|
include Tire::Model::Search
|
260
306
|
include Tire::Model::Callbacks
|
@@ -267,12 +313,14 @@ For serious usage, though, you'll definitely want to define a custom mapping for
|
|
267
313
|
indexes :published_on, :type => 'date', :include_in_all => false
|
268
314
|
end
|
269
315
|
end
|
316
|
+
```
|
270
317
|
|
271
318
|
In this case, _only_ the defined model attributes are indexed when adding to the index.
|
272
319
|
|
273
320
|
When you want tight grip on how your model attributes are added to the index, just
|
274
321
|
provide the `to_indexed_json` method yourself:
|
275
322
|
|
323
|
+
```ruby
|
276
324
|
class Article < ActiveRecord::Base
|
277
325
|
include Tire::Model::Search
|
278
326
|
include Tire::Model::Callbacks
|
@@ -293,15 +341,20 @@ provide the `to_indexed_json` method yourself:
|
|
293
341
|
end
|
294
342
|
|
295
343
|
end
|
344
|
+
```
|
296
345
|
|
297
346
|
Note that _Tire_-enhanced models are fully compatible with [`will_paginate`](https://github.com/mislav/will_paginate),
|
298
347
|
so you can pass any parameters to the `search` method in the controller, as usual:
|
299
348
|
|
349
|
+
```ruby
|
300
350
|
@articles = Article.search params[:q], :page => (params[:page] || 1)
|
351
|
+
```
|
301
352
|
|
302
353
|
OK. Chances are, you have lots of records stored in the underlying database. How will you get them to _ElasticSearch_? Easy:
|
303
354
|
|
355
|
+
```ruby
|
304
356
|
Article.elasticsearch_index.import Article.all
|
357
|
+
```
|
305
358
|
|
306
359
|
However, this way, all your records are loaded into memory, serialized into JSON,
|
307
360
|
and sent down the wire to _ElasticSearch_. Not practical, you say? You're right.
|
@@ -309,12 +362,16 @@ and sent down the wire to _ElasticSearch_. Not practical, you say? You're right.
|
|
309
362
|
Provided your model implements some sort of _pagination_ — and it probably does, for so much data —,
|
310
363
|
you can just run:
|
311
364
|
|
365
|
+
```ruby
|
312
366
|
Article.import
|
367
|
+
```
|
313
368
|
|
314
369
|
In this case, the `Article.paginate` method is called, and your records are sent to the index
|
315
370
|
in chunks of 1000. If that number doesn't suit you, just provide a better one:
|
316
371
|
|
372
|
+
```ruby
|
317
373
|
Article.import :per_page => 100
|
374
|
+
```
|
318
375
|
|
319
376
|
Any other parameters you provide to the `import` method are passed down to the `paginate` method.
|
320
377
|
|
@@ -343,6 +400,7 @@ another object mapping library, such as [Mongoid](http://mongoid.org/)?
|
|
343
400
|
|
344
401
|
Well, things stay mostly the same:
|
345
402
|
|
403
|
+
```ruby
|
346
404
|
class Article
|
347
405
|
include Mongoid::Document
|
348
406
|
field :title, :type => String
|
@@ -367,6 +425,7 @@ Well, things stay mostly the same:
|
|
367
425
|
Article.create :title => 'I Love ElasticSearch'
|
368
426
|
|
369
427
|
Article.search 'love'
|
428
|
+
```
|
370
429
|
|
371
430
|
That's kinda nice. But there's more.
|
372
431
|
|
@@ -387,6 +446,7 @@ advanced distributed features.
|
|
387
446
|
To use the persistence features, you have to include the `Tire::Persistence` module
|
388
447
|
in your class and define the properties (analogous to the way you do with CouchDB- or MongoDB-based models):
|
389
448
|
|
449
|
+
```ruby
|
390
450
|
class Article
|
391
451
|
include Tire::Model::Persistence
|
392
452
|
include Tire::Model::Search
|
@@ -398,8 +458,8 @@ in your class and define the properties (analogous to the way you do with CouchD
|
|
398
458
|
property :author
|
399
459
|
property :content
|
400
460
|
property :published_on
|
401
|
-
|
402
461
|
end
|
462
|
+
```
|
403
463
|
|
404
464
|
Of course, not all validations or `ActionPack` helpers will be available to your models,
|
405
465
|
but if you can live with that, you've just got a schema-free, highly-scalable storage
|
data/examples/tire-dsl.rb
CHANGED
@@ -26,6 +26,14 @@
|
|
26
26
|
# gem install tire
|
27
27
|
#
|
28
28
|
require 'rubygems'
|
29
|
+
|
30
|
+
# _Tire_ uses the [_multi_json_](https://github.com/intridea/multi_json) gem as a generic JSON library.
|
31
|
+
# We want to use the [_yajl-ruby_](https://github.com/brianmario/yajl-ruby) gem in its full on mode here.
|
32
|
+
#
|
33
|
+
require 'yajl/json_gem'
|
34
|
+
|
35
|
+
# Now, let's require the _Tire_ gem itself, and we're ready to go.
|
36
|
+
#
|
29
37
|
require 'tire'
|
30
38
|
|
31
39
|
#### Prerequisites
|
data/lib/tire/configuration.rb
CHANGED
@@ -20,8 +20,9 @@ module Tire
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.reset(*properties)
|
23
|
-
reset_variables = properties.empty? ? instance_variables : instance_variables
|
24
|
-
|
23
|
+
reset_variables = properties.empty? ? instance_variables : instance_variables.map { |p| p.to_s} & \
|
24
|
+
properties.map { |p| "@#{p}" }
|
25
|
+
reset_variables.each { |v| instance_variable_set(v.to_sym, nil) }
|
25
26
|
end
|
26
27
|
|
27
28
|
end
|
data/lib/tire/dsl.rb
CHANGED
@@ -16,7 +16,7 @@ module Tire
|
|
16
16
|
end
|
17
17
|
|
18
18
|
response = Configuration.client.post( "#{Configuration.url}/#{indices}/_search", payload)
|
19
|
-
json =
|
19
|
+
json = MultiJson.decode(response.body)
|
20
20
|
results = Results::Collection.new(json, options)
|
21
21
|
end
|
22
22
|
rescue Exception => error
|
data/lib/tire/index.rb
CHANGED
@@ -27,17 +27,17 @@ module Tire
|
|
27
27
|
|
28
28
|
def create(options={})
|
29
29
|
@options = options
|
30
|
-
@response = Configuration.client.post "#{Configuration.url}/#{@name}",
|
30
|
+
@response = Configuration.client.post "#{Configuration.url}/#{@name}", MultiJson.encode(options)
|
31
31
|
rescue Exception => error
|
32
32
|
false
|
33
33
|
ensure
|
34
|
-
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}" -d '#{
|
34
|
+
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}" -d '#{MultiJson.encode(options)}'|
|
35
35
|
logged(error, 'CREATE', curl)
|
36
36
|
end
|
37
37
|
|
38
38
|
def mapping
|
39
39
|
@response = Configuration.client.get("#{Configuration.url}/#{@name}/_mapping")
|
40
|
-
|
40
|
+
MultiJson.decode(@response.body)[@name]
|
41
41
|
end
|
42
42
|
|
43
43
|
def store(*args)
|
@@ -65,7 +65,7 @@ module Tire
|
|
65
65
|
url = id ? "#{Configuration.url}/#{@name}/#{type}/#{id}" : "#{Configuration.url}/#{@name}/#{type}/"
|
66
66
|
|
67
67
|
@response = Configuration.client.post url, document
|
68
|
-
|
68
|
+
MultiJson.decode(@response.body)
|
69
69
|
|
70
70
|
rescue Exception => error
|
71
71
|
raise
|
@@ -163,17 +163,17 @@ module Tire
|
|
163
163
|
$VERBOSE = old_verbose
|
164
164
|
|
165
165
|
result = Configuration.client.delete "#{Configuration.url}/#{@name}/#{type}/#{id}"
|
166
|
-
|
166
|
+
MultiJson.decode(result) if result
|
167
167
|
end
|
168
168
|
|
169
169
|
def retrieve(type, id)
|
170
170
|
@response = Configuration.client.get "#{Configuration.url}/#{@name}/#{type}/#{id}"
|
171
|
-
h =
|
171
|
+
h = MultiJson.decode(@response.body)
|
172
172
|
if Configuration.wrapper == Hash then h
|
173
173
|
else
|
174
174
|
document = {}
|
175
175
|
document = h['_source'] ? document.update( h['_source'] ) : document.update( h['fields'] )
|
176
|
-
document.update('id' => h['_id'], '_version' => h['_version'])
|
176
|
+
document.update('id' => h['_id'], '_type' => h['_type'], '_index' => h['_index'], '_version' => h['_version'])
|
177
177
|
Configuration.wrapper.new(document)
|
178
178
|
end
|
179
179
|
end
|
@@ -187,6 +187,28 @@ module Tire
|
|
187
187
|
logged(error, '_refresh', curl)
|
188
188
|
end
|
189
189
|
|
190
|
+
def open(options={})
|
191
|
+
# TODO: Remove the duplication in the execute > rescue > ensure chain
|
192
|
+
@response = Configuration.client.post "#{Configuration.url}/#{@name}/_open", MultiJson.encode(options)
|
193
|
+
MultiJson.decode(@response.body)['ok']
|
194
|
+
rescue Exception => error
|
195
|
+
raise
|
196
|
+
ensure
|
197
|
+
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}/open"|
|
198
|
+
logged(error, '_open', curl)
|
199
|
+
end
|
200
|
+
|
201
|
+
def close(options={})
|
202
|
+
# TODO: Remove the duplication in the execute > rescue > ensure chain
|
203
|
+
@response = Configuration.client.post "#{Configuration.url}/#{@name}/_close", MultiJson.encode(options)
|
204
|
+
MultiJson.decode(@response.body)['ok']
|
205
|
+
rescue Exception => error
|
206
|
+
raise
|
207
|
+
ensure
|
208
|
+
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}/_close"|
|
209
|
+
logged(error, '_close', curl)
|
210
|
+
end
|
211
|
+
|
190
212
|
def logged(error=nil, endpoint='/', curl='')
|
191
213
|
if Configuration.logger
|
192
214
|
|
@@ -196,7 +218,7 @@ module Tire
|
|
196
218
|
|
197
219
|
if Configuration.logger.level.to_s == 'debug'
|
198
220
|
# FIXME: Depends on RestClient implementation
|
199
|
-
body = @response ?
|
221
|
+
body = @response ? MultiJson.encode(@response.body, :pretty => true) : error.http_body rescue ''
|
200
222
|
else
|
201
223
|
body = ''
|
202
224
|
end
|
data/lib/tire/results/item.rb
CHANGED
@@ -9,7 +9,7 @@ module Tire
|
|
9
9
|
def initialize(args={})
|
10
10
|
raise ArgumentError, "Please pass a Hash-like object" unless args.respond_to?(:each_pair)
|
11
11
|
args.each_pair do |key, value|
|
12
|
-
self[key.to_sym] = value.
|
12
|
+
self[key.to_sym] = value.is_a?(Hash) ? self.class.new(value.to_hash) : value
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/lib/tire/rubyext/hash.rb
CHANGED
data/lib/tire/search/facet.rb
CHANGED
data/lib/tire/search/query.rb
CHANGED
@@ -3,6 +3,7 @@ module Tire
|
|
3
3
|
|
4
4
|
class Query
|
5
5
|
def initialize(&block)
|
6
|
+
@value = {}
|
6
7
|
self.instance_eval(&block) if block_given?
|
7
8
|
end
|
8
9
|
|
@@ -18,11 +19,17 @@ module Tire
|
|
18
19
|
|
19
20
|
def string(value, options={})
|
20
21
|
@value = { :query_string => { :query => value } }
|
21
|
-
@value[:query_string].update(
|
22
|
+
@value[:query_string].update(options)
|
22
23
|
# TODO: https://github.com/elasticsearch/elasticsearch/wiki/Query-String-Query
|
23
24
|
@value
|
24
25
|
end
|
25
26
|
|
27
|
+
def boolean(options={}, &block)
|
28
|
+
# TODO: Try to get rid of the `boolean` method
|
29
|
+
raise ArgumentError, "Please pass a block to boolean query" unless block_given?
|
30
|
+
@value = BooleanQuery.new(options, &block).to_hash
|
31
|
+
end
|
32
|
+
|
26
33
|
def all
|
27
34
|
@value = { :match_all => {} }
|
28
35
|
@value
|
@@ -32,10 +39,54 @@ module Tire
|
|
32
39
|
@value = { :ids => { :values => values, :type => type } }
|
33
40
|
end
|
34
41
|
|
42
|
+
def to_hash
|
43
|
+
@value
|
44
|
+
end
|
45
|
+
|
35
46
|
def to_json
|
36
|
-
|
47
|
+
to_hash.to_json
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class BooleanQuery
|
53
|
+
|
54
|
+
# TODO: Try to get rid of multiple `should`, `must`, etc invocations, and wrap queries directly:
|
55
|
+
#
|
56
|
+
# boolean do
|
57
|
+
# should do
|
58
|
+
# string 'foo'
|
59
|
+
# string 'bar'
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# Inherit from Query, implement `encode` method there, and overload it here, so it puts
|
64
|
+
# queries in an Array instead of hash.
|
65
|
+
|
66
|
+
def initialize(options={}, &block)
|
67
|
+
@options = options
|
68
|
+
@value = {}
|
69
|
+
self.instance_eval(&block)
|
37
70
|
end
|
38
71
|
|
72
|
+
def must(&block)
|
73
|
+
(@value[:must] ||= []) << Query.new(&block).to_hash
|
74
|
+
@value
|
75
|
+
end
|
76
|
+
|
77
|
+
def must_not(&block)
|
78
|
+
(@value[:must_not] ||= []) << Query.new(&block).to_hash
|
79
|
+
@value
|
80
|
+
end
|
81
|
+
|
82
|
+
def should(&block)
|
83
|
+
(@value[:should] ||= []) << Query.new(&block).to_hash
|
84
|
+
@value
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_hash
|
88
|
+
{ :bool => @value.update(@options) }
|
89
|
+
end
|
39
90
|
end
|
40
91
|
|
41
92
|
end
|
data/lib/tire/search.rb
CHANGED
@@ -67,7 +67,7 @@ module Tire
|
|
67
67
|
def perform
|
68
68
|
@url = "#{Configuration.url}/#{indices.join(',')}/_search"
|
69
69
|
@response = Configuration.client.post(@url, self.to_json)
|
70
|
-
@json =
|
70
|
+
@json = MultiJson.decode(@response.body)
|
71
71
|
@results = Results::Collection.new(@json, @options)
|
72
72
|
self
|
73
73
|
rescue Exception => error
|
@@ -91,7 +91,7 @@ module Tire
|
|
91
91
|
request.update( { :size => @size } ) if @size
|
92
92
|
request.update( { :from => @from } ) if @from
|
93
93
|
request.update( { :fields => @fields } ) if @fields
|
94
|
-
|
94
|
+
MultiJson.encode(request)
|
95
95
|
end
|
96
96
|
|
97
97
|
def logged(error=nil)
|
@@ -104,7 +104,7 @@ module Tire
|
|
104
104
|
|
105
105
|
if Configuration.logger.level.to_s == 'debug'
|
106
106
|
# FIXME: Depends on RestClient implementation
|
107
|
-
body = @response ?
|
107
|
+
body = @response ? MultiJson.encode(@json, :pretty => true) : body = error.http_body
|
108
108
|
else
|
109
109
|
body = ''
|
110
110
|
end
|
data/lib/tire/tasks.rb
CHANGED
@@ -53,7 +53,7 @@ namespace :tire do
|
|
53
53
|
|
54
54
|
unless index.exists?
|
55
55
|
puts "[IMPORT] Creating index '#{index.name}' with mapping:",
|
56
|
-
|
56
|
+
MultiJson.encode(klass.mapping_to_hash, :pretty => true)
|
57
57
|
index.create :mappings => klass.mapping_to_hash
|
58
58
|
end
|
59
59
|
|
data/lib/tire/version.rb
CHANGED
data/lib/tire.rb
CHANGED
@@ -7,9 +7,7 @@ module Tire
|
|
7
7
|
|
8
8
|
def setup
|
9
9
|
super
|
10
|
-
|
11
|
-
|
12
|
-
ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => fixtures_path.join('articles.db') )
|
10
|
+
ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
|
13
11
|
|
14
12
|
ActiveRecord::Migration.verbose = false
|
15
13
|
ActiveRecord::Schema.define(:version => 1) do
|
@@ -67,7 +65,7 @@ module Tire
|
|
67
65
|
|
68
66
|
a.index.refresh
|
69
67
|
results = ActiveRecordArticle.search 'test'
|
70
|
-
|
68
|
+
|
71
69
|
assert_equal 0, results.count
|
72
70
|
end
|
73
71
|
|