Skip to content
This repository has been archived by the owner on Jun 30, 2018. It is now read-only.

Data storage in Tire and wrapping the results

tieleman edited this page Oct 24, 2012 · 16 revisions

Data storage and retrieval in Tire and different ways to wrap the results

There's been a fair bit of discussion regarding the way Tire should retrieve objects from elasticsearch (ES) and present them to your Ruby code. Ultimately, there is no one correct way to do this due to the very different use cases that exist. Fortunately, Tire gives you a couple of options to customise the way this should be handled. This page serves as a summary of the different ways that you can use Tire to store and retrieve your data and the options available to the developer for wrapping the results.

Using the standard wrapper (Tire::Results::Item)

First and foremost, any data you retrieve from ES using Tire will be an instance of Tire::Results::Item. This is the default behaviour of Tire. (see source). They are fairly straight forward objects, they simply map whatever fields are returned from ES to corresponding attributes. Basically, they are a very thin wrapper around nested hashes representing the data returned from ES.

This is the most lightweight way to use Tire/ES in your Ruby code. Simply include the gem, store some objects and retrieve them by id or by querying (or query another existing ES index that is not necessarily part of your application). Note that this way you are working completely agnostic of document types, you are simply searching through the entire index and getting generic Item objects/hashes.

For use in Rails (and potentially other frameworks) Tire::Results::Item is extended with the ActiveModel::Naming module (Tire lists ActiveModel as a library dependency). This allows for naming, routing and using view templates. The instances of Item trick Rails in thinking it is a different class by overriding the #class method as such:

def class
  defined?(::Rails) && @attributes[:_type] ? @attributes[:_type].camelize.constantize : super
rescue NameError
  super
end

It attempts to deduce the correct (ActiveRecord) class and document type from the _type attribute in ES. If that is not present, or we are not in Rails it will just return super, which will default to Tire::Results::Item.

If you are using Rails, Tire will trick Rails into thinking it is actually returning your ActiveRecord objects:

> s = Article.search "title:*"
> item = s.results.first
> item.class
 => Article

This is done by Tire to make sure that all the standard Rails helpers will work and allow you to, for example, do routing with your objects. Things like article_path @article will just work in your views (where @article is a result coming from ES).

Note that as of this momentTire::Results::Item does not override the is_a? method, so if you're using that to check what kind of an object you're dealing with you need to be prepared for that:

> item.class
 => Article
> item.is_a? Article
 => false

For more discussion on this subject, see issue #159.

Using the standard persistence options (Tire::Model::Persistence)

A different way to use Tire is to use ES as your main data store, skipping the regular ActiveRecord options (such as MySQL, SQLite, etc.). For this Tire provides the Tire::Model::Persistence module. It includes all the ActiveModel requirements and provides callbacks and search functionality for your data models. Storing your models in ES would then be as easy as:

> a = Article.new(:title => "My interesting article")
> a.save

The simplest class definition for this would look something like:

class Article
  include Tire::Model::Persistence

  property :title
end

Note that this is not limited to Rails applications, you can use this in whatever project you like (Rack, plain Ruby, whatever). The resulting object will be a valid ActiveModel object, so if you are using Rails all the standard options for ActiveModel objects are available to you (again: the routing, view templates, etc.).

Using a custom wrapper

  • General info on why you would do such a thing
  • Using an object inheriting from SimpleDelegator