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.
@@ -3,27 +3,21 @@ Slingshot
3
3
 
4
4
  ![Slingshot](https://github.com/karmi/slingshot/raw/master/slingshot.png)
5
5
 
6
- _Slingshot_ aims to provide rich and comfortable Ruby API and DSL for the
7
- [ElasticSearch](http://www.elasticsearch.org/) search engine/database.
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.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.15.0.tar.gz
25
- $ tar -zxvf elasticsearch-0.15.0.tar.gz
26
- $ ./elasticsearch-0.15.0/bin/elasticsearch -f
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
- You can also create the
67
- index with specific [mappings](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html), such as:
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.lucidimagination.com/Community/Hear-from-the-Experts/Articles/Faceted-Search-Solr)
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 only a limited subset of vast _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/):
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
- * Creating, deleting and refreshing the index
154
- * Creating the index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
155
- * Storing a document in the index
156
- * [Querying](https://github.com/karmi/slingshot/blob/master/examples/dsl.rb) the index with the `query_string`, `term` and `terms` types of queries
157
- * [Sorting](http://elasticsearch.org/guide/reference/api/search/sort.html) the results by `fields`
158
- * [Filtering](http://elasticsearch.org/guide/reference/query-dsl/) the results
159
- * Retrieving a _terms_ type of [facets](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html) -- other types are high priority
160
- * Returning just specific `fields` from documents
161
- * Paging with `from` and `size` query options
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
- Check out file `test/unit/results_collection_test.rb` to see how to do that.
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 & Plans
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
- In order of importance:
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.pattern = 'test/**/*_test.rb'
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
@@ -1,6 +1,13 @@
1
- # **Slingshot** is a rich and comfortable Ruby API and DSL for the
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.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.15.0.tar.gz
26
- tar -zxvf elasticsearch-0.15.0.tar.gz
27
- ./elasticsearch-0.15.0/bin/elasticsearch -f
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
- # Of course, we may want to define our queries more expressively, for instance
131
- # when we're searching for articles with specific _tags_.
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_ allows us to do many more types of queries.
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.lucidimagination.com/Community/Hear-from-the-Experts/Articles/Faceted-Search-Solr).
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
- # to the current query with global facets.
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.karmi.cz/guide/reference/api/search/filter.html),
252
- # though:
299
+ # [filters](http://elasticsearch.org/guide/reference/api/search/filter.html).
253
300
 
254
- # > When doing things like facet navigation,
255
- # > sometimes only the hits are needed to be filtered by the chosen facet,
256
- # > and all the facets should continue to be calculated based on the original query.
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 out search a bit complex. Let's search for articles whose titles begin
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, price, etc? We can do that.
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 { string '*' }
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 matched snippets in your text.
347
- # _ElasticSearch_ provides many features for
348
- # [highlighting](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html),
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
- # And use the `highlight` method.
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
- # Slingshot allows you to specify options for the highlighting, such as:
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.
@@ -7,17 +7,24 @@ module Slingshot
7
7
  end
8
8
 
9
9
  def delete
10
- response = Configuration.client.delete "#{Configuration.url}/#{@name}"
11
- return response =~ /error/ ? false : true
12
- rescue
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
- # http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html
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
- if id
50
- result = Configuration.client.post "#{Configuration.url}/#{@name}/#{type}/#{id}", document
51
- else
52
- result = Configuration.client.post "#{Configuration.url}/#{@name}/#{type}/", document
53
- end
54
- JSON.parse(result)
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
- result = Configuration.client.get "#{Configuration.url}/#{@name}/#{type}/#{id}"
59
- h = JSON.parse(result)
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
@@ -21,21 +21,21 @@ module Slingshot
21
21
  end
22
22
 
23
23
  def log_request(endpoint, params=nil, curl='')
24
- # [_search] (articles,users) 2001-02-12 18:20:42:32
24
+ # 2001-02-12 18:20:42:32 [_search] (articles,users)
25
25
  #
26
26
  # curl -X POST ....
27
27
  #
28
- content = "# [#{endpoint}] "
29
- content += "(#{params.inspect}) " if params
30
- content += time
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 OK] (4 msec) Sat Feb 12 19:20:47 2011
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
- took = JSON.parse(json)['took'] rescue nil
47
- content = "# [#{status}] "
48
- content += "(#{took} msec) " if took
49
- content += time
50
- content += "\n#\n"
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
@@ -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 => e
71
- STDERR.puts "[REQUEST FAILED]\n#{self.to_curl}\n"
70
+ rescue Exception => error
71
+ STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
72
72
  raise
73
73
  ensure
74
- if Configuration.logger
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
@@ -1,3 +1,3 @@
1
1
  module Slingshot
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
@@ -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
- # query { string '-w' }
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
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  module Slingshot
4
4
 
5
- class IndexMappingTest < Test::Unit::TestCase
5
+ class IndexMappingIntegrationTest < Test::Unit::TestCase
6
6
  include Test::Integration
7
7
 
8
8
  context "Default mapping" do
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  module Slingshot
4
4
 
5
- class IndexStoreTest < Test::Unit::TestCase
5
+ class IndexStoreIntegrationTest < Test::Unit::TestCase
6
6
  include Test::Integration
7
7
 
8
8
  context "Storing the documents in index" do
@@ -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 "have set the level" do
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"]) 2011-03-19 11:00:00:L
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
- # [200 OK] (4 msec) 2011-03-19 11:00:00:L
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
@@ -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
- Configuration.client.expects(:post).returns('{"hits":[]}')
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
- Configuration.client.expects(:post).returns('{"hits":[]}')
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).never
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: 17
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 7
10
- version: 0.0.7
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-02 00:00:00 +02:00
18
+ date: 2011-04-24 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency