1
1
# Elasticsearch::Persistence
2
2
3
- Persistence layer for Ruby domain objects in Elasticsearch, using the Repository and ActiveRecord patterns .
3
+ Persistence layer for Ruby domain objects in Elasticsearch, using the Repository pattern .
4
4
5
5
## Compatibility
6
6
@@ -14,6 +14,7 @@ is compatible with the Elasticsearch `master` branch, therefore, with the next m
14
14
| 0.1 | → | 1.x |
15
15
| 2.x | → | 2.x |
16
16
| 5.x | → | 5.x |
17
+ | 6.x | → | 6.x |
17
18
| master | → | master |
18
19
19
20
## Installation
@@ -24,7 +25,7 @@ Install the package from [Rubygems](https://rubygems.org):
24
25
25
26
To use an unreleased version, either add it to your ` Gemfile ` for [ Bundler] ( http://bundler.io ) :
26
27
27
- gem 'elasticsearch-persistence', git: 'git://github.com/elastic/elasticsearch-rails.git', branch: '5 .x'
28
+ gem 'elasticsearch-persistence', git: 'git://github.com/elastic/elasticsearch-rails.git', branch: '6 .x'
28
29
29
30
or install it from a source code checkout:
30
31
@@ -35,9 +36,7 @@ or install it from a source code checkout:
35
36
36
37
## Usage
37
38
38
- The library provides two different patterns for adding persistence to your Ruby objects:
39
-
40
- * [ Repository Pattern] ( #the-repository-pattern )
39
+ The library provides the Repository pattern for adding persistence to your Ruby objects.
41
40
42
41
### The Repository Pattern
43
42
@@ -67,7 +66,8 @@ Let's create a default, "dumb" repository, as a first step:
67
66
68
67
``` ruby
69
68
require ' elasticsearch/persistence'
70
- repository = Elasticsearch ::Persistence ::Repository .new
69
+ class MyRepository ; include Elasticsearch ::Persistence ::Repository ; end
70
+ repository = MyRepository .new
71
71
```
72
72
73
73
We can save a ` Note ` instance into the repository...
@@ -120,32 +120,17 @@ The repository module provides a number of features and facilities to configure
120
120
* Providing access to the Elasticsearch response for search results (aggregations, total, ...)
121
121
* Defining the methods for serialization and deserialization
122
122
123
- You can use the default repository class, or include the module in your own. Let's review it in detail.
123
+ There are two mixins you can include in your Repository class. The first ` Elasticsearch::Persistence::Repository ` ,
124
+ provides the basic methods and settings you'll need. The second, ` Elasticsearch::Persistence::Repository::DSL ` adds
125
+ some additional class methods that allow you to set options that instances of the class will share.
124
126
125
- #### The Default Class
127
+ #### Basic Repository mixin
126
128
127
- For simple cases, you can use the default, bundled repository class, and configure/customize it :
129
+ For simple cases, you can just include the Elasticsearch::Persistence::Repository mixin to your class :
128
130
129
131
``` ruby
130
- repository = Elasticsearch ::Persistence ::Repository .new do
131
- # Configure the Elasticsearch client
132
- client Elasticsearch ::Client .new url: ENV [' ELASTICSEARCH_URL' ], log: true
133
-
134
- # Set a custom index name
135
- index :my_notes
136
-
137
- # Set a custom document type
138
- type :my_note
139
-
140
- # Specify the class to initialize when deserializing documents
141
- klass Note
142
-
143
- # Configure the settings and mappings for the Elasticsearch index
144
- settings number_of_shards: 1 do
145
- mapping do
146
- indexes :text , analyzer: ' snowball'
147
- end
148
- end
132
+ class MyRepository
133
+ include Elasticsearch ::Persistence ::Repository
149
134
150
135
# Customize the serialization logic
151
136
def serialize (document )
@@ -154,10 +139,18 @@ repository = Elasticsearch::Persistence::Repository.new do
154
139
155
140
# Customize the de-serialization logic
156
141
def deserialize (document )
157
- puts " # ***** CUSTOM DESERIALIZE LOGIC KICKING IN ... *****"
142
+ puts " # ***** CUSTOM DESERIALIZE LOGIC... *****"
158
143
super
159
144
end
160
145
end
146
+
147
+ client = Elasticsearch ::Client .new (url: ENV [' ELASTICSEARCH_URL' ], log: true )
148
+ repository = MyRepository .new (client: client, index_name: :my_notes , type: :my_note , klass: Note )
149
+ repository.settings number_of_shards: 1 do
150
+ mapping do
151
+ indexes :text , analyzer: ' snowball'
152
+ end
153
+ end
161
154
```
162
155
163
156
The custom Elasticsearch client will be used now, with a custom index and type names,
@@ -188,28 +181,27 @@ repository.find(1)
188
181
< Note: 0x007f9bd782b7a0 @attributes = {... " my_special_key" =>" my_special_stuff" }>
189
182
```
190
183
191
- #### A Custom Class
184
+ #### The DSL mixin
192
185
193
- In most cases, though, you'll want to use a custom class for the repository, so let's do that:
186
+ In some cases, you'll want to set some of the repository configurations at the class level. This makes
187
+ most sense when the instances of the repository will use that same configuration:
194
188
195
189
``` ruby
196
190
require ' base64'
197
191
198
192
class NoteRepository
199
193
include Elasticsearch ::Persistence ::Repository
194
+ include Elasticsearch ::Persistence ::Repository ::DSL
200
195
201
- def initialize (options = {})
202
- index options[:index ] || ' notes'
203
- client Elasticsearch ::Client .new url: options[:url ], log: options[:log ]
204
- end
205
-
196
+ index_name ' notes'
197
+ document_type ' note'
206
198
klass Note
207
199
208
200
settings number_of_shards: 1 do
209
201
mapping do
210
202
indexes :text , analyzer: ' snowball'
211
203
# Do not index images
212
- indexes :image , index: ' no '
204
+ indexes :image , index: false
213
205
end
214
206
end
215
207
@@ -231,23 +223,26 @@ class NoteRepository
231
223
end
232
224
```
233
225
234
- Include the ` Elasticsearch::Persistence::Repository ` module to add the repository methods into the class .
226
+ You can create an instance of this custom class and get each of the configurations .
235
227
236
- You can customize the repository in the familiar way, by calling the DSL-like methods.
228
+ ``` ruby
229
+ client = Elasticsearch ::Client .new (url: ' http://localhost:9200' , log: true )
230
+ repository = NoteRepository .new (client: client)
231
+ repository.index_name
232
+ # => 'notes'
237
233
238
- You can implement a custom initializer for your repository, add complex logic in its
239
- class and instance methods -- in general, have all the freedom of a standard Ruby class.
234
+ ```
235
+
236
+ You can also override the default configuration with options passed to the initialize method:
240
237
241
238
``` ruby
242
- repository = NoteRepository .new url: ' http://localhost:9200' , log: true
239
+ client = Elasticsearch ::Client .new (url: ' http://localhost:9250' , log: true )
240
+ client.transport.logger.formatter = proc { |s , d , p , m | " \e [2m# #{ m } \n\e [0m" }
241
+ repository = NoteRepository .new (client: client, index_name: ' notes_development' )
243
242
244
- # Configure the repository instance
245
- repository.index = ' notes_development'
246
- repository.client.transport.logger.formatter = proc { |s , d , p , m | " \e [2m# #{ m } \n\e [0m" }
243
+ repository.create_index!(force: true )
247
244
248
- repository.create_index! force: true
249
-
250
- note = Note .new ' id' => 1 , ' text' => ' Document with image' , ' image' => ' ... BINARY DATA ...'
245
+ note = Note .new (' id' => 1 , ' text' => ' Document with image' , ' image' => ' ... BINARY DATA ...' )
251
246
252
247
repository.save(note)
253
248
# PUT http://localhost:9200/notes_development/note/1
@@ -258,47 +253,110 @@ puts repository.find(1).attributes['image']
258
253
# => ... BINARY DATA ...
259
254
```
260
255
261
- #### Methods Provided by the Repository
256
+ #### Functionality Provided by the Repository mixin
257
+
258
+ Each of the following configurations can be set for a repository instance.
259
+ If you have included the ` Elasticsearch::Persistence::Repository::DSL ` mixin, then you can use the class-level DSL
260
+ methods to set each configuration. You can override the configuration for any instance by passing options to the
261
+ ` #initialize ` method.
262
+ If you don't use the DSL mixin, you can set also the instance configuration with options passed the ` #initialize ` method.
262
263
263
264
##### Client
264
265
265
- The repository uses the standard Elasticsearch [ client] ( https://github.com/elastic/elasticsearch-ruby#usage ) ,
266
- which is accessible with the ` client ` getter and setter methods:
266
+ The repository uses the standard Elasticsearch [ client] ( https://github.com/elastic/elasticsearch-ruby#usage ) .
267
267
268
268
``` ruby
269
- repository.client = Elasticsearch ::Client .new url: ' http://search.server.org'
269
+ client = Elasticsearch ::Client .new (url: ' http://search.server.org' )
270
+ repository = NoteRepository .new (client: client)
270
271
repository.client.transport.logger = Logger .new (STDERR )
271
272
```
272
273
274
+ or with the DSL mixin:
275
+
276
+ ``` ruby
277
+ class NoteRepository
278
+ include Elasticsearch ::Persistence ::Repository
279
+ include Elasticsearch ::Persistence ::Repository ::DSL
280
+
281
+ client Elasticsearch ::Client .new url: ' http://search.server.org'
282
+ end
283
+
284
+ repository = NoteRepository .new
285
+
286
+ ```
287
+
273
288
##### Naming
274
289
275
- The ` index ` method specifies the Elasticsearch index to use for storage, lookup and search
276
- (when not set, the value is inferred from the repository class name):
290
+ The ` index_name ` method specifies the Elasticsearch index to use for storage, lookup and search. The default index name
291
+ is 'repository'.
292
+
293
+ ``` ruby
294
+ repository = NoteRepository .new (index_name: ' notes_development' )
295
+ ```
296
+
297
+ or with the DSL mixin:
298
+
299
+ ``` ruby
300
+ class NoteRepository
301
+ include Elasticsearch ::Persistence ::Repository
302
+ include Elasticsearch ::Persistence ::Repository ::DSL
303
+
304
+ index_name ' notes_development'
305
+ end
306
+
307
+ repository = NoteRepository .new
308
+
309
+ ```
310
+
311
+ The ` type ` method specifies the Elasticsearch document type to use for storage, lookup and search. The default value is
312
+ '_ doc'. Keep in mind that future versions of Elasticsearch will not allow you to set this yourself and will use the type,
313
+ '_ doc'.
277
314
278
315
``` ruby
279
- repository.index = ' notes_development '
316
+ repository = NoteRepository . new ( document_type: ' note ' )
280
317
```
281
318
282
- The ` type ` method specifies the Elasticsearch document type to use for storage, lookup and search
283
- (when not set, the value is inferred from the document class name, or ` _all ` is used):
319
+ or with the DSL mixin:
284
320
285
321
``` ruby
286
- repository.type = ' my_note'
322
+ class NoteRepository
323
+ include Elasticsearch ::Persistence ::Repository
324
+ include Elasticsearch ::Persistence ::Repository ::DSL
325
+
326
+ document_type ' note'
327
+ end
328
+
329
+ repository = NoteRepository .new
330
+
287
331
```
288
332
289
333
The ` klass ` method specifies the Ruby class name to use when initializing objects from
290
- documents retrieved from the repository (when not set, the value is inferred from the
291
- document ` _type ` as fetched from Elasticsearch):
334
+ documents retrieved from the repository. If this value is not set, a Hash representation of the document will be
335
+ returned instead.
336
+
337
+ ``` ruby
338
+ repository = NoteRepository .new (klass: Note )
339
+ ```
340
+
341
+ or with the DSL mixin:
292
342
293
343
``` ruby
294
- repository.klass = MyNote
344
+ class NoteRepository
345
+ include Elasticsearch ::Persistence ::Repository
346
+ include Elasticsearch ::Persistence ::Repository ::DSL
347
+
348
+ klass Note
349
+ end
350
+
351
+ repository = NoteRepository .new
352
+
295
353
```
296
354
297
355
##### Index Configuration
298
356
299
357
The ` settings ` and ` mappings ` methods, provided by the
300
358
[ ` elasticsearch-model ` ] ( http://rubydoc.info/gems/elasticsearch-model/Elasticsearch/Model/Indexing/ClassMethods )
301
- gem, allow to configure the index properties:
359
+ gem, allow you to configure the index properties:
302
360
303
361
``` ruby
304
362
repository.settings number_of_shards: 1
@@ -310,7 +368,39 @@ repository.mappings.to_hash
310
368
# => { :note => {:properties=> ... }}
311
369
```
312
370
371
+ or with the DSL mixin:
372
+
373
+ ``` ruby
374
+ class NoteRepository
375
+ include Elasticsearch ::Persistence ::Repository
376
+ include Elasticsearch ::Persistence ::Repository ::DSL
377
+
378
+ mappings { indexes :title , analyzer: ' snowball' }
379
+ settings number_of_shards: 1
380
+ end
381
+
382
+ repository = NoteRepository .new
383
+
384
+ ```
385
+
386
+ You can also use the ` #create ` method defined on the repository class to create and set the mappings and settings
387
+ on an instance with a block in one call:
388
+
389
+ ``` ruby
390
+ repository = NoteRepository .create(index_name: ' notes_development' ) do
391
+ settings number_of_shards: 1 , number_of_replicas: 0 do
392
+ mapping dynamic: ' strict' do
393
+ indexes :foo do
394
+ indexes :bar
395
+ end
396
+ indexes :baz
397
+ end
398
+ end
399
+ end
400
+ ```
401
+
313
402
The convenience methods ` create_index! ` , ` delete_index! ` and ` refresh_index! ` allow you to manage the index lifecycle.
403
+ These methods can only be called on repository instances and are not implemented at the class level.
314
404
315
405
##### Serialization
316
406
@@ -319,9 +409,12 @@ to the storage, and the initialization procedure when loading it from the storag
319
409
320
410
``` ruby
321
411
class NoteRepository
412
+ include Elasticsearch ::Persistence ::Repository
413
+
322
414
def serialize (document )
323
415
Hash [document.to_hash.map() { |k ,v | v.upcase! if k == :title ; [k,v] }]
324
416
end
417
+
325
418
def deserialize (document )
326
419
MyNote .new ActiveSupport ::HashWithIndifferentAccess .new (document[' _source' ]).deep_symbolize_keys
327
420
end
426
519
results.total
427
520
# => 2
428
521
429
- # Access the raw response as a Hashie::Mash instance
522
+ # Access the raw response as a Hashie::Mash instance.
523
+ # Note that a Hashie::Mash will only be created if the 'response' method is called on the results.
430
524
results.response._shards .failed
431
525
# => 0
526
+
527
+ # Access the raw response
528
+ results.raw_response
529
+ # => 0
530
+
432
531
```
433
532
434
533
#### Example Application
0 commit comments