elasticsearch-persistence 5.1.0 → 6.0.0.pre

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/Gemfile +9 -0
  4. data/README.md +164 -323
  5. data/Rakefile +8 -8
  6. data/elasticsearch-persistence.gemspec +4 -5
  7. data/lib/elasticsearch/persistence.rb +2 -110
  8. data/lib/elasticsearch/persistence/repository.rb +212 -53
  9. data/lib/elasticsearch/persistence/repository/dsl.rb +94 -0
  10. data/lib/elasticsearch/persistence/repository/find.rb +27 -10
  11. data/lib/elasticsearch/persistence/repository/response/results.rb +17 -5
  12. data/lib/elasticsearch/persistence/repository/search.rb +15 -4
  13. data/lib/elasticsearch/persistence/repository/serialize.rb +65 -7
  14. data/lib/elasticsearch/persistence/repository/store.rb +38 -44
  15. data/lib/elasticsearch/persistence/version.rb +1 -1
  16. data/spec/repository/find_spec.rb +179 -0
  17. data/spec/repository/response/results_spec.rb +105 -0
  18. data/spec/repository/search_spec.rb +181 -0
  19. data/spec/repository/serialize_spec.rb +53 -0
  20. data/spec/repository/store_spec.rb +327 -0
  21. data/spec/repository_spec.rb +716 -0
  22. data/spec/spec_helper.rb +28 -0
  23. metadata +25 -80
  24. data/lib/elasticsearch/persistence/client.rb +0 -51
  25. data/lib/elasticsearch/persistence/model.rb +0 -153
  26. data/lib/elasticsearch/persistence/model/base.rb +0 -87
  27. data/lib/elasticsearch/persistence/model/errors.rb +0 -8
  28. data/lib/elasticsearch/persistence/model/find.rb +0 -180
  29. data/lib/elasticsearch/persistence/model/rails.rb +0 -47
  30. data/lib/elasticsearch/persistence/model/store.rb +0 -254
  31. data/lib/elasticsearch/persistence/model/utils.rb +0 -0
  32. data/lib/elasticsearch/persistence/repository/class.rb +0 -71
  33. data/lib/elasticsearch/persistence/repository/naming.rb +0 -115
  34. data/lib/rails/generators/elasticsearch/model/model_generator.rb +0 -21
  35. data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +0 -9
  36. data/lib/rails/generators/elasticsearch_generator.rb +0 -2
  37. data/test/integration/model/model_basic_test.rb +0 -238
  38. data/test/integration/repository/custom_class_test.rb +0 -85
  39. data/test/integration/repository/customized_class_test.rb +0 -82
  40. data/test/integration/repository/default_class_test.rb +0 -116
  41. data/test/integration/repository/virtus_model_test.rb +0 -118
  42. data/test/test_helper.rb +0 -55
  43. data/test/unit/model_base_test.rb +0 -72
  44. data/test/unit/model_find_test.rb +0 -153
  45. data/test/unit/model_gateway_test.rb +0 -101
  46. data/test/unit/model_rails_test.rb +0 -112
  47. data/test/unit/model_store_test.rb +0 -576
  48. data/test/unit/persistence_test.rb +0 -32
  49. data/test/unit/repository_class_test.rb +0 -51
  50. data/test/unit/repository_client_test.rb +0 -32
  51. data/test/unit/repository_find_test.rb +0 -388
  52. data/test/unit/repository_indexing_test.rb +0 -37
  53. data/test/unit/repository_module_test.rb +0 -146
  54. data/test/unit/repository_naming_test.rb +0 -146
  55. data/test/unit/repository_response_results_test.rb +0 -98
  56. data/test/unit/repository_search_test.rb +0 -117
  57. data/test/unit/repository_serialize_test.rb +0 -57
  58. data/test/unit/repository_store_test.rb +0 -303
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dad7450ff0696099a095419ee12da5d4ea875646ad591545f95bae8fc2408ce2
4
- data.tar.gz: 339a759ce92884321d4adbb7fa63f4ee3a155958d97e5ea3ee8c55b60cd21e8a
3
+ metadata.gz: dd3b68c6704df1a51940545080b631c127701074727d9879c5eaf9bf29513d63
4
+ data.tar.gz: 966be9deff09a99f149b8c7206b383737be278dd96f368fbda45d30b59fc0439
5
5
  SHA512:
