Class: ThinkingSphinx::Attribute

Inherits:
Property
  • Object
show all
Defined in:
lib/thinking_sphinx/attribute.rb

Overview

Attributes - eternally useful when it comes to filtering, sorting or grouping. This class isn’t really useful to you unless you’re hacking around with the internals of Thinking Sphinx - but hey, don’t let that stop you.

One key thing to remember - if you’re using the attribute manually to generate SQL statements, you’ll need to set the base model, and all the associations. Which can get messy. Use Index.link!, it really helps.

Instance Attribute Summary collapse

Attributes inherited from Property

#admin, #alias, #associations, #columns, #faceted, #model

Instance Method Summary collapse

Methods inherited from Property

#admin?, #changed?, #public?, #to_facet, #to_group_sql, #unique_name

Constructor Details

#initialize(source, columns, options = {}) ⇒ Attribute

To create a new attribute, you’ll need to pass in either a single Column or an array of them, and some (optional) options.

Valid options are:

  • :as => :alias_name

  • :type => :attribute_type

  • :source => :field, :query, :ranged_query

Alias is only required in three circumstances: when there’s another attribute or field with the same name, when the column name is ‘id’, or when there’s more than one column.

Type is not required, unless you want to force a column to be a certain type (but keep in mind the value will not be CASTed in the SQL statements). The only time you really need to use this is when the type can’t be figured out by the column - ie: when not actually using a database column as your source.

Source is only used for multi-value attributes (MVA). By default this will use a left-join and a group_concat to obtain the values. For better performance during indexing it can be beneficial to let Sphinx use a separate query to retrieve all document,value-pairs. Either :query or :ranged_query will enable this feature, where :ranged_query will cause the query to be executed incremental.

Example usage:

Attribute.new(
  Column.new(:created_at)
)

Attribute.new(
  Column.new(:posts, :id),
  :as => :post_ids
)

Attribute.new(
  Column.new(:posts, :id),
  :as => :post_ids,
  :source => :ranged_query
)

Attribute.new(
  [Column.new(:pages, :id), Column.new(:articles, :id)],
  :as => :content_ids
)

Attribute.new(
  Column.new("NOW()"),
  :as   => :indexed_at,
  :type => :datetime
)

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



70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/thinking_sphinx/attribute.rb', line 70

def initialize(source, columns, options = {})
  super
  
  @type           = options[:type]
  @query_source   = options[:source]
  @crc            = options[:crc]
  
  @type         ||= :multi    unless @query_source.nil?
  if @type == :string && @crc
    @type = is_many? ? :multi : :integer
  end
  
  source.attributes << self
end

Instance Attribute Details

#query_sourceObject

Returns the value of attribute query_source.



12
13
14
# File 'lib/thinking_sphinx/attribute.rb', line 12

def query_source
  @query_source
end

Instance Method Details

#all_datetimes?Boolean

Returns:

  • (Boolean)


189
190
191
# File 'lib/thinking_sphinx/attribute.rb', line 189

def all_datetimes?
  all_of_type?(:datetime, :date, :timestamp)
end

#all_ints?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'lib/thinking_sphinx/attribute.rb', line 185

def all_ints?
  all_of_type?(:integer)
end

#config_value(offset = nil, delta = false) ⇒ Object

Returns the configuration value that should be used for the attribute. Special case is the multi-valued attribute that needs some extra configuration.



137
138
139
140
141
142
143
144
145
# File 'lib/thinking_sphinx/attribute.rb', line 137

def config_value(offset = nil, delta = false)
  if type == :multi && ThinkingSphinx::Configuration.instance.type != "xml"
    multi_config = include_as_association? ? "field" :
      source_value(offset, delta).gsub(/\s+/m, " ").strip
    "uint #{unique_name} from #{multi_config}"
  else
    unique_name
  end
end

#include_as_association?Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/thinking_sphinx/attribute.rb', line 128

def include_as_association?
  ! (type == :multi && (query_source == :query || query_source == :ranged_query))
end

#live_value(instance) ⇒ Object



178
179
180
181
182
183
# File 'lib/thinking_sphinx/attribute.rb', line 178

def live_value(instance)
  object = instance
  column = @columns.first
  column.__stack.each { |method| object = object.send(method) }
  object.send(column.__name)
end

#to_select_sqlObject

Get the part of the SELECT clause related to this attribute. Don’t forget to set your model and associations first though.

This will concatenate strings and arrays of integers, and convert datetimes to timestamps, as needed.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/thinking_sphinx/attribute.rb', line 91

def to_select_sql
  return nil unless include_as_association?
  
  separator = all_ints? || all_datetimes? || @crc ? ',' : ' '
  
  clause = @columns.collect { |column|
    part = column_with_prefix(column)
    case type
    when :string
      adapter.convert_nulls(part)
    when :datetime
      adapter.cast_to_datetime(part)
    else
      part
    end
  }.join(', ')
  
  # clause = adapter.cast_to_datetime(clause)             if type == :datetime
  clause = adapter.crc(clause)                          if @crc
  clause = adapter.concatenate(clause, separator)       if concat_ws?
  clause = adapter.group_concatenate(clause, separator) if is_many?
  
  "#{clause} AS #{quote_column(unique_name)}"
end

#typeObject

Returns the type of the column. If that’s not already set, it returns :multi if there’s the possibility of more than one value, :string if there’s more than one association, otherwise it figures out what the actual column’s datatype is and returns that.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/thinking_sphinx/attribute.rb', line 152

def type
  @type ||= begin
    base_type = case
    when is_many_datetimes?
      :datetime
    when is_many?, is_many_ints?
      :multi
    when @associations.values.flatten.length > 1
      :string
    else
      translated_type_from_database
    end
    
    if base_type == :string && @crc
      :integer
    else
      @crc = false
      base_type
    end
  end
end

#type_to_configObject



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/thinking_sphinx/attribute.rb', line 116

def type_to_config
  prefix = ThinkingSphinx::Configuration.instance.type == "xml" ? "xmlpipe" : "sql"
  {
    :multi    => :"#{prefix}_attr_multi",
    :datetime => :"#{prefix}_attr_timestamp",
    :string   => :"#{prefix}_attr_str2ordinal",
    :float    => :"#{prefix}_attr_float",
    :boolean  => :"#{prefix}_attr_bool",
    :integer  => :"#{prefix}_attr_uint"
  }[type]
end

#updatable?Boolean

Returns:

  • (Boolean)


174
175
176
# File 'lib/thinking_sphinx/attribute.rb', line 174

def updatable?
  [:integer, :datetime, :boolean].include?(type) && !is_string?
end