Class SearchApi::TextCriterion
In: lib/search_api/text_criterion.rb
Parent: Object

Utility class that implements fulltext search.

Includes some Google-like features.

Methods

Attributes

mandatory_keywords  [RW] 
meta_keywords  [RW] 
negative_keywords  [RW] 
optional_keywords  [RW] 

Public Class methods

[Source]

    # File lib/search_api/text_criterion.rb, line 21
21:     def initialize(inSearchString='', inOptions= {})
22:       # inOptions may contain :
23:       # :exclude => string, Regexp, or list of Strings and Regexp to exclude (strings are case insensitive)
24:     
25:       @options = inOptions
26:       @options[:exclude] = [@options[:exclude]] unless @options[:exclude].nil? || (@options[:exclude].is_a?(Enumerable) && !@options[:exclude].is_a?(String))
27:       @options[:parse_meta?] = true if @options[:parse_meta?].nil?
28:     
29:       @meta_keywords = {}
30:       @mandatory_keywords = []
31:       @negative_keywords = []
32:       @optional_keywords = []
33:     
34:       unless inSearchString.blank?
35:     
36:         currentMeta = nil
37:       
38:         splitter = /
39:               (
40:                   [-+]?\b[^ ":]+\b:?
41:               )
42:               |
43:               (
44:                   [-+]?"[^"]*"
45:               )
46:             /x
47:     
48:         inSearchString.gsub(/\s+/, ' ').scan(splitter).each { |keyword|
49:           keyword=(keyword[0]||keyword[1]).gsub(/"/, '')
50:       
51:           if currentMeta
52:             @meta_keywords[currentMeta] ||= []
53:             @meta_keywords[currentMeta] << keyword
54:             currentMeta = nil
55:           else
56:             case keyword
57:             when /^-/
58:               @negative_keywords << keyword[1..-1] unless exclude_keyword?(keyword[1..-1])
59:             when /^\+/
60:               @mandatory_keywords << keyword[1..-1] unless exclude_keyword?(keyword[1..-1])
61:             when /:$/
62:               if @options[:parse_meta?]
63:                 currentMeta = keyword[0..-2]
64:               else
65:                 @optional_keywords << keyword unless exclude_keyword?(keyword)
66:               end
67:             else
68:               @optional_keywords << keyword unless exclude_keyword?(keyword)
69:             end
70:           end
71:         }
72:     
73:         # if everything is excluded, look for the whole search string
74:         @optional_keywords << inSearchString if @meta_keywords.empty? && @mandatory_keywords.empty? && @negative_keywords.empty? && @optional_keywords.empty?
75:       end
76:     end

Public Instance methods

[Source]

     # File lib/search_api/text_criterion.rb, line 99
 99:     def condition(inFields)
100:       conditions = SqlFragment.new
101:     
102:       conditions << (@mandatory_keywords.inject(SqlFragment.new) { |cv, value|
103:         value = "%#{value}%"
104:         cv.and(inFields.inject(SqlFragment.new) { |cf, field|
105:           cf.or(["#{field} like ?", value])
106:         })
107:       })
108:   
109:       conditions << (@negative_keywords.inject(SqlFragment.new) { |cv, value|
110:         value = "%#{value}%"
111:         cv.and(inFields.inject(SqlFragment.new) { |cf, field|
112:           cf.or(["#{field} is not null AND #{field} like ?", value])
113:         })
114:       }.not)
115:   
116:       conditions << (@optional_keywords.inject(SqlFragment.new) { |cv, value|
117:         value = "%#{value}%"
118:         cv.or(inFields.inject(SqlFragment.new) { |cf, field|
119:           cf.or(["#{field} like ?", value])
120:         })
121:       })
122:     
123:       conditions
124:     end

[Source]

    # File lib/search_api/text_criterion.rb, line 95
95:     def positive_keywords
96:       @mandatory_keywords + @optional_keywords
97:     end

[Source]

    # File lib/search_api/text_criterion.rb, line 78
78:     def to_s
79:       chunks = []
80:       chunks += @mandatory_keywords.map { |x| (x =~ / /) ? "+\"#{x}\"" : "+#{x}" } unless @mandatory_keywords.blank?
81:       chunks += @negative_keywords.map { |x| (x =~ / /) ? "-\"#{x}\"" : "-#{x}" } unless @negative_keywords.blank?
82:       chunks += @optional_keywords.map { |x| (x =~ / /) ? "\"#{x}\"" : x.to_s } unless @optional_keywords.blank?
83:       chunks += @meta_keywords.inject([]) { |s, key_value|
84:         key, value = key_value
85:         if value.is_a?(Array)
86:           s += value.map { |x| (x =~ / /) ? "#{key}:\"#{x}\"" : "#{key}:#{x}" }
87:         else
88:           s << ((value =~ / /) ? "#{key}:\"#{value}\"" : "#{key}:#{value}")
89:         end
90:         s
91:       } if @meta_keywords
92:       chunks.join(' ')
93:     end

Protected Instance methods

[Source]

     # File lib/search_api/text_criterion.rb, line 127
127:       def exclude_keyword?(inKeyword)
128:         return false unless @options[:exclude]
129:         return @options[:exclude].any? { |exclude| inKeyword =~ (exclude.is_a?(Regexp) ? exclude : Regexp.new(Regexp.escape(exclude), 'i')) }
130:       end

[Validate]