6
- metadata.gz: 8a1477b512b7cdbb812866c7b582e1417871713689c165d876b49df44e7d8095d08d7b5993110b23071d784f9c9c19db6d58a771bd0b1627d1f9c75bd75586ed
7
- data.tar.gz: 7eb56ed82336e9648a0fe3a3288c854621811f13b07ee6402138086c4dfe28fd78b4f283b4a0ea1a140678d8f625794c6aad3c386cfa346eec017b92ee007a06
6
+ metadata.gz: 0ffc1d1c058660ee3060ffaac44449e4a19b0ee34b2f12f1818aa22753c4a670c7c3dfe9975f44bc41cff8913cb6604bcc6e8e61633e8263ef1888c4a024382b
7
+ data.tar.gz: 2e589e4a386389e4c41a95c92738d79def0ff49ec18ee2add9ba07e813ae4d2704156dbbe0a9dafeff3cc9767fda38fbfd570ad3c6a7d0be7973f92ab33e35b4
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --tty
2
+ --colour
data/Gemfile CHANGED
@@ -2,3 +2,12 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in elasticsearch-persistence.gemspec
4
4
  gemspec
5
+
6
+ gem 'elasticsearch-model', :path => File.expand_path("../../elasticsearch-model", __FILE__), :require => false
7
+
8
+ gem 'virtus'
9
+
10
+ group :development, :testing do
11
+ gem 'rspec'
12
+ gem 'pry-nav'
13
+ end
data/README.md CHANGED
@@ -1,6 +1,6 @@
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,10 +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)
41
- * [ActiveRecord Pattern](#the-activerecord-pattern)
39
+ The library provides the Repository pattern for adding persistence to your Ruby objects.
42
40
 
43
41
  ### The Repository Pattern
44
42
 
@@ -68,7 +66,8 @@ Let's create a default, "dumb" repository, as a first step:
68
66
 
69
67
  ```ruby
70
68
  require 'elasticsearch/persistence'
71
- repository = Elasticsearch::Persistence::Repository.new
69
+ class MyRepository; include Elasticsearch::Persistence::Repository; end
70
+ repository = MyRepository.new
72
71
  ```
73
72
 
74
73
  We can save a `Note` instance into the repository...
@@ -121,32 +120,17 @@ The repository module provides a number of features and facilities to configure
121
120
  * Providing access to the Elasticsearch response for search results (aggregations, total, ...)
122
121
  * Defining the methods for serialization and deserialization
123
122
 
124
- 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.
125
126
 
126
- #### The Default Class
127
+ #### Basic Repository mixin
127
128
 
128
- 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:
129
130
 
130
131
  ```ruby
131
- repository = Elasticsearch::Persistence::Repository.new do
132
- # Configure the Elasticsearch client
133
- client Elasticsearch::Client.new url: ENV['ELASTICSEARCH_URL'], log: true
134
-
135
- # Set a custom index name
136
- index :my_notes
137
-
138
- # Set a custom document type
139
- type :my_note
140
-
141
- # Specify the class to initialize when deserializing documents
142
- klass Note
143
-
144
- # Configure the settings and mappings for the Elasticsearch index
145
- settings number_of_shards: 1 do
146
- mapping do
147
- indexes :text, analyzer: 'snowball'
148
- end
149
- end
132
+ class MyRepository
133
+ include Elasticsearch::Persistence::Repository
150
134
 
151
135
  # Customize the serialization logic
152
136
  def serialize(document)
@@ -155,10 +139,18 @@ repository = Elasticsearch::Persistence::Repository.new do
155
139
 
156
140
  # Customize the de-serialization logic
157
141
  def deserialize(document)
158
- puts "# ***** CUSTOM DESERIALIZE LOGIC KICKING IN... *****"
142
+ puts "# ***** CUSTOM DESERIALIZE LOGIC... *****"
159
143
  super
160
144
  end
161
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
162
154
  ```
163
155
 
164
156
  The custom Elasticsearch client will be used now, with a custom index and type names,
@@ -189,28 +181,27 @@ repository.find(1)
189
181
  <Note:0x007f9bd782b7a0 @attributes={... "my_special_key"=>"my_special_stuff"}>
190
182
  ```
191
183
 
192
- #### A Custom Class
184
+ #### The DSL mixin
193
185
 
194
- 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:
195
188
 
196
189
  ```ruby
197
190
  require 'base64'
198
191
 
199
192
  class NoteRepository
200
193
  include Elasticsearch::Persistence::Repository
194
+ include Elasticsearch::Persistence::Repository::DSL
201
195
 
202
- def initialize(options={})
203
- index options[:index] || 'notes'
204
- client Elasticsearch::Client.new url: options[:url], log: options[:log]
205
- end
206
-
196
+ index_name 'notes'
197
+ document_type 'note'
207
198
  klass Note
208
199
 
209
200
  settings number_of_shards: 1 do
210
201
  mapping do
211
202
  indexes :text, analyzer: 'snowball'
212
203
  # Do not index images
213
- indexes :image, index: 'no'
204
+ indexes :image, index: false
214
205
  end
215
206
  end
216
207
 
@@ -232,23 +223,26 @@ class NoteRepository
232
223
  end
233
224
  ```
234
225
 
235
- 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.
236
227
 
237
- 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'
238
233
 
239
- You can implement a custom initializer for your repository, add complex logic in its
240
- 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:
241
237
 
242
238
  ```ruby
243
- 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')
244
242
 
245
- # Configure the repository instance
246
- repository.index = 'notes_development'
247
- repository.client.transport.logger.formatter = proc { |s, d, p, m| "\e[2m# #{m}\n\e[0m" }
243
+ repository.create_index!(force: true)
248
244
 
249
- repository.create_index! force: true
250
-
251
- 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 ...')
252
246
 
253
247
  repository.save(note)
254
248
  # PUT http://localhost:9200/notes_development/note/1
@@ -259,47 +253,110 @@ puts repository.find(1).attributes['image']
259
253
  # => ... BINARY DATA ...
260
254
  ```
261
255
 
262
- #### 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.
263
263
 
264
264
  ##### Client
265
265
 
266
- The repository uses the standard Elasticsearch [client](https://github.com/elastic/elasticsearch-ruby#usage),
267
- 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).
268
267
 
269
268
  ```ruby
270
- 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)
271
271
  repository.client.transport.logger = Logger.new(STDERR)
