root/lib/search.rb

Revision 1, 3.4 kB (checked in by falcon, 16 years ago)

Version one -> initial work from the laptop.

Line 
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")
3module 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
81end
Note: See TracBrowser for help on using the browser.