persistence.asciidoc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. == Persistence
  2. The `elasticsearch-persistence` http://rubygems.org/gems/elasticsearch-persistence[Rubygem]
  3. provides persistence layer for Ruby domain objects.
  4. It supports two design patterns for integrating with your objects: _repository_ and _active record_.
  5. === Repository
  6. The `Elasticsearch::Persistence::Repository` module provides an implementation of the repository pattern and allows to save, delete, find and search objects stored in Elasticsearch, as well as configure mappings and settings for the index.
  7. ==== Features At a Glance
  8. * Access to the Elasticsearch client
  9. * Setting the index name, document type, and object class for deserialization
  10. * Composing mappings and settings for the index
  11. * Creating, deleting or refreshing the index
  12. * Finding or searching for documents
  13. * Providing access both to domain objects and hits for search results
  14. * Providing access to the Elasticsearch response for search results (aggregations, total, ...)
  15. * Defining the methods for serialization and deserialization
  16. ==== Usage
  17. Let's have a simple plain old Ruby object (PORO):
  18. [source,ruby]
  19. ------------------------------------
  20. class Note
  21. attr_reader :attributes
  22. def initialize(attributes={})
  23. @attributes = attributes
  24. end
  25. def to_hash
  26. @attributes
  27. end
  28. end
  29. ------------------------------------
  30. Let's create a default, "dumb" repository, as a first step:
  31. [source,ruby]
  32. ------------------------------------
  33. require 'elasticsearch/persistence'
  34. repository = Elasticsearch::Persistence::Repository.new
  35. ------------------------------------
  36. We can save a `Note` instance into the repository, find it, search it, delete it:
  37. [source,ruby]
  38. ------------------------------------
  39. note = Note.new id: 1, text: 'Test'
  40. repository.save(note)
  41. # PUT http://localhost:9200/repository/note/1
  42. # > {"id":1,"text":"Test"}
  43. # < {"_index":"repository","_type":"note","_id":"1","_version":1,"created":true}
  44. n = repository.find(1)
  45. # GET http://localhost:9200/repository/_all/1
  46. # < {"_index":"repository","_type":"note","_id":"1","_version":2,"found":true, "_source" : {"id":1,"text":"Test"}}
  47. => <Note:0x007fcbfc0c4980 @attributes={"id"=>1, "text"=>"Test"}>
  48. repository.search(query: { match: { text: 'test' } }).first
  49. # GET http://localhost:9200/repository/_search
  50. # > {"query":{"match":{"text":"test"}}}
  51. # < {"took":2, ... "hits":{"total":1, ... "hits":[{ ... "_source" : {"id":1,"text":"Test"}}]}}
  52. => <Note:0x007fcbfc1c7b70 @attributes={"id"=>1, "text"=>"Test"}>
  53. repository.delete(note)
  54. # DELETE http://localhost:9200/repository/note/1
  55. # < {"found":true,"_index":"repository","_type":"note","_id":"1","_version":3}
  56. => {"found"=>true, "_index"=>"repository", "_type"=>"note", "_id"=>"1", "_version"=>2}
  57. ------------------------------------
  58. The repository module provides a number of features and facilities to configure and customize the behaviour,
  59. as well as support for extending your own, custom repository class.
  60. Please refer to the
  61. https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#the-repository-pattern[documentation]
  62. for more information.
  63. Also, check out the
  64. https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#example-application[example application] which demonstrates the usage patterns of the _repository_ approach to persistence.
  65. === Active Record
  66. The `Elasticsearch::Persistence::Model` module provides an implementation of the active record pattern, with
  67. a familiar interface for using Elasticsearch as a persistence layer in Ruby on Rails applications. The model
  68. is fully compatible with Rails' conventions and helpers, such as `url_for`.
  69. All the methods are documented with comprehensive examples in the source code, available also
  70. http://rubydoc.info/gems/elasticsearch-persistence/Elasticsearch/Persistence/Model[online].
  71. ==== Features At a Glance
  72. * Familiar interface for persistence methods from ActiveRecord
  73. * Common model features such as validations and callbacks
  74. * Methods for defining model attributes, including Elasticsearch mappings
  75. * Support for fetching data in bulk (`find_in_batches`, `find_each`)
  76. * Decorated search results for easy access to model instances and meta data (such as highlights or aggregations)
  77. * Easy access to the underlying gateway and client
  78. ==== Usage
  79. To use the library in a Rails application, add it to your Gemfile with a require statement:
  80. [source,ruby]
  81. ------------------------------------
  82. gem "elasticsearch-persistence", require: 'elasticsearch/persistence/model'
  83. ------------------------------------
  84. Include the module in a plain Ruby class, and set up the properties, mappings, etc:
  85. [source,ruby]
  86. ------------------------------------
  87. class Article
  88. include Elasticsearch::Persistence::Model
  89. # Define a plain `title` attribute
  90. #
  91. attribute :title, String
  92. # Define an `author` attribute, with multiple analyzers for this field
  93. #
  94. attribute :author, String, mapping: { fields: {
  95. author: { type: 'string'},
  96. raw: { type: 'string', analyzer: 'keyword' }
  97. } }
  98. # Define a `views` attribute, with default value
  99. #
  100. attribute :views, Integer, default: 0, mapping: { type: 'integer' }
  101. # Validate the presence of the `title` attribute
  102. #
  103. validates :title, presence: true
  104. # Execute code after saving the model.
  105. #
  106. after_save { puts "Successfully saved: #{self}" }
  107. end
  108. ------------------------------------
  109. The model attribute definition support is implemented with the https://github.com/solnic/virtus[_Virtus_] Rubygem,
  110. and the naming, validation, etc. features with the https://github.com/rails/rails/tree/master/activemodel[_ActiveModel_] Rubygem.
  111. Attribute validations work like for any other ActiveModel-compatible implementation:
  112. [source,ruby]
  113. ------------------------------------
  114. article = Article.new # => #<Article { ... }>
  115. article.valid?
  116. # => false
  117. article.errors.to_a
  118. # => ["Title can't be blank"]
  119. ------------------------------------
  120. We can create a new article in the database and find it:
  121. [source,ruby]
  122. ------------------------------------
  123. Article.create id: 1, title: 'Test', author: 'John'
  124. # PUT http://localhost:9200/articles/article/1 [status:201, request:0.015s, query:n/a]
  125. article = Article.find(1)
  126. # => #<Article { ... }>
  127. article._index
  128. # => "articles"
  129. article.id
  130. # => "1"
  131. article.title
  132. # => "Test"
  133. ------------------------------------
  134. To update the model, either update the attribute and save the model or use the `update_attributes` method:
  135. [source,ruby]
  136. ------------------------------------
  137. article.title = 'Updated'
  138. article.save
  139. # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>2, "created"=>false}
  140. article.update_attributes title: 'Test', author: 'Mary'
  141. # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>3}
  142. ------------------------------------
  143. The implementation supports the familiar interface for updating model timestamps and numeric attributes:
  144. [source,ruby]
  145. ------------------------------------
  146. article.touch
  147. # => => { ... "_version"=>4}
  148. article.views
  149. # => 0
  150. article.increment :views
  151. article.views
  152. # => 1
  153. ------------------------------------
  154. Any callbacks defined in the model will be triggered during the persistence operations:
  155. [source,ruby]
  156. ------------------------------------
  157. article.save
  158. # Successfully saved: #<Article {...}>
  159. ------------------------------------
  160. Please see the extensive documentation in the library
  161. https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#the-activerecord-pattern[README]
  162. for detailed information.
  163. Also, check out the
  164. https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#example-application-1[example application] which demonstrates the usage patterns of the _active record_ approach to persistence.