elasticsearch-persistence 5.1.0 → 6.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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