Module: FullSearch::QueryParser
- Defined in:
- lib/full_search/query_parser.rb
Defined Under Namespace
Classes: Token
Class Method Summary collapse
- .build_and_expression(nodes) ⇒ Object
- .escape(value) ⇒ Object
- .node_to_match(node) ⇒ Object
- .parse(query) ⇒ Object
- .parse_and_clause(tokens) ⇒ Object
- .split_or(tokens) ⇒ Object
- .to_match_expression(parsed) ⇒ Object
- .tokenize(query) ⇒ Object
Class Method Details
.build_and_expression(nodes) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/full_search/query_parser.rb', line 38 def self.build_and_expression(nodes) non_excludes = nodes.reject { |n| n.first == :exclude } excludes = nodes.select { |n| n.first == :exclude } parts = non_excludes.map { |n| node_to_match(n) } result = parts.size == 1 ? parts.first : "(#{parts.join(' AND ')})" excludes.each do |node| _, value = node result = "#{result} NOT \"#{escape(value)}\"" end result end |
.escape(value) ⇒ Object
122 123 124 |
# File 'lib/full_search/query_parser.rb', line 122 def self.escape(value) value.to_s.gsub('"', '""') end |
.node_to_match(node) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/full_search/query_parser.rb', line 53 def self.node_to_match(node) type, value = node case type when :term '"' + escape(value) + '"*' when :phrase '"' + escape(value) + '"' when :exclude nil end end |
.parse(query) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/full_search/query_parser.rb', line 9 def self.parse(query) tokens = tokenize(query.to_s.strip) return [] if tokens.empty? or_clauses = split_or(tokens) if or_clauses.size == 1 parse_and_clause(or_clauses.first) else [:or, or_clauses.map { |clause| parse_and_clause(clause) }] end end |
.parse_and_clause(tokens) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/full_search/query_parser.rb', line 110 def self.parse_and_clause(tokens) nodes = tokens.map do |token| case token.type when :term then [:term, token.value] when :phrase then [:phrase, token.value] when :exclude then [:exclude, token.value] end end nodes.size == 1 ? nodes.first : [:and, nodes] end |
.split_or(tokens) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/full_search/query_parser.rb', line 94 def self.split_or(tokens) clauses = [] current = [] tokens.each do |token| if token.type == :or clauses << current unless current.empty? current = [] else current << token end end clauses << current unless current.empty? clauses end |
.to_match_expression(parsed) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/full_search/query_parser.rb', line 22 def self.to_match_expression(parsed) return '""' if parsed.empty? type, value = parsed case type when :and build_and_expression(value) when :or value.map { |sub| to_match_expression(sub) }.join(" OR ") else node_to_match(parsed) end end |
.tokenize(query) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/full_search/query_parser.rb', line 65 def self.tokenize(query) tokens = [] scanner = StringScanner.new(query) until scanner.eos? scanner.skip(/\s+/) break if scanner.eos? if scanner.scan(/"/) phrase = scanner.scan_until(/"/) if phrase tokens << Token.new(:phrase, phrase.chomp('"')) else tokens << Token.new(:term, scanner.rest) scanner.terminate end elsif scanner.scan(/-/) scanner.skip(/\s+/) tokens << Token.new(:exclude, scanner.scan(/\S+/)) elsif scanner.scan(/OR|or/i) tokens << Token.new(:or, nil) else tokens << Token.new(:term, scanner.scan(/\S+/)) end end tokens end |