negate search conditions

This commit is contained in:
Emi Matchu 2010-05-15 12:43:54 -04:00
parent 851aa49091
commit 5b86e640b0
3 changed files with 158 additions and 100 deletions

View file

@ -2,6 +2,8 @@ source 'http://rubygems.org'
gem 'rails', '3.0.0.beta2' gem 'rails', '3.0.0.beta2'
gem 'arel', :git => 'http://github.com/ernie/arel.git'
gem 'sqlite3-ruby', :require => 'sqlite3' gem 'sqlite3-ruby', :require => 'sqlite3'
group :test do group :test do

View file

@ -23,6 +23,8 @@ class Item < ActiveRecord::Base
in_phrase = !in_phrase in_phrase = !in_phrase
elsif c == ':' && !in_phrase elsif c == ':' && !in_phrase
query_conditions.last.to_property! query_conditions.last.to_property!
elsif c == '-' && !in_phrase && query_conditions.last.empty?
query_conditions.last.negate!
else else
query_conditions.last << c query_conditions.last << c
end end
@ -35,26 +37,34 @@ class Item < ActiveRecord::Base
private private
class Condition < String class Condition < String
attr_reader :property
def to_property! def to_property!
@property = self.clone @property = self.clone
self.replace '' self.replace ''
end end
def negate!
@negative = true
end
def narrow(scope) def narrow(scope)
items = Table(:objects)
if @property == 'species' if @property == 'species'
species = Species.find_by_name(self) species = Species.find_by_name(self)
# TODO: add a many-to-many table to handle this relationship # TODO: add a many-to-many table to handle this relationship
scope.where('species_support_ids = ? OR species_support_ids LIKE ? OR species_support_ids LIKE ? OR species_support_ids LIKE ?', condition = items[:species_support_ids].matches_any(
species.id, species.id,
"#{species.id},%", "#{species.id},%",
"%,#{species.id},%", "%,#{species.id},%",
"%,#{species.id}" "%,#{species.id}"
) )
else else
scope.where('name LIKE :matcher OR description LIKE :matcher', :matcher => "%#{self}%") matcher = "%#{self}%"
condition = items[:name].matches(matcher).or(
items[:description].matches(matcher)
)
end end
condition = condition.not if @negative
scope.where(condition)
end end
def inspect def inspect

View file

@ -1,6 +1,7 @@
require 'spec_helper' require 'spec_helper'
describe Item do describe Item do
context "an item" do
specify "should accept string or array for species_support_ids" do specify "should accept string or array for species_support_ids" do
items = [ items = [
Factory.build(:item, :species_support_ids => '1,2,3'), Factory.build(:item, :species_support_ids => '1,2,3'),
@ -8,8 +9,10 @@ describe Item do
] ]
items.each { |i| i.species_support_ids.should == [1,2,3] } items.each { |i| i.species_support_ids.should == [1,2,3] }
end end
end
specify "class should search name for word" do context "class" do
specify "should search name for word" do
query_should 'blue', query_should 'blue',
:return => [ :return => [
'A Hat That is Blue', 'A Hat That is Blue',
@ -23,7 +26,7 @@ describe Item do
] ]
end end
specify "class should search name for phrase" do specify "should search name for phrase" do
query_should '"one two"', query_should '"one two"',
:return => [ :return => [
'Zero one two three', 'Zero one two three',
@ -38,7 +41,7 @@ describe Item do
] ]
end end
specify "class should search name for multiple words" do specify "should search name for multiple words" do
query_should 'one two', query_should 'one two',
:return => [ :return => [
'Zero one two three', 'Zero one two three',
@ -53,7 +56,7 @@ describe Item do
] ]
end end
specify "class should search name for words and phrases" do specify "should search name for words and phrases" do
query_should 'zero "one two" three', query_should 'zero "one two" three',
:return => [ :return => [
'zero one two three', 'zero one two three',
@ -69,7 +72,7 @@ describe Item do
] ]
end end
specify "class should search name and description" do specify "should search name and description" do
query_should 'zero "one two"', query_should 'zero "one two"',
:return => [ :return => [
'zero one two', 'zero one two',
@ -84,7 +87,7 @@ describe Item do
] ]
end end
specify "class should search by species" do specify "should search by species" do
[[2],[1,2,3],[2,3],[3],[1,3]].each do |ids| [[2],[1,2,3],[2,3],[3],[1,3]].each do |ids|
Factory.create :item, :species_support_ids => ids Factory.create :item, :species_support_ids => ids
end end
@ -93,7 +96,7 @@ describe Item do
Item.search('species:blumaroo').count.should == 4 Item.search('species:blumaroo').count.should == 4
end end
specify "class should search by species and words" do specify "should search by species and words" do
Factory.create :item, :name => 'Blue Hat', :species_support_ids => [1] Factory.create :item, :name => 'Blue Hat', :species_support_ids => [1]
Factory.create :item, :name => 'Very Blue Hat', :species_support_ids => [1,2] Factory.create :item, :name => 'Very Blue Hat', :species_support_ids => [1,2]
Factory.create :item, :name => 'Red Hat', :species_support_ids => [2] Factory.create :item, :name => 'Red Hat', :species_support_ids => [2]
@ -102,4 +105,47 @@ describe Item do
Item.search('red species:acara').count.should == 0 Item.search('red species:acara').count.should == 0
Item.search('red species:aisha').count.should == 1 Item.search('red species:aisha').count.should == 1
end end
specify "should be able to negate word in search" do
query_should 'hat -blue',
:return => [
'Green Hat',
'Red Hat',
'Blu E Hat',
['Yellow Hat', 'This hat is not green!']
],
:not_return => [
'Blue Hat',
'Green Shirt',
'Blue Shirt',
['Orange Hat', 'This hat is not blue!'],
['Blue Pants', 'This is not a hat!']
]
end
specify "should be able to negate species in search" do
Factory.create :item, :name => 'Blue Hat', :species_support_ids => [1]
Factory.create :item, :name => 'Very Blue Hat', :species_support_ids => [1,2]
Factory.create :item, :name => 'Red Hat', :species_support_ids => [1,2]
Factory.create :item, :name => 'Green Hat', :species_support_ids => [3]
Factory.create :item, :name => 'Red Shirt', :species_support_ids => [3]
Item.search('hat -species:acara').count.should == 1
Item.search('hat -species:aisha').count.should == 2
Item.search('hat -species:acara -species:aisha').count.should == 1
end
specify "should be able to negate phrase in search" do
query_should 'zero -"one two"',
:return => [
'Zero two one',
['Two one', 'Zero'],
'One three two zero'
],
:not_return => [
'Zero one two',
['Zero', 'one two three'],
['One two four', 'Zero one']
]
end
end
end end