Class: ThinkingSphinx::Index::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/thinking_sphinx/index/builder.rb

Overview

The Builder class is the core for the index definition block processing. There are four methods you really need to pay attention to:

  • indexes

  • has

  • where

  • set_property/set_properties

The first two of these methods allow you to define what data makes up your indexes. #where provides a method to add manual SQL conditions, and set_property allows you to set some settings on a per-index basis. Check out each method’s documentation for better ideas of usage.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(index, &block) ⇒ Builder

Returns a new instance of Builder.



28
29
30
31
32
33
34
35
36
37
# File 'lib/thinking_sphinx/index/builder.rb', line 28

def initialize(index, &block)
  @index  = index
  @explicit_source = false

  self.instance_eval &block

  if no_fields?
    raise "At least one field is necessary for an index"
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

Handles the generation of new columns for the field and attribute definitions.



243
244
245
# File 'lib/thinking_sphinx/index/builder.rb', line 243

def method_missing(method, *args)
  ::ThinkingSphinx::Index::FauxColumn.new(method, *args)
end

Class Method Details

.generate(model, name = nil, &block) ⇒ Object



18
19
20
21
22
23
24
25
26
# File 'lib/thinking_sphinx/index/builder.rb', line 18

def self.generate(model, name = nil, &block)
  index  = ::ThinkingSphinx::Index.new(model)
  index.name = name unless name.nil?

  new(index, &block) if block_given?

  index.delta_object = ::ThinkingSphinx::Deltas.parse index
  index
end

Instance Method Details

#assoc(assoc, *args) ⇒ Object

A method to allow adding fields from associations which have names that clash with method names in the Builder class (ie: properties, fields, attributes).

Example: indexes assoc(:properties).column



253
254
255
# File 'lib/thinking_sphinx/index/builder.rb', line 253

def assoc(assoc, *args)
  ::ThinkingSphinx::Index::FauxColumn.new(assoc, *args)
end

#define_source(&block) ⇒ Object



39
40
41
42
43
44
45
46
47
48
# File 'lib/thinking_sphinx/index/builder.rb', line 39

def define_source(&block)
  if @explicit_source
    @source = ::ThinkingSphinx::Source.new(@index)
    @index.sources << @source
  else
    @explicit_source = true
  end

  self.instance_eval &block
end

#facet(*args) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
# File 'lib/thinking_sphinx/index/builder.rb', line 158

def facet(*args)
  options = args.extract_options!
  options[:facet] = true

  args.each do |columns|
    attribute = ::ThinkingSphinx::Attribute.new(source,
      ::ThinkingSphinx::Index::FauxColumn.coerce(columns), options)

    add_facet_attribute attribute, options
  end
end

#group_by(*args) ⇒ Object

Use this method to add some manual SQL strings to the GROUP BY clause. You can pass in as many strings as you’d like, they’ll get joined together with commas later on.

group_by "lat", "lng"


193
194
195
# File 'lib/thinking_sphinx/index/builder.rb', line 193

def group_by(*args)
  source.groupings += args
end

#has(*args) ⇒ Object

This is the method to add attributes to your index (hence why it is aliased as ‘attribute’). The syntax is the same as #indexes, so use that as starting point, but keep in mind the following points.

An attribute can have an alias (the :as option), but it is always sortable - so you don’t need to explicitly request that. You can specify the data type of the attribute (the :type option), but the code’s pretty good at figuring that out itself from peering into the database.

Attributes are limited to the following types: integers, floats, datetimes (converted to timestamps), booleans, strings and MVAs (:multi). Don’t forget that Sphinx converts string attributes to integers, which are useful for sorting, but that’s about it.

Collection of integers are known as multi-value attributes (MVAs). Generally these would be through a has_many relationship, like in this example:

has posts(:id), :as => :post_ids

This allows you to filter on any of the values tied to a specific record. Might be best to read through the Sphinx documentation to get a better idea of that though.

Adding SQL Fragment Attributes

You can also define an attribute using an SQL fragment, useful for when you would like to index a calculated value. Don’t forget to set the type of the attribute though:

has "age < 18", :as => :minor, :type => :boolean

If you’re creating attributes for latitude and longitude, don’t forget that Sphinx expects these values to be in radians.



148
149
150
151
152
153
154
155
156
# File 'lib/thinking_sphinx/index/builder.rb', line 148

def has(*args)
  options = args.extract_options!
  args.each do |columns|
    attribute = ::ThinkingSphinx::Attribute.new(source,
      ::ThinkingSphinx::Index::FauxColumn.coerce(columns), options)

    add_facet_attribute attribute, options if attribute.faceted
  end
end

#indexes(*args) ⇒ Object

This is how you add fields - the strings Sphinx looks at - to your index. Technically, to use this method, you need to pass in some columns and options - but there’s some neat method_missing stuff happening, so lets stick to the expected syntax within a define_index block.

Expected options are :as, which points to a column alias in symbol form, and :sortable, which indicates whether you want to sort by this field.

Adding Single-Column Fields:

You can use symbols or methods - and can chain methods together to get access down the associations tree.

indexes :id, :as => :my_id
indexes :name, :sortable => true
indexes first_name, last_name, :sortable => true
indexes users.posts.content, :as => :post_content
indexes users(:id), :as => :user_ids

Keep in mind that if any keywords for Ruby methods - such as id or name - clash with your column names, you need to use the symbol version (see the first, second and last examples above).

If you specify multiple columns (example #2), a field will be created for each. Don’t use the :as option in this case. If you want to merge those columns together, continue reading.

Adding Multi-Column Fields:

indexes [first_name, last_name], :as => :name
indexes [location, parent.location], :as => :location

To combine multiple columns into a single field, you need to wrap them in an Array, as shown by the above examples. There’s no limitations on whether they’re symbols or methods or what level of associations they come from.

Adding SQL Fragment Fields

You can also define a field using an SQL fragment, useful for when you would like to index a calculated value.

indexes "age < 18", :as => :minor


101
102
103
104
105
106
107
108
109
110
# File 'lib/thinking_sphinx/index/builder.rb', line 101

def indexes(*args)
  options = args.extract_options!
  args.each do |columns|
    field = ::ThinkingSphinx::Field.new(source,
      ::ThinkingSphinx::Index::FauxColumn.coerce(columns), options)

    add_sort_attribute  field, options   if field.sortable
    add_facet_attribute field, options   if field.faceted
  end
end

#join(*args) ⇒ Object



170
171
172
173
174
# File 'lib/thinking_sphinx/index/builder.rb', line 170

def join(*args)
  args.each do |association|
    ::ThinkingSphinx::Join.new(source, association)
  end
end

#sanitize_sql(*args) ⇒ Object

Use this method to generate SQL for your attributes, conditions, etc. You can pass in as whatever ActiveRecord::Base.sanitize_sql accepts.

where sanitize_sql(["active = ?", true])
#=> WHERE active = 1


263
264
265
# File 'lib/thinking_sphinx/index/builder.rb', line 263

def sanitize_sql(*args)
  @index.model.send(:sanitize_sql, *args)
end

#set_property(*args) ⇒ Object Also known as: set_properties

This is what to use to set properties on the index. Chief amongst those is the delta property - to allow automatic updates to your indexes as new models are added and edited - but also you can define search-related properties which will be the defaults for all searches on the model.

set_property :delta => true
set_property :field_weights => {"name" => 100}
set_property :order => "name ASC"
set_property :select => 'name'

Also, the following two properties are particularly relevant for geo-location searching - latitude_attr and longitude_attr. If your attributes for these two values are named something other than lat/latitude or lon/long/longitude, you can dictate what they are when defining the index, so you don’t need to specify them for every geo-related search.

set_property :latitude_attr => "lt", :longitude_attr => "lg"

Please don’t forget to add a boolean field named ‘delta’ to your model’s database table if enabling the delta index for it. Valid options for the delta property are:

true false :default :delayed :datetime

You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement your own handling for delta indexing.



230
231
232
233
234
235
236
237
# File 'lib/thinking_sphinx/index/builder.rb', line 230

def set_property(*args)
  options = args.extract_options!
  options.each do |key, value|
    set_single_property key, value
  end

  set_single_property args[0], args[1] if args.length == 2
end

#use_local_indices(*indexes) ⇒ Object Also known as: use_local_index



50
51
52
# File 'lib/thinking_sphinx/index/builder.rb', line 50

def use_local_indices(*indexes)
  @index.additional_indices += indexes.map {|index_name| "#{index_name.to_s}_core"}
end

#where(*args) ⇒ Object

Use this method to add some manual SQL conditions for your index request. You can pass in as many strings as you like, they’ll get joined together with ANDs later on.

where "user_id = 10"
where "parent_type = 'Article'", "created_at < NOW()"


183
184
185
# File 'lib/thinking_sphinx/index/builder.rb', line 183

def where(*args)
  source.conditions += args
end