slingshot-rb 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +41 -38
- data/Rakefile +2 -1
- data/examples/slingshot-dsl.rb +97 -28
- data/lib/slingshot/index.rb +49 -15
- data/lib/slingshot/logger.rb +11 -12
- data/lib/slingshot/search.rb +24 -18
- data/lib/slingshot/version.rb +1 -1
- data/test/integration/highlight_test.rb +29 -3
- data/test/integration/index_mapping_test.rb +1 -1
- data/test/integration/index_store_test.rb +1 -1
- data/test/unit/logger_test.rb +5 -5
- data/test/unit/search_test.rb +14 -3
- metadata +4 -4
data/README.markdown
CHANGED
@@ -3,27 +3,21 @@ Slingshot
|
|
3
3
|
|
4
4
|
![Slingshot](https://github.com/karmi/slingshot/raw/master/slingshot.png)
|
5
5
|
|
6
|
-
_Slingshot_
|
7
|
-
|
6
|
+
_Slingshot_ is a Ruby client for the [ElasticSearch](http://www.elasticsearch.org/) search engine/database.
|
7
|
+
It aims to provide rich and comfortable Ruby API in the form of a simple domain-specific language.
|
8
8
|
|
9
|
-
_ElasticSearch_ is a scalable, distributed, highly-available,
|
9
|
+
_ElasticSearch_ is a scalable, distributed, cloud-ready, highly-available,
|
10
10
|
RESTful database communicating by JSON over HTTP, based on [Lucene](http://lucene.apache.org/),
|
11
|
-
written in Java. It manages to be very simple and very powerful at the same time.
|
12
|
-
You should seriously consider it to power search in your Ruby applications:
|
13
|
-
it will deliver all the features you want — and many more you may have not
|
14
|
-
imagined yet (native geo search? date histogram facets? _percolator_?)
|
15
|
-
|
16
|
-
_Slingshot_ currently allows basic operation with the index and searching. More is planned.
|
17
|
-
|
11
|
+
written in Java. It manages to be very simple to use and very powerful at the same time.
|
18
12
|
|
19
13
|
Installation
|
20
14
|
------------
|
21
15
|
|
22
16
|
First, you need a running _ElasticSearch_ server. Thankfully, it's easy. Let's define easy:
|
23
17
|
|
24
|
-
$ curl -k -L -o elasticsearch-0.15.
|
25
|
-
$ tar -zxvf elasticsearch-0.15.
|
26
|
-
$ ./elasticsearch-0.15.
|
18
|
+
$ curl -k -L -o elasticsearch-0.15.2.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.15.2.tar.gz
|
19
|
+
$ tar -zxvf elasticsearch-0.15.2.tar.gz
|
20
|
+
$ ./elasticsearch-0.15.2/bin/elasticsearch -f
|
27
21
|
|
28
22
|
OK, easy. Now, install the gem via Rubygems:
|
29
23
|
|
@@ -63,8 +57,8 @@ First, let's create an index named `articles` and store/index some documents:
|
|
63
57
|
refresh
|
64
58
|
end
|
65
59
|
|
66
|
-
|
67
|
-
index with specific [
|
60
|
+
We can also create the
|
61
|
+
index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html):
|
68
62
|
|
69
63
|
Slingshot.index 'articles' do
|
70
64
|
create :mappings => {
|
@@ -82,7 +76,7 @@ index with specific [mappings](http://www.elasticsearch.org/guide/reference/api/
|
|
82
76
|
Now, let's query the database.
|
83
77
|
|
84
78
|
We are searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
|
85
|
-
filtering them for ones tagged “ruby”, and also retrieving some [_facets_](http://www.
|
79
|
+
filtering them for ones tagged “ruby”, and also retrieving some [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/)
|
86
80
|
from the database:
|
87
81
|
|
88
82
|
s = Slingshot.search 'articles' do
|
@@ -139,56 +133,65 @@ We can display the full query JSON:
|
|
139
133
|
puts s.to_json
|
140
134
|
# {"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"}]}
|
141
135
|
|
142
|
-
Or, we can display the corresponding `curl` command for easy debugging:
|
136
|
+
Or, better, we can display the corresponding `curl` command for easy debugging:
|
143
137
|
|
144
138
|
puts s.to_curl
|
145
139
|
# 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"}]}'
|
146
140
|
|
141
|
+
Since `curl` is the crucial debugging tool in _ElasticSearch_ land, we can log every search query in `curl` format:
|
142
|
+
|
143
|
+
Slingshot.configure { logger 'elasticsearch.log' }
|
144
|
+
|
147
145
|
|
148
146
|
Features
|
149
147
|
--------
|
150
148
|
|
151
|
-
Currently, _Slingshot_ supports
|
149
|
+
Currently, _Slingshot_ supports main features of the _ElasticSearch_ [Search API](http://www.elasticsearch.org/guide/reference/api/search/request-body.html) and it's [Query DSL](http://www.elasticsearch.org/guide/reference/query-dsl/). In present, it allows you to:
|
152
150
|
|
153
|
-
*
|
154
|
-
*
|
155
|
-
*
|
156
|
-
* [
|
157
|
-
* [
|
158
|
-
* [
|
159
|
-
*
|
160
|
-
*
|
161
|
-
*
|
151
|
+
* Create, delete and refresh the index
|
152
|
+
* Create the index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
|
153
|
+
* Store a document in the index
|
154
|
+
* [Query](https://github.com/karmi/slingshot/blob/master/examples/dsl.rb) the index with the `query_string`, `term`, `terms` and `match_all` types of queries
|
155
|
+
* [Sort](http://elasticsearch.org/guide/reference/api/search/sort.html) the results by `fields`
|
156
|
+
* [Filter](http://elasticsearch.org/guide/reference/query-dsl/) the results
|
157
|
+
* Retrieve the _terms_ and _date histogram_ types of [facets](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html) (other types are high priority)
|
158
|
+
* [Highlight](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html) matching fields
|
159
|
+
* Return just specific `fields` from documents
|
160
|
+
* Page the results with `from` and `size` query options
|
161
|
+
* Log the `curl`-equivalent of requests and response JSON
|
162
162
|
|
163
|
-
See the [`examples/dsl.rb`](blob/master/examples/dsl.rb).
|
163
|
+
See the [`examples/slingshot-dsl.rb`](blob/master/examples/slingshot-dsl.rb) file for the full, working examples.
|
164
164
|
|
165
165
|
_Slingshot_ wraps the results in a enumerable `Results::Collection` class, and every result in a `Results::Item` class,
|
166
|
-
which looks like a child of `Hash` and `Openstruct
|
166
|
+
which looks like a child of `Hash` and `Openstruct`, for smooth iterating and displaying the results.
|
167
167
|
|
168
|
-
You may wrap the result items in your own class by setting the `Configuration.wrapper` property
|
169
|
-
|
168
|
+
You may wrap the result items in your own class just by setting the `Configuration.wrapper` property,
|
169
|
+
supposed your class takes a hash of attributes upon initialization, in ActiveModel/ActiveRecord manner.
|
170
|
+
Please see the files `test/models/article.rb` and `test/unit/results_collection_test.rb` for details.
|
170
171
|
|
171
172
|
|
172
|
-
Todo &
|
173
|
-
|
173
|
+
Todo, Plans & Ideas
|
174
|
+
-------------------
|
175
|
+
|
176
|
+
_Slingshot_ is already used in production by its authors. Nevertheless, it's not finished yet.
|
174
177
|
|
175
|
-
|
178
|
+
The todos and plans are vast, and the most important are listed below, in the order of importance:
|
176
179
|
|
177
|
-
* Seamless _ActiveModel_ compatibility for easy usage in _Rails_ applications (this also means nearly full _ActiveRecord_ compatibility). See the [`activemodel`](https://github.com/karmi/slingshot/compare/activemodel) branch
|
178
|
-
* Seamless [will_paginate](https://github.com/mislav/will_paginate) compatibility for easy pagination
|
180
|
+
* Seamless _ActiveModel_ compatibility for easy usage in _Rails_ applications (this also means nearly full _ActiveRecord_ compatibility). See the ongoing work in the [`activemodel`](https://github.com/karmi/slingshot/compare/activemodel) branch
|
181
|
+
* Seamless [will_paginate](https://github.com/mislav/will_paginate) compatibility for easy pagination. Already [implemented](https://github.com/karmi/slingshot/commit/e1351f6) on the `activemodel` branch
|
179
182
|
* [Mapping](http://www.elasticsearch.org/guide/reference/mapping/) definition for models
|
180
183
|
* Proper RDoc annotations for the source code
|
181
184
|
* Dual interface: allow to simply pass queries/options for _ElasticSearch_ as a Hash in any method
|
182
185
|
* [Histogram](http://www.elasticsearch.org/guide/reference/api/search/facets/histogram-facet.html) facets
|
183
|
-
* Seamless support for [auto-updating _river_ index](http://www.elasticsearch.org/guide/reference/river/couchdb.html) for _CouchDB_ `_changes` feed
|
184
186
|
* [Statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html) facets
|
185
187
|
* [Geo Distance](http://www.elasticsearch.org/guide/reference/api/search/facets/geo-distance-facet.html) facets
|
186
188
|
* [Index aliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases.html) management
|
187
189
|
* [Analyze](http://www.elasticsearch.org/guide/reference/api/admin-indices-analyze.html) API support
|
188
|
-
* [Highligting](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html) support
|
189
190
|
* [Bulk](http://www.elasticsearch.org/guide/reference/api/bulk.html) API
|
190
191
|
* Embedded webserver to display statistics and to allow easy searches
|
192
|
+
* Seamless support for [auto-updating _river_ index](http://www.elasticsearch.org/guide/reference/river/couchdb.html) for _CouchDB_ `_changes` feed
|
191
193
|
|
194
|
+
The full ActiveModel integration is planned for the 1.0 release.
|
192
195
|
|
193
196
|
Other Clients
|
194
197
|
-------------
|
data/Rakefile
CHANGED
@@ -6,8 +6,9 @@ task :default => :test
|
|
6
6
|
require 'rake/testtask'
|
7
7
|
Rake::TestTask.new(:test) do |test|
|
8
8
|
test.libs << 'lib' << 'test'
|
9
|
-
test.
|
9
|
+
test.test_files = FileList['test/unit/*_test.rb', 'test/integration/*_test.rb']
|
10
10
|
test.verbose = true
|
11
|
+
# test.warning = true
|
11
12
|
end
|
12
13
|
|
13
14
|
namespace :test do
|
data/examples/slingshot-dsl.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
# **Slingshot** is a rich and comfortable Ruby
|
1
|
+
# **Slingshot** is a rich and comfortable Ruby client for the
|
2
2
|
# [_ElasticSearch_](http://www.elasticsearch.org/) search engine/database.
|
3
3
|
#
|
4
|
+
# It provides the API for the main _ElasticSearch_ features, and this document
|
5
|
+
# will walk you through them.
|
6
|
+
#
|
7
|
+
# <img src="http://github.com/favicon.ico" style="position:relative; top:2px">
|
8
|
+
# _Slingshot_ is open source, and you can download or clone the source code
|
9
|
+
# from <https://github.com/karmi/slingshot>.
|
10
|
+
#
|
4
11
|
# _ElasticSearch_ is a scalable, distributed, highly-available,
|
5
12
|
# RESTful database communicating by JSON over HTTP, based on [Lucene](http://lucene.apache.org/),
|
6
13
|
# written in Java. It manages to be very simple and very powerful at the same time.
|
@@ -22,9 +29,9 @@ require 'slingshot'
|
|
22
29
|
# You'll need a working and running _ElasticSearch_ server. Thankfully, that's easy.
|
23
30
|
( puts <<-"INSTALL" ; exit(1) ) unless RestClient.get('http://localhost:9200') rescue false
|
24
31
|
[!] You don’t appear to have ElasticSearch installed. Please install and launch it with the following commands.
|
25
|
-
curl -k -L -o elasticsearch-0.15.
|
26
|
-
tar -zxvf elasticsearch-0.15.
|
27
|
-
./elasticsearch-0.15.
|
32
|
+
curl -k -L -o elasticsearch-0.15.2.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.15.2.tar.gz
|
33
|
+
tar -zxvf elasticsearch-0.15.2.tar.gz
|
34
|
+
./elasticsearch-0.15.2/bin/elasticsearch -f
|
28
35
|
INSTALL
|
29
36
|
|
30
37
|
### Simple Usage
|
@@ -124,11 +131,41 @@ puts s.to_json
|
|
124
131
|
puts "", "Try the query in Curl:", "-"*80
|
125
132
|
puts s.to_curl
|
126
133
|
|
134
|
+
# For debugging more complex situations, you can enable logging, so requests and responses
|
135
|
+
# will be logged using the `curl`-based format.
|
136
|
+
|
137
|
+
Slingshot.configure do
|
138
|
+
|
139
|
+
# By default, at the _info_ level, only the `curl`-format of request and
|
140
|
+
# basic information about the response will be logged:
|
141
|
+
#
|
142
|
+
# # 2011-04-24 11:34:01:150 [CREATE] ("articles")
|
143
|
+
# #
|
144
|
+
# curl -X POST "http://localhost:9200/articles" -d '{
|
145
|
+
#
|
146
|
+
# }'
|
147
|
+
#
|
148
|
+
# # 2011-04-24 11:34:01:152 [200]
|
149
|
+
#
|
150
|
+
logger 'elasticsearch.log'
|
151
|
+
|
152
|
+
# For debugging, you can switch to the _debug_ level, which will log the complete JSON responses.
|
153
|
+
#
|
154
|
+
# That's very convenient if you want to post a recreation of some problem or solution to the mailing list, IRC, etc.
|
155
|
+
#
|
156
|
+
logger 'elasticsearch.log', :level => 'debug'
|
157
|
+
|
158
|
+
# Note that you can pass any [`IO`](http://www.ruby-doc.org/core/classes/IO.html)-compatible Ruby object as a logging device.
|
159
|
+
#
|
160
|
+
logger STDERR
|
161
|
+
end
|
127
162
|
|
128
163
|
##### Other Types of Queries
|
129
164
|
|
130
|
-
#
|
131
|
-
#
|
165
|
+
# Well, query strings are convenient for simple searches, but
|
166
|
+
# we may want to define our queries more expressively.
|
167
|
+
#
|
168
|
+
# For instance when we're searching for articles with specific _tags_.
|
132
169
|
|
133
170
|
# Let's suppose we want to search for articles tagged “ruby” _or_ “python”.
|
134
171
|
# That's a great excuse to use a [_terms_](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
|
@@ -163,17 +200,20 @@ s.results.each do |document|
|
|
163
200
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
164
201
|
end
|
165
202
|
|
166
|
-
# _ElasticSearch_
|
203
|
+
# _ElasticSearch_ supports many types of [queries](http://www.elasticsearch.org/guide/reference/query-dsl/).
|
204
|
+
#
|
167
205
|
# Eventually, _Slingshot_ will support all of them.
|
168
206
|
# So far, only these are supported:
|
169
207
|
#
|
208
|
+
# * [string](http://www.elasticsearch.org/guide/reference/query-dsl/query-string-query.html)
|
170
209
|
# * [term](http://elasticsearch.org/guide/reference/query-dsl/term-query.html)
|
171
210
|
# * [terms](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
|
211
|
+
# * [all](http://www.elasticsearch.org/guide/reference/query-dsl/match-all-query.html)
|
172
212
|
|
173
213
|
##### Faceted Search
|
174
214
|
|
175
215
|
# _ElasticSearch_ makes it trivial to retrieve complex aggregated data from our index/database,
|
176
|
-
# so called [_facets_](http://www.
|
216
|
+
# so called [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html).
|
177
217
|
|
178
218
|
# Let's say we want to display article counts for every tag in the database.
|
179
219
|
# For that, we'll use a _terms_ facet.
|
@@ -183,7 +223,7 @@ s = Slingshot.search 'articles' do
|
|
183
223
|
# We will search for articles whose title begins with letter “T”,
|
184
224
|
query { string 'title:T*' }
|
185
225
|
|
186
|
-
# and retrieve their counts “bucketed” by their `tags`.
|
226
|
+
# and retrieve their counts, “bucketed” by their `tags`.
|
187
227
|
facet 'tags' do
|
188
228
|
terms :tags
|
189
229
|
end
|
@@ -216,8 +256,8 @@ s = Slingshot.search 'articles' do
|
|
216
256
|
terms :tags, :global => true
|
217
257
|
end
|
218
258
|
|
219
|
-
# As you can see, we can even combine facets scoped
|
220
|
-
#
|
259
|
+
# As you can see, we can even combine facets scoped to the current query
|
260
|
+
# with global facets — we'll just use a different name.
|
221
261
|
facet 'current-tags' do
|
222
262
|
terms :tags
|
223
263
|
end
|
@@ -247,18 +287,25 @@ s.results.facets['global-tags']['terms'].each do |f|
|
|
247
287
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
248
288
|
end
|
249
289
|
|
290
|
+
# _ElasticSearch_ supports many types of advanced facets.
|
291
|
+
#
|
292
|
+
# Eventually, _Slingshot_ will support all of them.
|
293
|
+
# So far, only these are supported:
|
294
|
+
#
|
295
|
+
# * [terms](http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html)
|
296
|
+
# * [date](http://www.elasticsearch.org/guide/reference/api/search/facets/date-histogram-facet.html)
|
297
|
+
|
250
298
|
# The real power of facets lies in their combination with
|
251
|
-
# [filters](http://elasticsearch.
|
252
|
-
# though:
|
299
|
+
# [filters](http://elasticsearch.org/guide/reference/api/search/filter.html).
|
253
300
|
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
301
|
+
# Filters enable us to restrict the returned documents, for example to a specific category,
|
302
|
+
# but to calculate the facet counts based on the original query.
|
303
|
+
# The immediate use case for that is the „faceted navigation“.
|
257
304
|
|
258
305
|
|
259
306
|
##### Filtered Search
|
260
307
|
|
261
|
-
# So, let's make
|
308
|
+
# So, let's make our search a bit more complex. Let's search for articles whose titles begin
|
262
309
|
# with letter “T”, again, but filter the results, so only the articles tagged “ruby”
|
263
310
|
# are returned.
|
264
311
|
s = Slingshot.search 'articles' do
|
@@ -301,9 +348,9 @@ end
|
|
301
348
|
# (available as the `_score` property).
|
302
349
|
|
303
350
|
# But, what if we want to sort the results based on some other criteria,
|
304
|
-
# such as published date
|
351
|
+
# such as published date or product price? We can do that.
|
305
352
|
s = Slingshot.search 'articles' do
|
306
|
-
# We search for articles tagged “ruby
|
353
|
+
# We search for articles tagged “ruby”.
|
307
354
|
query { string 'tags:ruby' }
|
308
355
|
|
309
356
|
# And sort them by their `title`, in descending order.
|
@@ -322,7 +369,7 @@ end
|
|
322
369
|
|
323
370
|
s = Slingshot.search 'articles' do
|
324
371
|
# We will just get all articles for this case.
|
325
|
-
query {
|
372
|
+
query { all }
|
326
373
|
|
327
374
|
sort do
|
328
375
|
# We will sort the results by their `published_on` property in ascending (default) order,
|
@@ -343,15 +390,19 @@ end
|
|
343
390
|
|
344
391
|
##### Highlighting
|
345
392
|
|
346
|
-
# Often, you want to highlight the
|
347
|
-
#
|
348
|
-
#
|
349
|
-
#
|
393
|
+
# Often, you want to highlight the snippets matching your query in the
|
394
|
+
# displayed results.
|
395
|
+
# _ElasticSearch_ provides rich
|
396
|
+
# [highlighting](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html)
|
397
|
+
# features, and Slingshot makes them trivial to use.
|
398
|
+
#
|
399
|
+
# Let's suppose that we want to highlight terms of our query.
|
400
|
+
#
|
350
401
|
s = Slingshot.search 'articles' do
|
351
|
-
# Let's search for documents containing “Two” in their titles
|
402
|
+
# Let's search for documents containing word “Two” in their titles,
|
352
403
|
query { string 'title:Two' }
|
353
404
|
|
354
|
-
#
|
405
|
+
# and instruct _ElasticSearch_ to highlight relevant snippets.
|
355
406
|
highlight :title
|
356
407
|
end
|
357
408
|
|
@@ -361,7 +412,7 @@ s.results.each do |document|
|
|
361
412
|
puts "Title: #{ document.title }, highlighted title: #{document.highlight.title}"
|
362
413
|
end
|
363
414
|
|
364
|
-
#
|
415
|
+
# We can configure many options for highlighting, such as:
|
365
416
|
#
|
366
417
|
s = Slingshot.search 'articles' do
|
367
418
|
query { string 'title:Two' }
|
@@ -372,7 +423,25 @@ s = Slingshot.search 'articles' do
|
|
372
423
|
# • specifying their options
|
373
424
|
highlight :title, :body => { :number_of_fragments => 0 }
|
374
425
|
|
375
|
-
# • or specifying highlighting options, such as the wrapper tag
|
426
|
+
# • or specifying global highlighting options, such as the wrapper tag
|
376
427
|
highlight :title, :body, :options => { :tag => '<strong class="highlight">' }
|
377
428
|
end
|
378
429
|
|
430
|
+
|
431
|
+
|
432
|
+
#### What's next?
|
433
|
+
|
434
|
+
# As you can see, [_Slingshot_](https://github.com/karmi/slingshot) supports the
|
435
|
+
# main features of _ElasticSearch_ in Ruby.
|
436
|
+
#
|
437
|
+
# It allows you to create and delete indices, add documents, search them, retrieve facets, highlight the results,
|
438
|
+
# and comes with usable logging facility.
|
439
|
+
#
|
440
|
+
# Of course, the holy grail of any search library is easy, painless integration with your Ruby classes, and,
|
441
|
+
# most importantly, with ActiveRecord/ActiveModel classes.
|
442
|
+
#
|
443
|
+
# _Slingshot_ already provides such integration, though in experimental mode.
|
444
|
+
# Check out the tests in the
|
445
|
+
# [`activemodel`](https://github.com/karmi/slingshot/blob/activemodel/test/integration/active_model_searchable_test.rb) branch.
|
446
|
+
#
|
447
|
+
# Send any feedback via Github issues, or ask questions in the [#elasticsearch](irc://irc.freenode.net/#elasticsearch) IRC channel.
|
data/lib/slingshot/index.rb
CHANGED
@@ -7,17 +7,24 @@ module Slingshot
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def delete
|
10
|
-
response
|
11
|
-
|
12
|
-
|
10
|
+
# FIXME: RestClient does not return response for DELETE requests?
|
11
|
+
@response = Configuration.client.delete "#{Configuration.url}/#{@name}"
|
12
|
+
return @response =~ /error/ ? false : true
|
13
|
+
rescue Exception => error
|
13
14
|
false
|
15
|
+
ensure
|
16
|
+
curl = %Q|curl -X DELETE "#{Configuration.url}/#{@name}"|
|
17
|
+
logged(error, 'DELETE', curl)
|
14
18
|
end
|
15
19
|
|
16
20
|
def create(options={})
|
17
|
-
|
18
|
-
Configuration.client.post "#{Configuration.url}/#{@name}", Yajl::Encoder.encode(options)
|
19
|
-
rescue
|
21
|
+
@options = options
|
22
|
+
@response = Configuration.client.post "#{Configuration.url}/#{@name}", Yajl::Encoder.encode(options)
|
23
|
+
rescue Exception => error
|
20
24
|
false
|
25
|
+
ensure
|
26
|
+
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}" -d '#{Yajl::Encoder.encode(options, :pretty => true)}'|
|
27
|
+
logged(error, 'CREATE', curl)
|
21
28
|
end
|
22
29
|
|
23
30
|
def mapping
|
@@ -46,17 +53,21 @@ module Slingshot
|
|
46
53
|
else raise ArgumentError, "Please pass a JSON string or object with a 'to_indexed_json' method"
|
47
54
|
end
|
48
55
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
56
|
+
url = id ? "#{Configuration.url}/#{@name}/#{type}/#{id}" : "#{Configuration.url}/#{@name}/#{type}/"
|
57
|
+
|
58
|
+
@response = Configuration.client.post url, document
|
59
|
+
JSON.parse(@response)
|
60
|
+
|
61
|
+
rescue Exception => error
|
62
|
+
raise
|
63
|
+
ensure
|
64
|
+
curl = %Q|curl -X POST "#{url}" -d '#{document}'|
|
65
|
+
logged(error, "/#{@name}/#{type}/", curl)
|
55
66
|
end
|
56
67
|
|
57
68
|
def retrieve(type, id)
|
58
|
-
|
59
|
-
h = JSON.parse(
|
69
|
+
@response = Configuration.client.get "#{Configuration.url}/#{@name}/#{type}/#{id}"
|
70
|
+
h = JSON.parse(@response)
|
60
71
|
if Configuration.wrapper == Hash then h
|
61
72
|
else
|
62
73
|
document = h['_source'] ? h['_source'] : h['fields']
|
@@ -66,7 +77,30 @@ module Slingshot
|
|
66
77
|
end
|
67
78
|
|
68
79
|
def refresh
|
69
|
-
Configuration.client.post "#{Configuration.url}/#{@name}/_refresh", ''
|
80
|
+
@response = Configuration.client.post "#{Configuration.url}/#{@name}/_refresh", ''
|
81
|
+
rescue Exception => error
|
82
|
+
raise
|
83
|
+
ensure
|
84
|
+
curl = %Q|curl -X POST "#{Configuration.url}/#{@name}/_refresh"|
|
85
|
+
logged(error, '_refresh', curl)
|
86
|
+
end
|
87
|
+
|
88
|
+
def logged(error=nil, endpoint='/', curl='')
|
89
|
+
if Configuration.logger
|
90
|
+
|
91
|
+
Configuration.logger.log_request endpoint, @name, curl
|
92
|
+
|
93
|
+
code = @response ? @response.code : error.message rescue 200
|
94
|
+
|
95
|
+
if Configuration.logger.level.to_s == 'debug'
|
96
|
+
# FIXME: Depends on RestClient implementation
|
97
|
+
body = @response ? Yajl::Encoder.encode(@response.body, :pretty => true) : error.http_body rescue ''
|
98
|
+
else
|
99
|
+
body = ''
|
100
|
+
end
|
101
|
+
|
102
|
+
Configuration.logger.log_response code, nil, body
|
103
|
+
end
|
70
104
|
end
|
71
105
|
|
72
106
|
end
|
data/lib/slingshot/logger.rb
CHANGED
@@ -21,21 +21,21 @@ module Slingshot
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def log_request(endpoint, params=nil, curl='')
|
24
|
-
#
|
24
|
+
# 2001-02-12 18:20:42:32 [_search] (articles,users)
|
25
25
|
#
|
26
26
|
# curl -X POST ....
|
27
27
|
#
|
28
|
-
content = "#
|
29
|
-
content += "
|
30
|
-
content +=
|
28
|
+
content = "# #{time}"
|
29
|
+
content += " [#{endpoint}]"
|
30
|
+
content += " (#{params.inspect})" if params
|
31
31
|
content += "\n#\n"
|
32
32
|
content += curl
|
33
33
|
content += "\n\n"
|
34
34
|
write content
|
35
35
|
end
|
36
36
|
|
37
|
-
def log_response(status, json)
|
38
|
-
# [200
|
37
|
+
def log_response(status, took=nil, json='')
|
38
|
+
# 2001-02-12 18:20:42:32 [200] (4 msec)
|
39
39
|
#
|
40
40
|
# {
|
41
41
|
# "took" : 4,
|
@@ -43,12 +43,11 @@ module Slingshot
|
|
43
43
|
# ...
|
44
44
|
# }
|
45
45
|
#
|
46
|
-
|
47
|
-
content
|
48
|
-
content += "(#{took} msec)
|
49
|
-
content +=
|
50
|
-
content += "
|
51
|
-
json.each_line { |line| content += "# #{line}" }
|
46
|
+
content = "# #{time}"
|
47
|
+
content += " [#{status}]"
|
48
|
+
content += " (#{took} msec)" if took
|
49
|
+
content += "\n#\n" unless json == ''
|
50
|
+
json.each_line { |line| content += "# #{line}" } unless json == ''
|
52
51
|
content += "\n\n"
|
53
52
|
write content
|
54
53
|
end
|
data/lib/slingshot/search.rb
CHANGED
@@ -64,31 +64,18 @@ module Slingshot
|
|
64
64
|
def perform
|
65
65
|
@url = "#{Configuration.url}/#{indices.join(',')}/_search"
|
66
66
|
@response = Configuration.client.post(@url, self.to_json)
|
67
|
-
@json = Yajl::Parser.parse(@response)
|
67
|
+
@json = Yajl::Parser.parse(@response.body)
|
68
68
|
@results = Results::Collection.new(@json)
|
69
69
|
self
|
70
|
-
rescue Exception =>
|
71
|
-
STDERR.puts "[REQUEST FAILED]
|
70
|
+
rescue Exception => error
|
71
|
+
STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
|
72
72
|
raise
|
73
73
|
ensure
|
74
|
-
|
75
|
-
Configuration.logger.log_request '_search', indices, to_curl
|
76
|
-
if Configuration.logger.level == 'debug'
|
77
|
-
# FIXME: Depends on RestClient implementation
|
78
|
-
if @response
|
79
|
-
code = @response.code
|
80
|
-
body = Yajl::Encoder.encode(@json, :pretty => true)
|
81
|
-
else
|
82
|
-
code = e.message
|
83
|
-
body = e.http_body
|
84
|
-
end
|
85
|
-
Configuration.logger.log_response code, body
|
86
|
-
end
|
87
|
-
end
|
74
|
+
logged(error)
|
88
75
|
end
|
89
76
|
|
90
77
|
def to_curl
|
91
|
-
%Q|curl -X POST "#{Configuration.url}/#{indices}/_search?pretty=true" -d '#{self.to_json}'|
|
78
|
+
%Q|curl -X POST "#{Configuration.url}/#{indices.join(',')}/_search?pretty=true" -d '#{self.to_json}'|
|
92
79
|
end
|
93
80
|
|
94
81
|
def to_json
|
@@ -104,6 +91,25 @@ module Slingshot
|
|
104
91
|
Yajl::Encoder.encode(request)
|
105
92
|
end
|
106
93
|
|
94
|
+
def logged(error=nil)
|
95
|
+
if Configuration.logger
|
96
|
+
|
97
|
+
Configuration.logger.log_request '_search', indices, to_curl
|
98
|
+
|
99
|
+
code = @response ? @response.code : error.message
|
100
|
+
took = @json['took'] rescue nil
|
101
|
+
|
102
|
+
if Configuration.logger.level.to_s == 'debug'
|
103
|
+
# FIXME: Depends on RestClient implementation
|
104
|
+
body = @response ? Yajl::Encoder.encode(@json, :pretty => true) : body = error.http_body
|
105
|
+
else
|
106
|
+
body = ''
|
107
|
+
end
|
108
|
+
|
109
|
+
Configuration.logger.log_response code, took, body
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
107
113
|
end
|
108
114
|
|
109
115
|
end
|
data/lib/slingshot/version.rb
CHANGED
@@ -5,12 +5,13 @@ module Slingshot
|
|
5
5
|
class HighlightIntegrationTest < Test::Unit::TestCase
|
6
6
|
include Test::Integration
|
7
7
|
|
8
|
-
context "Highlight" do
|
8
|
+
context "Highlight" do
|
9
|
+
teardown { Slingshot.index('highlight-test').delete }
|
9
10
|
|
10
11
|
should "add 'highlight' field to the result item" do
|
11
|
-
Slingshot::Configuration.logger STDERR, :level => 'debug'
|
12
|
+
# Slingshot::Configuration.logger STDERR, :level => 'debug'
|
12
13
|
s = Slingshot.search('articles-test') do
|
13
|
-
|
14
|
+
query { string 'Two' }
|
14
15
|
highlight :title
|
15
16
|
end
|
16
17
|
|
@@ -20,6 +21,31 @@ module Slingshot
|
|
20
21
|
assert doc.highlight.title.to_s.include?('<em>'), "Highlight does not include default highlight tag"
|
21
22
|
end
|
22
23
|
|
24
|
+
should "return entire content with highlighted fragments in ElasticSearch >= 0.16.0" do
|
25
|
+
# Slingshot::Configuration.logger STDERR, :level => 'debug'
|
26
|
+
|
27
|
+
content = "A Fox one day fell into a deep well and could find no means of escape. A Goat, overcome with thirst, came to the same well, and seeing the Fox, inquired if the water was good. Concealing his sad plight under a merry guise, the Fox indulged in a lavish praise of the water, saying it was excellent beyond measure, and encouraging him to descend. The Goat, mindful only of his thirst, thoughtlessly jumped down, but just as he drank, the Fox informed him of the difficulty they were both in and suggested a scheme for their common escape. \"If,\" said he, \"you will place your forefeet upon the wall and bend your head, I will run up your back and escape, and will help you out afterwards.\" The Goat readily assented and the Fox leaped upon his back. Steadying himself with the Goat horns, he safely reached the mouth of the well and made off as fast as he could. When the Goat upbraided him for breaking his promise, he turned around and cried out, \"You foolish old fellow! If you had as many brains in your head as you have hairs in your beard, you would never have gone down before you had inspected the way up, nor have exposed yourself to dangers from which you had no means of escape.\" Look before you leap."
|
28
|
+
|
29
|
+
Slingshot.index 'highlight-test' do
|
30
|
+
delete
|
31
|
+
create
|
32
|
+
store :id => 1, :content => content
|
33
|
+
refresh
|
34
|
+
end
|
35
|
+
sleep(0.5)
|
36
|
+
|
37
|
+
s = Slingshot.search('highlight-test') do
|
38
|
+
query { string 'fox' }
|
39
|
+
highlight :content => { :number_of_fragments => 0 }
|
40
|
+
end
|
41
|
+
|
42
|
+
doc = s.results.first
|
43
|
+
assert_not_nil doc.highlight.content
|
44
|
+
|
45
|
+
highlight = doc.highlight.content
|
46
|
+
assert highlight.to_s.include?('<em>'), "Highlight does not include default highlight tag"
|
47
|
+
end
|
48
|
+
|
23
49
|
end
|
24
50
|
|
25
51
|
end
|
data/test/unit/logger_test.rb
CHANGED
@@ -46,12 +46,12 @@ module Slingshot
|
|
46
46
|
|
47
47
|
context "levels" do
|
48
48
|
|
49
|
-
should "have default level" do
|
49
|
+
should "have the default level" do
|
50
50
|
logger = Logger.new STDERR
|
51
51
|
assert_equal 'info', logger.level
|
52
52
|
end
|
53
53
|
|
54
|
-
should "
|
54
|
+
should "set the level" do
|
55
55
|
logger = Logger.new STDERR, :level => 'debug'
|
56
56
|
assert_equal 'debug', logger.level
|
57
57
|
end
|
@@ -66,7 +66,7 @@ module Slingshot
|
|
66
66
|
|
67
67
|
should "log request in correct format" do
|
68
68
|
log = (<<-"log;").gsub(/^ +/, '')
|
69
|
-
# [_search] (["articles", "users"])
|
69
|
+
# 2011-03-19 11:00:00:L [_search] (["articles", "users"])
|
70
70
|
#
|
71
71
|
curl -X GET http://...
|
72
72
|
|
@@ -98,14 +98,14 @@ module Slingshot
|
|
98
98
|
}
|
99
99
|
json;
|
100
100
|
log = (<<-"log;").gsub(/^\s*/, '')
|
101
|
-
#
|
101
|
+
# 2011-03-19 11:00:00:L [200 OK] (4 msec)
|
102
102
|
#
|
103
103
|
log;
|
104
104
|
# log += json.split.map { |line| "# #{line}" }.join("\n")
|
105
105
|
json.each_line { |line| log += "# #{line}" }
|
106
106
|
log += "\n\n"
|
107
107
|
@logger.expects(:write).with(log)
|
108
|
-
@logger.log_response('200 OK', json)
|
108
|
+
@logger.log_response('200 OK', 4, json)
|
109
109
|
end
|
110
110
|
|
111
111
|
end
|
data/test/unit/search_test.rb
CHANGED
@@ -32,6 +32,13 @@ module Slingshot
|
|
32
32
|
s.to_curl
|
33
33
|
end
|
34
34
|
|
35
|
+
should "return curl snippet with multiple indices for debugging" do
|
36
|
+
s = Search::Search.new('index_1', 'index_2') do
|
37
|
+
query { string 'title:foo' }
|
38
|
+
end
|
39
|
+
assert_match /index_1,index_2/, s.to_curl
|
40
|
+
end
|
41
|
+
|
35
42
|
should "allow chaining" do
|
36
43
|
assert_nothing_raised do
|
37
44
|
Search::Search.new('index').query { }.sort { title 'desc' }.size(5).sort { name 'asc' }.from(1)
|
@@ -39,8 +46,10 @@ module Slingshot
|
|
39
46
|
end
|
40
47
|
|
41
48
|
should "perform the search" do
|
42
|
-
|
49
|
+
response = stub(:body => '{"took":1,"hits":[]}', :code => 200)
|
50
|
+
Configuration.client.expects(:post).returns(response)
|
43
51
|
Results::Collection.expects(:new).returns([])
|
52
|
+
|
44
53
|
s = Search::Search.new('index')
|
45
54
|
s.perform
|
46
55
|
assert_not_nil s.results
|
@@ -58,10 +67,12 @@ module Slingshot
|
|
58
67
|
should "log request, but not response, when logger is set" do
|
59
68
|
Configuration.logger STDERR
|
60
69
|
|
61
|
-
|
70
|
+
response = stub(:body => '{"took":1,"hits":[]}', :code => 200)
|
71
|
+
Configuration.client.expects(:post).returns(response)
|
72
|
+
|
62
73
|
Results::Collection.expects(:new).returns([])
|
63
74
|
Configuration.logger.expects(:log_request).returns(true)
|
64
|
-
Configuration.logger.expects(:log_response).
|
75
|
+
Configuration.logger.expects(:log_response).with(200, 1, '')
|
65
76
|
|
66
77
|
Search::Search.new('index').perform
|
67
78
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slingshot-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 8
|
10
|
+
version: 0.0.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Karel Minarik
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-24 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|