0
0
Fork 0

searches are now done with Queries, which contain Terms

This commit is contained in:
Gibheer 2011-06-06 16:08:37 +02:00
parent 80151adab2
commit ffa9e96a9f
8 changed files with 183 additions and 8 deletions

View File

@ -3,4 +3,6 @@ class Polecat
require 'polecat/index_reader'
require 'polecat/index_searcher'
require 'polecat/document'
require 'polecat/query'
require 'polecat/term'
end

View File

@ -35,10 +35,50 @@ class Polecat
@reader.path
end
# searches through all documents
#
# Run the query against the @default_field@ of every stored document to get
# a list of all matching documents.
# @param [String] query a String which get's matched against the documents
# @return [Array] a list of all matching documents
def search query
@reader.read.select do |doc|
doc.attributes.fetch(@default_field).fetch(:value) == query
#doc.attributes.fetch(@default_field).fetch(:value) == query
rs = []
query.terms.each do |term|
val = doc.attributes.fetch(term.field.to_sym).fetch(:value)
if compare val, term.operator, term.value
rs << true
end
end
if query.relation == :and
rs.count == query.terms.count
else
rs.empty?
end
end
end
# compare the document value with the searched value
#
# This compares the two values with the operator
# @return [Any] trueish for matches or falsey
# @private
def compare ival, op, tval
if op == :eq
if tval.class == Regexp
ival.match tval
else
ival == tval
end
elsif op == :gt
ival < tval
elsif op == :lt
ival > tval
else
false
end
end
private :compare
end
end

35
lib/polecat/query.rb Normal file
View File

@ -0,0 +1,35 @@
class Polecat
# The Query manages a number of terms or queries which are set into a
# relation. A relation is needed to say, which documents shall be
# returned.
# In a @and@ relation only the documents, which are returned of the query
# parts get returned. For @or@ all documents found in a part get returned.
class Query
# returns the relation of the terms
# @return [Symbol] :and, :or
attr_accessor :relation
# returns the list of all terms
attr_reader :terms
# creates a new query object
#
# Create a new query object. As a default, the relation is set to @:and@
# (@see Query#relation)
def initialize relation = :and
if relation == :and || relation == :or
@relation = relation
else
raise ArgumentError, 'no valid relation'
end
@terms = []
end
# add a new term or query
def add term
@terms << term
self
end
end
end

21
lib/polecat/term.rb Normal file
View File

@ -0,0 +1,21 @@
class Polecat
class Term
# the field name which should be found
attr_reader :field
# the operator to match the field with the value
attr_reader :operator
# the search value which get's matched against the document field
attr_reader :value
# create a new Term for a query
def initialize field, operator, value
@field = field
@operator = operator
if @operator == :eq && value.class == String
@value = /^#{value}$/
else
@value = value
end
end
end
end

View File

@ -5,16 +5,16 @@ describe "IndexSearcher#search" do
let(:w) { Polecat::IndexWriter.new(path) }
let(:s) { Polecat::IndexSearcher.new :path => path }
it "returns an empty array when nothing was found" do
s.search("foo").should == []
it "returns an empty array when the query is empty" do
s.search(Polecat::Query.new).should == []
end
context "searching on a filled index" do
before do
w.add Spec::FooDocument.new(:name => 'foo')
w.add Spec::FooDocument.new(:name => 'bar')
w.add Spec::FooDocument.new(:name => 'baz')
w.add Spec::FooDocument.new(:name => 'foobar')
w.add Spec::FooDocument.new(:id => 0, :name => 'foo')
w.add Spec::FooDocument.new(:id => 1, :name => 'bar')
w.add Spec::FooDocument.new(:id => 2, :name => 'baz')
w.add Spec::FooDocument.new(:id => 3, :name => 'foobar')
w.write
end
@ -25,8 +25,33 @@ describe "IndexSearcher#search" do
)
end
let (:q1) { Polecat::Query.new.add(Polecat::Term.new(:id, :eq, 1)) }
it "returns an array of documents, when a document was found" do
s.search('foo').count.should == 1
s.search(q1).count.should == 1
end
let (:q2) { Polecat::Query.new.add(Polecat::Term.new(:name, :eq, 'foo')) }
it "returns only matches for a String query" do
s.search(q2).count.should == 1
end
let (:q3) { Polecat::Query.new.add(Polecat::Term.new(:name, :eq, /foo/)) }
it "returns all documents when an regexp is given" do
s.search(q3).count.should == 2
end
let (:q4) { Polecat::Query.new.add(Polecat::Term.new(:id, :eq, 33)) }
it "returns an empty array when no document matched" do
s.search(q4).count.should == 0
end
let (:q5) {
Polecat::Query.new.
add(Polecat::Term.new(:id, :eq, 3)).
add(Polecat::Term.new(:name, :eq, 'foobar'))
}
it "returns a document for a query with multiple terms" do
s.search(q5).count.should == 1
end
end
end

16
spec/query/add_spec.rb Normal file
View File

@ -0,0 +1,16 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "Query#add" do
let (:term1) { Polecat::Term.new(:id, :eq, 23) }
let (:term2) { Polecat::Term.new(:name, :eq, 'foo') }
let (:term3) { Polecat::Term.new(:lastname, :eq, /foo/) }
let (:query) { Polecat::Query.new }
it "returns the query object for chaining" do
query.add(term1).should be(query)
end
it "adds the term to the list of terms" do
query.add(term1).terms.count.should be(1)
end
end

17
spec/query/new_spec.rb Normal file
View File

@ -0,0 +1,17 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "Query#new" do
it "uses 'and' as an default" do
q = Polecat::Query.new
q.relation.should be(:and)
end
it "takes a relation operator as an argument" do
q = Polecat::Query.new :or
q.relation.should be(:or)
end
it "raises an error, when the relation is not known" do
lambda { Polecat::Query.new :foo }.should raise_error(ArgumentError)
end
end

19
spec/term/new_spec.rb Normal file
View File

@ -0,0 +1,19 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "Term#new" do
it "takes a field, a operator and a value" do
t = Polecat::Term.new :id, :eq, 23
t.field.should be(:id)
t.operator.should be(:eq)
t.value.should be(23)
end
it "converts Strings to Regexps, if the operator is :eq" do
t = Polecat::Term.new :name, :eq, "foo"
t.value.should == /^foo$/
end
it "raises an error if no argument is given" do
lambda { Polecat::Term.new }.should raise_error
end
end