slingshot-rb 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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/examples/slingshot-dsl.rb
CHANGED
@@ -1,23 +1,27 @@
|
|
1
|
-
# **Slingshot**
|
1
|
+
# **Slingshot** provides rich and comfortable Ruby API for the
|
2
2
|
# [_ElasticSearch_](http://www.elasticsearch.org/) search engine/database.
|
3
3
|
#
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# _ElasticSearch_ is a scalable, distributed, cloud-ready, highly-available
|
5
|
+
# full-text search engine and database, communicating by JSON over RESTful HTTP,
|
6
|
+
# based on [Lucene](http://lucene.apache.org/), written in Java.
|
6
7
|
#
|
7
8
|
# <img src="http://github.com/favicon.ico" style="position:relative; top:2px">
|
8
9
|
# _Slingshot_ is open source, and you can download or clone the source code
|
9
10
|
# from <https://github.com/karmi/slingshot>.
|
10
11
|
#
|
11
|
-
# _ElasticSearch_ is a scalable, distributed, highly-available,
|
12
|
-
# RESTful database communicating by JSON over HTTP, based on [Lucene](http://lucene.apache.org/),
|
13
|
-
# written in Java. It manages to be very simple and very powerful at the same time.
|
14
|
-
#
|
15
12
|
# By following these instructions you should have the search running
|
16
13
|
# on a sane operation system in less then 10 minutes.
|
17
14
|
|
15
|
+
# Note, that this file can be executed directly:
|
16
|
+
#
|
17
|
+
# ruby examples/slingshot-dsl.rb
|
18
|
+
#
|
19
|
+
|
20
|
+
|
18
21
|
#### Installation
|
19
22
|
|
20
|
-
# Install
|
23
|
+
# Install _Slingshot_ with Rubygems.
|
24
|
+
|
21
25
|
#
|
22
26
|
# gem install slingshot-rb
|
23
27
|
#
|
@@ -27,31 +31,37 @@ require 'slingshot'
|
|
27
31
|
#### Prerequisites
|
28
32
|
|
29
33
|
# You'll need a working and running _ElasticSearch_ server. Thankfully, that's easy.
|
30
|
-
( puts <<-"INSTALL" ; exit(1) ) unless RestClient.get('http://localhost:9200') rescue false
|
31
|
-
|
32
|
-
|
33
|
-
tar -zxvf elasticsearch-0.15.2.tar.gz
|
34
|
-
./elasticsearch-0.15.2/bin/elasticsearch -f
|
35
|
-
INSTALL
|
34
|
+
( puts <<-"INSTALL" ; exit(1) ) unless (RestClient.get('http://localhost:9200') rescue false)
|
35
|
+
|
36
|
+
[ERROR] You don’t appear to have ElasticSearch installed. Please install and launch it with the following commands:
|
36
37
|
|
37
|
-
|
38
|
+
curl -k -L -o elasticsearch-0.16.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.16.0.tar.gz
|
39
|
+
tar -zxvf elasticsearch-0.16.0.tar.gz
|
40
|
+
./elasticsearch-0.16.0/bin/elasticsearch -f
|
41
|
+
INSTALL
|
38
42
|
|
39
|
-
|
43
|
+
### Storing and indexing documents
|
40
44
|
|
41
45
|
# Let's initialize an index named “articles”.
|
46
|
+
#
|
42
47
|
Slingshot.index 'articles' do
|
43
48
|
# To make sure it's fresh, let's delete any existing index with the same name.
|
49
|
+
#
|
44
50
|
delete
|
45
51
|
# And then, let's create it.
|
52
|
+
#
|
46
53
|
create
|
47
54
|
|
48
|
-
# We want to store and index some articles with title
|
55
|
+
# We want to store and index some articles with `title`, `tags` and `published_on` properties.
|
56
|
+
# Simple Hashes are OK.
|
57
|
+
#
|
49
58
|
store :title => 'One', :tags => ['ruby'], :published_on => '2011-01-01'
|
50
59
|
store :title => 'Two', :tags => ['ruby', 'python'], :published_on => '2011-01-02'
|
51
60
|
store :title => 'Three', :tags => ['java'], :published_on => '2011-01-02'
|
52
61
|
store :title => 'Four', :tags => ['ruby', 'php'], :published_on => '2011-01-03'
|
53
62
|
|
54
63
|
# We force refreshing the index, so we can query it immediately.
|
64
|
+
#
|
55
65
|
refresh
|
56
66
|
end
|
57
67
|
|
@@ -60,34 +70,87 @@ end
|
|
60
70
|
|
61
71
|
Slingshot.index 'articles' do
|
62
72
|
# To do so, just pass a Hash containing the specified mapping to the `Index#create` method.
|
73
|
+
#
|
63
74
|
create :mappings => {
|
64
|
-
|
75
|
+
|
76
|
+
# Specify for which type of documents this mapping should be used.
|
77
|
+
# (The documents must provide a `type` method or property then.)
|
78
|
+
#
|
65
79
|
:article => {
|
66
80
|
:properties => {
|
81
|
+
|
67
82
|
# Specify the type of the field, whether it should be analyzed, etc.
|
83
|
+
#
|
68
84
|
:id => { :type => 'string', :index => 'not_analyzed', :include_in_all => false },
|
69
|
-
|
70
|
-
#
|
71
|
-
|
85
|
+
|
86
|
+
# Set the boost or analyzer settings for the field, ... The _ElasticSearch_ guide
|
87
|
+
# has [more information](http://elasticsearch.org/guide/reference/mapping/index.html)
|
88
|
+
# about this. Proper mapping is key to efficient and effective search.
|
89
|
+
# But don't fret about getting the mapping right the first time, you won't.
|
90
|
+
# In most cases, the default mapping is just fine for prototyping.
|
91
|
+
#
|
92
|
+
:title => { :type => 'string', :analyzer => 'snowball', :boost => 2.0 },
|
72
93
|
:tags => { :type => 'string', :analyzer => 'keyword' },
|
73
|
-
:content => { :type => 'string', :analyzer => '
|
94
|
+
:content => { :type => 'string', :analyzer => 'czech' }
|
74
95
|
}
|
75
96
|
}
|
76
97
|
}
|
77
98
|
end
|
78
99
|
|
100
|
+
#### Bulk Storage
|
101
|
+
|
102
|
+
# Of course, we may have large amounts of data, and adding them to the index one by one really isn't the best idea.
|
103
|
+
# We can use _ElasticSearch's_ [bulk storage](http://www.elasticsearch.org/guide/reference/api/bulk.html)
|
104
|
+
# for importing the data.
|
105
|
+
|
106
|
+
# So, for demonstration purposes, let's suppose we have a plain collection of hashes to store.
|
107
|
+
#
|
108
|
+
articles = [
|
109
|
+
|
110
|
+
# Notice that such objects must have an `id` property!
|
111
|
+
#
|
112
|
+
{ :id => '1', :title => 'one', :tags => ['ruby'], :published_on => '2011-01-01' },
|
113
|
+
{ :id => '2', :title => 'two', :tags => ['ruby', 'python'], :published_on => '2011-01-02' },
|
114
|
+
{ :id => '3', :title => 'three', :tags => ['java'], :published_on => '2011-01-02' },
|
115
|
+
{ :id => '4', :title => 'four', :tags => ['ruby', 'php'], :published_on => '2011-01-03' }
|
116
|
+
]
|
117
|
+
|
118
|
+
# We can just push them into the index in one go.
|
119
|
+
#
|
120
|
+
Slingshot.index 'articles' do
|
121
|
+
import articles
|
122
|
+
end
|
123
|
+
|
124
|
+
# Of course, we can easily manipulate the documents before storing them in the index.
|
125
|
+
#
|
126
|
+
Slingshot.index 'articles' do
|
127
|
+
delete
|
128
|
+
|
129
|
+
# ... by just passing a block to the `import` method. The collection will
|
130
|
+
# be available in the block argument.
|
131
|
+
#
|
132
|
+
import articles do |documents|
|
133
|
+
|
134
|
+
# We will capitalize every _title_ and return the manipulated collection
|
135
|
+
# back to the `import` method.
|
136
|
+
#
|
137
|
+
documents.map { |document| document.update(:title => document[:title].capitalize) }
|
138
|
+
end
|
79
139
|
|
140
|
+
refresh
|
141
|
+
end
|
80
142
|
|
81
|
-
|
143
|
+
### Searching
|
82
144
|
|
83
|
-
# With the documents indexed and stored in the _ElasticSearch_ database, we
|
145
|
+
# With the documents indexed and stored in the _ElasticSearch_ database, we can search them, finally.
|
84
146
|
#
|
85
147
|
# Slingshot exposes the search interface via simple domain-specific language.
|
86
148
|
|
87
149
|
|
88
|
-
|
150
|
+
#### Simple Query String Searches
|
89
151
|
|
90
152
|
# We can do simple searches, like searching for articles containing “One” in their title.
|
153
|
+
#
|
91
154
|
s = Slingshot.search('articles') do
|
92
155
|
query do
|
93
156
|
string "title:One"
|
@@ -96,23 +159,45 @@ end
|
|
96
159
|
|
97
160
|
# The results:
|
98
161
|
# * One [tags: ruby]
|
162
|
+
#
|
99
163
|
s.results.each do |document|
|
100
164
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
101
165
|
end
|
102
166
|
|
167
|
+
# Or, we can search for articles published between January, 1st and January, 2nd.
|
168
|
+
#
|
169
|
+
s = Slingshot.search('articles') do
|
170
|
+
query do
|
171
|
+
string "published_on:[2011-01-01 TO 2011-01-02]"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# The results:
|
176
|
+
# * One [published: 2011-01-01]
|
177
|
+
# * Two [published: 2011-01-02]
|
178
|
+
# * Three [published: 2011-01-02]
|
179
|
+
#
|
180
|
+
s.results.each do |document|
|
181
|
+
puts "* #{ document.title } [published: #{document.published_on}]"
|
182
|
+
end
|
183
|
+
|
103
184
|
# Of course, we may write the blocks in shorter notation.
|
185
|
+
# Local variables from outer scope are passed down the chain.
|
104
186
|
|
105
187
|
# Let's search for articles whose titles begin with letter “T”.
|
106
|
-
|
188
|
+
#
|
189
|
+
q = "title:T*"
|
190
|
+
s = Slingshot.search('articles') { query { string q } }
|
107
191
|
|
108
192
|
# The results:
|
109
193
|
# * Two [tags: ruby, python]
|
110
194
|
# * Three [tags: java]
|
195
|
+
#
|
111
196
|
s.results.each do |document|
|
112
197
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
113
198
|
end
|
114
199
|
|
115
|
-
#
|
200
|
+
# In fact, we can use any valid [Lucene query syntax](http://lucene.apache.org/java/3_0_3/queryparsersyntax.html)
|
116
201
|
# for the query string queries.
|
117
202
|
|
118
203
|
# For debugging, we can display the JSON which is being sent to _ElasticSearch_.
|
@@ -122,8 +207,8 @@ end
|
|
122
207
|
puts "", "Query:", "-"*80
|
123
208
|
puts s.to_json
|
124
209
|
|
125
|
-
# Or better, we may display a complete `curl` command
|
126
|
-
#
|
210
|
+
# Or better, we may display a complete `curl` command to recreate the request in terminal,
|
211
|
+
# so we can see the naked response, tweak request parameters and meditate on problems.
|
127
212
|
#
|
128
213
|
# curl -X POST "http://localhost:9200/articles/_search?pretty=true" \
|
129
214
|
# -d '{"query":{"query_string":{"query":"title:T*"}}}'
|
@@ -131,8 +216,11 @@ puts s.to_json
|
|
131
216
|
puts "", "Try the query in Curl:", "-"*80
|
132
217
|
puts s.to_curl
|
133
218
|
|
134
|
-
|
135
|
-
|
219
|
+
|
220
|
+
### Logging
|
221
|
+
|
222
|
+
# For debugging more complex situations, we can enable logging, so requests and responses
|
223
|
+
# will be logged using this `curl`-friendly format.
|
136
224
|
|
137
225
|
Slingshot.configure do
|
138
226
|
|
@@ -141,37 +229,62 @@ Slingshot.configure do
|
|
141
229
|
#
|
142
230
|
# # 2011-04-24 11:34:01:150 [CREATE] ("articles")
|
143
231
|
# #
|
144
|
-
# curl -X POST "http://localhost:9200/articles"
|
145
|
-
#
|
146
|
-
# }'
|
232
|
+
# curl -X POST "http://localhost:9200/articles"
|
147
233
|
#
|
148
234
|
# # 2011-04-24 11:34:01:152 [200]
|
149
235
|
#
|
150
236
|
logger 'elasticsearch.log'
|
151
237
|
|
152
|
-
# For debugging,
|
238
|
+
# For debugging, we can switch to the _debug_ level, which will log the complete JSON responses.
|
153
239
|
#
|
154
|
-
# That's very convenient if
|
240
|
+
# That's very convenient if we want to post a recreation of some problem or solution
|
241
|
+
# to the mailing list, IRC channel, etc.
|
155
242
|
#
|
156
243
|
logger 'elasticsearch.log', :level => 'debug'
|
157
244
|
|
158
|
-
# Note that
|
245
|
+
# Note that we can pass any [`IO`](http://www.ruby-doc.org/core/classes/IO.html)-compatible Ruby object as a logging device.
|
159
246
|
#
|
160
247
|
logger STDERR
|
161
248
|
end
|
162
249
|
|
163
|
-
|
250
|
+
### Configuration
|
164
251
|
|
165
|
-
#
|
166
|
-
# we may want to define our queries more expressively.
|
252
|
+
# As we have just seen with logging, we can configure various parts of _Slingshot_.
|
167
253
|
#
|
168
|
-
|
254
|
+
Slingshot.configure do
|
255
|
+
|
256
|
+
# First of all, we can configure the URL for _ElasticSearch_.
|
257
|
+
#
|
258
|
+
url "http://search.example.com"
|
259
|
+
|
260
|
+
# Second, we may want to wrap the result items in our own class.
|
261
|
+
#
|
262
|
+
class MySpecialWrapper; end
|
263
|
+
wrapper MySpecialWrapper
|
264
|
+
|
265
|
+
# Finally, we can reset one or all configuration settings to their defaults.
|
266
|
+
#
|
267
|
+
reset
|
169
268
|
|
170
|
-
|
171
|
-
|
172
|
-
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
### Complex Searching
|
273
|
+
|
274
|
+
#### Other Types of Queries
|
275
|
+
|
276
|
+
# Query strings are convenient for simple searches, but we may want to define our queries more expressively,
|
277
|
+
# using the _ElasticSearch_ [Query DSL](http://www.elasticsearch.org/guide/reference/query-dsl/index.html).
|
278
|
+
#
|
173
279
|
s = Slingshot.search('articles') do
|
280
|
+
|
281
|
+
# Let's suppose we want to search for articles with specific _tags_, in our case “ruby” _or_ “python”.
|
282
|
+
#
|
174
283
|
query do
|
284
|
+
|
285
|
+
# That's a great excuse to use a [_terms_](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
|
286
|
+
# query.
|
287
|
+
#
|
175
288
|
terms :tags, ['ruby', 'python']
|
176
289
|
end
|
177
290
|
end
|
@@ -181,14 +294,18 @@ end
|
|
181
294
|
# * Two [tags: ruby, python]
|
182
295
|
# * One [tags: ruby]
|
183
296
|
# * Four [tags: ruby, php]
|
297
|
+
#
|
184
298
|
s.results.each do |document|
|
185
299
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
186
300
|
end
|
187
301
|
|
188
|
-
# What if we wanted to search for articles tagged both “ruby” _and_ “python
|
189
|
-
#
|
302
|
+
# What if we wanted to search for articles tagged both “ruby” _and_ “python”?
|
303
|
+
#
|
190
304
|
s = Slingshot.search('articles') do
|
191
305
|
query do
|
306
|
+
|
307
|
+
# That's a great excuse to specify `minimum_match` for the query.
|
308
|
+
#
|
192
309
|
terms :tags, ['ruby', 'python'], :minimum_match => 2
|
193
310
|
end
|
194
311
|
end
|
@@ -196,21 +313,22 @@ end
|
|
196
313
|
# The search, as expected, returns one article, tagged with _both_ “ruby” and “python”:
|
197
314
|
#
|
198
315
|
# * Two [tags: ruby, python]
|
316
|
+
#
|
199
317
|
s.results.each do |document|
|
200
318
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
201
319
|
end
|
202
320
|
|
203
321
|
# _ElasticSearch_ supports many types of [queries](http://www.elasticsearch.org/guide/reference/query-dsl/).
|
204
322
|
#
|
205
|
-
# Eventually, _Slingshot_ will support all of them.
|
206
|
-
# So far, only these are supported:
|
323
|
+
# Eventually, _Slingshot_ will support all of them. So far, only these are supported:
|
207
324
|
#
|
208
325
|
# * [string](http://www.elasticsearch.org/guide/reference/query-dsl/query-string-query.html)
|
209
326
|
# * [term](http://elasticsearch.org/guide/reference/query-dsl/term-query.html)
|
210
327
|
# * [terms](http://elasticsearch.org/guide/reference/query-dsl/terms-query.html)
|
211
328
|
# * [all](http://www.elasticsearch.org/guide/reference/query-dsl/match-all-query.html)
|
329
|
+
# * [ids](http://www.elasticsearch.org/guide/reference/query-dsl/ids-query.html)
|
212
330
|
|
213
|
-
|
331
|
+
#### Faceted Search
|
214
332
|
|
215
333
|
# _ElasticSearch_ makes it trivial to retrieve complex aggregated data from our index/database,
|
216
334
|
# so called [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html).
|
@@ -220,147 +338,191 @@ end
|
|
220
338
|
|
221
339
|
#
|
222
340
|
s = Slingshot.search 'articles' do
|
341
|
+
|
223
342
|
# We will search for articles whose title begins with letter “T”,
|
343
|
+
#
|
224
344
|
query { string 'title:T*' }
|
225
345
|
|
226
|
-
# and retrieve
|
346
|
+
# and retrieve the counts “bucketed” by `tags`.
|
347
|
+
#
|
227
348
|
facet 'tags' do
|
228
349
|
terms :tags
|
229
350
|
end
|
230
351
|
end
|
231
352
|
|
232
353
|
# As we see, our query has found two articles, and if you recall our articles from above,
|
233
|
-
# _Two_ is tagged with “ruby” and “python”, _Three_ is tagged with “java”.
|
234
|
-
#
|
354
|
+
# _Two_ is tagged with “ruby” and “python”, while _Three_ is tagged with “java”.
|
355
|
+
#
|
235
356
|
# Found 2 articles: Three, Two
|
236
|
-
#
|
237
|
-
#
|
357
|
+
#
|
358
|
+
# The counts shouldn't surprise us:
|
359
|
+
#
|
360
|
+
# Counts by tag:
|
361
|
+
# -------------------------
|
238
362
|
# ruby 1
|
239
363
|
# python 1
|
240
364
|
# java 1
|
365
|
+
#
|
241
366
|
puts "Found #{s.results.count} articles: #{s.results.map(&:title).join(', ')}"
|
242
|
-
puts "Counts
|
367
|
+
puts "Counts by tag:", "-"*25
|
243
368
|
s.results.facets['tags']['terms'].each do |f|
|
244
369
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
245
370
|
end
|
246
371
|
|
247
|
-
# These counts are based on the scope of our current query
|
372
|
+
# These counts are based on the scope of our current query.
|
248
373
|
# What if we wanted to display aggregated counts by `tags` across the whole database?
|
249
374
|
|
250
375
|
#
|
251
376
|
s = Slingshot.search 'articles' do
|
377
|
+
|
378
|
+
# Let's repeat the search for “T”...
|
379
|
+
#
|
252
380
|
query { string 'title:T*' }
|
253
381
|
|
254
382
|
facet 'global-tags' do
|
255
|
-
|
383
|
+
|
384
|
+
# ...but set the `global` scope for the facet in this case.
|
385
|
+
#
|
256
386
|
terms :tags, :global => true
|
257
387
|
end
|
258
388
|
|
259
|
-
#
|
260
|
-
# with
|
389
|
+
# We can even _combine_ facets scoped to the current query
|
390
|
+
# with globally scoped facets — we'll just use a different name.
|
391
|
+
#
|
261
392
|
facet 'current-tags' do
|
262
393
|
terms :tags
|
263
394
|
end
|
264
395
|
end
|
265
396
|
|
266
397
|
# Aggregated results for the current query are the same as previously:
|
398
|
+
#
|
267
399
|
# Current query facets:
|
268
400
|
# -------------------------
|
269
401
|
# ruby 1
|
270
402
|
# python 1
|
271
403
|
# java 1
|
404
|
+
#
|
272
405
|
puts "Current query facets:", "-"*25
|
273
406
|
s.results.facets['current-tags']['terms'].each do |f|
|
274
407
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
275
408
|
end
|
276
409
|
|
277
|
-
#
|
410
|
+
# On the other hand, aggregated results for the global scope include also
|
278
411
|
# tags for articles not matched by the query, such as “java” or “php”:
|
412
|
+
#
|
279
413
|
# Global facets:
|
280
414
|
# -------------------------
|
281
415
|
# ruby 3
|
282
416
|
# python 1
|
283
417
|
# php 1
|
284
418
|
# java 1
|
419
|
+
#
|
285
420
|
puts "Global facets:", "-"*25
|
286
421
|
s.results.facets['global-tags']['terms'].each do |f|
|
287
422
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
288
423
|
end
|
289
424
|
|
290
|
-
# _ElasticSearch_ supports many types of
|
425
|
+
# _ElasticSearch_ supports many advanced types of facets, such as those for computing statistics or geographical distance.
|
291
426
|
#
|
292
|
-
# Eventually, _Slingshot_ will support all of them.
|
293
|
-
# So far, only these are supported:
|
427
|
+
# Eventually, _Slingshot_ will support all of them. So far, only these are supported:
|
294
428
|
#
|
295
429
|
# * [terms](http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html)
|
296
430
|
# * [date](http://www.elasticsearch.org/guide/reference/api/search/facets/date-histogram-facet.html)
|
297
431
|
|
298
|
-
#
|
299
|
-
#
|
300
|
-
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
432
|
+
# We have seen that _ElasticSearch_ facets enable us to fetch complex aggregations from our data.
|
433
|
+
#
|
434
|
+
# They are frequently used for another feature, „faceted navigation“.
|
435
|
+
# We can be combine query and facets with
|
436
|
+
# [filters](http://elasticsearch.org/guide/reference/api/search/filter.html),
|
437
|
+
# so the returned documents are restricted by certain criteria — for example to a specific category —,
|
438
|
+
# but the aggregation calculations are still based on the original query.
|
304
439
|
|
305
440
|
|
306
|
-
|
441
|
+
#### Filtered Search
|
307
442
|
|
308
443
|
# So, let's make our search a bit more complex. Let's search for articles whose titles begin
|
309
444
|
# with letter “T”, again, but filter the results, so only the articles tagged “ruby”
|
310
445
|
# are returned.
|
446
|
+
#
|
311
447
|
s = Slingshot.search 'articles' do
|
312
448
|
|
313
|
-
# We use the same **query** as before.
|
449
|
+
# We will use just the same **query** as before.
|
450
|
+
#
|
314
451
|
query { string 'title:T*' }
|
315
452
|
|
316
|
-
#
|
453
|
+
# But we will add a _terms_ **filter** based on tags.
|
454
|
+
#
|
317
455
|
filter :terms, :tags => ['ruby']
|
318
456
|
|
319
457
|
# And, of course, our facet definition.
|
458
|
+
#
|
320
459
|
facet('tags') { terms :tags }
|
321
460
|
|
322
461
|
end
|
323
462
|
|
324
|
-
# We see that only the article _Two_ (tagged “ruby” and “python”)
|
463
|
+
# We see that only the article _Two_ (tagged “ruby” and “python”) is returned,
|
325
464
|
# _not_ the article _Three_ (tagged “java”):
|
326
465
|
#
|
327
466
|
# * Two [tags: ruby, python]
|
467
|
+
#
|
328
468
|
s.results.each do |document|
|
329
469
|
puts "* #{ document.title } [tags: #{document.tags.join(', ')}]"
|
330
470
|
end
|
331
471
|
|
332
|
-
#
|
472
|
+
# The _count_ for article _Three_'s tags, “java”, on the other hand, _is_ in fact included:
|
333
473
|
#
|
334
|
-
# Counts
|
474
|
+
# Counts by tag:
|
335
475
|
# -------------------------
|
336
476
|
# ruby 1
|
337
477
|
# python 1
|
338
478
|
# java 1
|
339
|
-
|
479
|
+
#
|
480
|
+
puts "Counts by tag:", "-"*25
|
340
481
|
s.results.facets['tags']['terms'].each do |f|
|
341
482
|
puts "#{f['term'].ljust(10)} #{f['count']}"
|
342
483
|
end
|
343
484
|
|
485
|
+
#### Sorting
|
344
486
|
|
345
|
-
|
487
|
+
# By default, the results are sorted according to their relevancy.
|
488
|
+
#
|
489
|
+
s = Slingshot.search('articles') { query { string 'tags:ruby' } }
|
346
490
|
|
347
|
-
|
348
|
-
#
|
491
|
+
s.results.each do |document|
|
492
|
+
puts "* #{ document.title } " +
|
493
|
+
"[tags: #{document.tags.join(', ')}; " +
|
494
|
+
|
495
|
+
# The score is available as the `_score` property.
|
496
|
+
#
|
497
|
+
"score: #{document._score}]"
|
498
|
+
end
|
499
|
+
|
500
|
+
# The results:
|
501
|
+
#
|
502
|
+
# * One [tags: ruby; score: 0.30685282]
|
503
|
+
# * Four [tags: ruby, php; score: 0.19178301]
|
504
|
+
# * Two [tags: ruby, python; score: 0.19178301]
|
349
505
|
|
350
506
|
# But, what if we want to sort the results based on some other criteria,
|
351
507
|
# such as published date or product price? We can do that.
|
508
|
+
#
|
352
509
|
s = Slingshot.search 'articles' do
|
353
|
-
|
510
|
+
|
511
|
+
# We will search for articles tagged “ruby”, again, ...
|
512
|
+
#
|
354
513
|
query { string 'tags:ruby' }
|
355
514
|
|
356
|
-
#
|
515
|
+
# ... but will sort them by their `title`, in descending order.
|
516
|
+
#
|
357
517
|
sort { title 'desc' }
|
358
518
|
end
|
359
519
|
|
360
520
|
# The results:
|
521
|
+
#
|
361
522
|
# * Two
|
362
523
|
# * One
|
363
524
|
# * Four
|
525
|
+
#
|
364
526
|
s.results.each do |document|
|
365
527
|
puts "* #{ document.title }"
|
366
528
|
end
|
@@ -368,13 +530,19 @@ end
|
|
368
530
|
# Of course, it's possible to combine more fields in the sorting definition.
|
369
531
|
|
370
532
|
s = Slingshot.search 'articles' do
|
371
|
-
|
533
|
+
|
534
|
+
# We will just get all articles in this case.
|
535
|
+
#
|
372
536
|
query { all }
|
373
537
|
|
374
538
|
sort do
|
375
|
-
|
539
|
+
|
540
|
+
# We will sort the results by their `published_on` property in _ascending_ order (the default),
|
541
|
+
#
|
376
542
|
published_on
|
377
|
-
|
543
|
+
|
544
|
+
# and by their `title` property, in _descending_ order.
|
545
|
+
#
|
378
546
|
title 'desc'
|
379
547
|
end
|
380
548
|
end
|
@@ -384,32 +552,33 @@ end
|
|
384
552
|
# * Two (Published on: 2011-01-02)
|
385
553
|
# * Three (Published on: 2011-01-02)
|
386
554
|
# * Four (Published on: 2011-01-03)
|
555
|
+
#
|
387
556
|
s.results.each do |document|
|
388
557
|
puts "* #{ document.title.ljust(10) } (Published on: #{ document.published_on })"
|
389
558
|
end
|
390
559
|
|
391
|
-
|
560
|
+
#### Highlighting
|
392
561
|
|
393
|
-
# Often,
|
394
|
-
# displayed results.
|
562
|
+
# Often, we want to highlight the snippets matching our query in the displayed results.
|
395
563
|
# _ElasticSearch_ provides rich
|
396
564
|
# [highlighting](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html)
|
397
|
-
# features, and
|
398
|
-
#
|
399
|
-
# Let's suppose that we want to highlight terms of our query.
|
565
|
+
# features, and _Slingshot_ makes them trivial to use.
|
400
566
|
#
|
401
567
|
s = Slingshot.search 'articles' do
|
568
|
+
|
402
569
|
# Let's search for documents containing word “Two” in their titles,
|
403
570
|
query { string 'title:Two' }
|
404
571
|
|
405
572
|
# and instruct _ElasticSearch_ to highlight relevant snippets.
|
573
|
+
#
|
406
574
|
highlight :title
|
407
575
|
end
|
408
576
|
|
409
577
|
# The results:
|
410
|
-
# Title: Two
|
578
|
+
# Title: Two; Highlighted: <em>Two</em>
|
579
|
+
#
|
411
580
|
s.results.each do |document|
|
412
|
-
puts "Title: #{ document.title }
|
581
|
+
puts "Title: #{ document.title }; Highlighted: #{document.highlight.title}"
|
413
582
|
end
|
414
583
|
|
415
584
|
# We can configure many options for highlighting, such as:
|
@@ -417,31 +586,32 @@ end
|
|
417
586
|
s = Slingshot.search 'articles' do
|
418
587
|
query { string 'title:Two' }
|
419
588
|
|
420
|
-
# •
|
589
|
+
# • specify the fields to highlight
|
590
|
+
#
|
421
591
|
highlight :title, :body
|
422
592
|
|
423
|
-
# •
|
593
|
+
# • specify their individual options
|
594
|
+
#
|
424
595
|
highlight :title, :body => { :number_of_fragments => 0 }
|
425
596
|
|
426
|
-
# • or
|
597
|
+
# • or specify global highlighting options, such as the wrapper tag
|
598
|
+
#
|
427
599
|
highlight :title, :body, :options => { :tag => '<strong class="highlight">' }
|
428
600
|
end
|
429
601
|
|
430
602
|
|
431
|
-
|
432
|
-
#### What's next?
|
603
|
+
### ActiveModel Integration
|
433
604
|
|
434
605
|
# As you can see, [_Slingshot_](https://github.com/karmi/slingshot) supports the
|
435
606
|
# main features of _ElasticSearch_ in Ruby.
|
436
607
|
#
|
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.
|
608
|
+
# It allows you to create and delete indices, add documents, search them, retrieve the facets, highlight the results,
|
609
|
+
# and comes with a usable logging facility.
|
439
610
|
#
|
440
611
|
# Of course, the holy grail of any search library is easy, painless integration with your Ruby classes, and,
|
441
612
|
# most importantly, with ActiveRecord/ActiveModel classes.
|
442
613
|
#
|
443
|
-
#
|
444
|
-
#
|
445
|
-
# [`activemodel`](https://github.com/karmi/slingshot/blob/activemodel/test/integration/active_model_searchable_test.rb) branch.
|
614
|
+
# Please, check out the [README](https://github.com/karmi/slingshot/tree/master#readme) file for instructions
|
615
|
+
# how to include _Slingshot_-based search in your models..
|
446
616
|
#
|
447
617
|
# Send any feedback via Github issues, or ask questions in the [#elasticsearch](irc://irc.freenode.net/#elasticsearch) IRC channel.
|