searches are now done with Queries, which contain Terms
This commit is contained in:
parent
80151adab2
commit
ffa9e96a9f
|
@ -3,4 +3,6 @@ class Polecat
|
|||
require 'polecat/index_reader'
|
||||
require 'polecat/index_searcher'
|
||||
require 'polecat/document'
|
||||
require 'polecat/query'
|
||||
require 'polecat/term'
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue