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/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.
|