diff options
Diffstat (limited to 'lib/chef_zero/solr/query')
-rw-r--r-- | lib/chef_zero/solr/query/binary_operator.rb | 53 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/phrase.rb | 23 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/range_query.rb | 34 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/regexpable_query.rb | 29 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/subquery.rb | 35 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/term.rb | 45 | ||||
-rw-r--r-- | lib/chef_zero/solr/query/unary_operator.rb | 43 |
7 files changed, 262 insertions, 0 deletions
diff --git a/lib/chef_zero/solr/query/binary_operator.rb b/lib/chef_zero/solr/query/binary_operator.rb new file mode 100644 index 0000000..bd8fa4c --- /dev/null +++ b/lib/chef_zero/solr/query/binary_operator.rb @@ -0,0 +1,53 @@ +module ChefZero + module Solr + module Query + class BinaryOperator + def initialize(left, operator, right) + @left = left + @operator = operator + @right = right + end + + def to_s + "(#{left} #{operator} #{right})" + end + + attr_reader :left + attr_reader :operator + attr_reader :right + + def matches_doc?(doc) + case @operator + when 'AND' + left.matches_doc?(doc) && right.matches_doc?(doc) + when 'OR' + left.matches_doc?(doc) || right.matches_doc?(doc) + when '^' + left.matches_doc?(doc) + when ':' + if left.respond_to?(:literal_string) && left.literal_string + value = doc[left.literal_string] + right.matches_values?([value]) + else + values = doc.matching_values { |key| left.matches_values?([key]) } + right.matches_values?(values) + end + end + end + + def matches_values?(values) + case @operator + when 'AND' + left.matches_values?(values) && right.matches_values?(values) + when 'OR' + left.matches_values?(values) || right.matches_values?(values) + when '^' + left.matches_values?(values) + when ':' + raise ": does not work inside a : or term" + end + end + end + end + end +end diff --git a/lib/chef_zero/solr/query/phrase.rb b/lib/chef_zero/solr/query/phrase.rb new file mode 100644 index 0000000..f229da9 --- /dev/null +++ b/lib/chef_zero/solr/query/phrase.rb @@ -0,0 +1,23 @@ +require 'chef_zero/solr/query/regexpable_query' + +module ChefZero + module Solr + module Query + class Phrase < RegexpableQuery + def initialize(terms) + # Phrase is terms separated by whitespace + if terms.size == 0 && terms[0].literal_string + literal_string = terms[0].literal_string + else + literal_string = nil + end + super(terms.map { |term| term.regexp_string }.join("#{NON_WORD_CHARACTER}+"), literal_string) + end + + def to_s + "Phrase(\"#{@regexp_string}\")" + end + end + end + end +end diff --git a/lib/chef_zero/solr/query/range_query.rb b/lib/chef_zero/solr/query/range_query.rb new file mode 100644 index 0000000..db92548 --- /dev/null +++ b/lib/chef_zero/solr/query/range_query.rb @@ -0,0 +1,34 @@ +module ChefZero + module Solr + module Query + class RangeQuery + def initialize(from, to, from_inclusive, to_inclusive) + @from = from + @to = to + @from_inclusive = from_inclusive + @to_inclusive = to_inclusive + end + + def to_s + "#{@from_inclusive ? '[' : '{'}#{@from} TO #{@to}#{@to_inclusive ? '[' : '{'}" + end + + def matches?(key, value) + case @from <=> value + when -1 + return false + when 0 + return false if !@from_inclusive + end + case @to <=> value + when 1 + return false + when 0 + return false if !@to_inclusive + end + return true + end + end + end + end +end diff --git a/lib/chef_zero/solr/query/regexpable_query.rb b/lib/chef_zero/solr/query/regexpable_query.rb new file mode 100644 index 0000000..5166309 --- /dev/null +++ b/lib/chef_zero/solr/query/regexpable_query.rb @@ -0,0 +1,29 @@ +module ChefZero + module Solr + module Query + class RegexpableQuery + def initialize(regexp_string, literal_string) + @regexp_string = regexp_string + # Surround the regexp with word boundaries + @regexp = Regexp.new("(^|#{NON_WORD_CHARACTER})#{regexp_string}($|#{NON_WORD_CHARACTER})", true) + @literal_string = literal_string + end + + attr_reader :literal_string + attr_reader :regexp_string + attr_reader :regexp + + def matches_doc?(doc) + value = doc[DEFAULT_FIELD] + return value ? matches_values?([value]) : false + end + def matches_values?(values) + values.any? { |value| !@regexp.match(value).nil? } + end + + WORD_CHARACTER = "[A-Za-z0-9@._':]" + NON_WORD_CHARACTER = "[^A-Za-z0-9@._':]" + end + end + end +end diff --git a/lib/chef_zero/solr/query/subquery.rb b/lib/chef_zero/solr/query/subquery.rb new file mode 100644 index 0000000..3727a20 --- /dev/null +++ b/lib/chef_zero/solr/query/subquery.rb @@ -0,0 +1,35 @@ +module ChefZero + module Solr + module Query + class Subquery + def initialize(subquery) + @subquery = subquery + end + + def to_s + "(#{@subquery})" + end + + def literal_string + subquery.literal_string + end + + def regexp + subquery.regexp + end + + def regexp_string + subquery.regexp_string + end + + def matches_doc?(doc) + subquery.matches_doc?(doc) + end + + def matches_values?(values) + subquery.matches_values?(values) + end + end + end + end +end diff --git a/lib/chef_zero/solr/query/term.rb b/lib/chef_zero/solr/query/term.rb new file mode 100644 index 0000000..23f4a72 --- /dev/null +++ b/lib/chef_zero/solr/query/term.rb @@ -0,0 +1,45 @@ +require 'chef_zero/solr/query/regexpable_query' + +module ChefZero + module Solr + module Query + class Term < RegexpableQuery + def initialize(term) + # Get rid of escape characters, turn * and ? into .* and . for regex, and + # escape everything that needs escaping + literal_string = "" + regexp_string = "" + index = 0 + while index < term.length + if term[index] == '*' + regexp_string << "#{WORD_CHARACTER}*" + literal_string = nil + index += 1 + elsif term[index] == '?' + regexp_string << WORD_CHARACTER + literal_string = nil + index += 1 + elsif term[index] == '~' + raise "~ unsupported" + else + if term[index] == '\\' + index = index+1 + if index >= term.length + raise "Backslash at end of string '#{term}'" + end + end + literal_string << term[index] if literal_string + regexp_string << Regexp.escape(term[index]) + index += 1 + end + end + super(regexp_string, literal_string) + end + + def to_s + "Term(#{regexp_string})" + end + end + end + end +end diff --git a/lib/chef_zero/solr/query/unary_operator.rb b/lib/chef_zero/solr/query/unary_operator.rb new file mode 100644 index 0000000..fc46c0d --- /dev/null +++ b/lib/chef_zero/solr/query/unary_operator.rb @@ -0,0 +1,43 @@ +module ChefZero + module Solr + module Query + class UnaryOperator + def initialize(operator, operand) + @operator = operator + @operand = operand + end + + def to_s + "#{operator} #{operand}" + end + + attr_reader :operator + attr_reader :operand + + def matches_doc?(doc) + case @operator + when '-' + when 'NOT' + !operand.matches_doc?(doc) + when '+' + # TODO This operator uses relevance to eliminate other, unrelated + # expressions. +a OR b means "if it has b but not a, don't return it" + raise "+ not supported yet, because it is hard." + end + end + + def matches_values?(values) + case @operator + when '-' + when 'NOT' + !operand.matches_values?(values) + when '+' + # TODO This operator uses relevance to eliminate other, unrelated + # expressions. +a OR b means "if it has b but not a, don't return it" + raise "+ not supported yet, because it is hard." + end + end + end + end + end +end |