require 'ultrasphinx'
module ActiveRecord
class Base
=begin rdoc
The is_indexed macro configures a model for indexing. Its parameters are used to generate SQL queries for Sphinx.
== Indexing single fields
Use the :fields key.
Accepts an array of field names.
:fields => ["created_at", "title", "body"]
== Indexing fields from belongs_to associations
Use the :includes key.
Accepts an array of hashes.
Each should contain a :model 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 will not be generated.
== Scoping the searchable records
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 multiple fields
Use the :concats key (MySQL only).
Accepts an array of option hashes, which can be of two types:
1. To concatenate many fields within one record, 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:
:concats => [{:fields => ["title", "body"], :as => "text"}]
2. To group and concatenate a field from a set of associated records, use a group (or vertical) concatenation. Group concatenations join into another table, and can be used to index a number of associated models as one field in a parent model. Group concatenations contain a :model 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:
:concats => {:model => "Post", :field => "body", :as => "responses"}
Optional group concatenation keys are :association_name (if your has_many association can't be derived from the model name), :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).
== Example
Here's an example configuration using most of the options, taken from production code:
class Story < ActiveRecord::Base
is_indexed :fields => [
"title",
"published_at"
],
:includes => [
{:model => "Category", :field => "name", :as => "category"}
],
:concats => [
{:fields => ["title", "long_description", "short_description"], :as => "editorial"},
{:model => "Page", :field => "body", :as => "body", :association_name => "pages"},
{:model => "Comment", :field => "body", :as => "comments",
:conditions => "comments.item_type = '#{base_class}'"}
],
:conditions => self.live_condition_string
end
=end
def self.is_indexed opts = {}
opts.assert_valid_keys [:fields, :concats, :conditions, :includes, :nulls]
Array(opts[:concats]).each do |concat|
concat.assert_valid_keys [:model, :conditions, :field, :as, :fields, :association_name, :association_sql]
raise Ultrasphinx::ConfigurationError, "You can't mix regular concat and group concats" if concat[:fields] and (concat[:field] or concat[:model] or concat[:association_name])
raise Ultrasphinx::ConfigurationError, "Group concats must not have multiple fields" if concat[:field].is_a? Array
raise Ultrasphinx::ConfigurationError, "Regular concats should have multiple fields" if concat[:fields] and !concat[:fields].is_a?(Array)
end
Array(opts[:joins]).each do |join|
join.assert_valid_keys [:model, :field, :as]
end
Ultrasphinx::MODEL_CONFIGURATION[self.name] = opts
end
end
end