diff --git a/examples/facebook_v2.rb b/examples/facebook_v2.rb new file mode 100644 index 0000000..57f82e0 --- /dev/null +++ b/examples/facebook_v2.rb @@ -0,0 +1,26 @@ +require 'rubygems' +require 'neography' + +Neography::Config.server = 'neography.org' +@neo = Neography::Rest.new + +def suggestions_for(node) + node.incoming(:friends).order("breadth first").uniqueness("node global").filter("position.length() == 2;").depth(2) +end + +johnathan = Neography::Node.create("name" =>'Johnathan') +mark = Neography::Node.create("name" =>'Mark') +phill = Neography::Node.create("name" =>'Phill') +mary = Neography::Node.create("name" =>'Mary') +luke = Neography::Node.create("name" =>'Luke') + +johnathan.both(:friends) << mark +mark.both(:friends) << mary +mark.both(:friends) << phill +phill.both(:friends) << mary +phill.both(:friends) << luke + +puts "Johnathan should become friends with #{suggestions_for(johnathan).map{|n| n.name }.join(', ')}" + +# RESULT +# Johnathan should become friends with Mary, Phill \ No newline at end of file diff --git a/examples/linkedin_v2.rb b/examples/linkedin_v2.rb new file mode 100644 index 0000000..fd68af0 --- /dev/null +++ b/examples/linkedin_v2.rb @@ -0,0 +1,23 @@ +require 'rubygems' +require 'neography' + +Neography::Config.server = 'neography.org' +@neo = Neography::Rest.new + +johnathan = Neography::Node.create("name" =>'Johnathan') +mark = Neography::Node.create("name" =>'Mark') +phill = Neography::Node.create("name" =>'Phill') +mary = Neography::Node.create("name" =>'Mary') + +johnathan.both(:friends) << mark +mark.both(:friends) << phill +phill.both(:friends) << mary +mark.both(:friends) << mary + +johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).nodes.each do |node| + puts node.map{|n| n.name }.join(' => friends => ') +end + +# RESULT +# Johnathan => friends => Mark => friends => Phill => friends => Mary +# Johnathan => friends => Mark => friends => Mary \ No newline at end of file diff --git a/lib/neography.rb b/lib/neography.rb index 3d53fe1..070e412 100644 --- a/lib/neography.rb +++ b/lib/neography.rb @@ -29,11 +29,12 @@ def find_and_require_user_defined_code require 'neography/property_container' require 'neography/property' require 'neography/node_relationship' +require 'neography/node_path' require 'neography/relationship_traverser' require 'neography/node_traverser' +require 'neography/path_traverser' require 'neography/equal' - require 'neography/node' require 'neography/relationship' diff --git a/lib/neography/node.rb b/lib/neography/node.rb index 602a83f..3cdcff4 100644 --- a/lib/neography/node.rb +++ b/lib/neography/node.rb @@ -1,6 +1,7 @@ module Neography class Node < PropertyContainer include Neography::NodeRelationship + include Neography::NodePath include Neography::Equal include Neography::Property diff --git a/lib/neography/node_path.rb b/lib/neography/node_path.rb new file mode 100644 index 0000000..3bf2d18 --- /dev/null +++ b/lib/neography/node_path.rb @@ -0,0 +1,29 @@ +module Neography + module NodePath + + def all_paths_to(to) + PathTraverser.new(self, to, "allPaths", true) + end + + def all_simple_paths_to(to) + PathTraverser.new(self, to, "allSimplePaths", true) + end + + def all_shortest_paths_to(to) + PathTraverser.new(self, to, "shortestPath", true) + end + + def path_to(to) + PathTraverser.new(self, to, "allPaths", false) + end + + def simple_path_to(to) + PathTraverser.new(self, to, "allSimplePaths", false) + end + + def shortest_path_to(to) + PathTraverser.new(self, to, "shortestPath", false) + end + + end +end \ No newline at end of file diff --git a/lib/neography/node_traverser.rb b/lib/neography/node_traverser.rb index cb31176..308af45 100644 --- a/lib/neography/node_traverser.rb +++ b/lib/neography/node_traverser.rb @@ -48,6 +48,16 @@ def incoming(type) self end + def uniqueness(u) + @uniqueness = u + self + end + + def order(o) + @order = o + self + end + def filter(body) @filter = Hash.new @filter["language"] = "javascript" diff --git a/lib/neography/path_traverser.rb b/lib/neography/path_traverser.rb new file mode 100644 index 0000000..81e356e --- /dev/null +++ b/lib/neography/path_traverser.rb @@ -0,0 +1,93 @@ +module Neography + class PathTraverser + include Enumerable + + attr_accessor :depth, :algorithm, :relationships, :get + + def initialize(from, to, algorithm, all=false, types = nil, dir = "all" ) + @from = from + @to = to + @all = all + @relationships = Array.new + types.each do |type| + @relationships << {"type" => type.to_s, "direction" => dir.to_s } + end unless types.nil? + @get = ["node","rel"] + @loaded = Array.new + end + + def nodes + @get = ["node"] + self + end + + def relationships + @get = ["rel"] + self + end + + alias_method :rels, :relationships + + def both(type) + @relationships << {"type" => type.to_s, "direction" => "all"} + self + end + + def outgoing(type) + @relationships << {"type" => type.to_s, "direction" => "out"} + self + end + + def incoming(type) + @relationships << {"type" => type.to_s, "direction" => "in"} + self + end + + def depth(d) + d = 2147483647 if d == :all + @depth = d + self + end + + def size + [*self].size + end + + alias_method :length, :size + + def each + iterator.each do |path| + paths = Array.new + + if @get.include?("node") + path["nodes"].each_with_index do |n, i| + @loaded[n.split('/').last.to_i] = Neography::Node.load(n) if @loaded.at(n.split('/').last.to_i).nil? + paths[i * 2] = @loaded[n.split('/').last.to_i] + end + end + + if @get.include?("rel") + path["relationships"].each_with_index do |r, i| + @loaded[r.split('/').last.to_i] = Neography::Relationship.load(r) if @loaded.at(r.split('/').last.to_i).nil? + paths[i * 2 + 1] = @loaded[r.split('/').last.to_i] + end + end + + yield paths.compact + end + end + + def empty? + first == nil + end + + def iterator + if @all.nil? + @from.neo_server.get_path(@from, @to, @relationships, @depth, @algorithm) + else + @from.neo_server.get_paths(@from, @to, @relationships, @depth, @algorithm) + end + end + + end +end \ No newline at end of file diff --git a/spec/integration/node_path_spec.rb b/spec/integration/node_path_spec.rb new file mode 100644 index 0000000..6ed6cde --- /dev/null +++ b/spec/integration/node_path_spec.rb @@ -0,0 +1,222 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Neography::NodePath do + + def create_nodes + johnathan = Neography::Node.create("name" =>'Johnathan') + mark = Neography::Node.create("name" =>'Mark') + phill = Neography::Node.create("name" =>'Phill') + mary = Neography::Node.create("name" =>'Mary') + + johnathan.both(:friends) << mark + mark.both(:friends) << mary + mark.both(:friends) << phill + phill.both(:friends) << mary + + [johnathan, mark, phill, mary] + end + + describe "all_paths" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_paths_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_paths_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_paths_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end + + describe "all_simple_paths" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_simple_paths_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end + + describe "all_shortest_paths_to" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_shortest_paths_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_shortest_paths_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.all_shortest_paths_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end + + describe "path_to" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.path_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.path_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.path_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end + + describe "simple_path_to" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.simple_path_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.simple_path_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.simple_path_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end + + describe "shortest_path_to" do + it "can return nodes" do + johnathan, mark, phill, mary = create_nodes + + johnathan.shortest_path_to(mary).incoming(:friends).depth(4).nodes.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_true} + path.map{|n| n.is_a?(Neography::Relationship).should be_false} + end + end + + it "can return relationships" do + johnathan, mark, phill, mary = create_nodes + + johnathan.shortest_path_to(mary).incoming(:friends).depth(4).rels.each do |path| + path.map{|n| n.is_a?(Neography::Node).should be_false} + path.map{|n| n.is_a?(Neography::Relationship).should be_true} + end + end + + it "can return both" do + johnathan, mark, phill, mary = create_nodes + + johnathan.shortest_path_to(mary).incoming(:friends).depth(4).each do |path| + path.each_with_index do |n,i| + if i.even? + n.is_a?(Neography::Node).should be_true + else + n.is_a?(Neography::Relationship).should be_true + end + end + end + end + end +end \ No newline at end of file