[1] | 1 | # This is an add-on to the ActiveRecord::Base class. It allows simple searching to be |
---|
| 2 | # accomplished by using, for example, @movies = Movie.search("text") |
---|
| 3 | module ActiveRecord |
---|
| 4 | class Base |
---|
| 5 | # Allow the user to set the default searchable fields |
---|
| 6 | def self.searches_on(*args) |
---|
| 7 | if not args.empty? and args.first != :all |
---|
| 8 | @searchable_fields = args.collect { |f| f.to_s } |
---|
| 9 | end |
---|
| 10 | end |
---|
| 11 | |
---|
| 12 | # Return the default set of fields to search on |
---|
| 13 | def self.searchable_fields(tables = nil, klass = self) |
---|
| 14 | # If the model has declared what it searches_on, then use that... |
---|
| 15 | return @searchable_fields unless @searchable_fields.nil? |
---|
| 16 | |
---|
| 17 | # ... otherwise, use all text/varchar fields as the default |
---|
| 18 | fields = [] |
---|
| 19 | tables ||= [] |
---|
| 20 | string_columns = klass.columns.select { |c| c.type == :text or c.type == :string } |
---|
| 21 | fields = string_columns.collect { |c| klass.table_name + "." + c.name } |
---|
| 22 | |
---|
| 23 | if not tables.empty? |
---|
| 24 | tables.each do |table| |
---|
| 25 | klass = eval table.to_s.classify |
---|
| 26 | fields += searchable_fields([], klass) |
---|
| 27 | end |
---|
| 28 | end |
---|
| 29 | |
---|
| 30 | return fields |
---|
| 31 | end |
---|
| 32 | |
---|
| 33 | # Search the movie database for the given parameters: |
---|
| 34 | # text = a string to search for |
---|
| 35 | # :only => an array of fields in which to search for the text; |
---|
| 36 | # default is 'all text or string columns' |
---|
| 37 | # :except => an array of fields to exclude from the default searchable columns |
---|
| 38 | # :case => :sensitive or :insensitive |
---|
| 39 | # :include => an array of tables to include in the joins. Fields that |
---|
| 40 | # have searchable text will automatically be included in the default |
---|
| 41 | # set of :search_columns. |
---|
| 42 | # :join_include => an array of tables to include in the joins, but only |
---|
| 43 | # for joining. (Searchable fields will not automatically be included.) |
---|
| 44 | # :conditions => a string of additional conditions (constraints) |
---|
| 45 | # :offset => paging offset (integer) |
---|
| 46 | # :limit => number of rows to return (integer) |
---|
| 47 | # :order => sort order (order_by SQL snippet) |
---|
| 48 | def self.search(text = nil, options = {}) |
---|
| 49 | options.assert_valid_keys(:only, :except, :case, :include, |
---|
| 50 | :join_include, :conditions, :offset, :limit, :order) |
---|
| 51 | case_insensitive = true unless options[:case] == :sensitive |
---|
| 52 | |
---|
| 53 | # The fields to search (default is all text fields) |
---|
| 54 | fields = options[:only] || searchable_fields(options[:include]) |
---|
| 55 | fields -= options[:except] if not options[:except].nil? |
---|
| 56 | |
---|
| 57 | # Now build the SQL for the search if there is text to search for |
---|
| 58 | condition_list = [] |
---|
| 59 | unless text.nil? |
---|
| 60 | text_condition = if case_insensitive |
---|
| 61 | fields.collect { |f| "UCASE(#{f}) LIKE #{sanitize('%'+text.upcase+'%')}" }.join " OR " |
---|
| 62 | else |
---|
| 63 | fields.collect { |f| "#{f} LIKE #{sanitize('%'+text+'%')}" }.join " OR " |
---|
| 64 | end |
---|
| 65 | |
---|
| 66 | # Add the text search term's SQL to the conditions string unless |
---|
| 67 | # the text was nil to begin with. |
---|
| 68 | condition_list << "(" + text_condition + ")" |
---|
| 69 | end |
---|
| 70 | condition_list << "#{options[:conditions]}" if options[:conditions] |
---|
| 71 | conditions = condition_list.join " AND " |
---|
| 72 | conditions = nil if conditions.empty? |
---|
| 73 | |
---|
| 74 | includes = (options[:include] || []) + (options[:join_include] || []) |
---|
| 75 | includes = nil if includes.size == 0 |
---|
| 76 | |
---|
| 77 | find :all, :include => includes, :conditions => conditions, |
---|
| 78 | :offset => options[:offset], :limit => options[:limit], :order => options[:order] |
---|
| 79 | end |
---|
| 80 | end |
---|
| 81 | end |
---|