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_reader'
|
||||||
require 'polecat/index_searcher'
|
require 'polecat/index_searcher'
|
||||||
require 'polecat/document'
|
require 'polecat/document'
|
||||||
|
require 'polecat/query'
|
||||||
|
require 'polecat/term'
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,10 +35,50 @@ class Polecat
|
||||||
@reader.path
|
@reader.path
|
||||||
end
|
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
|
def search query
|
||||||
@reader.read.select do |doc|
|
@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
|
||||||
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
|
||||||
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(:w) { Polecat::IndexWriter.new(path) }
|
||||||
let(:s) { Polecat::IndexSearcher.new :path => path }
|
let(:s) { Polecat::IndexSearcher.new :path => path }
|
||||||
|
|
||||||
it "returns an empty array when nothing was found" do
|
it "returns an empty array when the query is empty" do
|
||||||
s.search("foo").should == []
|
s.search(Polecat::Query.new).should == []
|
||||||
end
|
end
|
||||||
|
|
||||||
context "searching on a filled index" do
|
context "searching on a filled index" do
|
||||||
before do
|
before do
|
||||||
w.add Spec::FooDocument.new(:name => 'foo')
|
w.add Spec::FooDocument.new(:id => 0, :name => 'foo')
|
||||||
w.add Spec::FooDocument.new(:name => 'bar')
|
w.add Spec::FooDocument.new(:id => 1, :name => 'bar')
|
||||||
w.add Spec::FooDocument.new(:name => 'baz')
|
w.add Spec::FooDocument.new(:id => 2, :name => 'baz')
|
||||||
w.add Spec::FooDocument.new(:name => 'foobar')
|
w.add Spec::FooDocument.new(:id => 3, :name => 'foobar')
|
||||||
w.write
|
w.write
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,8 +25,33 @@ describe "IndexSearcher#search" do
|
||||||
)
|
)
|
||||||
end
|
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
|
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
|
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