272
272
  ```
273
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
+
274
288
  ##### Naming
275
289
 
276
- The `index` method specifies the Elasticsearch index to use for storage, lookup and search
277
- (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'.
278
314
 
279
315
  ```ruby
280
- repository.index = 'notes_development'
316
+ repository = NoteRepository.new(document_type: 'note')
281
317
  ```
282
318
 
283
- The `type` method specifies the Elasticsearch document type to use for storage, lookup and search
284
- (when not set, the value is inferred from the document class name, or `_all` is used):
319
+ or with the DSL mixin:
285
320
 
286
321
  ```ruby
287
- 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
+
288
331
  ```
289
332
 
290
333
  The `klass` method specifies the Ruby class name to use when initializing objects from
291
- documents retrieved from the repository (when not set, the value is inferred from the
292
- 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.
293
336
 
294
337
  ```ruby
295
- repository.klass = MyNote
338
+ repository = NoteRepository.new(klass: Note)
339
+ ```
340
+
341
+ or with the DSL mixin:
342
+
343
+ ```ruby
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
+
296
353
  ```
297
354
 
298
355
  ##### Index Configuration
299
356
 
300
357
  The `settings` and `mappings` methods, provided by the
301
358
  [`elasticsearch-model`](http://rubydoc.info/gems/elasticsearch-model/Elasticsearch/Model/Indexing/ClassMethods)
302
- gem, allow to configure the index properties:
359
+ gem, allow you to configure the index properties:
303
360
 
304
361
  ```ruby
305
362
  repository.settings number_of_shards: 1
@@ -311,7 +368,39 @@ repository.mappings.to_hash
311
368
  # => { :note => {:properties=> ... }}
312
369
  ```
313
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
+
314
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.
315
404
 
316
405
  ##### Serialization
317
406
 
@@ -320,9 +409,12 @@ to the storage, and the initialization procedure when loading it from the storag
320
409
 
321
410
  ```ruby
322
411
  class NoteRepository
412
+ include Elasticsearch::Persistence::Repository
413
+
323
414
  def serialize(document)
324
415
  Hash[document.to_hash.map() { |k,v| v.upcase! if k == :title; [k,v] }]
325
416
  end
417
+
326
418
  def deserialize(document)
327
419
  MyNote.new ActiveSupport::HashWithIndifferentAccess.new(document['_source']).deep_symbolize_keys
328
420
  end
@@ -427,9 +519,15 @@ end
427
519
  results.total
428
520
  # => 2
429
521
 
430
- # 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.
431
524
  results.response._shards.failed
432
525
  # => 0
