Of course storing raw data is cool, but what happens if you need to sort ? or build queries related to the values in the property hash ?
You have three main choices to index content:
- create key/value tables
- use fields in the object’s table reserved for indexing (in Zena, these fields start with “idx_” (idx_datetime1, idx_integer1, etc).
- create a custom class
key/value tables
class AddStringIndexToContact < ActiveRecord::Migration def self.up # Read the name as "index contacts with strings" create_table 'idx_contacts_strings' do |t| t.integer 'contact_id' t.string 'key' t.string 'value' end end def self.down drop_table :idx_string_contacts end end
When you have migrated your database, you can start setting indices on your properties, either by adding :indexed => true
or by using a Proc
:
class Contact < ActiveRecord::Base include Property property do |p| p.string 'last_name', :indexed => true p.string 'first_name', :index => Proc.new {|rec| { 'fullname' => rec.fullname }} p.index(:string) do |record| { 'fulltext' => "name:#{record.name} first_name:#{record.first_name}", "name_#{record.lang}" => record.name } end end end
You can also use a Proc
on a single property or build dynamic keys. In fact you can do whatever you want inside the index block as long as you return a Hash
with string keys pointing to values compatible with the type for ‘value’ in your corresponding table (:string ==> i_string_…).
field indexing
Once you have created some index fields in the table (“contacts” in this case), you simply have to prefix the field name with ”.” when defining the index:
class Contact < ActiveRecord::Base include Property property do |p| p.string 'first_name' p.string 'last_name', :index => '.idx_strings1' end end
This does not look very interesting but it really becomes useful when you use single table inheritance and versioning (properties are not stored in the same table as the index fields).
indexing with a custom class
Sometimes key/value storage is not optimal for indices and you want to group values together on single record or use a legacy table (maybe the one used before Property). You can achieve this easily:
class Contact < ActiveRecord::Base include Property property do |p| p.string 'last_name' p.string 'first_name' p.index(ContactIndexer) do |record| { 'last_name' => record.last_name, 'first_name' => record.first_name, } end end end # And here is the class to manage the index class ContactIndexer < ActiveRecord::Base def self.set_property_index(contact, indices) if index = first(:conditions => ['contact_id = ?', contact.id]) index.update_attributes(indices) else create(indices.merge(:contact_id => contact.id)) end end def self.delete_property_index(contact) delete_all(['contact_id = ?', contact.id]) end end
When you need to search and sort using the keys defined here, the easiest way is to create a query builder processor that knows about the index classes (see Zena’s QueryNode processor for example).