Class: ActiveRecord::Base

Inherits:
Object show all
Defined in:
lib/ultrasphinx/is_indexed.rb

Class Method Summary collapse

Class Method Details

.is_indexed(opts = {}) ⇒ Object

The is_indexed method configures a model for indexing. Its parameters help generate SQL queries for Sphinx.

Options

Including regular fields

Use the :fields key.

Accepts an array of field names or field hashes.

:fields => [
  'created_at', 
  'title', 
  {:field => 'body', :as => 'description'},
  {:field => 'user_category', :facet => true, :as => 'category' }
]

To alias a field, pass a hash instead of a string and set the :as key.

To allow faceting support on a text field, also pass a hash and set the :facet key to true. Faceting is off by default for text fields because there is some indexing overhead associated with it. Faceting is always on for numeric or date fields.

To allow sorting by a text field, also pass a hash and set the :sortable key to true. This is turned off by default for the same reason as above. Sorting is always on for numeric or date fields.

To apply an SQL function to a field before it is indexed, use the key :function_sql. Pass a string such as "REPLACE(?, '_', ' ')". The table and column name for your field will be interpolated into the first ? in the string.

Note that float fields are supported, but require Sphinx 0.98.

Including a field from an association

Use the :include key.

Accepts an array of hashes.

:include => [{:class_name => 'Category', :field => 'name', :as => 'category'}]

Each should contain a :class_name key (the class name of the included model), a :field key (the name of the field to include), and an optional :as key (what to name the field in the parent). You can use the optional key :association_sql if you need to pass a custom JOIN string, in which case the default JOIN for belongs_to will not be generated.

The keys :facet, :sortable, and :function_sql are also recognized, just like for regular fields.

Requiring conditions

Use the :conditions key.

SQL conditions, to scope which records are selected for indexing. Accepts a string.

:conditions => "created_at < NOW() AND deleted IS NOT NULL"

The :conditions key is especially useful if you delete records by marking them deleted rather than removing them from the database.

Concatenating several fields within a record

Use the :concatenate key (MySQL only).

Accepts an array of option hashes.

To concatenate several fields within one record as a combined field, use a regular (or horizontal) concatenation. Regular concatenations contain a :fields key (again, an array of field names), and a mandatory :as key (the name of the result of the concatenation). For example, to concatenate the title and body into one field called text:

:concatenate => [{:fields => ['title', 'body'], :as => 'text'}]

The keys :facet, :sortable, and :function_sql are also recognized, just like for regular fields.

Concatenating one field from a set of associated records

Also use the :concatenate key.

To concatenate one field from a set of associated records as a combined field in the parent record, use a group (or vertical) concatenation. A group concatenation should contain a :class_name key (the class name of the included model), a :field key (the field on the included model to concatenate), and an optional :as key (also the name of the result of the concatenation). For example, to concatenate all Post#body contents into the parent’s responses field:

:concatenate => [{:class_name => 'Post', :field => 'body', :as => 'responses'}]

Optional group concatenation keys are :association_sql, if you need to pass a custom JOIN string (for example, a double JOIN for a has_many :through), and :conditions (if you need custom WHERE conditions for this particular association).

The keys :facet, :sortable, and :function_sql are also recognized, just like for regular fields.

Ultrasphinx is not an object-relational mapper, and the association generation is intended to stay minimal–don’t be afraid of :association_sql.

Examples

Complex configuration

Here’s an example configuration using most of the options, taken from production code:

class Story < ActiveRecord::Base  
  is_indexed :fields => [
      'title', 
      'published_at',
      {:field => 'author', :facet => true}
    ],
    :include => [
      {:class_name => 'Category', :field => 'name', :as => 'category'}
    ],      
    :concatenate => [
      {:fields => ['title', 'long_description', 'short_description'], 
        :as => 'editorial'},
      {:class_name => 'Page', :field => 'body', :as => 'body'},
      {:class_name => 'Comment', :field => 'body', :as => 'comments', 
        :conditions => "comments.item_type = '#{base_class}'"}
    ],
    :conditions => self.live_condition_string
end

Note how setting the :conditions on Comment is enough to configure a polymorphic has_many.

Association scoping

A common use case is to only search records that belong to a particular parent model. Ultrasphinx configures Sphinx to support a :filter element on any date or numeric field, so any *_id fields you have will be filterable.

For example, say a Company has_many :users and each User has_many :articles. If you want to to filter Articles by Company, add company_id to the Article’s is_indexed method. The best way is to grab it from the User association:

class Article < ActiveRecord::Base 
   is_indexed :include => [{:class_name => 'User', :field => 'company_id'}]
end

Now you can run:

@search = Ultrasphinx::Search.new('something', 
  :filter => {'company_id' => 493})

If the associations weren’t just has_many and belongs_to, you would need to use the :association_sql key to set up a custom JOIN.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/ultrasphinx/is_indexed.rb', line 125

def self.is_indexed opts = {}    
  opts = HashWithIndifferentAccess.new(opts)
      
  opts.assert_valid_keys ['fields', 'concatenate', 'conditions', 'include']
  
  Array(opts['fields']).each do |entry|
    if entry.is_a? Hash
      entry.stringify_keys!
      entry.assert_valid_keys ['field', 'as', 'facet', 'function_sql', 'sortable']
    end
  end
  
  Array(opts['concatenate']).each do |entry|
    entry.stringify_keys!
    entry.assert_valid_keys ['class_name', 'conditions', 'field', 'as', 'fields', 'association_sql', 'facet', 'function_sql', 'sortable']
    raise Ultrasphinx::ConfigurationError, "You can't mix regular concat and group concats" if entry['fields'] and (entry['field'] or entry['class_name'])
    raise Ultrasphinx::ConfigurationError, "Concatenations must specify an :as key" unless entry['as']
    raise Ultrasphinx::ConfigurationError, "Group concatenations must not have multiple fields" if entry['field'].is_a? Array
    raise Ultrasphinx::ConfigurationError, "Regular concatenations should have multiple fields" if entry['fields'] and !entry['fields'].is_a?(Array)
  end
  
  Array(opts['include']).each do |entry|
    entry.stringify_keys!
    entry.assert_valid_keys ['class_name', 'field', 'as', 'association_sql', 'facet', 'function_sql', 'sortable']
  end
  
  Ultrasphinx::MODEL_CONFIGURATION[self.name] = opts
end