slingshot-rb 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/README.markdown +276 -50
- data/examples/rails-application-template.rb +144 -0
- data/examples/slingshot-dsl.rb +272 -102
- data/lib/slingshot.rb +13 -0
- data/lib/slingshot/client.rb +10 -1
- data/lib/slingshot/dsl.rb +17 -1
- data/lib/slingshot/index.rb +109 -7
- data/lib/slingshot/model/callbacks.rb +23 -0
- data/lib/slingshot/model/import.rb +18 -0
- data/lib/slingshot/model/indexing.rb +50 -0
- data/lib/slingshot/model/naming.rb +30 -0
- data/lib/slingshot/model/persistence.rb +34 -0
- data/lib/slingshot/model/persistence/attributes.rb +60 -0
- data/lib/slingshot/model/persistence/finders.rb +61 -0
- data/lib/slingshot/model/persistence/storage.rb +75 -0
- data/lib/slingshot/model/search.rb +97 -0
- data/lib/slingshot/results/collection.rb +35 -10
- data/lib/slingshot/results/item.rb +10 -7
- data/lib/slingshot/results/pagination.rb +30 -0
- data/lib/slingshot/rubyext/symbol.rb +11 -0
- data/lib/slingshot/search.rb +3 -2
- data/lib/slingshot/search/facet.rb +8 -6
- data/lib/slingshot/search/filter.rb +7 -8
- data/lib/slingshot/search/highlight.rb +1 -3
- data/lib/slingshot/search/query.rb +4 -0
- data/lib/slingshot/search/sort.rb +5 -0
- data/lib/slingshot/tasks.rb +88 -0
- data/lib/slingshot/version.rb +1 -1
- data/slingshot.gemspec +17 -4
- data/test/integration/active_model_searchable_test.rb +80 -0
- data/test/integration/active_record_searchable_test.rb +193 -0
- data/test/integration/highlight_test.rb +1 -1
- data/test/integration/index_mapping_test.rb +1 -1
- data/test/integration/index_store_test.rb +27 -0
- data/test/integration/persistent_model_test.rb +35 -0
- data/test/integration/query_string_test.rb +3 -3
- data/test/integration/sort_test.rb +2 -2
- data/test/models/active_model_article.rb +31 -0
- data/test/models/active_model_article_with_callbacks.rb +49 -0
- data/test/models/active_model_article_with_custom_index_name.rb +5 -0
- data/test/models/active_record_article.rb +12 -0
- data/test/models/persistent_article.rb +11 -0
- data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
- data/test/models/supermodel_article.rb +22 -0
- data/test/models/validated_model.rb +11 -0
- data/test/test_helper.rb +4 -0
- data/test/unit/active_model_lint_test.rb +17 -0
- data/test/unit/client_test.rb +4 -0
- data/test/unit/configuration_test.rb +4 -0
- data/test/unit/index_test.rb +240 -17
- data/test/unit/model_callbacks_test.rb +90 -0
- data/test/unit/model_import_test.rb +71 -0
- data/test/unit/model_persistence_test.rb +400 -0
- data/test/unit/model_search_test.rb +289 -0
- data/test/unit/results_collection_test.rb +69 -7
- data/test/unit/results_item_test.rb +8 -14
- data/test/unit/rubyext_hash_test.rb +19 -0
- data/test/unit/search_facet_test.rb +25 -7
- data/test/unit/search_filter_test.rb +3 -0
- data/test/unit/search_query_test.rb +11 -0
- data/test/unit/search_sort_test.rb +8 -0
- data/test/unit/search_test.rb +14 -0
- data/test/unit/slingshot_test.rb +38 -0
- metadata +133 -26
data/.gitignore
CHANGED
data/README.markdown
CHANGED
@@ -4,26 +4,32 @@ Slingshot
|
|
4
4
|

