Persistenceedit
The elasticsearch-persistence
Rubygem
provides persistence layer for Ruby domain objects.
It supports two design patterns for integrating with your objects: repository and active record.
Repositoryedit
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.
Features At a Glanceedit
- Access to the Elasticsearch client
- Setting the index name, document type, and object class for deserialization
- Composing mappings and settings for the index
- Creating, deleting or refreshing the index
- Finding or searching for documents
- Providing access both to domain objects and hits for search results
- Providing access to the Elasticsearch response for search results (aggregations, total, …)
- Defining the methods for serialization and deserialization
Usageedit
Let’s have a simple plain old Ruby object (PORO):
class Note attr_reader :attributes def initialize(attributes={}) @attributes = attributes end def to_hash @attributes end end
Let’s create a default, "dumb" repository, as a first step:
require 'elasticsearch/persistence' repository = Elasticsearch::Persistence::Repository.new
We can save a Note
instance into the repository, find it, search it, delete it:
note = Note.new id: 1, text: 'Test' repository.save(note) # PUT http://localhost:9200/repository/note/1 # > {"id":1,"text":"Test"} # < {"_index":"repository","_type":"note","_id":"1","_version":1,"created":true} n = repository.find(1) # GET http://localhost:9200/repository/_all/1 # < {"_index":"repository","_type":"note","_id":"1","_version":2,"found":true, "_source" : {"id":1,"text":"Test"}} => <Note:0x007fcbfc0c4980 @attributes={"id"=>1, "text"=>"Test"}> repository.search(query: { match: { text: 'test' } }).first # GET http://localhost:9200/repository/_search # > {"query":{"match":{"text":"test"}}} # < {"took":2, ... "hits":{"total":1, ... "hits":[{ ... "_source" : {"id":1,"text":"Test"}}]}} => <Note:0x007fcbfc1c7b70 @attributes={"id"=>1, "text"=>"Test"}> repository.delete(note) # DELETE http://localhost:9200/repository/note/1 # < {"found":true,"_index":"repository","_type":"note","_id":"1","_version":3} => {"found"=>true, "_index"=>"repository", "_type"=>"note", "_id"=>"1", "_version"=>2}
The repository module provides a number of features and facilities to configure and customize the behaviour, as well as support for extending your own, custom repository class.
Please refer to the documentation for more information.
Also, check out the example application which demonstrates the usage patterns of the repository approach to persistence.
Active Recordedit
The ‘Elasticsearch::Persistence::Model` module provides an implementation of the active record pattern, with
a familiar interface for using Elasticsearch as a persistence layer in Ruby on Rails applications. The model
is fully compatible with Rails’ conventions and helpers, such as url_for
.
All the methods are documented with comprehensive examples in the source code, available also online.
Features At a Glanceedit
- Familiar interface for persistence methods from ActiveRecord
- Common model features such as validations and callbacks
- Methods for defining model attributes, including Elasticsearch mappings
-
Support for fetching data in bulk (
find_in_batches
,find_each
) - Decorated search results for easy access to model instances and meta data (such as highlights or aggregations)
- Easy access to the underlying gateway and client
Usageedit
To use the library in a Rails application, add it to your Gemfile with a require statement:
gem "elasticsearch-persistence", require: 'elasticsearch/persistence/model'
Include the module in a plain Ruby class, and set up the properties, mappings, etc:
class Article include Elasticsearch::Persistence::Model # Define a plain `title` attribute # attribute :title, String # Define an `author` attribute, with multiple analyzers for this field # attribute :author, String, mapping: { fields: { author: { type: 'string'}, raw: { type: 'string', analyzer: 'keyword' } } } # Define a `views` attribute, with default value # attribute :views, Integer, default: 0, mapping: { type: 'integer' } # Validate the presence of the `title` attribute # validates :title, presence: true # Execute code after saving the model. # after_save { puts "Successfully saved: #{self}" } end
The model attribute definition support is implemented with the Virtus Rubygem, and the naming, validation, etc. features with the ActiveModel Rubygem.
Attribute validations work like for any other ActiveModel-compatible implementation:
article = Article.new # => #<Article { ... }> article.valid? # => false article.errors.to_a # => ["Title can't be blank"]
We can create a new article in the database and find it:
Article.create id: 1, title: 'Test', author: 'John' # PUT http://localhost:9200/articles/article/1 [status:201, request:0.015s, query:n/a] article = Article.find(1) # => #<Article { ... }> article._index # => "articles" article.id # => "1" article.title # => "Test"
To update the model, either update the attribute and save the model or use the update_attributes
method:
article.title = 'Updated' article.save # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>2, "created"=>false} article.update_attributes title: 'Test', author: 'Mary' # => {"_index"=>"articles", "_type"=>"article", "_id"=>"1", "_version"=>3}
The implementation supports the familiar interface for updating model timestamps and numeric attributes:
article.touch # => => { ... "_version"=>4} article.views # => 0 article.increment :views article.views # => 1
Any callbacks defined in the model will be triggered during the persistence operations:
article.save # Successfully saved: #<Article {...}>
Please see the extensive documentation in the library README for detailed information.
Also, check out the example application which demonstrates the usage patterns of the active record approach to persistence.