526
+
527
+ # Access the raw response
528
+ results.raw_response
529
+ # => {...}
530
+
433
531
  ```
434
532
 
435
533
  #### Example Application
@@ -445,265 +543,8 @@ and demonstrates a rich set of features:
445
543
 
446
544
  ### The ActiveRecord Pattern
447
545
 
448
- Please note that this pattern is deprecated and will be removed in version 6.0.
449
- The [Repository Pattern](#the-repository-pattern) is recommended instead.
450
-
451
- The `Elasticsearch::Persistence::Model` module provides an implementation of the
452
- active record [pattern](http://www.martinfowler.com/eaaCatalog/activeRecord.html),
453
- with a familiar interface for using Elasticsearch as a persistence layer in
454
- Ruby on Rails applications.
455
-
456
- All the methods are documented with comprehensive examples in the source code,
457
- available also online at <http://rubydoc.info/gems/elasticsearch-persistence/Elasticsearch/Persistence/Model>.
458
-
459
- #### Installation/Usage
460
-
461
- To use the library in a Rails application, add it to your `Gemfile` with a `require` statement:
462
-
463
- ```ruby
464
- gem "elasticsearch-persistence", require: 'elasticsearch/persistence/model'
465
- ```
466
-
467
- To use the library without Bundler, install it, and require the file:
468
-
469
- ```bash
470
- gem install elasticsearch-persistence
471
- ```
472
-
473
- ```ruby
474
- # In your code
475
- require 'elasticsearch/persistence/model'
476
- ```
477
-
478
- #### Model Definition
479
-
480
- The integration is implemented by including the module in a Ruby class.
481
- The model attribute definition support is implemented with the
482
- [_Virtus_](https://github.com/solnic/virtus) Rubygem, and the
483
- naming, validation, etc. features with the
484
- [_ActiveModel_](https://github.com/rails/rails/tree/master/activemodel) Rubygem.
485
-
486
- ```ruby
487
- class Article
488
- include Elasticsearch::Persistence::Model
489
-
490
- # Define a plain `title` attribute
491
- #
492
- attribute :title, String
493
-
494
- # Define an `author` attribute, with multiple analyzers for this field
495
- #
496
- attribute :author, String, mapping: { fields: {
497
- author: { type: 'text'},
498
- raw: { type: 'keyword' }
499
- } }
500
-
501
-
502
- # Define a `views` attribute, with default value
503
- #
504
- attribute :views, Integer, default: 0, mapping: { type: 'integer' }
505
-
506
- # Validate the presence of the `title` attribute
507
- #
508
- validates :title, presence: true
509
-
510
- # Execute code after saving the model.
511
- #
512
- after_save { puts "Successfully saved: #{self}" }
513
- end
514
- ```
515
-
516
- Attribute validations work like for any other _ActiveModel_-compatible implementation:
517
-
518
- ```ruby
519
- article = Article.new # => #<Article { ... }>
520
-
521
- article.valid?
522
- # => false
523
-
524
- article.errors.to_a
525
- # => ["Title can't be blank"]
526
- ```
527
-
528
- #### Persistence
529
-
530
- We can create a new article in the database...
531
-
532
- ```ruby
533
- Article.create id: 1, title: 'Test', author: 'John'
534
- # PUT http://localhost:9200/articles/article/1 [status:201, request:0.015s, query:n/a]
535
- ```
536
-
537
- ... and find it:
538
-
539
- ```ruby
540
- article = Article.find(1)
541
- # => #<Article { ... }>
542
-
543
- article._index
544
- # => "articles"
545
-
546
- article.id
547
- # => "1"
548
-
549
- article.title
550
- # => "Test"
551
- ```
552
-
553
- To update the model, either update the attribute and save the model:
554
-
555
- ```ruby
556
- article.title = 'Updated'
557
-
558
- article.save
559
- # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>2, "created"=>false}
560
- ```
561
-
562
- ... or use the `update_attributes` method:
563
-
564
- ```ruby
565
- article.update_attributes title: 'Test', author: 'Mary'
566
- # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>3}
567
- ```
568
-
569
- The implementation supports the familiar interface for updating model timestamps:
570
-
571
- ```ruby
572
- article.touch
573
- # => => { ... "_version"=>4}
574
- ```
575
-
576
- ... and numeric attributes:
577
-
578
- ```ruby
579
- article.views
580
- # => 0
581
-
582
- article.increment :views
583
- article.views
584
- # => 1
585
- ```
586
-
587
- Any callbacks defined in the model will be triggered during the persistence operations:
588
-
589
- ```ruby
590
- article.save
591
- # Successfully saved: #<Article {...}>
592
- ```
593
-
594
- The model also supports familiar `find_in_batches` and `find_each` methods to efficiently
595
- retrieve big collections of model instances, using the Elasticsearch's _Scan API_:
596
-
597
- ```ruby
598
- Article.find_each(_source_include: 'title') { |a| puts "===> #{a.title.upcase}" }
599
- # GET http://localhost:9200/articles/article/_search?scroll=5m&size=20
600
- # GET http://localhost:9200/_search/scroll?scroll=5m&scroll_id=c2Nhb...
601
- # ===> TEST
602
- # GET http://localhost:9200/_search/scroll?scroll=5m&scroll_id=c2Nhb...
603
- # => "c2Nhb..."
604
- ```
605
-
606
- #### Search
607
-
608
- The model class provides a `search` method to retrieve model instances with a regular
609
- search definition, including highlighting, aggregations, etc:
610
-
611
- ```ruby
612
- results = Article.search query: { match: { title: 'test' } },
613
- aggregations: { authors: { terms: { field: 'author.raw' } } },
614
- highlight: { fields: { title: {} } }
615
-
616
- puts results.first.title
617
- # Test
618
-
619
- puts results.first.hit.highlight['title']
620
- # <em>Test</em>
621
-
622
- puts results.response.aggregations.authors.buckets.each { |b| puts "#{b['key']} : #{b['doc_count']}" }
623
- # John : 1
624
- ```
625
-
626
- #### The Elasticsearch Client
627
-
628
- The module will set up a [client](https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch),
629
- connected to `localhost:9200`, by default.
630
-
631
- To use a client with different configuration:
632
-
633
- ```ruby
634
- Elasticsearch::Persistence.client = Elasticsearch::Client.new log: true
635
- ```
636
-
637
- To set up a specific client for a specific model:
638
-
639
- ```ruby
640
- Article.gateway.client = Elasticsearch::Client.new host: 'api.server.org'
641
- ```
642
-
643
- You might want to do this during you application bootstrap process, e.g. in a Rails initializer.
644
-
645
- Please refer to the
646
- [`elasticsearch-transport`](https://github.com/elasticsearch/elasticsearch-ruby/tree/master/elasticsearch-transport)
647
- library documentation for all the configuration options, and to the
648
- [`elasticsearch-api`](http://rubydoc.info/gems/elasticsearch-api) library documentation
649
- for information about the Ruby client API.
650
-
651
- #### Accessing the Repository Gateway and the Client
652
-
653
- The integration with Elasticsearch is implemented by embedding the repository object in the model.
654
- You can access it through the `gateway` method:
655
-
656
- ```ruby
657
- Artist.gateway.client.info
658
- # GET http://localhost:9200/ [status:200, request:0.011s, query:n/a]
659
- # => {"status"=>200, "name"=>"Lightspeed", ...}
660
- ```
661
-
662
- #### Rails Compatibility
663
-
664
- The model instances are fully compatible with Rails' conventions and helpers:
665
-
666
- ```ruby
667
- url_for article
668
- # => "http://localhost:3000/articles/1"
669
-
670
- div_for article
671
- # => '<div class="article" id="article_1"></div>'
672
- ```
673
-
674
- ... as well as form values for dates and times:
675
-
676
- ```ruby
677
- article = Article.new "title" => "Date", "published(1i)"=>"2014", "published(2i)"=>"1", "published(3i)"=>"1"
678
-
679
- article.published.iso8601
680
- # => "2014-01-01"
681
- ```
682
-
683
- The library provides a Rails ORM generator to facilitate building the application scaffolding:
684
-
685
- ```bash
686
- rails generate scaffold Person name:String email:String birthday:Date --orm=elasticsearch
687
- ```
688
-
689
- #### Example application
690
-
691
- A fully working Ruby on Rails application can be generated with the following command:
692
-
693
- ```bash
694
- rails new music --force --skip --skip-bundle --skip-active-record --template https://raw.githubusercontent.com/elastic/elasticsearch-rails/master/elasticsearch-persistence/examples/music/template.rb
695
- ```
696
-
697
- The application demonstrates:
698
-
699
- * How to set up model attributes with custom mappings
700
- * How to define model relationships with Elasticsearch's parent/child
701
- * How to configure models to use a common index, and create the index with proper mappings
702
- * How to use Elasticsearch's completion suggester to drive auto-complete functionality
703
- * How to use Elasticsearch-persisted models in Rails' views and forms
704
- * How to write controller tests
705
-
706
- The source files for the application are available in the [`examples/music`](examples/music) folder.
546
+ The ActiveRecord pattern has been deprecated as of version 6.0.0 of this gem. Please use the
547
+ [Repository Pattern](#the-repository-pattern) instead.
707
548
 
708
549
  ## License
709
550