summaryrefslogtreecommitdiff
path: root/lib/chef_zero/solr/query
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef_zero/solr/query')
-rw-r--r--lib/chef_zero/solr/query/binary_operator.rb53
-rw-r--r--lib/chef_zero/solr/query/phrase.rb23
-rw-r--r--lib/chef_zero/solr/query/range_query.rb34
-rw-r--r--lib/chef_zero/solr/query/regexpable_query.rb29
-rw-r--r--lib/chef_zero/solr/query/subquery.rb35
-rw-r--r--lib/chef_zero/solr/query/term.rb45
-rw-r--r--lib/chef_zero/solr/query/unary_operator.rb43
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