|
5
5
|
|
6
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
7
|
|
9
8
|
_ElasticSearch_ is a scalable, distributed, cloud-ready, highly-available,
|
10
|
-
|
11
|
-
|
9
|
+
full-text search engine and database, communicating by JSON over RESTful HTTP,
|
10
|
+
based on [Lucene](http://lucene.apache.org/), written in Java.
|
11
|
+
|
12
|
+
This document provides just a brief overview of _Slingshot's_ features. Be sure to check out also
|
13
|
+
the extensive documentation at <http://karmi.github.com/slingshot/> if you're interested.
|
12
14
|
|
13
15
|
Installation
|
14
16
|
------------
|
15
17
|
|
16
18
|
First, you need a running _ElasticSearch_ server. Thankfully, it's easy. Let's define easy:
|
17
19
|
|
18
|
-
$ curl -k -L -o elasticsearch-0.
|
19
|
-
$ tar -zxvf elasticsearch-0.
|
20
|
-
$ ./elasticsearch-0.
|
20
|
+
$ curl -k -L -o elasticsearch-0.16.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.16.0.tar.gz
|
21
|
+
$ tar -zxvf elasticsearch-0.16.0.tar.gz
|
22
|
+
$ ./elasticsearch-0.16.0/bin/elasticsearch -f
|
23
|
+
|
24
|
+
OK. Easy. On a Mac, you can also use _Homebrew_:
|
25
|
+
|
26
|
+
$ brew install elasticsearch
|
21
27
|
|
22
|
-
OK
|
28
|
+
OK. Let's install the gem via Rubygems:
|
23
29
|
|
24
30
|
$ gem install slingshot-rb
|
25
31
|
|
26
|
-
|
32
|
+
Of course, you can install it from the source as well:
|
27
33
|
|
28
34
|
$ git clone git://github.com/karmi/slingshot.git
|
29
35
|
$ cd slingshot
|
@@ -33,17 +39,20 @@ or from source:
|
|
33
39
|
Usage
|
34
40
|
-----
|
35
41
|
|
36
|
-
|
37
|
-
Plans for full ActiveModel integration (and other convenience layers) are in progress
|
38
|
-
(see the [`activemodel`](https://github.com/karmi/slingshot/compare/activemodel) branch).
|
42
|
+
_Slingshot_ exposes easy-to-use domain specific language for fluent communication with _ElasticSearch_.
|
39
43
|
|
40
|
-
|
41
|
-
|
44
|
+
It also blends with your [ActiveModel](https://github.com/rails/rails/tree/master/activemodel)
|
45
|
+
classes for convenient usage in Rails applications.
|
46
|
+
|
47
|
+
To test-drive the core _ElasticSearch_ functionality, let's require the gem:
|
42
48
|
|
43
49
|
require 'rubygems'
|
44
50
|
require 'slingshot'
|
45
51
|
|
46
|
-
|
52
|
+
Please note that you can copy these snippets from the much more extensive and heavily annotated file
|
53
|
+
in [examples/slingshot-dsl.rb](http://karmi.github.com/slingshot/).
|
54
|
+
|
55
|
+
OK. Let's create an index named `articles` and store/index some documents:
|
47
56
|
|
48
57
|
Slingshot.index 'articles' do
|
49
58
|
delete
|
@@ -57,8 +66,9 @@ First, let's create an index named `articles` and store/index some documents:
|
|
57
66
|
refresh
|
58
67
|
end
|
59
68
|
|
60
|
-
We can also create the
|
61
|
-
|
69
|
+
We can also create the index with custom
|
70
|
+
[mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
|
71
|
+
for a specific document type:
|
62
72
|
|
63
73
|
Slingshot.index 'articles' do
|
64
74
|
create :mappings => {
|
@@ -73,9 +83,32 @@ index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/a
|
|
73
83
|
}
|
74
84
|
end
|
75
85
|
|
76
|
-
|
86
|
+
Of course, we may have large amounts of data, and it may be impossible or impractical to add them to the index
|
87
|
+
one by one. We can use _ElasticSearch's_ [bulk storage](http://www.elasticsearch.org/guide/reference/api/bulk.html):
|
88
|
+
|
89
|
+
articles = [
|
90
|
+
{ :id => '1', :title => 'one' },
|
91
|
+
{ :id => '2', :title => 'two' },
|
92
|
+
{ :id => '3', :title => 'three' }
|
93
|
+
]
|
94
|
+
|
95
|
+
Slingshot.index 'bulk' do
|
96
|
+
import articles
|
97
|
+
end
|
98
|
+
|
99
|
+
We can also easily manipulate the documents before storing them in the index, by passing a block to the
|
100
|
+
`import` method:
|
101
|
+
|
102
|
+
Slingshot.index 'bulk' do
|
103
|
+
import articles do |documents|
|
104
|
+
|
105
|
+
documents.each { |document| document[:title].capitalize! }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
OK. Now, let's go search all the data.
|
77
110
|
|
78
|
-
We
|
111
|
+
We will be searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
|
79
112
|
filtering them for ones tagged “ruby”, and also retrieving some [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/)
|
80
113
|
from the database:
|
81
114
|
|
@@ -97,6 +130,9 @@ from the database:
|
|
97
130
|
end
|
98
131
|
end
|
99
132
|
|
133
|
+
(Of course, we may also page the results with `from` and `size` query options, retrieve only specific fields
|
134
|
+
or highlight content matching our query, etc.)
|
135
|
+
|
100
136
|
Let's display the results:
|
101
137
|
|
102
138
|
s.results.each do |document|
|
@@ -128,70 +164,260 @@ count for articles tagged 'php' is excluded, since they don't match the current
|
|
128
164
|
# python 1
|
129
165
|
# java 1
|
130
166
|
|
131
|
-
|
167
|
+
If configuring the search payload with a block somehow feels too weak for you, you can simply pass
|
168
|
+
a Ruby `Hash` (or JSON string) with the query declaration to the `search` method:
|
169
|
+
|
170
|
+
Slingshot.search 'articles', :query => { :fuzzy => { :title => 'Sour' } }
|
171
|
+
|
172
|
+
If this sounds like a great idea to you, you are probably able to write your application
|
173
|
+
using just `curl`, `sed` and `awk`.
|
174
|
+
|
175
|
+
We can display the full query JSON for close inspection:
|
132
176
|
|
133
177
|
puts s.to_json
|
134
178
|
# {"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"}]}
|
135
179
|
|
136
|
-
Or, better, we can display the corresponding `curl` command
|
180
|
+
Or, better, we can display the corresponding `curl` command to recreate and debug the request in the terminal:
|
137
181
|
|
138
182
|
puts s.to_curl
|
139
183
|
# 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"}]}'
|
140
184
|
|
141
|
-
|
185
|
+
However, we can simply log every search query (and other requests) in this `curl`-friendly format:
|
142
186
|
|
143
187
|
Slingshot.configure { logger 'elasticsearch.log' }
|
144
188
|
|
189
|
+
When you set the log level to _debug_:
|
145
190
|
|
146
|
-
|
147
|
-
|
191
|
+
Slingshot.configure { logger 'elasticsearch.log', :level => 'debug' }
|
192
|
+
|
193
|
+
the JSON responses are logged as well. This is not a great idea for production environment,
|
194
|
+
but it's priceless when you want to paste a complicated transaction to the mailing list or IRC channel.
|
195
|
+
|
196
|
+
The _Slingshot_ DSL tries hard to provide a strong Ruby-like API for the main _ElasticSearch_ features.
|
197
|
+
|
198
|
+
By default, _Slingshot_ wraps the results collection in a enumerable `Results::Collection` class,
|
199
|
+
and result items in a `Results::Item` class, which looks like a child of `Hash` and `Openstruct`,
|
200
|
+
for smooth iterating and displaying the results.
|
201
|
+
|
202
|
+
You may wrap the result items in your own class by setting the `Slingshot.configuration.wrapper`
|
203
|
+
property. Your class must take a `Hash` of attributes on initialization.
|
204
|
+
|
205
|
+
If that seems like a great idea to you, there's a big chance you already have such class, and one would bet
|
206
|
+
it's an `ActiveRecord` or `ActiveModel` class, containing model of your Rails application.
|
207
|
+
|
208
|
+
Fortunately, _Slingshot_ makes blending _ElasticSearch_ features into your models trivially possible.
|
209
|
+
|
210
|
+
|
211
|
+
ActiveModel Integration
|
212
|
+
-----------------------
|
213
|
+
|
214
|
+
Let's suppose you have an `Article` class in your Rails application. To make it searchable with
|
215
|
+
_Slingshot_, you just `include` it:
|
216
|
+
|
217
|
+
class Article < ActiveRecord::Base
|
218
|
+
include Slingshot::Model::Search
|
219
|
+
include Slingshot::Model::Callbacks
|
220
|
+
end
|
221
|
+
|
222
|
+
When you now save a record:
|
223
|
+
|
224
|
+
Article.create :title => "I Love ElasticSearch",
|
225
|
+
:content => "...",
|
226
|
+
:author => "Captain Nemo",
|
227
|
+
:published_on => Time.now
|
228
|
+
|
229
|
+
it is automatically added into the index, because of the included callbacks. The document attributes
|
230
|
+
are indexed exactly as when you call the `Article#to_json` method.
|
231
|
+
|
232
|
+
Now you can search the records:
|
233
|
+
|
234
|
+
Article.search 'love'
|
235
|
+
|
236
|
+
OK. Often, this is where the game stops. Not here.
|
237
|
+
|
238
|
+
First of all, you may use the full query DSL, as explained above, with filters, sorting,
|
239
|
+
advanced facet aggregation, highlighting, etc:
|
240
|
+
|
241
|
+
q = 'love'
|
242
|
+
Article.search do
|
243
|
+
query { string q }
|
244
|
+
facet('timeline') { date :published_on, :interval => 'month' }
|
245
|
+
sort { published_on 'desc' }
|
246
|
+
end
|
247
|
+
|
248
|
+
Dynamic mapping is a godsend when you're prototyping.
|
249
|
+
For serious usage, though, you'll definitely want to define a custom mapping for your model:
|
250
|
+
|
251
|
+
class Article < ActiveRecord::Base
|
252
|
+
include Slingshot::Model::Search
|
253
|
+
include Slingshot::Model::Callbacks
|
254
|
+
|
255
|
+
mapping do
|
256
|
+
indexes :id, :type => 'string', :analyzed => false
|
257
|
+
indexes :title, :type => 'string', :analyzer => 'snowball', :boost => 100
|
258
|
+
indexes :content, :type => 'string', :analyzer => 'snowball'
|
259
|
+
indexes :author, :type => 'string', :analyzer => 'keyword'
|
260
|
+
indexes :published_on, :type => 'date', :include_in_all => false
|
261
|
+
end
|
262
|
+
end
|
148
263
|
|
149
|
-
|
264
|
+
In this case, _only_ the defined model attributes are indexed when adding to the index.
|
150
265
|
|
151
|
-
|
152
|
-
|
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
|
266
|
+
When you want tight grip on how your model attributes are added to the index, just
|
267
|
+
provide the `to_indexed_json` method yourself:
|
162
268
|
|
163
|
-
|
269
|
+
class Article < ActiveRecord::Base
|
270
|
+
include Slingshot::Model::Search
|
271
|
+
include Slingshot::Model::Callbacks
|
164
272
|
|
165
|
-
|
166
|
-
|
273
|
+
def to_indexed_json
|
274
|
+
names = author.split(/\W/)
|
275
|
+
last_name = names.pop
|
276
|
+
first_name = names.join
|
167
277
|
|
168
|
-
|
169
|
-
|
170
|
-
|
278
|
+
{
|
279
|
+
:title => title,
|
280
|
+
:content => content,
|
281
|
+
:author => {
|
282
|
+
:first_name => first_name,
|
283
|
+
:last_name => last_name
|
284
|
+
}
|
285
|
+
}.to_json
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
Note that _Slingshot_-enhanced models are fully compatible with [`will_paginate`](https://github.com/mislav/will_paginate),
|
291
|
+
so you can pass any parameters to the `search` method in the controller, as usual:
|
292
|
+
|
293
|
+
@articles = Article.search params[:q], :page => (params[:page] || 1)
|
294
|
+
|
295
|
+
OK. Chances are, you have lots of records stored in the underlying database. How will you get them to _ElasticSearch_? Easy:
|
296
|
+
|
297
|
+
Article.index.import Article.all
|
298
|
+
|
299
|
+
However, this way, all your records are loaded into memory, serialized into JSON,
|
300
|
+
and sent down the wire to _ElasticSearch_. Not practical, you say? You're right.
|
301
|
+
|
302
|
+
Provided your model implements some sort of _pagination_ — and it probably does, for so much data —,
|
303
|
+
you can just run:
|
304
|
+
|
305
|
+
Article.import
|
306
|
+
|
307
|
+
In this case, the `Article.paginate` method is called, and your records are sent to the index
|
308
|
+
in chunks of 1000. If that number doesn't suit you, just provide a better one:
|
309
|
+
|
310
|
+
Article.import :per_page => 100
|
311
|
+
|
312
|
+
Any other parameters you provide to the `import` method are passed down to the `paginate` method.
|
313
|
+
|
314
|
+
Are we saying you have to fiddle with this thing in a `rails console` or silly Ruby scripts? No.
|
315
|
+
Just call the included _Rake_ task on the commandline:
|
316
|
+
|
317
|
+
$ rake environment slingshot:import CLASS='Article'
|
318
|
+
|
319
|
+
You can also force-import the data by deleting the index first (and creating it with mapping
|
320
|
+
provided by the `mapping` block in your model):
|
321
|
+
|
322
|
+
$ rake environment slingshot:import CLASS='Article' FORCE=true
|
323
|
+
|
324
|
+
When you'll spend more time with _ElasticSearch_, you'll notice how
|
325
|
+
[index aliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases.html)
|
326
|
+
are the best idea since the invention of inverted index.
|
327
|
+
You can index your data into a fresh index (and possibly update an alias if everything's fine):
|
328
|
+
|
329
|
+
$ rake environment slingshot:import CLASS='Article' INDEX='articles-2011-05'
|
330
|
+
|
331
|
+
If you're the type who has no time for long introductions, you can generate a fully working
|
332
|
+
example Rails application, with an `ActiveRecord` model and a search form, to play with:
|
333
|
+
|
334
|
+
$ rails new searchapp -m https://github.com/karmi/slingshot/raw/master/examples/rails-application-template.rb
|
335
|
+
|
336
|
+
OK. All this time we have been talking about `ActiveRecord` models, since
|
337
|
+
it is a reasonable Rails' default for the storage layer.
|
338
|
+
|
339
|
+
But what if you use another database such as [MongoDB](http://www.mongodb.org/),
|
340
|
+
another object mapping library, such as [Mongoid](http://mongoid.org/)?
|
341
|
+
|
342
|
+
Well, things stay mostly the same:
|
343
|
+
|
344
|
+
class Article
|
345
|
+
include Mongoid::Document
|
346
|
+
field :title, :type => String
|
347
|
+
field :content, :type => String
|
348
|
+
|
349
|
+
include Slingshot::Model::Search
|
350
|
+
include Slingshot::Model::Callbacks
|
351
|
+
|
352
|
+
# Let's use a different index name so stuff doesn't get mixed up
|
353
|
+
#
|
354
|
+
index_name 'mongo-articles'
|
355
|
+
|
356
|
+
# These Mongo guys sure do some funky stuff with their IDs
|
357
|
+
# in +serializable_hash+, let's fix it.
|
358
|
+
#
|
359
|
+
def to_indexed_json
|
360
|
+
self.to_json
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
Article.create :title => 'I Love ElasticSearch'
|
366
|
+
|
367
|
+
Article.search 'love'
|
368
|
+
|
369
|
+
That's kinda nice. But there's more.
|
370
|
+
|
371
|
+
_Slingshot_ implements not only _searchable_ features, but also _persistence_ features.
|
372
|
+
|
373
|
+
This means that you can use a _Slingshot_ model **instead of** your database, not just
|
374
|
+
for searching your database. Why would you like to do that?
|
375
|
+
|
376
|
+
Well, because you're tired of database migrations and lots of hand-holding with your
|
377
|
+
database to store stuff like `{ :name => 'Slingshot', :tags => [ 'ruby', 'search' ] }`.
|
378
|
+
Because what you need is to just dump a JSON-representation of your data into a database and
|
379
|
+
load it back when needed.
|
380
|
+
Because you've noticed that _searching_ your data is a much more effective way of retrieval
|
381
|
+
then constructing elaborate database query conditions.
|
382
|
+
Because you have _lots_ of data and want to use _ElasticSearch's_
|
383
|
+
advanced distributed features.
|
384
|
+
|
385
|
+
To use the persistence features, you have to include the `Slingshot::Persistence` module
|
386
|
+
in your class and define the properties (analogous to the way you do with CouchDB- or MongoDB-based models):
|
387
|
+
|
388
|
+
class Article
|
389
|
+
include Slingshot::Model::Persistence
|
390
|
+
include Slingshot::Model::Search
|
391
|
+
include Slingshot::Model::Callbacks
|
392
|
+
|
393
|
+
validates_presence_of :title, :author
|
394
|
+
|
395
|
+
property :title
|
396
|
+
property :author
|
397
|
+
property :content
|
398
|
+
property :published_on
|
399
|
+
|
400
|
+
end
|
171
401
|
|
402
|
+
Of course, not all validations or `ActionPack` helpers will be available to your models,
|
403
|
+
but if you can live with that, you've just got a schema-free, highly-scalable storage
|
404
|
+
and retrieval engine for your data.
|
172
405
|
|
173
406
|
Todo, Plans & Ideas
|
174
407
|
-------------------
|
175
408
|
|
176
|
-
_Slingshot_ is already used in production by its authors. Nevertheless, it's not finished yet.
|
409
|
+
_Slingshot_ is already used in production by its authors. Nevertheless, it's not considered finished yet.
|
177
410
|
|
178
|
-
|
411
|
+
There are todos, plans and ideas, some of which are listed below, in the order of importance:
|
179
412
|
|
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
|
182
|
-
* [Mapping](http://www.elasticsearch.org/guide/reference/mapping/) definition for models
|
183
413
|
* Proper RDoc annotations for the source code
|
184
|
-
* Dual interface: allow to simply pass queries/options for _ElasticSearch_ as a Hash in any method
|
185
414
|
* [Histogram](http://www.elasticsearch.org/guide/reference/api/search/facets/histogram-facet.html) facets
|
186
415
|
* [Statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html) facets
|
187
416
|
* [Geo Distance](http://www.elasticsearch.org/guide/reference/api/search/facets/geo-distance-facet.html) facets
|
188
417
|
* [Index aliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases.html) management
|
189
418
|
* [Analyze](http://www.elasticsearch.org/guide/reference/api/admin-indices-analyze.html) API support
|
190
|
-
* [Bulk](http://www.elasticsearch.org/guide/reference/api/bulk.html) API
|
191
419
|
* 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
|
193
420
|
|
194
|
-
The full ActiveModel integration is planned for the 1.0 release.
|
195
421
|
|
196
422
|
Other Clients
|
197
423
|
-------------
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# ===================================================================================================================
|
2
|
+
# Template for generating a no-frills Rails application with support for ElasticSearch full-text search via Slingshot
|
3
|
+
# ===================================================================================================================
|
4
|
+
#
|
5
|
+
# This file creates a basic Rails application with support for ElasticSearch full-text via the Slingshot gem
|
6
|
+
#
|
7
|
+
# Run it like this:
|
8
|
+
#
|
9
|
+
# rails new searchapp -m https://github.com/karmi/slingshot/raw/master/examples/rails-application-template.rb
|
10
|
+
#
|
11
|
+
|
12
|
+
run "rm public/index.html"
|
13
|
+
run "rm public/images/rails.png"
|
14
|
+
run "touch tmp/.gitignore log/.gitignore vendor/.gitignore"
|
15
|
+
|
16
|
+
git :init
|
17
|
+
git :add => '.'
|
18
|
+
git :commit => "-m 'Initial commit: Clean application'"
|
19
|
+
|
20
|
+
puts
|
21
|
+
say_status "Rubygems", "Adding Rubygems into Gemfile...\n", :yellow
|
22
|
+
puts '-'*80, ''
|
23
|
+
|
24
|
+
gem 'slingshot-rb', :git => 'https://github.com/karmi/slingshot.git', :branch => 'activemodel'
|
25
|
+
gem 'will_paginate', '~>3.0.pre'
|
26
|
+
git :add => '.'
|
27
|
+
git :commit => "-m 'Added gems'"
|
28
|
+
|
29
|
+
puts
|
30
|
+
say_status "Rubygems", "Installing Rubygems...", :yellow
|
31
|
+
|
32
|
+
puts
|
33
|
+
puts "********************************************************************************"
|
34
|
+
puts " Running `bundle install`. Let's watch a movie!"
|
35
|
+
puts "********************************************************************************", ""
|
36
|
+
|
37
|
+
run "bundle install"
|
38
|
+
|
39
|
+
puts
|
40
|
+
say_status "Model", "Adding search support into the Article model...", :yellow
|
41
|
+
puts '-'*80, ''
|
42
|
+
|
43
|
+
generate :scaffold, "Article title:string content:text published_on:date"
|
44
|
+
route "root :to => 'articles#index'"
|
45
|
+
rake "db:migrate"
|
46
|
+
|
47
|
+
git :add => '.'
|
48
|
+
git :commit => "-m 'Added the Article resource'"
|
49
|
+
|
50
|
+
run "rm -f app/models/article.rb"
|
51
|
+
file 'app/models/article.rb', <<-CODE
|
52
|
+
class Article < ActiveRecord::Base
|
53
|
+
include Slingshot::Model::Search
|
54
|
+
include Slingshot::Model::Callbacks
|
55
|
+
end
|
56
|
+
CODE
|
57
|
+
|
58
|
+
initializer 'slingshot.rb', <<-CODE
|
59
|
+
Slingshot.configure do
|
60
|
+
logger STDERR
|
61
|
+
end
|
62
|
+
CODE
|
63
|
+
|
64
|
+
git :commit => "-a -m 'Added Slingshot support into the Article class and an initializer'"
|
65
|
+
|
66
|
+
puts
|
67
|
+
say_status "Controller", "Adding controller action, route, and neccessary HTML for search...", :yellow
|
68
|
+
puts '-'*80, ''
|
69
|
+
|
70
|
+
gsub_file 'app/controllers/articles_controller.rb', %r{# GET /articles/1$}, <<-CODE
|
71
|
+
# GET /articles/search
|
72
|
+
def search
|
73
|
+
@articles = Article.search params[:q]
|
74
|
+
|
75
|
+
render :action => "index"
|
76
|
+
end
|
77
|
+
|
78
|
+
# GET /articles/1
|
79
|
+
CODE
|
80
|
+
|
81
|
+
gsub_file 'app/views/articles/index.html.erb', %r{<h1>Listing articles</h1>}, <<-CODE
|
82
|
+
<h1>Listing articles</h1>
|
83
|
+
|
84
|
+
<%= form_tag search_articles_path, :method => 'get' do %>
|
85
|
+
<%= label_tag :query %>
|
86
|
+
<%= text_field_tag :q, params[:q] %>
|
87
|
+
<%= submit_tag :search %>
|
88
|
+
<% end %>
|
89
|
+
|
90
|
+
<hr>
|
91
|
+
CODE
|
92
|
+
|
93
|
+
gsub_file 'app/views/articles/index.html.erb', %r{<%= link_to 'New Article', new_article_path %>}, <<-CODE
|
94
|
+
<%= link_to 'New Article', new_article_path %>
|
95
|
+
<%= link_to 'Back', articles_path if params[:q] %>
|
96
|
+
CODE
|
97
|
+
|
98
|
+
gsub_file 'config/routes.rb', %r{resources :articles}, <<-CODE
|
99
|
+
resources :articles do
|
100
|
+
collection { get :search }
|
101
|
+
end
|
102
|
+
CODE
|
103
|
+
|
104
|
+
git :commit => "-a -m 'Added Slingshot support into the frontend of application'"
|
105
|
+
|
106
|
+
puts
|
107
|
+
say_status "Database", "Seeding the database with data...", :yellow
|
108
|
+
puts '-'*80, ''
|
109
|
+
|
110
|
+
run "rm -f db/seeds.rb"
|
111
|
+
file 'db/seeds.rb', <<-CODE
|
112
|
+
contents = [
|
113
|
+
'Lorem ipsum dolor sit amet.',
|
114
|
+
'Consectetur adipisicing elit, sed do eiusmod tempor incididunt.',
|
115
|
+
'Labore et dolore magna aliqua.',
|
116
|
+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
|
117
|
+
'Excepteur sint occaecat cupidatat non proident.'
|
118
|
+
]
|
119
|
+
|
120
|
+
puts "Deleting all articles..."
|
121
|
+
Article.delete_all
|
122
|
+
|
123
|
+
puts "Creating articles:"
|
124
|
+
%w[ One Two Three Four Five ].each_with_index do |title, i|
|
125
|
+
Article.create :title => title, :content => contents[i], :published_on => i.days.ago.utc
|
126
|
+
end
|
127
|
+
CODE
|
128
|
+
|
129
|
+
rake "db:seed"
|
130
|
+
|
131
|
+
git :add => "db/seeds.rb"
|
132
|
+
git :commit => "-m 'Added database seeding script'"
|
133
|
+
|
134
|
+
puts
|
135
|
+
say_status "Index", "Indexing database...", :yellow
|
136
|
+
puts '-'*80, ''
|
137
|
+
|
138
|
+
rake "environment slingshot:import CLASS='Article' FORCE=true"
|
139
|
+
|
140
|
+
puts "", "="*80
|
141
|
+
say_status "DONE", "\e[1mStarting the application. Open http://localhost:3000 and search for something...\e[0m", :yellow
|
142
|
+
puts "="*80, ""
|
143
|
+
|
144
|
+
run "rails server"
|