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