Browse files

Use Cypher query when finder method does not have a lucene idex, #210

  • Loading branch information...
1 parent 5b73375 commit fb25b117b740e03ed32ba334fe3e59c390348a34 @andreasronge andreasronge committed Aug 14, 2012
Showing with 87 additions and 5 deletions.
  1. +47 −4 lib/neo4j/rails/finders.rb
  2. +40 −1 spec/integration/finders_spec.rb
View
51 lib/neo4j/rails/finders.rb
@@ -57,7 +57,7 @@ def index(*args)
if self._decl_props[field.to_sym] && self._decl_props[field.to_sym][:type] == Fixnum
module_eval <<-RUBY, __FILE__, __LINE__
def self.all_by_#{field}(value)
- find_with_indexer(:#{field} => value)
+ find_with_indexer_or_traversal(:#{field} => value)
end
def self.find_by_#{field}(value)
all_by_#{field}(value).first
@@ -66,7 +66,7 @@ def self.find_by_#{field}(value)
else
module_eval <<-RUBY, __FILE__, __LINE__
def self.all_by_#{field}(value)
- find_with_indexer("#{field}: \\"\#{value}\\"")
+ find_with_indexer_or_traversal("#{field}: \\"\#{value}\\"")
end
def self.find_by_#{field}(value)
@@ -175,13 +175,19 @@ def all(*args, &block)
if ids
[find_with_ids(ids)].flatten
else
- find_with_indexer(*args, &block)
+ find_with_indexer_or_traversal(*args, &block)
end
end
end
def first(*args, &block)
- all(*args, &block).first
+ found = all(*args, &block).first
+ if found && args.first.is_a?(Hash) && args.first.include?(:id)
+ # if search for an id then all the other properties must match
+ args.first.find{|k,v| k != :id && found.send(k) != v} ? nil : found
+ else
+ found
+ end
end
def last(*args)
@@ -245,6 +251,28 @@ def findable?(entity)
entity.is_a?(self) and entity.reachable_from_ref_node?
end
+
+ def use_traversal_finder?(*args)
+ # Conditions for using a traversal:
+ # 1. the first argument is a hash
+ return false unless args.first.is_a?(Hash)
+
+ # 2. no support for :condition hash
+ return false if args.first.include?(:conditions)
+
+ # 3. there is at least one property which does not have a lucene index
+ args.first.keys.find{|k| !index?(k)}
+ end
+
+
+ def find_with_indexer_or_traversal(*args, &block)
+ if use_traversal_finder?(*args)
+ find_with_traversal(args.first)
+ else
+ find_with_indexer(*args, &block)
+ end
+ end
+
def find_with_indexer(*args, &block)
hits = if args.first.is_a?(Hash) && args.first.include?(:conditions)
params = args.first.clone
@@ -262,6 +290,21 @@ def find_with_indexer(*args, &block)
hits
end
+ def find_with_traversal(conditions)
+ this = self
+ all.query do |cypher|
+ conditions.each_pair do |k,v|
+ if this._decl_rels.keys.include?(k)
+ n = node(v.id)
+ rel_name = rel(this._decl_rels[k].rel_type)
+ this._decl_rels[k].dir == :outgoing ? cypher > rel_name > n : cypher < rel_name < n
+ else
+ cypher[k] == v
+ end
+ end
+ end
+ end
+
# Find the first object or create/initialize it.
#
# @example Find or perform an action.
View
41 spec/integration/finders_spec.rb
@@ -7,8 +7,10 @@
create_model do
property :name, :index => :exact
property :age, :type => Fixnum, :index => :exact
+ property :status, :default => 'normal'
validates_presence_of :name
has_n(:items)
+ has_n(:items_from).from(:items)
def to_s
name
end
@@ -19,9 +21,46 @@ def to_s
before(:each) do
@test_0 = findable_class.create!(:name => "Test 0")
- @test_2 = findable_class.create!(:name => "Test 2")
+ @test_2 = findable_class.create!(:name => "Test 2", :status => 'warning')
@test_3 = findable_class.create!(:name => "Test 3", :age => 3)
@test_4 = findable_class.create!(:name => "Test 1")
+
+ @test_2.items << @test_3 << @test_4
+ @test_2.save!
+ @test_4.items << @test_0
+ @test_4.save!
+ end
+
+ context "find without using a lucene index (use cypher)" do
+ it "can find it with #all" do
+ findable_class.all(:status => 'warning').first.should == @test_2
+ findable_class.all(:status => 'normal').to_a.should =~ [@test_0, @test_3, @test_4]
+ end
+
+ it "can find it with #first" do
+ findable_class.first(:status => 'warning').should == @test_2
+ end
+
+ it "can find it with #find" do
+ findable_class.find(:status => 'warning').should == @test_2
+ end
+
+ it 'can find outgoing relationships' do
+ findable_class.all(:items => @test_4).first.should == @test_2
+ findable_class.first(:items => @test_4).should == @test_2
+ findable_class.all(:items => @test_3).first.should == @test_2
+ findable_class.first(:items => @test_3).should == @test_2
+ findable_class.all(:items => @test_2).first.should be_nil
+ findable_class.first(:items => @test_0).should == @test_4
+ end
+
+ it 'can find incoming relationships' do
+ findable_class.all(:items_from => @test_2).to_a.should =~ [@test_3, @test_4]
+ end
+
+ it 'should not find things that should not be found' do
+ findable_class.first(:status => 'oj').should be_nil
+ end
end
context "index property with type Fixnum, :age, :type => Fixnum" do

0 comments on commit fb25b11

Please sign in to comment.