class ScopedSearch::Definition
The ScopedSearch
definition class defines on what fields should be search in the model in what cases
A definition can be created by calling the scoped_search
method on an ActiveRecord-based class, so you should not create an instance of this class yourself.
Constants
- INTEGER_REGXP
- NUMERICAL_REGXP
- UUID_REGXP
Attributes
Public Class Methods
Initializes a ScopedSearch
definition instance. This method will also setup a database adapter and create the :search_for named scope if it does not yet exist.
# File lib/scoped_search/definition.rb 197 def initialize(klass) 198 @klass = klass 199 @fields = {} 200 @unique_fields = [] 201 @profile_fields = {:default => {}} 202 @profile_unique_fields = {:default => []} 203 204 register_named_scope! unless klass.respond_to?(:search_for) 205 register_complete_for! unless klass.respond_to?(:complete_for) 206 end
Public Instance Methods
Returns a list of fields that should be searched on by default.
Every field will show up in this method's result, except for fields for which the only_explicit parameter is set to true.
# File lib/scoped_search/definition.rb 303 def default_fields 304 unique_fields.reject { |field| field.only_explicit } 305 end
Returns a list of appropriate fields to search in given a search keyword and operator.
# File lib/scoped_search/definition.rb 266 def default_fields_for(value, operator = nil) 267 268 column_types = [:virtual] 269 column_types += [:string, :text] if [nil, :like, :unlike, :ne, :eq].include?(operator) 270 column_types += [:double, :float, :decimal] if value =~ NUMERICAL_REGXP 271 column_types += [:integer] if value =~ INTEGER_REGXP 272 column_types += [:uuid] if value =~ UUID_REGXP 273 column_types += [:datetime, :date, :timestamp] if (parse_temporal(value)) 274 275 default_fields.select { |field| !field.set? && column_types.include?(field.type) } 276 end
Defines a new search field for this search definition.
# File lib/scoped_search/definition.rb 308 def define(*args) 309 Field.new(self, *args) 310 end
# File lib/scoped_search/definition.rb 214 def define_field(name, field) 215 @profile ||= :default 216 @profile_fields[@profile] ||= {} 217 @profile_fields[@profile][name.to_sym] ||= field 218 @profile_unique_fields[@profile] ||= [] 219 @profile_unique_fields[@profile] = (@profile_unique_fields[@profile] + [field]).uniq 220 field 221 end
this method return definitions::field object from string
# File lib/scoped_search/definition.rb 236 def field_by_name(name) 237 field = fields[name.to_sym] unless name.blank? 238 if field.nil? 239 dotted = name.to_s.split('.')[0] 240 field = fields[dotted.to_sym] unless dotted.blank? 241 if field && field.key_relation.nil? 242 return nil 243 end 244 end 245 field 246 end
# File lib/scoped_search/definition.rb 223 def fields 224 @profile ||= :default 225 @profile_fields[@profile] ||= {} 226 super_definition ? super_definition.fields.merge(@profile_fields[@profile]) : @profile_fields[@profile] 227 end
this method is used by the syntax auto completer to suggest operators.
# File lib/scoped_search/definition.rb 249 def operator_by_field_name(name) 250 field = field_by_name(name) 251 return [] if field.nil? 252 return field.operators if field.operators 253 return ['=', '!=', '>', '<', '<=', '>=', '~', '!~', '^', '!^'] if field.virtual? 254 return ['=', '!='] if field.set? || field.uuid? 255 return ['=', '>', '<', '<=', '>=', '!=', '^', '!^'] if field.numerical? 256 return ['=', '!=', '~', '!~', '^', '!^'] if field.textual? 257 return ['=', '>', '<'] if field.temporal? 258 raise ScopedSearch::QueryNotSupported, "Unsupported type '#{field.type.inspect}')' for field '#{name}'. This can be a result of a search definition problem." 259 end
Try to parse a string as a datetime. Supported formats are Today, Yesterday, Sunday, '1 day ago', '2 hours ago', '3 months ago', '4 weeks from now', 'Jan 23, 2004' And many more formats that are documented in Ruby DateTime API Doc. In case Time responds to zone, we know this is Rails
environment and we can use Time.zone.parse. The benefit is that the current timezone is respected and does not have to be specified explicitly. That way even relative dates work as expected.
# File lib/scoped_search/definition.rb 283 def parse_temporal(value) 284 return Date.current if value =~ /\btoday\b/i 285 return 1.day.ago.to_date if value =~ /\byesterday\b/i 286 return 1.day.from_now.to_date if value =~ /\btomorrow\b/i 287 return (eval($1.strip.gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bago)\b\s*\z/i 288 return (eval($1.strip.gsub(/\s+/,'.').downcase)).to_date if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bago)\b\s*\z/i 289 return (eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)).to_datetime if value =~ /\A\s*(\d+\s+\b(?:hours?|minutes?)\b\s+\bfrom\s+now)\b\s*\z/i 290 return (eval($1.strip.gsub(/from\s+now/i,'from_now').gsub(/\s+/,'.').downcase)).to_date if value =~ /\A\s*(\d+\s+\b(?:days?|weeks?|months?|years?)\b\s+\bfrom\s+now)\b\s*\z/i 291 if Time.respond_to?(:zone) && !Time.zone.nil? 292 parsed = Time.zone.parse(value) rescue nil 293 parsed && parsed.to_datetime 294 else 295 DateTime.parse(value, true) rescue nil 296 end 297 end
Returns a reflection for a given klass and name
# File lib/scoped_search/definition.rb 313 def reflection_by_name(klass, name) 314 return if name.nil? 315 klass.reflections[name.to_sym] || klass.reflections[name.to_s] 316 end
# File lib/scoped_search/definition.rb 210 def super_definition 211 klass.superclass.try(:scoped_search_definition) 212 end
# File lib/scoped_search/definition.rb 229 def unique_fields 230 @profile ||= :default 231 @profile_unique_fields[@profile] ||= [] 232 super_definition ? (super_definition.unique_fields + @profile_unique_fields[@profile]).uniq : @profile_unique_fields[@profile] 233 end
Protected Instance Methods
Registers the complete_for method within the class that is used for searching.
# File lib/scoped_search/definition.rb 340 def register_complete_for! # :nodoc 341 @klass.extend(ScopedSearch::AutoCompleteClassMethods) 342 end
Registers the search_for named scope within the class that is used for searching.
# File lib/scoped_search/definition.rb 321 def register_named_scope! # :nodoc 322 @klass.define_singleton_method(:search_for) do |query = '', options = {}| 323 # klass may be different to @klass if the scope is called on a subclass 324 klass = self 325 definition = klass.scoped_search_definition 326 327 search_scope = klass.all 328 find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options) 329 search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions] 330 search_scope = search_scope.includes(find_options[:include]) if find_options[:include] 331 search_scope = search_scope.joins(find_options[:joins]) if find_options[:joins] 332 search_scope = search_scope.reorder(Arel.sql(find_options[:order])) if find_options[:order] 333 search_scope = search_scope.references(find_options[:include]) if find_options[:include] 334 335 search_scope 336 end 337 end