Skip to content

Commit

Permalink
Initial commit: LuceneQuery, examples and Rakefile
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy Voorhis committed Apr 30, 2008
0 parents commit 44b5d26
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Rakefile
@@ -0,0 +1,9 @@
require 'rake'
require 'spec/rake/spectask'

desc "Run all examples"
Spec::Rake::SpecTask.new('examples') do |t|
t.spec_files = FileList['examples/**/*.rb']
end

task :default => :examples
71 changes: 71 additions & 0 deletions examples/lucene_query.rb
@@ -0,0 +1,71 @@
require File.dirname(__FILE__) + '/../lib/lucene_query'

describe LuceneQuery do

it "should passthru most primitives" do
lambda { :example }.should generate_query("example")
lambda { 42 }.should generate_query("42")
lambda { 3.14159 }.should generate_query("3.14159")
lambda { true }.should generate_query("true")
lambda { false }.should generate_query("false")
end

it "should quote Strings" do
lambda { "example" }.should generate_query("'example'")
end

it "should group Arrays" do
lambda { [:red, :green, :blue] }.should generate_query("(red green blue)")
end

it "should join terms with AND" do
lambda { And(:symbol, 42, "string") }.should generate_query("(symbol AND 42 AND 'string')")
end

it "should join terms with OR" do
lambda { Or(:symbol, 42, "string") }.should generate_query("(symbol OR 42 OR 'string')")
end

it "should support fields" do
lambda { Field(:city, "Portland") }.should generate_query("city:'Portland'")
lambda { Field("city", "Portland") }.should generate_query("'city':'Portland'")
end

it "should AND together Hash terms" do
lambda { { :city => "Portland", :state => "Oregon" } }.should generate_query("(state:'Oregon' AND city:'Portland')")
end

it "should OR together IN terms" do
lambda { In(:id, [110, 220, 330]) }.should generate_query("(id:110 OR id:220 OR id:330)")
end

it "should require terms" do
lambda { Required("lucene") }.should generate_query("+'lucene'")
lambda { { :marine_life => [Required("fish"), Required("dolphins")] } }.should generate_query("(marine_life:(+'fish' +'dolphins'))")
end

it "should prohibit terms" do
lambda { Prohibit("bugs") }.should generate_query("-'bugs'")
lambda { { :marine_life => [Required("fish"), Prohibit("eels")] } }.should generate_query("(marine_life:(+'fish' -'eels'))")
end
end

class QueryMatcher
def initialize(expected)
@expected = expected
end

def matches?(target)
@target = target
@actual = LuceneQuery.new(&@target).to_s
@expected == @actual
end

def failure_message
"\tExpected\n#@expected\n\tbut received\n#@actual"
end
end

def generate_query(query)
QueryMatcher.new(query)
end
113 changes: 113 additions & 0 deletions lib/lucene_query.rb
@@ -0,0 +1,113 @@
class LuceneQuery
## Syntax Nodes
::String.class_eval do
def to_lucene; "'#{escape_lucene}'" end
def parens; "(#{self})" end
def escape_lucene
gsub(/([-+!\(\)\{\}\[\]^"~*?:\\]|&&|\|\|)/) { |m| "\\#{m}" }
end
end

::Symbol.class_eval do
def to_lucene; to_s end
end

::Array.class_eval do
def to_lucene
hd, *tl = self
tl.inject(hd.to_lucene) { |q,t| q + " " + t.to_lucene }.parens
end
end

::Hash.class_eval do
def to_lucene
inner = map { |k,v| Field.new(k, v) }
LuceneQuery::And.new(*inner).to_lucene
end
end

::Numeric.module_eval do
def to_lucene; to_s end
end

::TrueClass.class_eval do
def to_lucene; to_s end
end

::FalseClass.class_eval do
def to_lucene; to_s end
end

class Field
def initialize(key, val)
@key, @val = key, val
end

def to_lucene
@key.to_lucene + ":" + @val.to_lucene
end
end

class BooleanOperator
def initialize(*terms) @terms = terms end

def to_lucene
hd, *tl = @terms
tl.inject(hd.to_lucene) { |q,t|
q + " " + operator + " " + t.to_lucene
}.parens
end
end

class And < BooleanOperator
def operator; "AND" end
end

class Or < BooleanOperator
def operator; "OR" end
end

class Not
def initialize(term) @term = term end

def to_lucene
"NOT #{@term.to_lucene}"
end
end

class Required
def initialize(term) @term = term end

def to_lucene
"+" + @term.to_lucene
end
end

class Prohibit
def initialize(term) @term = term end

def to_lucene
"-" + @term.to_lucene
end
end

## DSL Helpers
def Field(key, val) Field.new(key, val) end
def And(*terms) And.new(*terms) end
def Or(*terms) Or.new(*terms) end
def In(field, terms)
Or.new(*terms.map { |term| Field.new(field, term) })
end
def Not(term) Not.new(term) end
def Required(term) Required.new(term) end
def Prohibit(term) Prohibit.new(term) end

def initialize(&block)
@term = instance_eval(&block)
end

def to_s; @term.to_lucene end
alias :to_str :to_s
end

SolrQuery = LuceneQuery unless defined?(SolrQuery)

0 comments on commit 44b5d26

Please sign in to comment.