diff --git a/Gemfile.lock b/Gemfile.lock index 17d0604..7eb1dae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - neography (0.0.3) + neography (0.0.4) httparty (~> 0.6.1) json diff --git a/README.rdoc b/README.rdoc index ed64ffa..1af151e 100644 --- a/README.rdoc +++ b/README.rdoc @@ -104,6 +104,21 @@ To Use: Please see the specs for more examples. +Experimental: + + nodes = @neo.create_nodes(5) # Create 5 empty nodes + nodes = @neo.create_nodes_threaded(5) # Create 5 empty nodes using threads + nodes = @neo.create_node_nodes([{"age" => 31, "name" => "Max"}, + {"age" => 24, "name" => "Alex"}) # Create two nodes with properties + nodes = @neo.create_node_nodes_threaded([{"age" => 31, "name" => "Max"}, + {"age" => 24, "name" => "Alex"}) # Create two nodes with properties threaded + nodes = @neo.get_nodes([17,86,397,33]) # Get four nodes by their id + + one_set_nodes = @neo.create_nodes(3) + another_node = @neo.create_node("age" => 31, "name" => "Max") + nodes = @neo.get_nodes([one_set_nodes, another_node]) # Get four nodes + + See Neo4j API for: * {Order}[http://components.neo4j.org/neo4j-examples/1.2.M04/apidocs/org/neo4j/graphdb/Traverser.Order.html] * {Uniqueness}[http://components.neo4j.org/neo4j-examples/1.2.M04/apidocs/org/neo4j/kernel/Uniqueness.html] diff --git a/lib/neography/rest.rb b/lib/neography/rest.rb index 81d1499..63582a5 100644 --- a/lib/neography/rest.rb +++ b/lib/neography/rest.rb @@ -1,15 +1,16 @@ module Neography class Rest include HTTParty - attr_accessor :protocol, :server, :port, :log_file, :log_enabled, :logger + attr_accessor :protocol, :server, :port, :log_file, :log_enabled, :logger, :max_threads - def initialize(protocol='http://', server='localhost', port=7474, log_file='neography.log', log_enabled=false) + def initialize(protocol='http://', server='localhost', port=7474, log_file='neography.log', log_enabled=false, max_threads=20) @protocol = protocol @server = server @port = port @log_file = log_file @log_enabled = log_enabled @logger = Logger.new(@log_file) if @log_enabled + @max_threads = max_threads end def configure(protocol, server, port) @@ -35,10 +36,60 @@ def create_node(*args) end end + def create_nodes(nodes) + nodes = Array.new(nodes) if nodes.kind_of? Fixnum + created_nodes = Array.new + nodes.each do |node| + created_nodes << create_node(node) + end + created_nodes + end + + def create_nodes_threaded(nodes) + nodes = Array.new(nodes) if nodes.kind_of? Fixnum + + node_queue = Queue.new + thread_pool = [] + responses = Queue.new + + nodes.each do |node| + node_queue.push node + end + + [nodes.size, @max_threads].min.times do + thread_pool << Thread.new do + until node_queue.empty? do + node = node_queue.pop + if node.respond_to?(:each_pair) + responses.push( post("/node", { :body => node.to_json, :headers => {'Content-Type' => 'application/json'} } ) ) + else + responses.push( post("/node") ) + end + end + self.join + end + end + + created_nodes = Array.new + + while created_nodes.size < nodes.size + created_nodes << responses.pop + end + created_nodes + end + def get_node(id) get("/node/#{get_id(id)}") end + def get_nodes(*nodes) + gotten_nodes = Array.new + nodes.to_a.flatten.each do |node| + gotten_nodes << get_node(node) + end + gotten_nodes + end + def reset_node_properties(id, properties) options = { :body => properties.to_json, :headers => {'Content-Type' => 'application/json'} } put("/node/#{get_id(id)}/properties", options) diff --git a/spec/integration/rest_bulk_spec.rb b/spec/integration/rest_bulk_spec.rb new file mode 100644 index 0000000..be7cf24 --- /dev/null +++ b/spec/integration/rest_bulk_spec.rb @@ -0,0 +1,106 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper') + +describe Neography::Rest do + before(:each) do + @neo = Neography::Rest.new + end + + describe "can create many nodes threaded" do + it "can create empty nodes threaded" do + new_nodes = @neo.create_nodes_threaded(2) + new_nodes.should_not be_nil + new_nodes.size.should == 2 + end + + it "is faster than non-threaded?" do + Benchmark.bm do |x| + x.report("create 100 nodes ") { @not_threaded = @neo.create_nodes(100) } + x.report("create 100 nodes threaded") { @threaded = @neo.create_nodes_threaded(100) } + x.report("create 200 nodes threaded") { @threaded2c = @neo.create_nodes_threaded(200) } + end + + @not_threaded[99].should_not be_nil + @threaded[99].should_not be_nil + @threaded2c[199].should_not be_nil + end + + end + + describe "can create many nodes" do + it "can create empty nodes" do + new_nodes = @neo.create_nodes(2) + new_nodes.should_not be_nil + new_nodes.size.should == 2 + end + + it "can create nodes with one property" do + new_nodes = @neo.create_nodes([{"name" => "Max"}, {"name" => "Alex"}]) + new_nodes[0]["data"]["name"].should == "Max" + new_nodes[1]["data"]["name"].should == "Alex" + end + + it "can create nodes with one property that are different" do + new_nodes = @neo.create_nodes([{"name" => "Max"}, {"age" => 24}]) + new_nodes[0]["data"]["name"].should == "Max" + new_nodes[1]["data"]["age"].should == 24 + end + + it "can create nodes with more than one property" do + new_nodes = @neo.create_nodes([{"age" => 31, "name" => "Max"}, {"age" => 24, "name" => "Alex"}]) + new_nodes[0]["data"]["name"].should == "Max" + new_nodes[0]["data"]["age"].should == 31 + new_nodes[1]["data"]["name"].should == "Alex" + new_nodes[1]["data"]["age"].should == 24 + end + + it "can create nodes with more than one property that are different" do + new_nodes = @neo.create_nodes([{"age" => 31, "name" => "Max"}, {"height" => "5-11", "weight" => 215}]) + new_nodes[0]["data"]["name"].should == "Max" + new_nodes[0]["data"]["age"].should == 31 + new_nodes[1]["data"]["height"].should == "5-11" + new_nodes[1]["data"]["weight"].should == 215 + end + + it "is not super slow?" do + Benchmark.bm do |x| + x.report( "create 1 node" ) { @neo.create_nodes( 1) } + x.report( "create 10 nodes") { @neo.create_nodes( 10) } + x.report("create 100 nodes") { @neo.create_nodes(100) } + end + end + end + + describe "can get many nodes" do + it "can get 2 nodes passed in as an array" do + new_nodes = @neo.create_nodes(2) + existing_nodes = @neo.get_nodes(new_nodes) + existing_nodes.should_not be_nil + existing_nodes.size.should == 2 + end + + it "can get 2 nodes passed in by commas" do + new_nodes = @neo.create_nodes(2) + new_node1 = new_nodes[0]["self"].split('/').last + new_node2 = new_nodes[1]["self"].split('/').last + existing_nodes = @neo.get_nodes(new_node1, new_node2) + existing_nodes.should_not be_nil + existing_nodes.size.should == 2 + existing_nodes[0]["self"] == new_node1["self"] + existing_nodes[1]["self"] == new_node2["self"] + end + + it "is not super slow?" do + one_node = @neo.create_nodes( 1) + ten_nodes = @neo.create_nodes( 10) + one_hundred_nodes = @neo.create_nodes(100) + + Benchmark.bm do |x| + x.report( "get 1 node ") { @neo.get_nodes(one_node) } + x.report( "get 10 nodes") { @neo.get_nodes(ten_nodes) } + x.report("get 100 nodes") { @neo.get_nodes(one_hundred_nodes) } + end + end + + end + +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ef23d43..e9caad9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'neography' require 'fakeweb' +require 'benchmark' Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}