From db2f1ddaebc7fdd6af03e021ca0b07b6007f79cb Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Fri, 7 Sep 2012 22:58:22 +0200 Subject: [PATCH 01/21] First step of major refactor of REST class. --- lib/neography.rb | 12 + lib/neography/connection.rb | 118 ++++ lib/neography/rest.rb | 592 ++++++------------ lib/neography/rest/node_auto_indexes.rb | 61 ++ lib/neography/rest/node_indexes.rb | 100 +++ lib/neography/rest/node_properties.rb | 60 ++ lib/neography/rest/node_relationships.rb | 52 ++ lib/neography/rest/nodes.rb | 102 +++ lib/neography/rest/relationship_properties.rb | 52 ++ lib/neography/rest/relationships.rb | 23 + lib/neography/rest_helpers.rb | 26 + lib/neography/rest_paths.rb | 29 + spec/integration/rest_header_spec.rb | 11 +- spec/integration/rest_index_spec.rb | 20 + 14 files changed, 861 insertions(+), 397 deletions(-) create mode 100644 lib/neography/connection.rb create mode 100644 lib/neography/rest/node_auto_indexes.rb create mode 100644 lib/neography/rest/node_indexes.rb create mode 100644 lib/neography/rest/node_properties.rb create mode 100644 lib/neography/rest/node_relationships.rb create mode 100644 lib/neography/rest/nodes.rb create mode 100644 lib/neography/rest/relationship_properties.rb create mode 100644 lib/neography/rest/relationships.rb create mode 100644 lib/neography/rest_helpers.rb create mode 100644 lib/neography/rest_paths.rb diff --git a/lib/neography.rb b/lib/neography.rb index 2400f67..dafef9e 100644 --- a/lib/neography.rb +++ b/lib/neography.rb @@ -29,7 +29,19 @@ def find_and_require_user_defined_code require 'neography/version' require 'neography/config' + +require 'neography/rest_helpers' +require 'neography/rest_paths' +require 'neography/rest/nodes' +require 'neography/rest/node_properties' +require 'neography/rest/node_relationships' +require 'neography/rest/node_indexes' +require 'neography/rest/node_auto_indexes' +require 'neography/rest/relationships' +require 'neography/rest/relationship_properties' +require 'neography/connection' require 'neography/rest' + require 'neography/neography' require 'neography/property_container' diff --git a/lib/neography/connection.rb b/lib/neography/connection.rb new file mode 100644 index 0000000..fc33bfd --- /dev/null +++ b/lib/neography/connection.rb @@ -0,0 +1,118 @@ +module Neography + class Connection + + USER_AGENT = "Neography/#{Neography::VERSION}" + + attr_accessor :protocol, :server, :port, :directory, + :cypher_path, :gremlin_path, + :log_file, :log_enabled, :logger, + :max_threads, + :authentication, :username, :password, + :parser + + def initialize(options=ENV['NEO4J_URL'] || {}) + init = {:protocol => Neography::Config.protocol, + :server => Neography::Config.server, + :port => Neography::Config.port, + :directory => Neography::Config.directory, + :cypher_path => Neography::Config.cypher_path, + :gremlin_path => Neography::Config.gremlin_path, + :log_file => Neography::Config.log_file, + :log_enabled => Neography::Config.log_enabled, + :max_threads => Neography::Config.max_threads, + :authentication => Neography::Config.authentication, + :username => Neography::Config.username, + :password => Neography::Config.password, + :parser => Neography::Config.parser + } + + unless options.respond_to?(:each_pair) + url = URI.parse(options) + options = Hash.new + options[:protocol] = url.scheme + "://" + options[:server] = url.host + options[:port] = url.port + options[:directory] = url.path + options[:username] = url.user + options[:password] = url.password + options[:authentication] = 'basic' unless url.user.nil? + end + + init.merge!(options) + + @protocol = init[:protocol] + @server = init[:server] + @port = init[:port] + @directory = init[:directory] + @cypher_path = init[:cypher_path] + @gremlin_path = init[:gremlin_path] + @log_file = init[:log_file] + @log_enabled = init[:log_enabled] + @logger = Logger.new(@log_file) if @log_enabled + @max_threads = init[:max_threads] + @authentication = Hash.new + @authentication = {"#{init[:authentication]}_auth".to_sym => {:username => init[:username], :password => init[:password]}} unless init[:authentication].empty? + @parser = init[:parser] + @user_agent = {"User-Agent" => USER_AGENT} + end + + def configure(protocol, server, port, directory) + @protocol = protocol + @server = server + @port = port + @directory = directory + end + + def configuration + @protocol + @server + ':' + @port.to_s + @directory + "/db/data" + end + + def merge_options(options) + merged_options = options.merge!(@authentication).merge!(@parser) + merged_options[:headers].merge!(@user_agent) if merged_options[:headers] + merged_options + end + + def get(path, options={}) + evaluate_response(HTTParty.get(configuration + URI.encode(path), merge_options(options))) + end + + def post(path, options={}) + evaluate_response(HTTParty.post(configuration + URI.encode(path), merge_options(options))) + end + + def put(path, options={}) + evaluate_response(HTTParty.put(configuration + URI.encode(path), merge_options(options))) + end + + def delete(path, options={}) + evaluate_response(HTTParty.delete(configuration + URI.encode(path), merge_options(options))) + end + + def evaluate_response(response) + code = response.code + body = response.body + case code + when 200 + @logger.debug "OK" if @log_enabled + response.parsed_response + when 201 + @logger.debug "OK, created #{body}" if @log_enabled + response.parsed_response + when 204 + @logger.debug "OK, no content returned" if @log_enabled + nil + when 400 + @logger.error "Invalid data sent #{body}" if @log_enabled + nil + when 404 + @logger.error "Not Found #{body}" if @log_enabled + nil + when 409 + @logger.error "Node could not be deleted (still has relationships?)" if @log_enabled + nil + end + end + + end +end diff --git a/lib/neography/rest.rb b/lib/neography/rest.rb index 5e1d0ab..ccbe828 100644 --- a/lib/neography/rest.rb +++ b/lib/neography/rest.rb @@ -1,330 +1,228 @@ +require 'forwardable' + module Neography - + class Rest include HTTParty - USER_AGENT = "Neography/#{Neography::VERSION}" - - attr_accessor :protocol, :server, :port, :directory, :cypher_path, :gremlin_path, :log_file, :log_enabled, :logger, :max_threads, :authentication, :username, :password, :parser - - def initialize(options=ENV['NEO4J_URL'] || {}) - init = {:protocol => Neography::Config.protocol, - :server => Neography::Config.server, - :port => Neography::Config.port, - :directory => Neography::Config.directory, - :cypher_path => Neography::Config.cypher_path, - :gremlin_path => Neography::Config.gremlin_path, - :log_file => Neography::Config.log_file, - :log_enabled => Neography::Config.log_enabled, - :max_threads => Neography::Config.max_threads, - :authentication => Neography::Config.authentication, - :username => Neography::Config.username, - :password => Neography::Config.password, - :parser => Neography::Config.parser - } - - unless options.respond_to?(:each_pair) - url = URI.parse(options) - options = Hash.new - options[:protocol] = url.scheme + "://" - options[:server] = url.host - options[:port] = url.port - options[:directory] = url.path - options[:username] = url.user - options[:password] = url.password - options[:authentication] = 'basic' unless url.user.nil? - end + include Helpers + extend Forwardable - init.merge!(options) + attr_reader :connection - @protocol = init[:protocol] - @server = init[:server] - @port = init[:port] - @directory = init[:directory] - @cypher_path = init[:cypher_path] - @gremlin_path = init[:gremlin_path] - @log_file = init[:log_file] - @log_enabled = init[:log_enabled] - @logger = Logger.new(@log_file) if @log_enabled - @max_threads = init[:max_threads] - @authentication = Hash.new - @authentication = {"#{init[:authentication]}_auth".to_sym => {:username => init[:username], :password => init[:password]}} unless init[:authentication].empty? - @parser = init[:parser] - @user_agent = {"User-Agent" => USER_AGENT} - end + def_delegators :@connection, :configuration, + :get, :post, :put, :delete - def configure(protocol, server, port, directory) - @protocol = protocol - @server = server - @port = port - @directory = directory - end + def initialize(options=ENV['NEO4J_URL'] || {}) + @connection = Connection.new(options) - def configuration - @protocol + @server + ':' + @port.to_s + @directory + "/db/data" - end + @nodes = Nodes.new(@connection) + @node_properties = NodeProperties.new(@connection) + @node_relationships = NodeRelationships.new(@connection) + @node_indexes = NodeIndexes.new(@connection) + @node_auto_indexes = NodeAutoIndexes.new(@connection) + @relationships = Relationships.new(@connection) + @relationship_properties = RelationshipProperties.new(@connection) + end - def get_root - get("/node/#{get_id(get('/')["reference_node"])}") - end + def get_root + @nodes.root + end - def create_node(*args) - if args[0].respond_to?(:each_pair) && args[0] - options = { :body => args[0].delete_if { |k, v| v.nil? }.to_json, :headers => {'Content-Type' => 'application/json'} } - post("/node", options) - else - post("/node") - end - end + def create_node(*args) + @nodes.create(*args) + 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(nodes) + @nodes.create_multiple(nodes) + end - def create_nodes_threaded(nodes) - nodes = Array.new(nodes) if nodes.kind_of? Fixnum + def create_nodes_threaded(nodes) + @nodes.create_multiple_threaded(nodes) + end - node_queue = Queue.new - thread_pool = [] - responses = Queue.new + # This is not yet implemented in the REST API + # + # def get_all_node + # puts "get all nodes" + # get("/nodes/") + # end - nodes.each do |node| - node_queue.push node - end + # nodes - [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 + def get_node(id) + @nodes.get(id) + end - created_nodes = Array.new + def get_nodes(*nodes) + @nodes.get_each(*nodes) + end - while created_nodes.size < nodes.size - created_nodes << responses.pop - end - created_nodes - end + def delete_node(id) + @nodes.delete(id) + end -# This is not yet implemented in the REST API -# -# def get_all_node -# puts "get all nodes" -# get("/nodes/") -# end + def delete_node!(id) + relationships = get_node_relationships(get_id(id)) + relationships.each { |r| delete_relationship(r["self"].split('/').last) } unless relationships.nil? + delete_node(id) + end - def get_node(id) - get("/node/#{get_id(id)}") - end + # node properties - def get_nodes(*nodes) - gotten_nodes = Array.new - Array(nodes).flatten.each do |node| - gotten_nodes << get_node(node) - end - gotten_nodes - end + def reset_node_properties(id, properties) + @node_properties.reset(id, properties) + end - def reset_node_properties(id, properties) - options = { :body => properties.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/node/#{get_id(id)}/properties", options) - end + def get_node_properties(id, *properties) + @node_properties.get(id, *properties.flatten) + end - def get_node_properties(id, properties = nil) - if properties.nil? - get("/node/#{get_id(id)}/properties") - else - node_properties = Hash.new - Array(properties).each do |property| - value = get("/node/#{get_id(id)}/properties/#{property}") - node_properties[property] = value unless value.nil? - end - return nil if node_properties.empty? - node_properties - end - end + def remove_node_properties(id, *properties) + @node_properties.remove(id, *properties.flatten) + end - def remove_node_properties(id, properties = nil) - if properties.nil? - delete("/node/#{get_id(id)}/properties") - else - Array(properties).each do |property| - delete("/node/#{get_id(id)}/properties/#{property}") - end - end - end + def set_node_properties(id, properties) + @node_properties.set(id, properties) + end - def set_node_properties(id, properties) - properties.each do |key, value| - options = { :body => value.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/node/#{get_id(id)}/properties/#{key}", options) - end - end + # relationships - def delete_node(id) - delete("/node/#{get_id(id)}") - end + def get_relationship(id) + @relationships.get(id) + end - def create_relationship(type, from, to, props = nil) - options = { :body => {:to => self.configuration + "/node/#{get_id(to)}", :data => props, :type => type }.to_json, :headers => {'Content-Type' => 'application/json'} } - post("/node/#{get_id(from)}/relationships", options) - end + def delete_relationship(id) + @relationships.delete(id) + end - def create_unique_relationship(index, key, value, type, from, to) - body = {:key=>key,:value=>value, :type => type } - body[:start] = self.configuration + "/node/#{get_id(from)}" - body[:end] = self.configuration + "/node/#{get_id(to)}" - options = { :body => body.to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/relationship/#{index}?unique", options) - end + def get_relationship_start_node(rel) + get_node(rel["start"]) + end - def get_relationship(id) - get("/relationship/#{get_id(id)}") - end + def get_relationship_end_node(rel) + get_node(rel["end"]) + end - def get_relationship_start_node(rel) - get_node(rel["start"]) - end - - def get_relationship_end_node(rel) - get_node(rel["end"]) - end - - def reset_relationship_properties(id, properties) - options = { :body => properties.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/relationship/#{get_id(id)}/properties", options) - end + # relationship properties - def get_relationship_properties(id, properties = nil) - if properties.nil? - get("/relationship/#{get_id(id)}/properties") - else - relationship_properties = Hash.new - Array(properties).each do |property| - value = get("/relationship/#{get_id(id)}/properties/#{property}") - relationship_properties[property] = value unless value.nil? - end - return nil if relationship_properties.empty? - relationship_properties - end - end + def reset_relationship_properties(id, properties) + @relationship_properties.reset(id, properties) + end - def remove_relationship_properties(id, properties = nil) - if properties.nil? - delete("/relationship/#{get_id(id)}/properties") - else - Array(properties).each do |property| - delete("/relationship/#{get_id(id)}/properties/#{property}") - end - end - end + def get_relationship_properties(id, properties = nil) + @relationship_properties.get(id, properties) + end - def set_relationship_properties(id, properties) - properties.each do |key, value| - options = { :body => value.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/relationship/#{get_id(id)}/properties/#{key}", options) - end - end + def remove_relationship_properties(id, properties = nil) + @relationship_properties.remove(id, properties) + end - def delete_relationship(id) - delete("/relationship/#{get_id(id)}") - end + def set_relationship_properties(id, properties) + @relationship_properties.set(id, properties) + end - def get_node_relationships(id, dir=nil, types=nil) - dir = get_dir(dir) + # node relationships - if types.nil? - node_relationships = get("/node/#{get_id(id)}/relationships/#{dir}") || Array.new - else - node_relationships = get("/node/#{get_id(id)}/relationships/#{dir}/#{Array(types).join('&')}") || Array.new - end - return nil if node_relationships.empty? - node_relationships - end + def create_relationship(type, from, to, props = nil) + @node_relationships.create(type, from, to, props) + end - def delete_node!(id) - relationships = get_node_relationships(get_id(id)) - relationships.each { |r| delete_relationship(r["self"].split('/').last) } unless relationships.nil? - delete("/node/#{get_id(id)}") - end + def get_node_relationships(id, dir = nil, types = nil) + @node_relationships.get(id, dir, types) + end - def list_node_indexes - get("/index/node") - end + # node indexes - def create_node_index(name, type = "exact", provider = "lucene") - options = { :body => ({:name => name, :config => {:type => type, :provider => provider}}).to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/node", options) - end + def list_node_indexes + @node_indexes.list + end + alias_method :list_indexes, :list_node_indexes - def create_node_auto_index(type = "exact", provider = "lucene") - create_node_index("node_auto_index", type, provider) - end + def create_node_index(name, type = "exact", provider = "lucene") + @node_indexes.create(name, type, provider) + end - def add_node_to_index(index, key, value, id) - options = { :body => ({:uri => self.configuration + "/node/#{get_id(id)}", :key => key, :value => value }).to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/node/#{index}", options) - end + def create_node_auto_index(type = "exact", provider = "lucene") + @node_indexes.create_auto(type, provider) + end - def create_unique_node(index, key, value, props={}) - options = { :body => ({:properties=>props, :key => key, :value => value }).to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/node/#{index}?unique", options) - end + def add_node_to_index(index, key, value, id) + @node_indexes.add_node(index, key, value, id) + end + alias_method :add_to_index, :add_node_to_index - def remove_node_from_index(*args) - case args.size - when 4 then delete("/index/node/#{args[0]}/#{args[1]}/#{args[2]}/#{get_id(args[3])}") - when 3 then delete("/index/node/#{args[0]}/#{args[1]}/#{get_id(args[2])}") - when 2 then delete("/index/node/#{args[0]}/#{get_id(args[1])}") - end - end + def create_unique_node(index, key, value, props={}) + @node_indexes.create_unique_node(index, key, value, props) + end - def get_node_index(index, key, value) - index = get("/index/node/#{index}/#{key}/#{value}") || Array.new - return nil if index.empty? - index + def remove_node_from_index(*args) + case args.size + when 4 then @node_indexes.remove_node_by_value(args[0], args[3], args[1], args[2]) + when 3 then @node_indexes.remove_node_by_key(args[0], args[2], args[1]) + when 2 then @node_indexes.remove_node(args[0], args[1]) end + end + alias_method :remove_from_index, :remove_node_from_index - def get_node_auto_index(key, value) - index = get("/index/auto/node/#{key}/#{value}") || Array.new - return nil if index.empty? - index - end - def find_node_auto_index(*args) - case args.size - when 2 then index = get("/index/auto/node/#{args[0]}/#{args[1]}") || Array.new - when 1 then index = get("/index/auto/node/?query=#{args[0]}") || Array.new - end - return nil if index.empty? - index + def get_node_index(index, key, value) + @node_indexes.get_node(index, key, value) + end + alias_method :get_index, :get_node_index + + def find_node_index(*args) + case args.size + when 3 then index = @node_indexes.find_node_by_value(args[0], args[1], args[2]) + when 2 then index = @node_indexes.find_node_by_query(args[0], args[1]) end + return nil if index.empty? + index + end - def find_node_index(*args) - case args.size - when 3 then index = get("/index/node/#{args[0]}/#{args[1]}?query=\"#{args[2]}\"") || Array.new - when 2 then index = get("/index/node/#{args[0]}?query=#{args[1]}") || Array.new - end - return nil if index.empty? - index + # auto node indexes + + def get_node_auto_index(key, value) + @node_auto_indexes.get(key, value) + end + + def find_node_auto_index(*args) + case args.size + when 2 then index = @node_auto_indexes.find(args[0], args[1]) + when 1 then index = @node_auto_indexes.query(args[0]) end + return nil if index.empty? + index + end + + def get_node_auto_index_status + @node_auto_indexes.status + end + + def set_node_auto_index_status(change_to = true) + @node_auto_indexes.status = change_to + end + + def get_node_auto_index_properties + @node_auto_indexes.properties + end + + def add_node_auto_index_property(property) + @node_auto_indexes.add_property(property) + end - alias_method :list_indexes, :list_node_indexes - alias_method :add_to_index, :add_node_to_index - alias_method :remove_from_index, :remove_node_from_index - alias_method :get_index, :get_node_index + def remove_node_auto_index_property(property) + @node_auto_indexes.remove_property(property) + end + + # relationship indexes + + def create_unique_relationship(index, key, value, type, from, to) + body = {:key=>key,:value=>value, :type => type } + body[:start] = self.configuration + "/node/#{get_id(from)}" + body[:end] = self.configuration + "/node/#{get_id(to)}" + options = { :body => body.to_json, :headers => {'Content-Type' => 'application/json'} } + post("/index/relationship/#{index}?unique", options) + end def list_relationship_indexes get("/index/relationship") @@ -367,6 +265,8 @@ def find_relationship_index(*args) index end + # relationship auto indexes + def get_relationship_auto_index(key, value) index = get("/index/auto/relationship/#{key}/#{value}") || Array.new return nil if index.empty? @@ -382,41 +282,19 @@ def find_relationship_auto_index(*args) index end - def get_node_auto_index_status - get("/index/auto/node/status") - end - def get_relationship_auto_index_status get("/index/auto/relationship/status") end - def set_node_auto_index_status(change_to = true) - options = { :body => change_to.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/index/auto/node/status", options) - end - def set_relationship_auto_index_status(change_to = true) options = { :body => change_to.to_json, :headers => {'Content-Type' => 'application/json'} } put("/index/auto/relationship/status", options) end - def get_node_auto_index_properties - get("/index/auto/node/properties") - end - def get_relationship_auto_index_properties get("/index/auto/relationship/properties") end - def add_node_auto_index_property(property) - options = { :body => property, :headers => {'Content-Type' => 'application/json'} } - post("/index/auto/node/properties", options) - end - - def remove_node_auto_index_property(property) - delete("/index/auto/node/properties/#{property}") - end - def add_relationship_auto_index_property(property) options = { :body => property, :headers => {'Content-Type' => 'application/json'} } post("/index/auto/relationship/properties", options) @@ -426,6 +304,8 @@ def remove_relationship_auto_index_property(property) delete("/index/auto/relationship/properties/#{property}") end + # traversal + def traverse(id, return_type, description) options = { :body => {"order" => get_order(description["order"]), "uniqueness" => get_uniqueness(description["uniqueness"]), @@ -436,6 +316,8 @@ def traverse(id, return_type, description) traversal = post("/node/#{get_id(id)}/traverse/#{get_type(return_type)}", options) || Array.new end + # paths + def get_path(from, to, relationships, depth=1, algorithm="shortestPath") options = { :body => {"to" => self.configuration + "/node/#{get_id(to)}", "relationships" => relationships, "max_depth" => depth, "algorithm" => get_algorithm(algorithm) }.to_json, :headers => {'Content-Type' => 'application/json'} } path = post("/node/#{get_id(from)}/path", options) || Hash.new @@ -451,17 +333,23 @@ def get_shortest_weighted_path(from, to, relationships, weight_attr='weight', de paths = post("/node/#{get_id(from)}/paths", options) || Hash.new end + # query + def execute_query(query, params = {}) options = { :body => {:query => query, :params => params}.to_json, :headers => {'Content-Type' => 'application/json', 'Accept' => 'application/json;stream=true'} } - result = post(@cypher_path, options) + result = post(@connection.cypher_path, options) end + + # script def execute_script(script, params = {}) options = { :body => {:script => script, :params => params}.to_json , :headers => {'Content-Type' => 'application/json'} } - result = post(@gremlin_path, options) + result = post(@connection.gremlin_path, options) result == "null" ? nil : result end + # batch + def batch(*args) batch = [] Array(args).each_with_index do |c,i| @@ -480,25 +368,6 @@ def batch_not_streaming(*args) post("/batch", options) end - # For testing (use a separate neo4j instance) - # call this before each test or spec - def clean_database(sanity_check = "not_really") - if sanity_check == "yes_i_really_want_to_clean_the_database" - delete("/cleandb/secret-key") - true - else - false - end - end - - def merge_options(options) - merged_options = options.merge!(@authentication).merge!(@parser) - merged_options[:headers].merge!(@user_agent) if merged_options[:headers] - merged_options - end - - private - def get_batch(args) case args[0] when :get_node @@ -534,12 +403,12 @@ def get_batch(args) when :get_node_relationships {:method => "GET", :to => "/node/#{get_id(args[1])}/relationships/#{args[2] || 'all'}"} when :execute_script - {:method => "POST", :to => @gremlin_path, :body => {:script => args[1], :params => args[2]}} + {:method => "POST", :to => @connection.gremlin_path, :body => {:script => args[1], :params => args[2]}} when :execute_query if args[2] - {:method => "POST", :to => @cypher_path, :body => {:query => args[1], :params => args[2]}} + {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1], :params => args[2]}} else - {:method => "POST", :to => @cypher_path, :body => {:query => args[1]}} + {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1]}} end when :remove_node_from_index case args.size @@ -560,75 +429,20 @@ def get_batch(args) end end - def evaluate_response(response) - code = response.code - body = response.body - case code - when 200 - @logger.debug "OK" if @log_enabled - response.parsed_response - when 201 - @logger.debug "OK, created #{body}" if @log_enabled - response.parsed_response - when 204 - @logger.debug "OK, no content returned" if @log_enabled - nil - when 400 - @logger.error "Invalid data sent #{body}" if @log_enabled - nil - when 404 - @logger.error "Not Found #{body}" if @log_enabled - nil - when 409 - @logger.error "Node could not be deleted (still has relationships?)" if @log_enabled - nil - end - end - - def get(path,options={}) - evaluate_response(HTTParty.get(configuration + URI.encode(path), merge_options(options))) - end - + # delete - - def post(path,options={}) - evaluate_response(HTTParty.post(configuration + URI.encode(path), merge_options(options))) - end - - def put(path,options={}) - evaluate_response(HTTParty.put(configuration + URI.encode(path), merge_options(options))) - end - - def delete(path,options={}) - evaluate_response(HTTParty.delete(configuration + URI.encode(path), merge_options(options))) - end - - def get_id(id) - case id - when Array - get_id(id.first) - when Hash - id["self"].split('/').last - when String - id.split('/').last - when Neography::Node, Neography::Relationship - id.neo_id - else - id - end - end - - def get_dir(dir) - case dir - when :incoming, "incoming", :in, "in" - "in" - when :outgoing, "outgoing", :out, "out" - "out" - else - "all" + # For testing (use a separate neo4j instance) + # call this before each test or spec + def clean_database(sanity_check = "not_really") + if sanity_check == "yes_i_really_want_to_clean_the_database" + delete("/cleandb/secret-key") + true + else + false end end + def get_algorithm(algorithm) case algorithm when :shortest, "shortest", :shortestPath, "shortestPath", :short, "short" diff --git a/lib/neography/rest/node_auto_indexes.rb b/lib/neography/rest/node_auto_indexes.rb new file mode 100644 index 0000000..6e28001 --- /dev/null +++ b/lib/neography/rest/node_auto_indexes.rb @@ -0,0 +1,61 @@ +module Neography + class Rest + class NodeAutoIndexes + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :key_value, "/index/auto/node/:key/:value" + add_path :query_index, "/index/auto/node/?query=:query" + add_path :index_status, "/index/auto/node/status" + add_path :index_properties, "/index/auto/node/properties" + add_path :index_property, "/index/auto/node/properties/:property" + + def initialize(connection) + @connection = connection + end + + def get(key, value) + index = @connection.get(key_value(:key => key, :value => value)) || Array.new + return nil if index.empty? + index + end + + def find(key, value) + @connection.get(key_value(:key => key, :value => value)) || Array.new + end + + def query(query_expression) + @connection.get(query_index(:query => query_expression)) || Array.new + end + + def status + @connection.get(index_status) + end + + def status=(value) + options = { + :body => value.to_json, + :headers => json_content_type + } + @connection.put(index_status, options) + end + + def properties + @connection.get(index_properties) + end + + def add_property(property) + options = { + :body => property, + :headers => json_content_type + } + @connection.post(index_properties, options) + end + + def remove_property(property) + @connection.delete(index_property(:property => property)) + end + + end + end +end diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb new file mode 100644 index 0000000..0ce4d82 --- /dev/null +++ b/lib/neography/rest/node_indexes.rb @@ -0,0 +1,100 @@ +module Neography + class Rest + class NodeIndexes + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :all, "/index/node" + add_path :base, "/index/node/:index" + add_path :unique, "/index/node/:index?unique" + add_path :node, "/index/node/:index/:id" + add_path :key, "/index/node/:index/:key/:id" + add_path :value, "/index/node/:index/:key/:value/:id" + add_path :key_value, "/index/node/:index/:key/:value" + add_path :query, "/index/node/:index?query=:query" + + add_path :key_value2, "/index/node/:index/:key?query=\":value\"" # TODO FIX BUG %20 + + def initialize(connection) + @connection = connection + end + + def list + @connection.get(all) + end + + def create(name, type, provider) + options = { + :body => ( + { :name => name, + :config => { + :type => type, + :provider => provider + } + } + ).to_json, + :headers => json_content_type + } + @connection.post(all, options) + end + + def create_auto(type, provider) + create("node_auto_index", type, provider) + end + + def create_unique_node(index, key, value, props) + options = { + :body => ( + { :properties => props, + :key => key, + :value => value + } + ).to_json, + :headers => json_content_type + } + @connection.post(unique(:index => index), options) + end + + def add_node(index, key, value, id) + options = { + :body => ( + { :uri => @connection.configuration + "/node/#{get_id(id)}", + :key => key, + :value => value + } + ).to_json, + :headers => json_content_type + } + @connection.post(base(:index => index), options) + end + + def get_node(index, key, value) + index = @connection.get(key_value(:index => index, :key => key, :value => value)) || Array.new + return nil if index.empty? + index + end + + # TODO FIX BUG %20 + def find_node_by_value(index, key, value) + @connection.get(key_value2(:index => index, :key => key, :value => value)) || Array.new + end + + def find_node_by_query(index, query) + @connection.get(query(:index => index, :query => query)) || Array.new + end + + def remove_node(index, id) + @connection.delete(node(:index => index, :id => get_id(id))) + end + + def remove_node_by_key(index, id, key) + @connection.delete(key(:index => index, :id => get_id(id), :key => key)) + end + + def remove_node_by_value(index, id, key, value) + @connection.delete(value(:index => index, :id => get_id(id), :key => key, :value => value)) + end + + end + end +end diff --git a/lib/neography/rest/node_properties.rb b/lib/neography/rest/node_properties.rb new file mode 100644 index 0000000..b2dca28 --- /dev/null +++ b/lib/neography/rest/node_properties.rb @@ -0,0 +1,60 @@ +module Neography + class Rest + class NodeProperties + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :all, "/node/:id/properties" + add_path :single, "/node/:id/properties/:property" + + def initialize(connection) + @connection = connection + end + + def reset(id, properties) + options = { :body => properties.to_json, :headers => json_content_type } + @connection.put(all(:id => get_id(id)), options) + end + + def get(id, *properties) + if properties.none? + @connection.get(all(:id => get_id(id))) + else + get_each(id, *properties) + end + end + + def get_each(id, *properties) + node_properties = properties.inject({}) do |memo, property| + value = @connection.get(single(:id => get_id(id), :property => property)) + memo[property] = value unless value.nil? + memo + end + return nil if node_properties.empty? + node_properties + end + + def remove(id, *properties) + if properties.none? + @connection.delete(all(:id => get_id(id))) + else + remove_each(id, *properties) + end + end + + def remove_each(id, *properties) + properties.each do |property| + @connection.delete(single(:id => get_id(id), :property => property)) + end + end + + def set(id, properties) + properties.each do |property, value| + options = { :body => value.to_json, :headers => json_content_type } + @connection.put(single(:id => get_id(id), :property => property), options) + end + end + + end + end +end diff --git a/lib/neography/rest/node_relationships.rb b/lib/neography/rest/node_relationships.rb new file mode 100644 index 0000000..d00e22f --- /dev/null +++ b/lib/neography/rest/node_relationships.rb @@ -0,0 +1,52 @@ +module Neography + class Rest + class NodeRelationships + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :base, "/node/:id/relationships" + add_path :direction, "/node/:id/relationships/:direction" + add_path :type, "/node/:id/relationships/:direction/:types" + + def initialize(connection) + @connection = connection + end + + def create(type, from, to, props) + options = { + :body => { + :to => @connection.configuration + "/node/#{get_id(to)}", + :data => props, + :type => type + }.to_json, + :headers => json_content_type } + + @connection.post(base(:id => get_id(from)), options) + end + + def get(id, direction, types) + direction = get_direction(direction) + + if types.nil? + node_relationships = @connection.get(direction(:id => get_id(id), :direction => direction)) || Array.new + else + node_relationships = @connection.get(type(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || Array.new + end + return nil if node_relationships.empty? + node_relationships + end + + def get_direction(direction) + case direction + when :incoming, "incoming", :in, "in" + "in" + when :outgoing, "outgoing", :out, "out" + "out" + else + "all" + end + end + + end + end +end diff --git a/lib/neography/rest/nodes.rb b/lib/neography/rest/nodes.rb new file mode 100644 index 0000000..acecf26 --- /dev/null +++ b/lib/neography/rest/nodes.rb @@ -0,0 +1,102 @@ +module Neography + class Rest + class Nodes + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :index, "/node" + add_path :base, "/node/:id" + + def initialize(connection) + @connection = connection + end + + def get(id) + @connection.get(base(:id => get_id(id))) + end + + def get_each(*nodes) + gotten_nodes = Array.new + Array(nodes).flatten.each do |node| + gotten_nodes << get(node) + end + gotten_nodes + end + + def root + root_node = @connection.get('/')["reference_node"] + @connection.get("/node/#{get_id(root_node)}") + end + + def create(*args) + if args[0].respond_to?(:each_pair) && args[0] + create_with_attributes(args[0]) + else + create_empty + end + end + + def create_with_attributes(attributes) + options = { + :body => attributes.delete_if { |k, v| v.nil? }.to_json, + :headers => json_content_type + } + @connection.post(index, options) + end + + def create_empty + @connection.post(index) + end + + def create_multiple(nodes) + nodes = Array.new(nodes) if nodes.kind_of? Fixnum + created_nodes = Array.new + nodes.each do |node| + created_nodes << create(node) + end + created_nodes + end + + def create_multiple_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, @connection.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( @connection.post(index, { + :body => node.to_json, + :headers => json_content_type + } ) ) + else + responses.push( @connection.post(index) ) + 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 delete(id) + @connection.delete(base(:id => get_id(id))) + end + + end + end +end diff --git a/lib/neography/rest/relationship_properties.rb b/lib/neography/rest/relationship_properties.rb new file mode 100644 index 0000000..a8521fe --- /dev/null +++ b/lib/neography/rest/relationship_properties.rb @@ -0,0 +1,52 @@ +module Neography + class Rest + class RelationshipProperties + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :all, "/relationship/:id/properties" + add_path :single, "/relationship/:id/properties/:property" + + def initialize(connection) + @connection = connection + end + + def get(id, properties) + if properties.nil? + @connection.get(all(:id => get_id(id))) + else + relationship_properties = Hash.new + Array(properties).each do |property| + value = @connection.get(single(:id => get_id(id), :property => property)) + relationship_properties[property] = value unless value.nil? + end + return nil if relationship_properties.empty? + relationship_properties + end + end + + def reset(id, properties) + options = { :body => properties.to_json, :headers => json_content_type } + @connection.put(all(:id => get_id(id)), options) + end + + def remove(id, properties) + if properties.nil? + @connection.delete(all(id: get_id(id))) + else + Array(properties).each do |property| + @connection.delete(single(:id => get_id(id), :property => property)) + end + end + end + + def set(id, properties) + properties.each do |key, value| + options = { :body => value.to_json, :headers => json_content_type } + @connection.put(single(:id => get_id(id), :property => key), options) + end + end + + end + end +end diff --git a/lib/neography/rest/relationships.rb b/lib/neography/rest/relationships.rb new file mode 100644 index 0000000..9e7fd58 --- /dev/null +++ b/lib/neography/rest/relationships.rb @@ -0,0 +1,23 @@ +module Neography + class Rest + class Relationships + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :base, "/relationship/:id" + + def initialize(connection) + @connection = connection + end + + def get(id) + @connection.get(base(:id => get_id(id))) + end + + def delete(id) + @connection.delete(base(:id => get_id(id))) + end + + end + end +end diff --git a/lib/neography/rest_helpers.rb b/lib/neography/rest_helpers.rb new file mode 100644 index 0000000..cf4b398 --- /dev/null +++ b/lib/neography/rest_helpers.rb @@ -0,0 +1,26 @@ +module Neography + class Rest + module Helpers + + def get_id(id) + case id + when Array + get_id(id.first) + when Hash + id["self"].split('/').last + when String + id.split('/').last + when Neography::Node, Neography::Relationship + id.neo_id + else + id + end + end + + def json_content_type + {'Content-Type' => 'application/json'} + end + + end + end +end diff --git a/lib/neography/rest_paths.rb b/lib/neography/rest_paths.rb new file mode 100644 index 0000000..aae6efa --- /dev/null +++ b/lib/neography/rest_paths.rb @@ -0,0 +1,29 @@ +module Neography + class Rest + module Paths + + def self.included(mod) + mod.send :extend, ClassMethods + end + + def build_path(path, attributes) + path.gsub(/:([\w_]*)/) do + attributes[$1.to_sym].to_s + end + end + + module ClassMethods + def add_path(key, path) + define_method key do |*attributes| + if attributes.any? + build_path(path, *attributes) + else + path + end + end + end + end + + end + end +end diff --git a/spec/integration/rest_header_spec.rb b/spec/integration/rest_header_spec.rb index 15b0eef..fadaffc 100644 --- a/spec/integration/rest_header_spec.rb +++ b/spec/integration/rest_header_spec.rb @@ -1,19 +1,14 @@ require File.join(File.dirname(__FILE__), '..', 'spec_helper') -describe Neography::Rest do - before(:each) do - @neo = Neography::Rest.new - end +describe Neography::Connection do it "should not add a content-type header if there's no existing headers" do - @neo.merge_options({}).keys.should == [:parser] + subject.merge_options({}).keys.should == [:parser] end it "should add a content type if there's existing headers" do - @neo.merge_options({:headers => {'Content-Type' => 'foo/bar'}})[:headers].should == + subject.merge_options({:headers => {'Content-Type' => 'foo/bar'}})[:headers].should == {'Content-Type' => "foo/bar", "User-Agent" => "Neography/#{Neography::VERSION}"} - end - end diff --git a/spec/integration/rest_index_spec.rb b/spec/integration/rest_index_spec.rb index 2688ddf..45dcb02 100644 --- a/spec/integration/rest_index_spec.rb +++ b/spec/integration/rest_index_spec.rb @@ -180,6 +180,26 @@ @neo.remove_node_from_index("test_node_index", key, value, new_node) end + it "can get a node index with a space in the value" do + new_node = @neo.create_node + key = generate_text(6) + value = generate_text + " " + generate_text + @neo.add_node_to_index("test_node_index", key, value, new_node) + new_index = @neo.get_node_index("test_node_index", key, value) + new_index.should_not be_nil + @neo.remove_node_from_index("test_node_index", key, value, new_node) + end + + xit "can get a node index with a slash in the value" do + new_node = @neo.create_node + key = generate_text(6) + value = generate_text + "/" + generate_text + @neo.add_node_to_index("test_node_index", key, value, new_node) + new_index = @neo.get_node_index("test_node_index", key, value) + new_index.should_not be_nil + @neo.remove_node_from_index("test_node_index", key, value, new_node) + end + it "can get a relationship index" do new_node1 = @neo.create_node new_node2 = @neo.create_node From b8f1312c37a099004f4d7fd84f024ff41bb98e84 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 16:03:41 +0200 Subject: [PATCH 02/21] Second step of major refactor of REST class. --- lib/neography.rb | 8 + lib/neography/rest.rb | 413 ++++++------------ lib/neography/rest/batch.rb | 99 +++++ lib/neography/rest/clean.rb | 20 + lib/neography/rest/cypher.rb | 24 + lib/neography/rest/gremlin.rb | 24 + lib/neography/rest/node_indexes.rb | 16 +- lib/neography/rest/node_paths.rb | 68 +++ lib/neography/rest/node_traversal.rb | 81 ++++ .../rest/relationship_auto_indexes.rb | 61 +++ lib/neography/rest/relationship_indexes.rb | 101 +++++ 11 files changed, 628 insertions(+), 287 deletions(-) create mode 100644 lib/neography/rest/batch.rb create mode 100644 lib/neography/rest/clean.rb create mode 100644 lib/neography/rest/cypher.rb create mode 100644 lib/neography/rest/gremlin.rb create mode 100644 lib/neography/rest/node_paths.rb create mode 100644 lib/neography/rest/node_traversal.rb create mode 100644 lib/neography/rest/relationship_auto_indexes.rb create mode 100644 lib/neography/rest/relationship_indexes.rb diff --git a/lib/neography.rb b/lib/neography.rb index dafef9e..ecefbea 100644 --- a/lib/neography.rb +++ b/lib/neography.rb @@ -37,8 +37,16 @@ def find_and_require_user_defined_code require 'neography/rest/node_relationships' require 'neography/rest/node_indexes' require 'neography/rest/node_auto_indexes' +require 'neography/rest/node_traversal' +require 'neography/rest/node_paths' require 'neography/rest/relationships' require 'neography/rest/relationship_properties' +require 'neography/rest/relationship_indexes' +require 'neography/rest/relationship_auto_indexes' +require 'neography/rest/cypher' +require 'neography/rest/gremlin' +require 'neography/rest/batch' +require 'neography/rest/clean' require 'neography/connection' require 'neography/rest' diff --git a/lib/neography/rest.rb b/lib/neography/rest.rb index ccbe828..db25be4 100644 --- a/lib/neography/rest.rb +++ b/lib/neography/rest.rb @@ -9,19 +9,28 @@ class Rest attr_reader :connection - def_delegators :@connection, :configuration, - :get, :post, :put, :delete + def_delegators :@connection, :configuration def initialize(options=ENV['NEO4J_URL'] || {}) @connection = Connection.new(options) - @nodes = Nodes.new(@connection) - @node_properties = NodeProperties.new(@connection) - @node_relationships = NodeRelationships.new(@connection) - @node_indexes = NodeIndexes.new(@connection) - @node_auto_indexes = NodeAutoIndexes.new(@connection) - @relationships = Relationships.new(@connection) - @relationship_properties = RelationshipProperties.new(@connection) + @nodes = Nodes.new(@connection) + @node_properties = NodeProperties.new(@connection) + @node_relationships = NodeRelationships.new(@connection) + @node_indexes = NodeIndexes.new(@connection) + @node_auto_indexes = NodeAutoIndexes.new(@connection) + @node_traversal = NodeTraversal.new(@connection) + @node_paths = NodePaths.new(@connection) + + @relationships = Relationships.new(@connection) + @relationship_properties = RelationshipProperties.new(@connection) + @relationship_indexes = RelationshipIndexes.new(@connection) + @relationship_auto_indexes = RelationshipAutoIndexes.new(@connection) + + @cypher = Cypher.new(@connection) + @gremlin = Gremlin.new(@connection) + @batch = Batch.new(@connection) + @clean = Clean.new(@connection) end def get_root @@ -40,13 +49,6 @@ def create_nodes_threaded(nodes) @nodes.create_multiple_threaded(nodes) end - # This is not yet implemented in the REST API - # - # def get_all_node - # puts "get all nodes" - # get("/nodes/") - # end - # nodes def get_node(id) @@ -57,13 +59,23 @@ def get_nodes(*nodes) @nodes.get_each(*nodes) end + # This is not yet implemented in the REST API + # + # def get_all_node + # puts "get all nodes" + # get("/nodes/") + # end + def delete_node(id) @nodes.delete(id) end def delete_node!(id) relationships = get_node_relationships(get_id(id)) - relationships.each { |r| delete_relationship(r["self"].split('/').last) } unless relationships.nil? + relationships.each do |relationship| + delete_relationship(relationship["self"].split('/').last) + end unless relationships.nil? + delete_node(id) end @@ -147,33 +159,32 @@ def create_node_auto_index(type = "exact", provider = "lucene") end def add_node_to_index(index, key, value, id) - @node_indexes.add_node(index, key, value, id) + @node_indexes.add(index, key, value, id) end alias_method :add_to_index, :add_node_to_index def create_unique_node(index, key, value, props={}) - @node_indexes.create_unique_node(index, key, value, props) + @node_indexes.create_unique(index, key, value, props) end def remove_node_from_index(*args) case args.size - when 4 then @node_indexes.remove_node_by_value(args[0], args[3], args[1], args[2]) - when 3 then @node_indexes.remove_node_by_key(args[0], args[2], args[1]) - when 2 then @node_indexes.remove_node(args[0], args[1]) + when 4 then @node_indexes.remove_by_value(args[0], args[3], args[1], args[2]) + when 3 then @node_indexes.remove_by_key(args[0], args[2], args[1]) + when 2 then @node_indexes.remove(args[0], args[1]) end end alias_method :remove_from_index, :remove_node_from_index - def get_node_index(index, key, value) - @node_indexes.get_node(index, key, value) + @node_indexes.get(index, key, value) end alias_method :get_index, :get_node_index def find_node_index(*args) case args.size - when 3 then index = @node_indexes.find_node_by_value(args[0], args[1], args[2]) - when 2 then index = @node_indexes.find_node_by_query(args[0], args[1]) + when 3 then index = @node_indexes.find_by_value(args[0], args[1], args[2]) + when 2 then index = @node_indexes.find_by_query(args[0], args[1]) end return nil if index.empty? index @@ -214,294 +225,138 @@ def remove_node_auto_index_property(property) @node_auto_indexes.remove_property(property) end - # relationship indexes + # relationship indexes - def create_unique_relationship(index, key, value, type, from, to) - body = {:key=>key,:value=>value, :type => type } - body[:start] = self.configuration + "/node/#{get_id(from)}" - body[:end] = self.configuration + "/node/#{get_id(to)}" - options = { :body => body.to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/relationship/#{index}?unique", options) - end + def list_relationship_indexes + @relationship_indexes.list + end - def list_relationship_indexes - get("/index/relationship") - end + def create_relationship_index(name, type = "exact", provider = "lucene") + @relationship_indexes.create(name, type, provider) + end - def create_relationship_index(name, type = "exact", provider = "lucene") - options = { :body => ({:name => name, :config => {:type => type, :provider => provider}}).to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/relationship", options) - end + def create_relationship_auto_index(type = "exact", provider = "lucene") + @relationship_indexes.create_auto(type, provider) + end - def create_relationship_auto_index(type = "exact", provider = "lucene") - create_relationship_index("relationship_auto_index", type, provider) - end + def create_unique_relationship(index, key, value, type, from, to) + @relationship_indexes.create_unique(index, key, value, type, from, to) + end - def add_relationship_to_index(index, key, value, id) - options = { :body => ({:uri => self.configuration + "/relationship/#{get_id(id)}", :key => key, :value => value}).to_json, :headers => {'Content-Type' => 'application/json'} } - post("/index/relationship/#{index}", options) - end + def add_relationship_to_index(index, key, value, id) + @relationship_indexes.add(index, key, value, id) + end - def remove_relationship_from_index(*args) - case args.size - when 4 then delete("/index/relationship/#{args[0]}/#{args[1]}/#{args[2]}/#{get_id(args[3])}") - when 3 then delete("/index/relationship/#{args[0]}/#{args[1]}/#{get_id(args[2])}") - when 2 then delete("/index/relationship/#{args[0]}/#{get_id(args[1])}") - end + def remove_relationship_from_index(*args) + case args.size + when 4 then @relationship_indexes.remove_by_value(args[0], get_id(args[3]), args[1], args[2]) + when 3 then @relationship_indexes.remove_by_key(args[0], get_id(args[2]), args[1]) + when 2 then @relationship_indexes.remove(args[0], get_id(args[1])) end + end - def get_relationship_index(index, key, value) - index = get("/index/relationship/#{index}/#{key}/#{value}") || Array.new - return nil if index.empty? - index - end + def get_relationship_index(index, key, value) + @relationship_indexes.get(index, key, value) + end - def find_relationship_index(*args) - case args.size - when 3 then index = get("/index/relationship/#{args[0]}/#{args[1]}?query=#{args[2]}") || Array.new - when 2 then index = get("/index/relationship/#{args[0]}?query=#{args[1]}") || Array.new - end - return nil if index.empty? - index + def find_relationship_index(*args) + case args.size + when 3 then index = @relationship_indexes.find_by_key_query(args[0], args[1], args[2]) + when 2 then index = @relationship_indexes.find_by_query(args[0], args[1]) end + return nil if index.empty? + index + end - # relationship auto indexes - - def get_relationship_auto_index(key, value) - index = get("/index/auto/relationship/#{key}/#{value}") || Array.new - return nil if index.empty? - index - end + # relationship auto indexes - def find_relationship_auto_index(*args) - case args.size - when 2 then index = get("/index/auto/relationship/#{args[0]}/#{args[1]}") || Array.new - when 1 then index = get("/index/auto/relationship/?query=#{args[0]}") || Array.new - end - return nil if index.empty? - index - end - - def get_relationship_auto_index_status - get("/index/auto/relationship/status") - end + def get_relationship_auto_index(key, value) + @relationship_auto_indexes.get(key, value) + end - def set_relationship_auto_index_status(change_to = true) - options = { :body => change_to.to_json, :headers => {'Content-Type' => 'application/json'} } - put("/index/auto/relationship/status", options) + def find_relationship_auto_index(*args) + case args.size + when 2 then index = @relationship_auto_indexes.find(args[0], args[1]) + when 1 then index = @relationship_auto_indexes.query(args[0]) end + return nil if index.empty? + index + end - def get_relationship_auto_index_properties - get("/index/auto/relationship/properties") - end + def get_relationship_auto_index_status + @relationship_auto_indexes.status + end - def add_relationship_auto_index_property(property) - options = { :body => property, :headers => {'Content-Type' => 'application/json'} } - post("/index/auto/relationship/properties", options) - end + def set_relationship_auto_index_status(change_to = true) + @relationship_auto_indexes.status = change_to + end - def remove_relationship_auto_index_property(property) - delete("/index/auto/relationship/properties/#{property}") - end + def get_relationship_auto_index_properties + @relationship_auto_indexes.properties + end - # traversal + def add_relationship_auto_index_property(property) + @relationship_auto_indexes.add_property(property) + end - def traverse(id, return_type, description) - options = { :body => {"order" => get_order(description["order"]), - "uniqueness" => get_uniqueness(description["uniqueness"]), - "relationships" => description["relationships"], - "prune_evaluator" => description["prune evaluator"], - "return_filter" => description["return filter"], - "max_depth" => get_depth(description["depth"]), }.to_json, :headers => {'Content-Type' => 'application/json'} } - traversal = post("/node/#{get_id(id)}/traverse/#{get_type(return_type)}", options) || Array.new - end + def remove_relationship_auto_index_property(property) + @relationship_auto_indexes.remove_property(property) + end - # paths + # traversal - def get_path(from, to, relationships, depth=1, algorithm="shortestPath") - options = { :body => {"to" => self.configuration + "/node/#{get_id(to)}", "relationships" => relationships, "max_depth" => depth, "algorithm" => get_algorithm(algorithm) }.to_json, :headers => {'Content-Type' => 'application/json'} } - path = post("/node/#{get_id(from)}/path", options) || Hash.new - end + def traverse(id, return_type, description) + @node_traversal.traverse(id, return_type, description) + end - def get_paths(from, to, relationships, depth=1, algorithm="allPaths") - options = { :body => {"to" => self.configuration + "/node/#{get_id(to)}", "relationships" => relationships, "max_depth" => depth, "algorithm" => get_algorithm(algorithm) }.to_json, :headers => {'Content-Type' => 'application/json'} } - paths = post("/node/#{get_id(from)}/paths", options) || Array.new - end - - def get_shortest_weighted_path(from, to, relationships, weight_attr='weight', depth=1, algorithm="dijkstra") - options = { :body => {"to" => self.configuration + "/node/#{get_id(to)}", "relationships" => relationships, "cost_property" => weight_attr, "max_depth" => depth, "algorithm" => get_algorithm(algorithm) }.to_json, :headers => {'Content-Type' => 'application/json'} } - paths = post("/node/#{get_id(from)}/paths", options) || Hash.new - end + # paths - # query + def get_path(from, to, relationships, depth = 1, algorithm = "shortestPath") + @node_paths.get(from, to, relationships, depth, algorithm) + end - def execute_query(query, params = {}) - options = { :body => {:query => query, :params => params}.to_json, :headers => {'Content-Type' => 'application/json', 'Accept' => 'application/json;stream=true'} } - result = post(@connection.cypher_path, options) - end + def get_paths(from, to, relationships, depth = 1, algorithm = "allPaths") + @node_paths.get_all(from, to, relationships, depth, algorithm) + end - # script - - def execute_script(script, params = {}) - options = { :body => {:script => script, :params => params}.to_json , :headers => {'Content-Type' => 'application/json'} } - result = post(@connection.gremlin_path, options) - result == "null" ? nil : result - end + def get_shortest_weighted_path(from, to, relationships, weight_attr = "weight", depth = 1, algorithm = "dijkstra") + @node_paths.shortest_weighted(from, to, relationships, weight_attr, depth, algorithm) + end - # batch + # cypher query - def batch(*args) - batch = [] - Array(args).each_with_index do |c,i| - batch << {:id => i}.merge(get_batch(c)) - end - options = { :body => batch.to_json, :headers => {'Content-Type' => 'application/json', 'Accept' => 'application/json;stream=true'} } - post("/batch", options) - end - - def batch_not_streaming(*args) - batch = [] - Array(args).each_with_index do |c,i| - batch << {:id => i}.merge(get_batch(c)) - end - options = { :body => batch.to_json, :headers => {'Content-Type' => 'application/json'} } - post("/batch", options) - end - - def get_batch(args) - case args[0] - when :get_node - {:method => "GET", :to => "/node/#{get_id(args[1])}"} - when :create_node - {:method => "POST", :to => "/node/", :body => args[1]} - when :create_unique_node - {:method => "POST", :to => "/index/node/#{args[1]}?unique", :body => {:key => args[2], :value => args[3], :properties => args[4]}} - when :set_node_property - {:method => "PUT", :to => "/node/#{get_id(args[1])}/properties/#{args[2].keys.first}", :body => args[2].values.first} - when :reset_node_properties - {:method => "PUT", :to => "/node/#{get_id(args[1])}/properties", :body => args[2]} - when :get_relationship - {:method => "GET", :to => "/relationship/#{get_id(args[1])}"} - when :create_relationship - {:method => "POST", :to => (args[2].is_a?(String) && args[2].start_with?("{") ? "" : "/node/") + "#{get_id(args[2])}/relationships", :body => {:to => (args[3].is_a?(String) && args[3].start_with?("{") ? "" : "/node/") + "#{get_id(args[3])}", :type => args[1], :data => args[4] } } - when :create_unique_relationship - {:method => "POST", :to => "/index/relationship/#{args[1]}?unique", :body => {:key => args[2], :value => args[3], :type => args[4], :start => (args[5].is_a?(String) && args[5].start_with?("{") ? "" : "/node/") + "#{get_id(args[5])}", :end=> (args[6].is_a?(String) && args[6].start_with?("{") ? "" : "/node/") + "#{get_id(args[6])}"} } - when :delete_relationship - {:method => "DELETE", :to => "/relationship/#{get_id(args[1])}"} - when :set_relationship_property - {:method => "PUT", :to => "/relationship/#{get_id(args[1])}/properties/#{args[2].keys.first}", :body => args[2].values.first} - when :reset_relationship_properties - {:method => "PUT", :to => (args[1].is_a?(String) && args[1].start_with?("{") ? "" : "/relationship/") + "#{get_id(args[1])}/properties", :body => args[2]} - when :add_node_to_index - {:method => "POST", :to => "/index/node/#{args[1]}", :body => {:uri => (args[4].is_a?(String) && args[4].start_with?("{") ? "" : "/node/") + "#{get_id(args[4])}", :key => args[2], :value => args[3] } } - when :add_relationship_to_index - {:method => "POST", :to => "/index/relationship/#{args[1]}", :body => {:uri => (args[4].is_a?(String) && args[4].start_with?("{") ? "" : "/relationship/") + "#{get_id(args[4])}", :key => args[2], :value => args[3] } } - when :get_node_index - {:method => "GET", :to => "/index/node/#{args[1]}/#{args[2]}/#{args[3]}"} - when :get_relationship_index - {:method => "GET", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{args[3]}"} - when :get_node_relationships - {:method => "GET", :to => "/node/#{get_id(args[1])}/relationships/#{args[2] || 'all'}"} - when :execute_script - {:method => "POST", :to => @connection.gremlin_path, :body => {:script => args[1], :params => args[2]}} - when :execute_query - if args[2] - {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1], :params => args[2]}} - else - {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1]}} - end - when :remove_node_from_index - case args.size - when 5 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{args[2]}/#{args[3]}/#{get_id(args[4])}" } - when 4 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{args[2]}/#{get_id(args[3])}" } - when 3 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{get_id(args[2])}" } - end - when :remove_relationship_from_index - case args.size - when 5 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{args[3]}/#{get_id(args[4])}" } - when 4 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{get_id(args[3])}" } - when 3 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{get_id(args[2])}" } - end - when :delete_node - {:method => "DELETE", :to => "/node/#{get_id(args[1])}"} - else - raise "Unknown option #{args[0]}" - end - end + def execute_query(query, params = {}) + @cypher.query(query, params) + end - # delete - - # For testing (use a separate neo4j instance) - # call this before each test or spec - def clean_database(sanity_check = "not_really") - if sanity_check == "yes_i_really_want_to_clean_the_database" - delete("/cleandb/secret-key") - true - else - false - end - end + # gremlin script + def execute_script(script, params = {}) + @gremlin.execute(script, params) + end - def get_algorithm(algorithm) - case algorithm - when :shortest, "shortest", :shortestPath, "shortestPath", :short, "short" - "shortestPath" - when :allSimplePaths, "allSimplePaths", :simple, "simple" - "allSimplePaths" - when :dijkstra, "dijkstra" - "dijkstra" - else - "allPaths" - end - end + # batch - def get_order(order) - case order - when :breadth, "breadth", "breadth first", "breadthFirst", :wide, "wide" - "breadth first" - else - "depth first" - end - end + def batch(*args) + @batch.execute(*args) + end - def get_type(type) - case type - when :relationship, "relationship", :relationships, "relationships" - "relationship" - when :path, "path", :paths, "paths" - "path" - when :fullpath, "fullpath", :fullpaths, "fullpaths" - "fullpath" - else - "node" - end - end + def batch_not_streaming(*args) + @batch.not_streaming(*args) + end - def get_uniqueness(uniqueness) - case uniqueness - when :nodeglobal, "node global", "nodeglobal", "node_global" - "node global" - when :nodepath, "node path", "nodepath", "node_path" - "node path" - when :noderecent, "node recent", "noderecent", "node_recent" - "node recent" - when :relationshipglobal, "relationship global", "relationshipglobal", "relationship_global" - "relationship global" - when :relationshippath, "relationship path", "relationshippath", "relationship_path" - "relationship path" - when :relationshiprecent, "relationship recent", "relationshiprecent", "relationship_recent" - "relationship recent" - else - "none" - end - end + # clean database - def get_depth(depth) - return nil if depth.nil? - return 1 if depth.to_i == 0 - depth.to_i + # For testing (use a separate neo4j instance) + # call this before each test or spec + def clean_database(sanity_check = "not_really") + if sanity_check == "yes_i_really_want_to_clean_the_database" + @clean.execute + true + else + false end + end end end diff --git a/lib/neography/rest/batch.rb b/lib/neography/rest/batch.rb new file mode 100644 index 0000000..6aec6c9 --- /dev/null +++ b/lib/neography/rest/batch.rb @@ -0,0 +1,99 @@ +module Neography + class Rest + class Batch + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :batch_path, "/batch" + + def initialize(connection) + @connection = connection + end + + def execute(*args) + batch({'Accept' => 'application/json;stream=true'}, *args) + end + + def not_streaming(*args) + batch({}, *args) + end + + private + + def batch(accept_header, *args) + batch = [] + Array(args).each_with_index do |c,i| + batch << {:id => i }.merge(get_batch(c)) + end + options = { + :body => batch.to_json, + :headers => json_content_type.merge(accept_header) + } + + @connection.post(batch_path, options) + end + + def get_batch(args) + case args[0] + when :get_node + {:method => "GET", :to => "/node/#{get_id(args[1])}"} + when :create_node + {:method => "POST", :to => "/node/", :body => args[1]} + when :create_unique_node + {:method => "POST", :to => "/index/node/#{args[1]}?unique", :body => {:key => args[2], :value => args[3], :properties => args[4]}} + when :set_node_property + {:method => "PUT", :to => "/node/#{get_id(args[1])}/properties/#{args[2].keys.first}", :body => args[2].values.first} + when :reset_node_properties + {:method => "PUT", :to => "/node/#{get_id(args[1])}/properties", :body => args[2]} + when :get_relationship + {:method => "GET", :to => "/relationship/#{get_id(args[1])}"} + when :create_relationship + {:method => "POST", :to => (args[2].is_a?(String) && args[2].start_with?("{") ? "" : "/node/") + "#{get_id(args[2])}/relationships", :body => {:to => (args[3].is_a?(String) && args[3].start_with?("{") ? "" : "/node/") + "#{get_id(args[3])}", :type => args[1], :data => args[4] } } + when :create_unique_relationship + {:method => "POST", :to => "/index/relationship/#{args[1]}?unique", :body => {:key => args[2], :value => args[3], :type => args[4], :start => (args[5].is_a?(String) && args[5].start_with?("{") ? "" : "/node/") + "#{get_id(args[5])}", :end=> (args[6].is_a?(String) && args[6].start_with?("{") ? "" : "/node/") + "#{get_id(args[6])}"} } + when :delete_relationship + {:method => "DELETE", :to => "/relationship/#{get_id(args[1])}"} + when :set_relationship_property + {:method => "PUT", :to => "/relationship/#{get_id(args[1])}/properties/#{args[2].keys.first}", :body => args[2].values.first} + when :reset_relationship_properties + {:method => "PUT", :to => (args[1].is_a?(String) && args[1].start_with?("{") ? "" : "/relationship/") + "#{get_id(args[1])}/properties", :body => args[2]} + when :add_node_to_index + {:method => "POST", :to => "/index/node/#{args[1]}", :body => {:uri => (args[4].is_a?(String) && args[4].start_with?("{") ? "" : "/node/") + "#{get_id(args[4])}", :key => args[2], :value => args[3] } } + when :add_relationship_to_index + {:method => "POST", :to => "/index/relationship/#{args[1]}", :body => {:uri => (args[4].is_a?(String) && args[4].start_with?("{") ? "" : "/relationship/") + "#{get_id(args[4])}", :key => args[2], :value => args[3] } } + when :get_node_index + {:method => "GET", :to => "/index/node/#{args[1]}/#{args[2]}/#{args[3]}"} + when :get_relationship_index + {:method => "GET", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{args[3]}"} + when :get_node_relationships + {:method => "GET", :to => "/node/#{get_id(args[1])}/relationships/#{args[2] || 'all'}"} + when :execute_script + {:method => "POST", :to => @connection.gremlin_path, :body => {:script => args[1], :params => args[2]}} + when :execute_query + if args[2] + {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1], :params => args[2]}} + else + {:method => "POST", :to => @connection.cypher_path, :body => {:query => args[1]}} + end + when :remove_node_from_index + case args.size + when 5 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{args[2]}/#{args[3]}/#{get_id(args[4])}" } + when 4 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{args[2]}/#{get_id(args[3])}" } + when 3 then {:method => "DELETE", :to => "/index/node/#{args[1]}/#{get_id(args[2])}" } + end + when :remove_relationship_from_index + case args.size + when 5 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{args[3]}/#{get_id(args[4])}" } + when 4 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{args[2]}/#{get_id(args[3])}" } + when 3 then {:method => "DELETE", :to => "/index/relationship/#{args[1]}/#{get_id(args[2])}" } + end + when :delete_node + {:method => "DELETE", :to => "/node/#{get_id(args[1])}"} + else + raise "Unknown option #{args[0]}" + end + end + + end + end +end diff --git a/lib/neography/rest/clean.rb b/lib/neography/rest/clean.rb new file mode 100644 index 0000000..ca16fc3 --- /dev/null +++ b/lib/neography/rest/clean.rb @@ -0,0 +1,20 @@ +module Neography + class Rest + class Clean + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :clean, "/cleandb/secret-key" + + def initialize(connection) + @connection = connection + end + + + def execute + @connection.delete(clean) + end + + end + end +end diff --git a/lib/neography/rest/cypher.rb b/lib/neography/rest/cypher.rb new file mode 100644 index 0000000..c959295 --- /dev/null +++ b/lib/neography/rest/cypher.rb @@ -0,0 +1,24 @@ +module Neography + class Rest + class Cypher + include Neography::Rest::Helpers + + def initialize(connection) + @connection = connection + end + + def query(query, parameters) + options = { + :body => { + :query => query, + :params => parameters + }.to_json, + :headers => json_content_type.merge({'Accept' => 'application/json;stream=true'}) + } + + @connection.post(@connection.cypher_path, options) + end + + end + end +end diff --git a/lib/neography/rest/gremlin.rb b/lib/neography/rest/gremlin.rb new file mode 100644 index 0000000..d109123 --- /dev/null +++ b/lib/neography/rest/gremlin.rb @@ -0,0 +1,24 @@ +module Neography + class Rest + class Gremlin + include Neography::Rest::Helpers + + def initialize(connection) + @connection = connection + end + + def execute(script, parameters) + options = { + :body => { + :script => script, + :params => parameters, + }.to_json, + :headers => json_content_type + } + result = @connection.post(@connection.gremlin_path, options) + result == "null" ? nil : result + end + + end + end +end diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb index 0ce4d82..83ff40d 100644 --- a/lib/neography/rest/node_indexes.rb +++ b/lib/neography/rest/node_indexes.rb @@ -42,7 +42,7 @@ def create_auto(type, provider) create("node_auto_index", type, provider) end - def create_unique_node(index, key, value, props) + def create_unique(index, key, value, props) options = { :body => ( { :properties => props, @@ -55,7 +55,7 @@ def create_unique_node(index, key, value, props) @connection.post(unique(:index => index), options) end - def add_node(index, key, value, id) + def add(index, key, value, id) options = { :body => ( { :uri => @connection.configuration + "/node/#{get_id(id)}", @@ -68,30 +68,30 @@ def add_node(index, key, value, id) @connection.post(base(:index => index), options) end - def get_node(index, key, value) + def get(index, key, value) index = @connection.get(key_value(:index => index, :key => key, :value => value)) || Array.new return nil if index.empty? index end # TODO FIX BUG %20 - def find_node_by_value(index, key, value) + def find_by_value(index, key, value) @connection.get(key_value2(:index => index, :key => key, :value => value)) || Array.new end - def find_node_by_query(index, query) + def find_by_query(index, query) @connection.get(query(:index => index, :query => query)) || Array.new end - def remove_node(index, id) + def remove(index, id) @connection.delete(node(:index => index, :id => get_id(id))) end - def remove_node_by_key(index, id, key) + def remove_by_key(index, id, key) @connection.delete(key(:index => index, :id => get_id(id), :key => key)) end - def remove_node_by_value(index, id, key, value) + def remove_by_value(index, id, key, value) @connection.delete(value(:index => index, :id => get_id(id), :key => key, :value => value)) end diff --git a/lib/neography/rest/node_paths.rb b/lib/neography/rest/node_paths.rb new file mode 100644 index 0000000..9f2dee5 --- /dev/null +++ b/lib/neography/rest/node_paths.rb @@ -0,0 +1,68 @@ +module Neography + class Rest + class NodePaths + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :base, "/node/:id/path" + add_path :all, "/node/:id/paths" + + def initialize(connection) + @connection = connection + end + + def get(from, to, relationships, depth, algorithm) + options = { :body => { + "to" => @connection.configuration + "/node/#{get_id(to)}", + "relationships" => relationships, + "max_depth" => depth, + "algorithm" => get_algorithm(algorithm) + }.to_json, + :headers => json_content_type + } + @connection.post(base(:id => get_id(from)), options) || Hash.new + end + + def get_all(from, to, relationships, depth, algorithm) + options = { :body => { + "to" => @connection.configuration + "/node/#{get_id(to)}", + "relationships" => relationships, + "max_depth" => depth, + "algorithm" => get_algorithm(algorithm) + }.to_json, + :headers => json_content_type + } + @connection.post(all(:id => get_id(from)), options) || Array.new + end + + def shortest_weighted(from, to, relationships, weight_attribute, depth, algorithm) + options = { :body => { + "to" => @connection.configuration + "/node/#{get_id(to)}", + "relationships" => relationships, + "cost_property" => weight_attribute, + "max_depth" => depth, + "algorithm" => get_algorithm(algorithm) + }.to_json, + :headers => json_content_type + } + @connection.post(all(:id => get_id(from)), options) || Hash.new + end + + private + + def get_algorithm(algorithm) + case algorithm + when :shortest, "shortest", :shortestPath, "shortestPath", :short, "short" + "shortestPath" + when :allSimplePaths, "allSimplePaths", :simple, "simple" + "allSimplePaths" + when :dijkstra, "dijkstra" + "dijkstra" + else + "allPaths" + end + end + + end + end +end diff --git a/lib/neography/rest/node_traversal.rb b/lib/neography/rest/node_traversal.rb new file mode 100644 index 0000000..fe3c542 --- /dev/null +++ b/lib/neography/rest/node_traversal.rb @@ -0,0 +1,81 @@ +module Neography + class Rest + class NodeTraversal + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :traversal, "/node/:id/traverse/:type" + + def initialize(connection) + @connection = connection + end + + def traverse(id, return_type, description) + options = { :body => { + "order" => get_order(description["order"]), + "uniqueness" => get_uniqueness(description["uniqueness"]), + "relationships" => description["relationships"], + "prune_evaluator" => description["prune evaluator"], + "return_filter" => description["return filter"], + "max_depth" => get_depth(description["depth"]) + }.to_json, + :headers => json_content_type + } + + type = get_type(return_type) + + @connection.post(traversal(:id => get_id(id), :type => type), options) || Array.new + end + + private + + def get_order(order) + case order + when :breadth, "breadth", "breadth first", "breadthFirst", :wide, "wide" + "breadth first" + else + "depth first" + end + end + + def get_uniqueness(uniqueness) + case uniqueness + when :nodeglobal, "node global", "nodeglobal", "node_global" + "node global" + when :nodepath, "node path", "nodepath", "node_path" + "node path" + when :noderecent, "node recent", "noderecent", "node_recent" + "node recent" + when :relationshipglobal, "relationship global", "relationshipglobal", "relationship_global" + "relationship global" + when :relationshippath, "relationship path", "relationshippath", "relationship_path" + "relationship path" + when :relationshiprecent, "relationship recent", "relationshiprecent", "relationship_recent" + "relationship recent" + else + "none" + end + end + + def get_depth(depth) + return nil if depth.nil? + return 1 if depth.to_i == 0 + depth.to_i + end + + def get_type(type) + case type + when :relationship, "relationship", :relationships, "relationships" + "relationship" + when :path, "path", :paths, "paths" + "path" + when :fullpath, "fullpath", :fullpaths, "fullpaths" + "fullpath" + else + "node" + end + end + + end + end +end diff --git a/lib/neography/rest/relationship_auto_indexes.rb b/lib/neography/rest/relationship_auto_indexes.rb new file mode 100644 index 0000000..c4d55c2 --- /dev/null +++ b/lib/neography/rest/relationship_auto_indexes.rb @@ -0,0 +1,61 @@ +module Neography + class Rest + class RelationshipAutoIndexes + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :key_value, "/index/auto/relationship/:key/:value" + add_path :query_index, "/index/auto/relationship/?query=:query" + add_path :index_status, "/index/auto/relationship/status" + add_path :index_properties, "/index/auto/relationship/properties" + add_path :index_property, "/index/auto/relationship/properties/:property" + + def initialize(connection) + @connection = connection + end + + def get(key, value) + index = @connection.get(key_value(:key => key, :value => value)) || Array.new + return nil if index.empty? + index + end + + def find(key, value) + @connection.get(key_value(:key => key, :value => value)) || Array.new + end + + def query(query_expression) + @connection.get(query_index(:query => query_expression)) || Array.new + end + + def status + @connection.get(index_status) + end + + def status=(value) + options = { + :body => value.to_json, + :headers => json_content_type + } + @connection.put(index_status, options) + end + + def properties + @connection.get(index_properties) + end + + def add_property(property) + options = { + :body => property, + :headers => json_content_type + } + @connection.post(index_properties, options) + end + + def remove_property(property) + @connection.delete(index_property(:property => property)) + end + + end + end +end diff --git a/lib/neography/rest/relationship_indexes.rb b/lib/neography/rest/relationship_indexes.rb new file mode 100644 index 0000000..58bd798 --- /dev/null +++ b/lib/neography/rest/relationship_indexes.rb @@ -0,0 +1,101 @@ +module Neography + class Rest + class RelationshipIndexes + include Neography::Rest::Paths + include Neography::Rest::Helpers + + add_path :all, "/index/relationship" + add_path :base, "/index/relationship/:index" + add_path :unique, "/index/relationship/:index?unique" + + add_path :relationship, "/index/relationship/:index/:id" + add_path :key, "/index/relationship/:index/:key/:id" + add_path :value, "/index/relationship/:index/:key/:value/:id" + add_path :key_value, "/index/relationship/:index/:key/:value" + + add_path :key_query, "/index/relationship/:index/:key?query=:query" + add_path :query, "/index/relationship/:index?query=:query" + + def initialize(connection) + @connection = connection + end + + def list + @connection.get(all) + end + + def create(name, type, provider) + options = { + :body => ( + { :name => name, + :config => { + :type => type, + :provider => provider + } + } + ).to_json, + :headers => json_content_type + } + @connection.post(all, options) + end + + def create_auto(type, provider) + create("relationship_auto_index", type, provider) + end + + def create_unique(index, key, value, type, from, to) + body = { + :key => key, + :value => value, + :type => type, + :start => @connection.configuration + "/node/#{get_id(from)}", + :end => @connection.configuration + "/node/#{get_id(to)}" + } + options = { :body => body.to_json, :headers => json_content_type } + + @connection.post(unique(:index => index), options) + end + + def add(index, key, value, id) + options = { + :body => ( + { :uri => @connection.configuration + "/relationship/#{get_id(id)}", + :key => key, + :value => value + } + ).to_json, + :headers => json_content_type + } + + @connection.post(base(:index => index), options) + end + + def get(index, key, value) + index = @connection.get(key_value(:index => index, :key => key, :value => value)) || Array.new + return nil if index.empty? + index + end + + def find_by_key_query(index, key, query) + @connection.get(key_query(:index => index, :key => key, :query => query)) || Array.new + end + + def find_by_query(index, query) + @connection.get(query(:index => index, :query => query)) || Array.new + end + + def remove(index, id) + @connection.delete(relationship(:index => index, :id => get_id(id))) + end + + def remove_by_key(index, id, key) + @connection.delete(key(:index => index, :id => get_id(id), :key => key)) + end + + def remove_by_value(index, id, key, value) + @connection.delete(value(:index => index, :id => get_id(id), :key => key, :value => value)) + end + + end + end +end From 3b9e97fe3d5dda5a906c0f4ed7cdf42028d42bf3 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 16:12:22 +0200 Subject: [PATCH 03/21] Add _path suffix in all REST classes. --- lib/neography/rest/batch.rb | 2 +- lib/neography/rest/clean.rb | 2 +- lib/neography/rest/node_auto_indexes.rb | 16 +++++++-------- lib/neography/rest/node_indexes.rb | 20 +++++++++---------- lib/neography/rest/node_paths.rb | 6 +++--- lib/neography/rest/node_properties.rb | 12 +++++------ lib/neography/rest/node_relationships.rb | 6 +++--- lib/neography/rest/node_traversal.rb | 2 +- lib/neography/rest/nodes.rb | 14 ++++++------- .../rest/relationship_auto_indexes.rb | 16 +++++++-------- lib/neography/rest/relationship_indexes.rb | 20 +++++++++---------- lib/neography/rest/relationship_properties.rb | 12 +++++------ lib/neography/rest/relationships.rb | 4 ++-- lib/neography/rest_paths.rb | 2 +- 14 files changed, 67 insertions(+), 67 deletions(-) diff --git a/lib/neography/rest/batch.rb b/lib/neography/rest/batch.rb index 6aec6c9..445ad0e 100644 --- a/lib/neography/rest/batch.rb +++ b/lib/neography/rest/batch.rb @@ -4,7 +4,7 @@ class Batch include Neography::Rest::Paths include Neography::Rest::Helpers - add_path :batch_path, "/batch" + add_path :batch, "/batch" def initialize(connection) @connection = connection diff --git a/lib/neography/rest/clean.rb b/lib/neography/rest/clean.rb index ca16fc3..5dfd686 100644 --- a/lib/neography/rest/clean.rb +++ b/lib/neography/rest/clean.rb @@ -12,7 +12,7 @@ def initialize(connection) def execute - @connection.delete(clean) + @connection.delete(clean_path) end end diff --git a/lib/neography/rest/node_auto_indexes.rb b/lib/neography/rest/node_auto_indexes.rb index 6e28001..e68afb8 100644 --- a/lib/neography/rest/node_auto_indexes.rb +++ b/lib/neography/rest/node_auto_indexes.rb @@ -15,21 +15,21 @@ def initialize(connection) end def get(key, value) - index = @connection.get(key_value(:key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:key => key, :value => value)) || Array.new return nil if index.empty? index end def find(key, value) - @connection.get(key_value(:key => key, :value => value)) || Array.new + @connection.get(key_value_path(:key => key, :value => value)) || Array.new end def query(query_expression) - @connection.get(query_index(:query => query_expression)) || Array.new + @connection.get(query_index_path(:query => query_expression)) || Array.new end def status - @connection.get(index_status) + @connection.get(index_status_path) end def status=(value) @@ -37,11 +37,11 @@ def status=(value) :body => value.to_json, :headers => json_content_type } - @connection.put(index_status, options) + @connection.put(index_status_path, options) end def properties - @connection.get(index_properties) + @connection.get(index_properties_path) end def add_property(property) @@ -49,11 +49,11 @@ def add_property(property) :body => property, :headers => json_content_type } - @connection.post(index_properties, options) + @connection.post(index_properties_path, options) end def remove_property(property) - @connection.delete(index_property(:property => property)) + @connection.delete(index_property_path(:property => property)) end end diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb index 83ff40d..9d2130b 100644 --- a/lib/neography/rest/node_indexes.rb +++ b/lib/neography/rest/node_indexes.rb @@ -20,7 +20,7 @@ def initialize(connection) end def list - @connection.get(all) + @connection.get(all_path) end def create(name, type, provider) @@ -35,7 +35,7 @@ def create(name, type, provider) ).to_json, :headers => json_content_type } - @connection.post(all, options) + @connection.post(all_path, options) end def create_auto(type, provider) @@ -52,7 +52,7 @@ def create_unique(index, key, value, props) ).to_json, :headers => json_content_type } - @connection.post(unique(:index => index), options) + @connection.post(unique_path(:index => index), options) end def add(index, key, value, id) @@ -65,34 +65,34 @@ def add(index, key, value, id) ).to_json, :headers => json_content_type } - @connection.post(base(:index => index), options) + @connection.post(base_path(:index => index), options) end def get(index, key, value) - index = @connection.get(key_value(:index => index, :key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || Array.new return nil if index.empty? index end # TODO FIX BUG %20 def find_by_value(index, key, value) - @connection.get(key_value2(:index => index, :key => key, :value => value)) || Array.new + @connection.get(key_value2_path(:index => index, :key => key, :value => value)) || Array.new end def find_by_query(index, query) - @connection.get(query(:index => index, :query => query)) || Array.new + @connection.get(query_path(:index => index, :query => query)) || Array.new end def remove(index, id) - @connection.delete(node(:index => index, :id => get_id(id))) + @connection.delete(node_path(:index => index, :id => get_id(id))) end def remove_by_key(index, id, key) - @connection.delete(key(:index => index, :id => get_id(id), :key => key)) + @connection.delete(key_path(:index => index, :id => get_id(id), :key => key)) end def remove_by_value(index, id, key, value) - @connection.delete(value(:index => index, :id => get_id(id), :key => key, :value => value)) + @connection.delete(value_path(:index => index, :id => get_id(id), :key => key, :value => value)) end end diff --git a/lib/neography/rest/node_paths.rb b/lib/neography/rest/node_paths.rb index 9f2dee5..28ddb58 100644 --- a/lib/neography/rest/node_paths.rb +++ b/lib/neography/rest/node_paths.rb @@ -20,7 +20,7 @@ def get(from, to, relationships, depth, algorithm) }.to_json, :headers => json_content_type } - @connection.post(base(:id => get_id(from)), options) || Hash.new + @connection.post(base_path(:id => get_id(from)), options) || Hash.new end def get_all(from, to, relationships, depth, algorithm) @@ -32,7 +32,7 @@ def get_all(from, to, relationships, depth, algorithm) }.to_json, :headers => json_content_type } - @connection.post(all(:id => get_id(from)), options) || Array.new + @connection.post(all_path(:id => get_id(from)), options) || Array.new end def shortest_weighted(from, to, relationships, weight_attribute, depth, algorithm) @@ -45,7 +45,7 @@ def shortest_weighted(from, to, relationships, weight_attribute, depth, algorith }.to_json, :headers => json_content_type } - @connection.post(all(:id => get_id(from)), options) || Hash.new + @connection.post(all_path(:id => get_id(from)), options) || Hash.new end private diff --git a/lib/neography/rest/node_properties.rb b/lib/neography/rest/node_properties.rb index b2dca28..f5480b8 100644 --- a/lib/neography/rest/node_properties.rb +++ b/lib/neography/rest/node_properties.rb @@ -13,12 +13,12 @@ def initialize(connection) def reset(id, properties) options = { :body => properties.to_json, :headers => json_content_type } - @connection.put(all(:id => get_id(id)), options) + @connection.put(all_path(:id => get_id(id)), options) end def get(id, *properties) if properties.none? - @connection.get(all(:id => get_id(id))) + @connection.get(all_path(:id => get_id(id))) else get_each(id, *properties) end @@ -26,7 +26,7 @@ def get(id, *properties) def get_each(id, *properties) node_properties = properties.inject({}) do |memo, property| - value = @connection.get(single(:id => get_id(id), :property => property)) + value = @connection.get(single_path(:id => get_id(id), :property => property)) memo[property] = value unless value.nil? memo end @@ -36,7 +36,7 @@ def get_each(id, *properties) def remove(id, *properties) if properties.none? - @connection.delete(all(:id => get_id(id))) + @connection.delete(all_path(:id => get_id(id))) else remove_each(id, *properties) end @@ -44,14 +44,14 @@ def remove(id, *properties) def remove_each(id, *properties) properties.each do |property| - @connection.delete(single(:id => get_id(id), :property => property)) + @connection.delete(single_path(:id => get_id(id), :property => property)) end end def set(id, properties) properties.each do |property, value| options = { :body => value.to_json, :headers => json_content_type } - @connection.put(single(:id => get_id(id), :property => property), options) + @connection.put(single_path(:id => get_id(id), :property => property), options) end end diff --git a/lib/neography/rest/node_relationships.rb b/lib/neography/rest/node_relationships.rb index d00e22f..76bf047 100644 --- a/lib/neography/rest/node_relationships.rb +++ b/lib/neography/rest/node_relationships.rb @@ -21,16 +21,16 @@ def create(type, from, to, props) }.to_json, :headers => json_content_type } - @connection.post(base(:id => get_id(from)), options) + @connection.post(base_path(:id => get_id(from)), options) end def get(id, direction, types) direction = get_direction(direction) if types.nil? - node_relationships = @connection.get(direction(:id => get_id(id), :direction => direction)) || Array.new + node_relationships = @connection.get(direction_path(:id => get_id(id), :direction => direction)) || Array.new else - node_relationships = @connection.get(type(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || Array.new + node_relationships = @connection.get(type_path(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || Array.new end return nil if node_relationships.empty? node_relationships diff --git a/lib/neography/rest/node_traversal.rb b/lib/neography/rest/node_traversal.rb index fe3c542..6cbd2ff 100644 --- a/lib/neography/rest/node_traversal.rb +++ b/lib/neography/rest/node_traversal.rb @@ -24,7 +24,7 @@ def traverse(id, return_type, description) type = get_type(return_type) - @connection.post(traversal(:id => get_id(id), :type => type), options) || Array.new + @connection.post(traversal_path(:id => get_id(id), :type => type), options) || Array.new end private diff --git a/lib/neography/rest/nodes.rb b/lib/neography/rest/nodes.rb index acecf26..abd0bd7 100644 --- a/lib/neography/rest/nodes.rb +++ b/lib/neography/rest/nodes.rb @@ -12,7 +12,7 @@ def initialize(connection) end def get(id) - @connection.get(base(:id => get_id(id))) + @connection.get(base_path(:id => get_id(id))) end def get_each(*nodes) @@ -25,7 +25,7 @@ def get_each(*nodes) def root root_node = @connection.get('/')["reference_node"] - @connection.get("/node/#{get_id(root_node)}") + @connection.get(base_path(:id => get_id(root_node))) end def create(*args) @@ -41,11 +41,11 @@ def create_with_attributes(attributes) :body => attributes.delete_if { |k, v| v.nil? }.to_json, :headers => json_content_type } - @connection.post(index, options) + @connection.post(index_path, options) end def create_empty - @connection.post(index) + @connection.post(index_path) end def create_multiple(nodes) @@ -73,12 +73,12 @@ def create_multiple_threaded(nodes) until node_queue.empty? do node = node_queue.pop if node.respond_to?(:each_pair) - responses.push( @connection.post(index, { + responses.push( @connection.post(index_path, { :body => node.to_json, :headers => json_content_type } ) ) else - responses.push( @connection.post(index) ) + responses.push( @connection.post(index_path) ) end end self.join @@ -94,7 +94,7 @@ def create_multiple_threaded(nodes) end def delete(id) - @connection.delete(base(:id => get_id(id))) + @connection.delete(base_path(:id => get_id(id))) end end diff --git a/lib/neography/rest/relationship_auto_indexes.rb b/lib/neography/rest/relationship_auto_indexes.rb index c4d55c2..04393b2 100644 --- a/lib/neography/rest/relationship_auto_indexes.rb +++ b/lib/neography/rest/relationship_auto_indexes.rb @@ -15,21 +15,21 @@ def initialize(connection) end def get(key, value) - index = @connection.get(key_value(:key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:key => key, :value => value)) || Array.new return nil if index.empty? index end def find(key, value) - @connection.get(key_value(:key => key, :value => value)) || Array.new + @connection.get(key_value_path(:key => key, :value => value)) || Array.new end def query(query_expression) - @connection.get(query_index(:query => query_expression)) || Array.new + @connection.get(query_index_path(:query => query_expression)) || Array.new end def status - @connection.get(index_status) + @connection.get(index_status_path) end def status=(value) @@ -37,11 +37,11 @@ def status=(value) :body => value.to_json, :headers => json_content_type } - @connection.put(index_status, options) + @connection.put(index_status_path, options) end def properties - @connection.get(index_properties) + @connection.get(index_properties_path) end def add_property(property) @@ -49,11 +49,11 @@ def add_property(property) :body => property, :headers => json_content_type } - @connection.post(index_properties, options) + @connection.post(index_properties_path, options) end def remove_property(property) - @connection.delete(index_property(:property => property)) + @connection.delete(index_property_path(:property => property)) end end diff --git a/lib/neography/rest/relationship_indexes.rb b/lib/neography/rest/relationship_indexes.rb index 58bd798..676f033 100644 --- a/lib/neography/rest/relationship_indexes.rb +++ b/lib/neography/rest/relationship_indexes.rb @@ -21,7 +21,7 @@ def initialize(connection) end def list - @connection.get(all) + @connection.get(all_path) end def create(name, type, provider) @@ -36,7 +36,7 @@ def create(name, type, provider) ).to_json, :headers => json_content_type } - @connection.post(all, options) + @connection.post(all_path, options) end def create_auto(type, provider) @@ -53,7 +53,7 @@ def create_unique(index, key, value, type, from, to) } options = { :body => body.to_json, :headers => json_content_type } - @connection.post(unique(:index => index), options) + @connection.post(unique_path(:index => index), options) end def add(index, key, value, id) @@ -67,33 +67,33 @@ def add(index, key, value, id) :headers => json_content_type } - @connection.post(base(:index => index), options) + @connection.post(base_path(:index => index), options) end def get(index, key, value) - index = @connection.get(key_value(:index => index, :key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || Array.new return nil if index.empty? index end def find_by_key_query(index, key, query) - @connection.get(key_query(:index => index, :key => key, :query => query)) || Array.new + @connection.get(key_query_path(:index => index, :key => key, :query => query)) || Array.new end def find_by_query(index, query) - @connection.get(query(:index => index, :query => query)) || Array.new + @connection.get(query_path(:index => index, :query => query)) || Array.new end def remove(index, id) - @connection.delete(relationship(:index => index, :id => get_id(id))) + @connection.delete(relationship_path(:index => index, :id => get_id(id))) end def remove_by_key(index, id, key) - @connection.delete(key(:index => index, :id => get_id(id), :key => key)) + @connection.delete(key_path(:index => index, :id => get_id(id), :key => key)) end def remove_by_value(index, id, key, value) - @connection.delete(value(:index => index, :id => get_id(id), :key => key, :value => value)) + @connection.delete(value_path(:index => index, :id => get_id(id), :key => key, :value => value)) end end diff --git a/lib/neography/rest/relationship_properties.rb b/lib/neography/rest/relationship_properties.rb index a8521fe..0821eb2 100644 --- a/lib/neography/rest/relationship_properties.rb +++ b/lib/neography/rest/relationship_properties.rb @@ -13,11 +13,11 @@ def initialize(connection) def get(id, properties) if properties.nil? - @connection.get(all(:id => get_id(id))) + @connection.get(all_path(:id => get_id(id))) else relationship_properties = Hash.new Array(properties).each do |property| - value = @connection.get(single(:id => get_id(id), :property => property)) + value = @connection.get(single_path(:id => get_id(id), :property => property)) relationship_properties[property] = value unless value.nil? end return nil if relationship_properties.empty? @@ -27,15 +27,15 @@ def get(id, properties) def reset(id, properties) options = { :body => properties.to_json, :headers => json_content_type } - @connection.put(all(:id => get_id(id)), options) + @connection.put(all_path(:id => get_id(id)), options) end def remove(id, properties) if properties.nil? - @connection.delete(all(id: get_id(id))) + @connection.delete(all_path(id: get_id(id))) else Array(properties).each do |property| - @connection.delete(single(:id => get_id(id), :property => property)) + @connection.delete(single_path(:id => get_id(id), :property => property)) end end end @@ -43,7 +43,7 @@ def remove(id, properties) def set(id, properties) properties.each do |key, value| options = { :body => value.to_json, :headers => json_content_type } - @connection.put(single(:id => get_id(id), :property => key), options) + @connection.put(single_path(:id => get_id(id), :property => key), options) end end diff --git a/lib/neography/rest/relationships.rb b/lib/neography/rest/relationships.rb index 9e7fd58..43cfa17 100644 --- a/lib/neography/rest/relationships.rb +++ b/lib/neography/rest/relationships.rb @@ -11,11 +11,11 @@ def initialize(connection) end def get(id) - @connection.get(base(:id => get_id(id))) + @connection.get(base_path(:id => get_id(id))) end def delete(id) - @connection.delete(base(:id => get_id(id))) + @connection.delete(base_path(:id => get_id(id))) end end diff --git a/lib/neography/rest_paths.rb b/lib/neography/rest_paths.rb index aae6efa..ba73c9e 100644 --- a/lib/neography/rest_paths.rb +++ b/lib/neography/rest_paths.rb @@ -14,7 +14,7 @@ def build_path(path, attributes) module ClassMethods def add_path(key, path) - define_method key do |*attributes| + define_method :"#{key}_path" do |*attributes| if attributes.any? build_path(path, *attributes) else From bbe019c24def91a192a1e25375dd8452d23bddb5 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 16:26:53 +0200 Subject: [PATCH 04/21] Rubified some code. --- lib/neography/connection.rb | 30 +++++++------ lib/neography/node_traverser.rb | 44 ++++++++++--------- lib/neography/rest.rb | 8 ++-- lib/neography/rest/node_auto_indexes.rb | 6 +-- lib/neography/rest/node_indexes.rb | 6 +-- lib/neography/rest/node_paths.rb | 6 +-- lib/neography/rest/node_relationships.rb | 4 +- lib/neography/rest/node_traversal.rb | 2 +- lib/neography/rest/nodes.rb | 6 +-- .../rest/relationship_auto_indexes.rb | 6 +-- lib/neography/rest/relationship_indexes.rb | 6 +-- lib/neography/rest/relationship_properties.rb | 42 +++++++++++------- 12 files changed, 90 insertions(+), 76 deletions(-) diff --git a/lib/neography/connection.rb b/lib/neography/connection.rb index fc33bfd..0b41639 100644 --- a/lib/neography/connection.rb +++ b/lib/neography/connection.rb @@ -11,14 +11,15 @@ class Connection :parser def initialize(options=ENV['NEO4J_URL'] || {}) - init = {:protocol => Neography::Config.protocol, - :server => Neography::Config.server, - :port => Neography::Config.port, + init = { + :protocol => Neography::Config.protocol, + :server => Neography::Config.server, + :port => Neography::Config.port, :directory => Neography::Config.directory, :cypher_path => Neography::Config.cypher_path, - :gremlin_path => Neography::Config.gremlin_path, - :log_file => Neography::Config.log_file, - :log_enabled => Neography::Config.log_enabled, + :gremlin_path => Neography::Config.gremlin_path, + :log_file => Neography::Config.log_file, + :log_enabled => Neography::Config.log_enabled, :max_threads => Neography::Config.max_threads, :authentication => Neography::Config.authentication, :username => Neography::Config.username, @@ -28,13 +29,14 @@ def initialize(options=ENV['NEO4J_URL'] || {}) unless options.respond_to?(:each_pair) url = URI.parse(options) - options = Hash.new - options[:protocol] = url.scheme + "://" - options[:server] = url.host - options[:port] = url.port - options[:directory] = url.path - options[:username] = url.user - options[:password] = url.password + options = { + :protocol => url.scheme + "://", + :server => url.host, + :port => url.port, + :directory => url.path, + :username => url.user, + :password => url.password + } options[:authentication] = 'basic' unless url.user.nil? end @@ -50,7 +52,7 @@ def initialize(options=ENV['NEO4J_URL'] || {}) @log_enabled = init[:log_enabled] @logger = Logger.new(@log_file) if @log_enabled @max_threads = init[:max_threads] - @authentication = Hash.new + @authentication = {} @authentication = {"#{init[:authentication]}_auth".to_sym => {:username => init[:username], :password => init[:password]}} unless init[:authentication].empty? @parser = init[:parser] @user_agent = {"User-Agent" => USER_AGENT} diff --git a/lib/neography/node_traverser.rb b/lib/neography/node_traverser.rb index b88e6b8..18b1d46 100644 --- a/lib/neography/node_traverser.rb +++ b/lib/neography/node_traverser.rb @@ -59,16 +59,18 @@ def order(o) end def filter(body) - @filter = Hash.new - @filter["language"] = "javascript" - @filter["body"] = body + @filter = { + "language" => "javascript", + "body" => body + } self end def prune(body) - @prune = Hash.new - @prune["language"] = "javascript" - @prune["body"] = body + @prune = { + "language" => "javascript", + "body" => body + } self end @@ -79,9 +81,10 @@ def depth(d) end def include_start_node - @filter = Hash.new - @filter["language"] = "builtin" - @filter["name"] = "all" + @filter = { + "language" => "builtin", + "name" => "all" + } self end @@ -108,13 +111,14 @@ def each end def iterator - options = Hash.new - options["order"] = @order - options["uniqueness"] = @uniqueness - options["relationships"] = @relationships - options["prune evaluator"] = @prune unless @prune.nil? - options["return filter"] = @filter unless @filter.nil? - options["depth"] = @depth unless @depth.nil? + options = { + "order" => @order, + "uniqueness" => @uniqueness, + "relationships" => @relationships + } + options["prune evaluator"] = @prune unless @prune.nil? + options["return filter"] = @filter unless @filter.nil? + options["depth"] = @depth unless @depth.nil? if @relationships[0]["type"].empty? rels = @from.neo_server.get_node_relationships(@from, @relationships[0]["direction"]) @@ -124,11 +128,11 @@ def iterator when "out" rels.collect { |r| @from.neo_server.get_node(r["end"]) } #.uniq else - rels.collect { |r| + rels.collect { |r| if @from.neo_id == r["start"].split('/').last - @from.neo_server.get_node(r["end"]) + @from.neo_server.get_node(r["end"]) else - @from.neo_server.get_node(r["start"]) + @from.neo_server.get_node(r["start"]) end } #.uniq end @@ -139,4 +143,4 @@ def iterator end -end \ No newline at end of file +end diff --git a/lib/neography/rest.rb b/lib/neography/rest.rb index db25be4..87a408d 100644 --- a/lib/neography/rest.rb +++ b/lib/neography/rest.rb @@ -121,12 +121,12 @@ def reset_relationship_properties(id, properties) @relationship_properties.reset(id, properties) end - def get_relationship_properties(id, properties = nil) - @relationship_properties.get(id, properties) + def get_relationship_properties(id, *properties) + @relationship_properties.get(id, *properties.flatten) end - def remove_relationship_properties(id, properties = nil) - @relationship_properties.remove(id, properties) + def remove_relationship_properties(id, *properties) + @relationship_properties.remove(id, *properties.flatten) end def set_relationship_properties(id, properties) diff --git a/lib/neography/rest/node_auto_indexes.rb b/lib/neography/rest/node_auto_indexes.rb index e68afb8..ae752da 100644 --- a/lib/neography/rest/node_auto_indexes.rb +++ b/lib/neography/rest/node_auto_indexes.rb @@ -15,17 +15,17 @@ def initialize(connection) end def get(key, value) - index = @connection.get(key_value_path(:key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:key => key, :value => value)) || [] return nil if index.empty? index end def find(key, value) - @connection.get(key_value_path(:key => key, :value => value)) || Array.new + @connection.get(key_value_path(:key => key, :value => value)) || [] end def query(query_expression) - @connection.get(query_index_path(:query => query_expression)) || Array.new + @connection.get(query_index_path(:query => query_expression)) || [] end def status diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb index 9d2130b..f8e638b 100644 --- a/lib/neography/rest/node_indexes.rb +++ b/lib/neography/rest/node_indexes.rb @@ -69,18 +69,18 @@ def add(index, key, value, id) end def get(index, key, value) - index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || [] return nil if index.empty? index end # TODO FIX BUG %20 def find_by_value(index, key, value) - @connection.get(key_value2_path(:index => index, :key => key, :value => value)) || Array.new + @connection.get(key_value2_path(:index => index, :key => key, :value => value)) || [] end def find_by_query(index, query) - @connection.get(query_path(:index => index, :query => query)) || Array.new + @connection.get(query_path(:index => index, :query => query)) || [] end def remove(index, id) diff --git a/lib/neography/rest/node_paths.rb b/lib/neography/rest/node_paths.rb index 28ddb58..97e0947 100644 --- a/lib/neography/rest/node_paths.rb +++ b/lib/neography/rest/node_paths.rb @@ -20,7 +20,7 @@ def get(from, to, relationships, depth, algorithm) }.to_json, :headers => json_content_type } - @connection.post(base_path(:id => get_id(from)), options) || Hash.new + @connection.post(base_path(:id => get_id(from)), options) || {} end def get_all(from, to, relationships, depth, algorithm) @@ -32,7 +32,7 @@ def get_all(from, to, relationships, depth, algorithm) }.to_json, :headers => json_content_type } - @connection.post(all_path(:id => get_id(from)), options) || Array.new + @connection.post(all_path(:id => get_id(from)), options) || [] end def shortest_weighted(from, to, relationships, weight_attribute, depth, algorithm) @@ -45,7 +45,7 @@ def shortest_weighted(from, to, relationships, weight_attribute, depth, algorith }.to_json, :headers => json_content_type } - @connection.post(all_path(:id => get_id(from)), options) || Hash.new + @connection.post(all_path(:id => get_id(from)), options) || {} end private diff --git a/lib/neography/rest/node_relationships.rb b/lib/neography/rest/node_relationships.rb index 76bf047..c90422d 100644 --- a/lib/neography/rest/node_relationships.rb +++ b/lib/neography/rest/node_relationships.rb @@ -28,9 +28,9 @@ def get(id, direction, types) direction = get_direction(direction) if types.nil? - node_relationships = @connection.get(direction_path(:id => get_id(id), :direction => direction)) || Array.new + node_relationships = @connection.get(direction_path(:id => get_id(id), :direction => direction)) || [] else - node_relationships = @connection.get(type_path(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || Array.new + node_relationships = @connection.get(type_path(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || [] end return nil if node_relationships.empty? node_relationships diff --git a/lib/neography/rest/node_traversal.rb b/lib/neography/rest/node_traversal.rb index 6cbd2ff..b6d6946 100644 --- a/lib/neography/rest/node_traversal.rb +++ b/lib/neography/rest/node_traversal.rb @@ -24,7 +24,7 @@ def traverse(id, return_type, description) type = get_type(return_type) - @connection.post(traversal_path(:id => get_id(id), :type => type), options) || Array.new + @connection.post(traversal_path(:id => get_id(id), :type => type), options) || [] end private diff --git a/lib/neography/rest/nodes.rb b/lib/neography/rest/nodes.rb index abd0bd7..3d95307 100644 --- a/lib/neography/rest/nodes.rb +++ b/lib/neography/rest/nodes.rb @@ -16,7 +16,7 @@ def get(id) end def get_each(*nodes) - gotten_nodes = Array.new + gotten_nodes = [] Array(nodes).flatten.each do |node| gotten_nodes << get(node) end @@ -50,7 +50,7 @@ def create_empty def create_multiple(nodes) nodes = Array.new(nodes) if nodes.kind_of? Fixnum - created_nodes = Array.new + created_nodes = [] nodes.each do |node| created_nodes << create(node) end @@ -85,7 +85,7 @@ def create_multiple_threaded(nodes) end end - created_nodes = Array.new + created_nodes = [] while created_nodes.size < nodes.size created_nodes << responses.pop diff --git a/lib/neography/rest/relationship_auto_indexes.rb b/lib/neography/rest/relationship_auto_indexes.rb index 04393b2..7b282bd 100644 --- a/lib/neography/rest/relationship_auto_indexes.rb +++ b/lib/neography/rest/relationship_auto_indexes.rb @@ -15,17 +15,17 @@ def initialize(connection) end def get(key, value) - index = @connection.get(key_value_path(:key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:key => key, :value => value)) || [] return nil if index.empty? index end def find(key, value) - @connection.get(key_value_path(:key => key, :value => value)) || Array.new + @connection.get(key_value_path(:key => key, :value => value)) || [] end def query(query_expression) - @connection.get(query_index_path(:query => query_expression)) || Array.new + @connection.get(query_index_path(:query => query_expression)) || [] end def status diff --git a/lib/neography/rest/relationship_indexes.rb b/lib/neography/rest/relationship_indexes.rb index 676f033..97f578a 100644 --- a/lib/neography/rest/relationship_indexes.rb +++ b/lib/neography/rest/relationship_indexes.rb @@ -71,17 +71,17 @@ def add(index, key, value, id) end def get(index, key, value) - index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || Array.new + index = @connection.get(key_value_path(:index => index, :key => key, :value => value)) || [] return nil if index.empty? index end def find_by_key_query(index, key, query) - @connection.get(key_query_path(:index => index, :key => key, :query => query)) || Array.new + @connection.get(key_query_path(:index => index, :key => key, :query => query)) || [] end def find_by_query(index, query) - @connection.get(query_path(:index => index, :query => query)) || Array.new + @connection.get(query_path(:index => index, :query => query)) || [] end def remove(index, id) diff --git a/lib/neography/rest/relationship_properties.rb b/lib/neography/rest/relationship_properties.rb index 0821eb2..14a82c1 100644 --- a/lib/neography/rest/relationship_properties.rb +++ b/lib/neography/rest/relationship_properties.rb @@ -11,32 +11,40 @@ def initialize(connection) @connection = connection end - def get(id, properties) - if properties.nil? + def reset(id, properties) + options = { :body => properties.to_json, :headers => json_content_type } + @connection.put(all_path(:id => get_id(id)), options) + end + + def get(id, *properties) + if properties.none? @connection.get(all_path(:id => get_id(id))) else - relationship_properties = Hash.new - Array(properties).each do |property| - value = @connection.get(single_path(:id => get_id(id), :property => property)) - relationship_properties[property] = value unless value.nil? - end - return nil if relationship_properties.empty? - relationship_properties + get_each(id, *properties) end end - def reset(id, properties) - options = { :body => properties.to_json, :headers => json_content_type } - @connection.put(all_path(:id => get_id(id)), options) + def get_each(id, *properties) + relationship_properties = properties.inject({}) do |memo, property| + value = @connection.get(single_path(:id => get_id(id), :property => property)) + memo[property] = value unless value.nil? + memo + end + return nil if relationship_properties.empty? + relationship_properties end - def remove(id, properties) - if properties.nil? + def remove(id, *properties) + if properties.none? @connection.delete(all_path(id: get_id(id))) else - Array(properties).each do |property| - @connection.delete(single_path(:id => get_id(id), :property => property)) - end + remove_each(id, *properties) + end + end + + def remove_each(id, *properties) + properties.each do |property| + @connection.delete(single_path(:id => get_id(id), :property => property)) end end From d1b50e839b151878350852ae2f7ad637f340e923 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 17:07:45 +0200 Subject: [PATCH 05/21] Add some basic unit tests for REST classes. --- lib/neography/rest/clean.rb | 1 - lib/neography/rest/nodes.rb | 8 +- spec/unit/rest/clean_spec.rb | 17 +++ spec/unit/rest/cypher_spec.rb | 21 ++++ spec/unit/rest/gremlin_spec.rb | 26 +++++ spec/unit/rest/nodes_spec.rb | 194 +++++++++++++++++++++++++++++++++ 6 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 spec/unit/rest/clean_spec.rb create mode 100644 spec/unit/rest/cypher_spec.rb create mode 100644 spec/unit/rest/gremlin_spec.rb create mode 100644 spec/unit/rest/nodes_spec.rb diff --git a/lib/neography/rest/clean.rb b/lib/neography/rest/clean.rb index 5dfd686..d0ea00c 100644 --- a/lib/neography/rest/clean.rb +++ b/lib/neography/rest/clean.rb @@ -10,7 +10,6 @@ def initialize(connection) @connection = connection end - def execute @connection.delete(clean_path) end diff --git a/lib/neography/rest/nodes.rb b/lib/neography/rest/nodes.rb index 3d95307..9f2cacb 100644 --- a/lib/neography/rest/nodes.rb +++ b/lib/neography/rest/nodes.rb @@ -48,6 +48,10 @@ def create_empty @connection.post(index_path) end + def delete(id) + @connection.delete(base_path(:id => get_id(id))) + end + def create_multiple(nodes) nodes = Array.new(nodes) if nodes.kind_of? Fixnum created_nodes = [] @@ -93,10 +97,6 @@ def create_multiple_threaded(nodes) created_nodes end - def delete(id) - @connection.delete(base_path(:id => get_id(id))) - end - end end end diff --git a/spec/unit/rest/clean_spec.rb b/spec/unit/rest/clean_spec.rb new file mode 100644 index 0000000..f10a1f6 --- /dev/null +++ b/spec/unit/rest/clean_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +module Neography + class Rest + describe Clean do + + let(:connection) { stub } + subject { Clean.new(connection) } + + it "cleans the database" do + connection.should_receive(:delete).with("/cleandb/secret-key") + subject.execute + end + + end + end +end diff --git a/spec/unit/rest/cypher_spec.rb b/spec/unit/rest/cypher_spec.rb new file mode 100644 index 0000000..8b34b57 --- /dev/null +++ b/spec/unit/rest/cypher_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +module Neography + class Rest + describe Cypher do + + let(:connection) { stub(:cypher_path => "/cypher") } + subject { Cypher.new(connection) } + + it "executes a cypher query" do + options = { + :body=>"{\"query\":\"SOME QUERY\",\"params\":{\"foo\":\"bar\",\"baz\":\"qux\"}}", + :headers=>{"Content-Type"=>"application/json", "Accept"=>"application/json;stream=true"} + } + connection.should_receive(:post).with("/cypher", options) + subject.query("SOME QUERY", { :foo => "bar", :baz => "qux" }) + end + + end + end +end diff --git a/spec/unit/rest/gremlin_spec.rb b/spec/unit/rest/gremlin_spec.rb new file mode 100644 index 0000000..44b78dd --- /dev/null +++ b/spec/unit/rest/gremlin_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +module Neography + class Rest + describe Gremlin do + + let(:connection) { stub(:gremlin_path => "/gremlin") } + subject { Gremlin.new(connection) } + + it "executes a gremlin script" do + options = { + :body=>"{\"script\":\"SOME SCRIPT\",\"params\":{\"foo\":\"bar\",\"baz\":\"qux\"}}", + :headers=>{"Content-Type"=>"application/json"} + } + connection.should_receive(:post).with("/gremlin", options) + subject.execute("SOME SCRIPT", { :foo => "bar", :baz => "qux" }) + end + + it "returns nil if script result is null" do + connection.stub(:post).and_return("null") + subject.execute("", {}).should be_nil + end + + end + end +end diff --git a/spec/unit/rest/nodes_spec.rb b/spec/unit/rest/nodes_spec.rb new file mode 100644 index 0000000..916b281 --- /dev/null +++ b/spec/unit/rest/nodes_spec.rb @@ -0,0 +1,194 @@ +require 'spec_helper' + +module Neography + class Rest + describe Nodes do + + let(:connection) { stub } + subject { Nodes.new(connection) } + + context "get nodes" do + it "gets single nodes" do + connection.should_receive(:get).with("/node/42") + subject.get("42") + end + + it "gets multiple nodes" do + connection.should_receive(:get).with("/node/42") + connection.should_receive(:get).with("/node/43") + subject.get_each("42", "43") + end + + it "returns multiple nodes in an array" do + connection.stub(:get).and_return("foo", "bar") + subject.get_each("42", "43").should == [ "foo", "bar" ] + end + + it "gets the root node" do + connection.stub(:get).with("/").and_return({ "reference_node" => "42" }) + connection.should_receive(:get).with("/node/42") + subject.root + end + + it "returns the root node" do + connection.stub(:get).and_return({ "reference_node" => "42" }, "foo") + subject.root.should == "foo" + end + end + + context "create nodes" do + + it "creates with attributes" do + options = { + :body => '{"foo":"bar","baz":"qux"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options) + subject.create_with_attributes({:foo => "bar", :baz => "qux"}) + end + + it "returns the created node" do + connection.stub(:post).and_return("foo") + subject.create_with_attributes({}).should == "foo" + end + + it "creates with attributes using #create method" do + options = { + :body => '{"foo":"bar","baz":"qux"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options) + subject.create({:foo => "bar", :baz => "qux"}) + end + + it "creates empty nodes" do + connection.should_receive(:post).with("/node") + subject.create_empty + end + + it "returns an empty node" do + connection.stub(:post).and_return("foo") + subject.create_empty.should == "foo" + end + + it "creates empty nodes using #create method" do + connection.should_receive(:post).with("/node") + subject.create + end + + end + + context "delete nodes" do + + it "deletes a node" do + connection.should_receive(:delete).with("/node/42") + subject.delete("42") + end + + end + + context "#create_multiple" do + + it "creates multiple with attributes" do + options1 = { + :body => '{"foo1":"bar1","baz1":"qux1"}', + :headers => json_content_type + } + options2 = { + :body => '{"foo2":"bar2","baz2":"qux2"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options1) + connection.should_receive(:post).with("/node", options2) + + subject.create_multiple([ + {:foo1 => "bar1", :baz1 => "qux1"}, + {:foo2 => "bar2", :baz2 => "qux2"} + ]) + end + + it "returns multiple nodes with attributes in an array" do + connection.stub(:post).and_return("foo", "bar") + subject.create_multiple([{},{}]).should == ["foo", "bar"] + end + + # exotic? + it "creates multiple with and without attributes" do + options1 = { + :body => '{"foo1":"bar1","baz1":"qux1"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options1) + connection.should_receive(:post).with("/node") + + subject.create_multiple([ + {:foo1 => "bar1", :baz1 => "qux1"}, + "not a hash" # ? + ]) + end + + it "creates multiple empty nodes" do + connection.should_receive(:post).with("/node").twice + subject.create_multiple(2) + end + + it "returns multiple empty nodes in an array" do + connection.stub(:post).and_return("foo", "bar") + subject.create_multiple(2).should == ["foo", "bar"] + end + + end + + context "#create_multiple_threaded" do + + let(:connection) { stub(:max_threads => 2) } + + it "creates multiple with attributes" do + options1 = { + :body => '{"foo1":"bar1","baz1":"qux1"}', + :headers => json_content_type + } + options2 = { + :body => '{"foo2":"bar2","baz2":"qux2"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options1) + connection.should_receive(:post).with("/node", options2) + + subject.create_multiple_threaded([ + {:foo1 => "bar1", :baz1 => "qux1"}, + {:foo2 => "bar2", :baz2 => "qux2"} + ]) + end + + # exotic? + it "creates multiple with and without attributes" do + options1 = { + :body => '{"foo1":"bar1","baz1":"qux1"}', + :headers => json_content_type + } + connection.should_receive(:post).with("/node", options1) + connection.should_receive(:post).with("/node") + + subject.create_multiple_threaded([ + {:foo1 => "bar1", :baz1 => "qux1"}, + "not a hash" # ? + ]) + end + + it "creates multiple empty nodes" do + connection.should_receive(:post).with("/node").twice + subject.create_multiple_threaded(2) + end + + end + + private + + def json_content_type + {"Content-Type"=>"application/json"} + end + + end + end +end From 5f88ebd66b51b5bcc02a4cb63936079bc6616d57 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 17:24:23 +0200 Subject: [PATCH 06/21] Add spec for REST node properties. --- lib/neography/rest/node_properties.rb | 14 ++--- spec/spec_helper.rb | 7 ++- spec/unit/rest/node_properties_spec.rb | 80 ++++++++++++++++++++++++++ spec/unit/rest/nodes_spec.rb | 6 -- 4 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 spec/unit/rest/node_properties_spec.rb diff --git a/lib/neography/rest/node_properties.rb b/lib/neography/rest/node_properties.rb index f5480b8..29f3f2a 100644 --- a/lib/neography/rest/node_properties.rb +++ b/lib/neography/rest/node_properties.rb @@ -11,6 +11,13 @@ def initialize(connection) @connection = connection end + def set(id, properties) + properties.each do |property, value| + options = { :body => value.to_json, :headers => json_content_type } + @connection.put(single_path(:id => get_id(id), :property => property), options) + end + end + def reset(id, properties) options = { :body => properties.to_json, :headers => json_content_type } @connection.put(all_path(:id => get_id(id)), options) @@ -48,13 +55,6 @@ def remove_each(id, *properties) end end - def set(id, properties) - properties.each do |property, value| - options = { :body => value.to_json, :headers => json_content_type } - @connection.put(single_path(:id => get_id(id), :property => property), options) - end - end - end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 78437da..917b593 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,4 +15,9 @@ def generate_text(length=8) RSpec.configure do |c| c.filter_run_excluding :slow => true, :break_gremlin => true -end \ No newline at end of file +end + +def json_content_type + {"Content-Type"=>"application/json"} +end + diff --git a/spec/unit/rest/node_properties_spec.rb b/spec/unit/rest/node_properties_spec.rb new file mode 100644 index 0000000..19a3a97 --- /dev/null +++ b/spec/unit/rest/node_properties_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodeProperties do + + let(:connection) { stub } + subject { NodeProperties.new(connection) } + + it "sets properties" do + options1 = { + :body => '"bar"', + :headers => json_content_type + } + options2 = { + :body => '"qux"', + :headers => json_content_type + } + connection.should_receive(:put).with("/node/42/properties/foo", options1) + connection.should_receive(:put).with("/node/42/properties/baz", options2) + subject.set("42", {:foo => "bar", :baz => "qux"}) + end + + it "resets properties" do + options = { + :body => '{"foo":"bar"}', + :headers => json_content_type + } + connection.should_receive(:put).with("/node/42/properties", options) + subject.reset("42", {:foo => "bar"}) + end + + context "getting properties" do + + it "gets all properties" do + connection.should_receive(:get).with("/node/42/properties") + subject.get("42") + end + + it "gets multiple properties" do + connection.should_receive(:get).with("/node/42/properties/foo") + connection.should_receive(:get).with("/node/42/properties/bar") + subject.get("42", "foo", "bar") + end + + it "returns multiple properties as a hash" do + connection.stub(:get).and_return("baz", "qux") + subject.get("42", "foo", "bar").should == { "foo" => "baz", "bar" => "qux" } + end + + it "returns nil if no properties were found" do + connection.stub(:get).and_return(nil, nil) + subject.get("42", "foo", "bar").should be_nil + end + + it "returns hash without nil return values" do + connection.stub(:get).and_return("baz", nil) + subject.get("42", "foo", "bar").should == { "foo" => "baz" } + end + + end + + context "removing properties" do + + it "removes all properties" do + connection.should_receive(:delete).with("/node/42/properties") + subject.remove("42") + end + + it "removes multiple properties" do + connection.should_receive(:delete).with("/node/42/properties/foo") + connection.should_receive(:delete).with("/node/42/properties/bar") + subject.remove("42", "foo", "bar") + end + + end + + end + end +end diff --git a/spec/unit/rest/nodes_spec.rb b/spec/unit/rest/nodes_spec.rb index 916b281..efe0775 100644 --- a/spec/unit/rest/nodes_spec.rb +++ b/spec/unit/rest/nodes_spec.rb @@ -183,12 +183,6 @@ class Rest end - private - - def json_content_type - {"Content-Type"=>"application/json"} - end - end end end From 9cd051b265dd84c8c27a648968926db8069b8270 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 17:49:13 +0200 Subject: [PATCH 07/21] Fix hash-rocket syntax. --- lib/neography/rest/relationship_properties.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/neography/rest/relationship_properties.rb b/lib/neography/rest/relationship_properties.rb index 14a82c1..c8ba4df 100644 --- a/lib/neography/rest/relationship_properties.rb +++ b/lib/neography/rest/relationship_properties.rb @@ -36,7 +36,7 @@ def get_each(id, *properties) def remove(id, *properties) if properties.none? - @connection.delete(all_path(id: get_id(id))) + @connection.delete(all_path(:id => get_id(id))) else remove_each(id, *properties) end From cf6f52c39400beabf67bf4036003e7e7532c54bb Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 20:08:27 +0200 Subject: [PATCH 08/21] Add NodeRelationships specs. --- lib/neography/rest/node_relationships.rb | 11 ++-- spec/matchers.rb | 16 ++++++ spec/spec_helper.rb | 2 + spec/unit/rest/node_relationships_spec.rb | 62 +++++++++++++++++++++++ 4 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 spec/matchers.rb create mode 100644 spec/unit/rest/node_relationships_spec.rb diff --git a/lib/neography/rest/node_relationships.rb b/lib/neography/rest/node_relationships.rb index c90422d..c9ce528 100644 --- a/lib/neography/rest/node_relationships.rb +++ b/lib/neography/rest/node_relationships.rb @@ -12,11 +12,11 @@ def initialize(connection) @connection = connection end - def create(type, from, to, props) + def create(type, from, to, properties) options = { :body => { :to => @connection.configuration + "/node/#{get_id(to)}", - :data => props, + :data => properties, :type => type }.to_json, :headers => json_content_type } @@ -24,19 +24,20 @@ def create(type, from, to, props) @connection.post(base_path(:id => get_id(from)), options) end - def get(id, direction, types) - direction = get_direction(direction) + def get(id, direction = nil, types = nil) + direction = parse_direction(direction) if types.nil? node_relationships = @connection.get(direction_path(:id => get_id(id), :direction => direction)) || [] else node_relationships = @connection.get(type_path(:id => get_id(id), :direction => direction, :types => Array(types).join('&'))) || [] end + return nil if node_relationships.empty? node_relationships end - def get_direction(direction) + def parse_direction(direction) case direction when :incoming, "incoming", :in, "in" "in" diff --git a/spec/matchers.rb b/spec/matchers.rb new file mode 100644 index 0000000..6c3cde2 --- /dev/null +++ b/spec/matchers.rb @@ -0,0 +1,16 @@ +# Convenience matcher for matching JSON fields with a hash +RSpec::Matchers.define :json_match do |field, expected| + + match do |actual| + expected == JSON.parse(actual[field]) + end + + failure_message_for_should do + "expected JSON in field '#{@field}' to not match '#{@expected}'" + end + + description do + "JSON in field '#{field}' should match '#{expected.inspect}'" + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 917b593..755d9fe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'neography' require 'benchmark' +require 'matchers' # If you want to see more, uncomment the next few lines # require 'net-http-spy' @@ -17,6 +18,7 @@ def generate_text(length=8) c.filter_run_excluding :slow => true, :break_gremlin => true end + def json_content_type {"Content-Type"=>"application/json"} end diff --git a/spec/unit/rest/node_relationships_spec.rb b/spec/unit/rest/node_relationships_spec.rb new file mode 100644 index 0000000..fb3fe37 --- /dev/null +++ b/spec/unit/rest/node_relationships_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodeRelationships do + + let(:connection) { stub(:configuration => "http://configuration") } + subject { NodeRelationships.new(connection) } + + it "creates a relationship" do + body_hash = { "type" => "some_type", + "to" => "http://configuration/node/43", + "data" => {"foo"=>"bar","baz"=>"qux"} + } + connection.should_receive(:post).with("/node/42/relationships", json_match(:body, body_hash)) + + subject.create("some_type", "42", "43", {:foo => "bar", :baz => "qux"}) + end + + it "gets relationships" do + connection.should_receive(:get).with("/node/42/relationships/all") + subject.get("42") + end + + it "gets relationships with direction" do + connection.should_receive(:get).with("/node/42/relationships/in") + subject.get("42", :in) + end + + it "gets relationships with direction and type" do + connection.should_receive(:get).with("/node/42/relationships/in/foo") + subject.get("42", :in, "foo") + end + + it "gets relationships with direction and types" do + connection.should_receive(:get).with("/node/42/relationships/in/foo&bar") + subject.get("42", :in, ["foo", "bar"]) + end + + context "directions" do + + [ :incoming, "incoming", :in, "in" ].each do |direction| + it "parses 'in' direction" do + NodeRelationships.new(nil).parse_direction(direction).should == "in" + end + end + + [ :outgoing, "outgoing", :out, "out" ].each do |direction| + it "parses 'out' direction" do + NodeRelationships.new(nil).parse_direction(direction).should == "out" + end + end + + it "parses 'all' direction by default" do + NodeRelationships.new(nil).parse_direction("foo").should == "all" + end + + end + + end + end +end From 7456f9ecc7e6970591729f17dc39c710ddd8862c Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 20:10:23 +0200 Subject: [PATCH 09/21] Update Gemfile.lock. --- Gemfile.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1ad352b..1f1c870 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - neography (0.0.28) + neography (0.0.30) httparty (>= 0.8.1) json multi_json @@ -13,10 +13,11 @@ GEM remote: http://rubygems.org/ specs: diff-lcs (1.1.3) - httparty (0.8.3) + httparty (0.9.0) multi_json (~> 1.0) multi_xml - json (1.7.3) + json (1.7.5) + json (1.7.5-java) multi_json (1.3.6) multi_xml (0.5.1) net-http-spy (0.2.1) @@ -30,7 +31,7 @@ GEM rspec-expectations (2.10.0) diff-lcs (~> 1.1.3) rspec-mocks (2.10.1) - rubyzip (0.9.8) + rubyzip (0.9.9) PLATFORMS java From 65ad5928f5afa0c897d848ffbb76e934e8dc504b Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 20:20:26 +0200 Subject: [PATCH 10/21] Add NodeRelationships return specs. --- spec/unit/rest/node_relationships_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/unit/rest/node_relationships_spec.rb b/spec/unit/rest/node_relationships_spec.rb index fb3fe37..2c8da00 100644 --- a/spec/unit/rest/node_relationships_spec.rb +++ b/spec/unit/rest/node_relationships_spec.rb @@ -17,6 +17,12 @@ class Rest subject.create("some_type", "42", "43", {:foo => "bar", :baz => "qux"}) end + it "returns the post results" do + connection.stub(:post).and_return("foo") + + subject.create("some_type", "42", "43", {}).should == "foo" + end + it "gets relationships" do connection.should_receive(:get).with("/node/42/relationships/all") subject.get("42") @@ -37,6 +43,16 @@ class Rest subject.get("42", :in, ["foo", "bar"]) end + it "returns nil if no relationships were found" do + connection.stub(:get).and_return(nil) + subject.get("42", :in).should be_nil + end + + it "returns nil if no relationships were found by type" do + connection.stub(:get).and_return(nil) + subject.get("42", :in, "foo") + end + context "directions" do [ :incoming, "incoming", :in, "in" ].each do |direction| From bff0665edeeb0b3c1ed0920f8af19cfb1c0c3648 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 20:56:15 +0200 Subject: [PATCH 11/21] Add NodeIndexes specs. --- lib/neography/rest/node_indexes.rb | 8 ++- spec/unit/rest/node_indexes_spec.rb | 101 ++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 spec/unit/rest/node_indexes_spec.rb diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb index f8e638b..049463f 100644 --- a/lib/neography/rest/node_indexes.rb +++ b/lib/neography/rest/node_indexes.rb @@ -13,7 +13,9 @@ class NodeIndexes add_path :key_value, "/index/node/:index/:key/:value" add_path :query, "/index/node/:index?query=:query" - add_path :key_value2, "/index/node/:index/:key?query=\":value\"" # TODO FIX BUG %20 + # this should be the same as the :key_value path above + # was changed to ?query="..." to fix bug with spaces and slashes + add_path :key_value2, "/index/node/:index/:key?query=\":value\"" def initialize(connection) @connection = connection @@ -42,10 +44,10 @@ def create_auto(type, provider) create("node_auto_index", type, provider) end - def create_unique(index, key, value, props) + def create_unique(index, key, value, properties) options = { :body => ( - { :properties => props, + { :properties => properties, :key => key, :value => value } diff --git a/spec/unit/rest/node_indexes_spec.rb b/spec/unit/rest/node_indexes_spec.rb new file mode 100644 index 0000000..5332cdd --- /dev/null +++ b/spec/unit/rest/node_indexes_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodeIndexes do + + let(:connection) { stub(:configuration => "http://configuration") } + subject { NodeIndexes.new(connection) } + + it "lists all indexes" do + connection.should_receive(:get).with("/index/node") + subject.list + end + + it "creates a node index" do + expected_body = { + "config" => { + "type" => "some_type", + "provider" => "some_provider" + }, + "name" => "some_index" + } + connection.should_receive(:post).with("/index/node", json_match(:body, expected_body)) + subject.create("some_index", "some_type", "some_provider") + end + + it "returns the post result after creation" do + connection.stub(:post).and_return("foo") + subject.create("some_index", "some_type", "some_provider").should == "foo" + end + + it "creates an auto-index" do + expected_body = { + "config" => { + "type" => "some_type", + "provider" => "some_provider" + }, + "name" => "node_auto_index" + } + connection.should_receive(:post).with("/index/node", json_match(:body, expected_body)) + subject.create_auto("some_type", "some_provider") + end + + it "creates a unique node in an index" do + expected_body = { + "properties" => "properties", + "key" => "key", + "value" => "value" + } + connection.should_receive(:post).with("/index/node/some_index?unique", json_match(:body, expected_body)) + subject.create_unique("some_index", "key", "value", "properties") + end + + it "adds a node to an index" do + expected_body = { + "uri" => "http://configuration/node/42", + "key" => "key", + "value" => "value" + } + connection.should_receive(:post).with("/index/node/some_index", json_match(:body, expected_body)) + subject.add("some_index", "key", "value", "42") + end + + it "gets a node from an index" do + connection.should_receive(:get).with("/index/node/some_index/some_key/some_value") + subject.get("some_index", "some_key", "some_value") + end + + it "returns nil if nothing was found in the index" do + connection.stub(:get).and_return(nil) + subject.get("some_index", "some_key", "some_value").should be_nil + end + + it "finds by key and value" do + connection.should_receive(:get).with("/index/node/some_index/some_key?query=\"some_value\"") + subject.find_by_value("some_index", "some_key", "some_value") + end + + it "finds by query" do + connection.should_receive(:get).with("/index/node/some_index?query=some_query") + subject.find_by_query("some_index", "some_query") + end + + it "removed a node from an index" do + connection.should_receive(:delete).with("/index/node/some_index/42") + subject.remove("some_index", "42") + end + + it "removed a node from an index by key" do + connection.should_receive(:delete).with("/index/node/some_index/some_key/42") + subject.remove_by_key("some_index", "42", "some_key") + end + + it "removed a node from an index by key and value" do + connection.should_receive(:delete).with("/index/node/some_index/some_key/some_value/42") + subject.remove_by_value("some_index", "42", "some_key", "some_value") + end + + end + end +end From 903aa56d7da8d0037d38a5c6f5aeaf3f62b56cb6 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 21:16:57 +0200 Subject: [PATCH 12/21] Add NodeAutoIndexes spec. --- spec/matchers.rb | 20 ++++++++- spec/unit/rest/node_auto_indexes_spec.rb | 57 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 spec/unit/rest/node_auto_indexes_spec.rb diff --git a/spec/matchers.rb b/spec/matchers.rb index 6c3cde2..955a23a 100644 --- a/spec/matchers.rb +++ b/spec/matchers.rb @@ -2,11 +2,12 @@ RSpec::Matchers.define :json_match do |field, expected| match do |actual| + p actual[field] expected == JSON.parse(actual[field]) end failure_message_for_should do - "expected JSON in field '#{@field}' to not match '#{@expected}'" + "expected JSON in field '#{field}' to match '#{expected}'" end description do @@ -14,3 +15,20 @@ end end + +# Convenience matcher for matching fields in a hash +RSpec::Matchers.define :hash_match do |field, expected| + + match do |actual| + expected == actual[field] + end + + failure_message_for_should do + "expected field '#{field}' to match '#{expected}'" + end + + description do + "field '#{field}' should match '#{expected.inspect}'" + end + +end diff --git a/spec/unit/rest/node_auto_indexes_spec.rb b/spec/unit/rest/node_auto_indexes_spec.rb new file mode 100644 index 0000000..8bd623e --- /dev/null +++ b/spec/unit/rest/node_auto_indexes_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodeAutoIndexes do + + let(:connection) { stub } + subject { NodeAutoIndexes.new(connection) } + + it "gets a node from an auto index" do + connection.should_receive(:get).with("/index/auto/node/some_key/some_value") + subject.get("some_key", "some_value") + end + + it "returns nil if nothing was found in the auto index" do + connection.stub(:get).and_return(nil) + subject.get("some_key", "some_value").should be_nil + end + + it "finds by key and value" do + connection.should_receive(:get).with("/index/auto/node/some_key/some_value") + subject.find("some_key", "some_value") + end + + it "finds by query" do + connection.should_receive(:get).with("/index/auto/node/?query=some_query") + subject.query("some_query") + end + + it "gets the status" do + connection.should_receive(:get).with("/index/auto/node/status") + subject.status + end + + it "sets the status" do + connection.should_receive(:put).with("/index/auto/node/status", hash_match(:body, '"foo"')) + subject.status = "foo" + end + + it "gets auto index properties" do + connection.should_receive(:get).with("/index/auto/node/properties") + subject.properties + end + + it "adds a property to an auto index" do + connection.should_receive(:post).with("/index/auto/node/properties", hash_match(:body, "foo")) + subject.add_property("foo") + end + + it "removes a property from an auto index" do + connection.should_receive(:delete).with("/index/auto/node/properties/foo") + subject.remove_property("foo") + end + + end + end +end From 0160de58090970fd8abbd0ebf5a2f2b01db7b860 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 21:26:55 +0200 Subject: [PATCH 13/21] Add traversal spec. --- spec/matchers.rb | 1 - spec/unit/rest/node_traversal_spec.rb | 36 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 spec/unit/rest/node_traversal_spec.rb diff --git a/spec/matchers.rb b/spec/matchers.rb index 955a23a..27d9695 100644 --- a/spec/matchers.rb +++ b/spec/matchers.rb @@ -2,7 +2,6 @@ RSpec::Matchers.define :json_match do |field, expected| match do |actual| - p actual[field] expected == JSON.parse(actual[field]) end diff --git a/spec/unit/rest/node_traversal_spec.rb b/spec/unit/rest/node_traversal_spec.rb new file mode 100644 index 0000000..0d339c2 --- /dev/null +++ b/spec/unit/rest/node_traversal_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodeTraversal do + + let(:connection) { stub } + subject { NodeTraversal.new(connection) } + + it "traverses" do + description = { + "order" => :breadth, + "uniqueness" => :nodeglobal, + "relationships" => "relationships", + "prune evaluator" => "prune_evaluator", + "return filter" => "return_filter", + "depth" => 4 + } + + expected_body = { + "order" => "breadth first", + "uniqueness" => "node global", + "relationships" => "relationships", + "prune_evaluator" => "prune_evaluator", + "return_filter" => "return_filter", + "max_depth" => 4 + } + + connection.should_receive(:post).with("/node/42/traverse/relationship", json_match(:body, expected_body)) + + subject.traverse("42", :relationship, description) + end + + end + end +end From 26f9e0ec13fae3ca045b4347ccc8ddb672ba6e45 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 21:52:02 +0200 Subject: [PATCH 14/21] Add traversal options spec. --- spec/unit/rest/node_traversal_spec.rb | 92 +++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/spec/unit/rest/node_traversal_spec.rb b/spec/unit/rest/node_traversal_spec.rb index 0d339c2..637e87a 100644 --- a/spec/unit/rest/node_traversal_spec.rb +++ b/spec/unit/rest/node_traversal_spec.rb @@ -31,6 +31,98 @@ class Rest subject.traverse("42", :relationship, description) end + context "options" do + let(:traversal) { NodeTraversal.new(nil) } + + context "order" do + [ :breadth, "breadth", "breadth first", "breadthFirst", :wide, "wide" ].each do |order| + it "parses breadth first" do + subject.send(:get_order, order).should == "breadth first" + end + end + + it "parses depth first by default" do + subject.send(:get_order, "foo").should == "depth first" + end + end + + context "uniqueness" do + [ :nodeglobal, "node global", "nodeglobal", "node_global" ].each do |order| + it "parses node global" do + subject.send(:get_uniqueness, order).should == "node global" + end + end + + [ :nodepath, "node path", "nodepath", "node_path" ].each do |order| + it "parses node path" do + subject.send(:get_uniqueness, order).should == "node path" + end + end + + [ :noderecent, "node recent", "noderecent", "node_recent" ].each do |order| + it "parses node recent" do + subject.send(:get_uniqueness, order).should == "node recent" + end + end + + [ :relationshipglobal, "relationship global", "relationshipglobal", "relationship_global" ].each do |order| + it "parses relationship global" do + subject.send(:get_uniqueness, order).should == "relationship global" + end + end + + [ :relationshippath, "relationship path", "relationshippath", "relationship_path" ].each do |order| + it "parses relationship path" do + subject.send(:get_uniqueness, order).should == "relationship path" + end + end + + [ :relationshiprecent, "relationship recent", "relationshiprecent", "relationship_recent" ].each do |order| + it "parses relationship recent" do + subject.send(:get_uniqueness, order).should == "relationship recent" + end + end + + it "parses none by default" do + subject.send(:get_uniqueness, "foo").should == "none" + end + end + + context "depth" do + it "parses nil as nil" do + subject.send(:get_depth, nil).should be_nil + end + it "parses 0 as 1" do + subject.send(:get_depth, "0").should == 1 + end + it "parses integers" do + subject.send(:get_depth, "42").should == 42 + end + end + + context "type" do + [ :relationship, "relationship", :relationships, "relationships" ].each do |type| + it "parses relationship" do + subject.send(:get_type, type).should == "relationship" + end + end + [ :path, "path", :paths, "paths" ].each do |type| + it "parses path" do + subject.send(:get_type, type).should == "path" + end + end + [ :fullpath, "fullpath", :fullpaths, "fullpaths" ].each do |type| + it "parses fullpath" do + subject.send(:get_type, type).should == "fullpath" + end + end + + it "parses node by default" do + subject.send(:get_type, "foo").should == "node" + end + end + end + end end end From 987f5f08f86f4a1c42ff08084278d3e97dcad485 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:15:30 +0200 Subject: [PATCH 15/21] Add NodePaths specs. --- lib/neography/rest/node_paths.rb | 2 +- spec/unit/rest/node_paths_spec.rb | 80 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 spec/unit/rest/node_paths_spec.rb diff --git a/lib/neography/rest/node_paths.rb b/lib/neography/rest/node_paths.rb index 97e0947..1b0cb47 100644 --- a/lib/neography/rest/node_paths.rb +++ b/lib/neography/rest/node_paths.rb @@ -31,7 +31,7 @@ def get_all(from, to, relationships, depth, algorithm) "algorithm" => get_algorithm(algorithm) }.to_json, :headers => json_content_type - } + } @connection.post(all_path(:id => get_id(from)), options) || [] end diff --git a/spec/unit/rest/node_paths_spec.rb b/spec/unit/rest/node_paths_spec.rb new file mode 100644 index 0000000..372509f --- /dev/null +++ b/spec/unit/rest/node_paths_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +module Neography + class Rest + describe NodePaths do + + let(:connection) { stub(:configuration => "http://configuration") } + subject { NodePaths.new(connection) } + + it "gets a shortest path between two nodes" do + expected_body = { + "to" => "http://configuration/node/43", + "relationships" => "relationships", + "max_depth" => 3, + "algorithm" => "shortestPath" + } + + connection.should_receive(:post).with("/node/42/path", json_match(:body, expected_body)) + + subject.get("42", "43", "relationships", 3, "shortestPath") + end + + it "gets all shortest paths between two nodes" do + expected_body = { + "to" => "http://configuration/node/43", + "relationships" => "relationships", + "max_depth" => 3, + "algorithm" => "shortestPath" + } + + connection.should_receive(:post).with("/node/42/paths", json_match(:body, expected_body)) + + subject.get_all("42", "43", "relationships", 3, "shortestPath") + end + + it "gets all shortest weighted paths between two nodes" do + expected_body = { + "to" => "http://configuration/node/43", + "relationships" => "relationships", + "cost_property" => "cost", + "max_depth" => 3, + "algorithm" => "shortestPath" + } + + connection.should_receive(:post).with("/node/42/paths", json_match(:body, expected_body)) + + subject.shortest_weighted("42", "43", "relationships", "cost", 3, "shortestPath") + end + + context "algorithm" do + + subject { NodePaths.new(nil) } + + [ :shortest, "shortest", :shortestPath, "shortestPath", :short, "short" ].each do |algorithm| + it "parses shortestPath" do + subject.send(:get_algorithm, algorithm).should == "shortestPath" + end + end + + [ :allSimplePaths, "allSimplePaths", :simple, "simple" ].each do |algorithm| + it "parses allSimplePaths" do + subject.send(:get_algorithm, algorithm).should == "allSimplePaths" + end + end + + [ :dijkstra, "dijkstra" ].each do |algorithm| + it "parses dijkstra" do + subject.send(:get_algorithm, algorithm).should == "dijkstra" + end + end + + it "parses allPaths by default" do + subject.send(:get_algorithm, "foo").should == "allPaths" + end + + end + + end + end +end From c758b82fe2cebee1d3eb262674f4ce849fb9ad35 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:19:09 +0200 Subject: [PATCH 16/21] Add Relationships specs. --- spec/unit/rest/relationships_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 spec/unit/rest/relationships_spec.rb diff --git a/spec/unit/rest/relationships_spec.rb b/spec/unit/rest/relationships_spec.rb new file mode 100644 index 0000000..7ef5e0e --- /dev/null +++ b/spec/unit/rest/relationships_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +module Neography + class Rest + describe Relationships do + + let(:connection) { stub } + subject { Relationships.new(connection) } + + it "gets a relationship" do + connection.should_receive(:get).with("/relationship/42") + subject.get("42") + end + + it "deletes a relationship" do + connection.should_receive(:delete).with("/relationship/42") + subject.delete("42") + end + + end + end +end From c7a17c8b7f7e6ad7ed3dc45d81f2d847a522bdbf Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:22:34 +0200 Subject: [PATCH 17/21] Add Relationships specs. --- lib/neography/rest/relationship_properties.rb | 14 ++-- .../unit/rest/relationship_properties_spec.rb | 80 +++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 spec/unit/rest/relationship_properties_spec.rb diff --git a/lib/neography/rest/relationship_properties.rb b/lib/neography/rest/relationship_properties.rb index c8ba4df..8f25d45 100644 --- a/lib/neography/rest/relationship_properties.rb +++ b/lib/neography/rest/relationship_properties.rb @@ -11,6 +11,13 @@ def initialize(connection) @connection = connection end + def set(id, properties) + properties.each do |key, value| + options = { :body => value.to_json, :headers => json_content_type } + @connection.put(single_path(:id => get_id(id), :property => key), options) + end + end + def reset(id, properties) options = { :body => properties.to_json, :headers => json_content_type } @connection.put(all_path(:id => get_id(id)), options) @@ -48,13 +55,6 @@ def remove_each(id, *properties) end end - def set(id, properties) - properties.each do |key, value| - options = { :body => value.to_json, :headers => json_content_type } - @connection.put(single_path(:id => get_id(id), :property => key), options) - end - end - end end end diff --git a/spec/unit/rest/relationship_properties_spec.rb b/spec/unit/rest/relationship_properties_spec.rb new file mode 100644 index 0000000..d110210 --- /dev/null +++ b/spec/unit/rest/relationship_properties_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +module Neography + class Rest + describe RelationshipProperties do + + let(:connection) { stub } + subject { RelationshipProperties.new(connection) } + + it "sets properties" do + options1 = { + :body => '"bar"', + :headers => json_content_type + } + options2 = { + :body => '"qux"', + :headers => json_content_type + } + connection.should_receive(:put).with("/relationship/42/properties/foo", options1) + connection.should_receive(:put).with("/relationship/42/properties/baz", options2) + subject.set("42", {:foo => "bar", :baz => "qux"}) + end + + it "resets properties" do + options = { + :body => '{"foo":"bar"}', + :headers => json_content_type + } + connection.should_receive(:put).with("/relationship/42/properties", options) + subject.reset("42", {:foo => "bar"}) + end + + context "getting properties" do + + it "gets all properties" do + connection.should_receive(:get).with("/relationship/42/properties") + subject.get("42") + end + + it "gets multiple properties" do + connection.should_receive(:get).with("/relationship/42/properties/foo") + connection.should_receive(:get).with("/relationship/42/properties/bar") + subject.get("42", "foo", "bar") + end + + it "returns multiple properties as a hash" do + connection.stub(:get).and_return("baz", "qux") + subject.get("42", "foo", "bar").should == { "foo" => "baz", "bar" => "qux" } + end + + it "returns nil if no properties were found" do + connection.stub(:get).and_return(nil, nil) + subject.get("42", "foo", "bar").should be_nil + end + + it "returns hash without nil return values" do + connection.stub(:get).and_return("baz", nil) + subject.get("42", "foo", "bar").should == { "foo" => "baz" } + end + + end + + context "removing properties" do + + it "removes all properties" do + connection.should_receive(:delete).with("/relationship/42/properties") + subject.remove("42") + end + + it "removes multiple properties" do + connection.should_receive(:delete).with("/relationship/42/properties/foo") + connection.should_receive(:delete).with("/relationship/42/properties/bar") + subject.remove("42", "foo", "bar") + end + + end + + end + end +end From 03d2e8083198db1014a8fcee92e44102fe399b2e Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:28:32 +0200 Subject: [PATCH 18/21] Add RelationshipIndexes spec. --- spec/unit/rest/relationship_indexes_spec.rb | 103 ++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 spec/unit/rest/relationship_indexes_spec.rb diff --git a/spec/unit/rest/relationship_indexes_spec.rb b/spec/unit/rest/relationship_indexes_spec.rb new file mode 100644 index 0000000..6cacf41 --- /dev/null +++ b/spec/unit/rest/relationship_indexes_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +module Neography + class Rest + describe RelationshipIndexes do + + let(:connection) { stub(:configuration => "http://configuration") } + subject { RelationshipIndexes.new(connection) } + + it "lists all indexes" do + connection.should_receive(:get).with("/index/relationship") + subject.list + end + + it "creates a relationship index" do + expected_body = { + "config" => { + "type" => "some_type", + "provider" => "some_provider" + }, + "name" => "some_index" + } + connection.should_receive(:post).with("/index/relationship", json_match(:body, expected_body)) + subject.create("some_index", "some_type", "some_provider") + end + + it "returns the post result after creation" do + connection.stub(:post).and_return("foo") + subject.create("some_index", "some_type", "some_provider").should == "foo" + end + + it "creates an auto-index" do + expected_body = { + "config" => { + "type" => "some_type", + "provider" => "some_provider" + }, + "name" => "relationship_auto_index" + } + connection.should_receive(:post).with("/index/relationship", json_match(:body, expected_body)) + subject.create_auto("some_type", "some_provider") + end + + it "creates a unique relationship in an index" do + expected_body = { + "key" => "key", + "value" => "value", + "type" => "type", + "start" => "http://configuration/node/42", + "end" => "http://configuration/node/43" + } + connection.should_receive(:post).with("/index/relationship/some_index?unique", json_match(:body, expected_body)) + subject.create_unique("some_index", "key", "value", "type", "42", "43") + end + + it "adds a relationship to an index" do + expected_body = { + "uri" => "http://configuration/relationship/42", + "key" => "key", + "value" => "value" + } + connection.should_receive(:post).with("/index/relationship/some_index", json_match(:body, expected_body)) + subject.add("some_index", "key", "value", "42") + end + + it "gets a relationship from an index" do + connection.should_receive(:get).with("/index/relationship/some_index/some_key/some_value") + subject.get("some_index", "some_key", "some_value") + end + + it "returns nil if nothing was found in the index" do + connection.stub(:get).and_return(nil) + subject.get("some_index", "some_key", "some_value").should be_nil + end + + it "finds by key query" do + connection.should_receive(:get).with("/index/relationship/some_index/some_key?query=some_query") + subject.find_by_key_query("some_index", "some_key", "some_query") + end + + it "finds by query" do + connection.should_receive(:get).with("/index/relationship/some_index?query=some_query") + subject.find_by_query("some_index", "some_query") + end + + it "removes a relationship from an index" do + connection.should_receive(:delete).with("/index/relationship/some_index/42") + subject.remove("some_index", "42") + end + + it "removes a relationship from an index by key" do + connection.should_receive(:delete).with("/index/relationship/some_index/some_key/42") + subject.remove_by_key("some_index", "42", "some_key") + end + + it "removes a relationship from an index by key and value" do + connection.should_receive(:delete).with("/index/relationship/some_index/some_key/some_value/42") + subject.remove_by_value("some_index", "42", "some_key", "some_value") + end + + end + end +end From d0a27e5ba4c1f9045898a80d243290b9ce115d5f Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:29:30 +0200 Subject: [PATCH 19/21] Fix typo. --- spec/unit/rest/node_indexes_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/unit/rest/node_indexes_spec.rb b/spec/unit/rest/node_indexes_spec.rb index 5332cdd..157e6c3 100644 --- a/spec/unit/rest/node_indexes_spec.rb +++ b/spec/unit/rest/node_indexes_spec.rb @@ -81,17 +81,17 @@ class Rest subject.find_by_query("some_index", "some_query") end - it "removed a node from an index" do + it "removes a node from an index" do connection.should_receive(:delete).with("/index/node/some_index/42") subject.remove("some_index", "42") end - it "removed a node from an index by key" do + it "removes a node from an index by key" do connection.should_receive(:delete).with("/index/node/some_index/some_key/42") subject.remove_by_key("some_index", "42", "some_key") end - it "removed a node from an index by key and value" do + it "removes a node from an index by key and value" do connection.should_receive(:delete).with("/index/node/some_index/some_key/some_value/42") subject.remove_by_value("some_index", "42", "some_key", "some_value") end From 5e00a5045fb4fef54cc477e6651e67485c089ce9 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:31:29 +0200 Subject: [PATCH 20/21] Add RelationshipAutoIndexes spec. --- spec/unit/rest/relationship_auto_indexes.rb | 57 +++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 spec/unit/rest/relationship_auto_indexes.rb diff --git a/spec/unit/rest/relationship_auto_indexes.rb b/spec/unit/rest/relationship_auto_indexes.rb new file mode 100644 index 0000000..74528af --- /dev/null +++ b/spec/unit/rest/relationship_auto_indexes.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +module Neography + class Rest + describe RelationshipAutoIndexes do + + let(:connection) { stub } + subject { RelationshipAutoIndexes.new(connection) } + + it "gets a relationship from an auto index" do + connection.should_receive(:get).with("/index/auto/relationship/some_key/some_value") + subject.get("some_key", "some_value") + end + + it "returns nil if nothing was found in the auto index" do + connection.stub(:get).and_return(nil) + subject.get("some_key", "some_value").should be_nil + end + + it "finds by key and value" do + connection.should_receive(:get).with("/index/auto/relationship/some_key/some_value") + subject.find("some_key", "some_value") + end + + it "finds by query" do + connection.should_receive(:get).with("/index/auto/relationship/?query=some_query") + subject.query("some_query") + end + + it "gets the status" do + connection.should_receive(:get).with("/index/auto/relationship/status") + subject.status + end + + it "sets the status" do + connection.should_receive(:put).with("/index/auto/relationship/status", hash_match(:body, '"foo"')) + subject.status = "foo" + end + + it "gets auto index properties" do + connection.should_receive(:get).with("/index/auto/relationship/properties") + subject.properties + end + + it "adds a property to an auto index" do + connection.should_receive(:post).with("/index/auto/relationship/properties", hash_match(:body, "foo")) + subject.add_property("foo") + end + + it "removes a property from an auto index" do + connection.should_receive(:delete).with("/index/auto/relationship/properties/foo") + subject.remove_property("foo") + end + + end + end +end From 0a1dcaa92edb6432113f3628e7547dfde523dfc4 Mon Sep 17 00:00:00 2001 From: Roel van Dijk Date: Sat, 8 Sep 2012 22:55:27 +0200 Subject: [PATCH 21/21] Fix URL encoding to make spaces and slashes possible. --- lib/neography.rb | 5 ++- lib/neography/connection.rb | 8 ++-- .../{rest_helpers.rb => rest/helpers.rb} | 0 lib/neography/rest/node_indexes.rb | 6 +-- .../{rest_paths.rb => rest/paths.rb} | 6 ++- spec/integration/rest_index_spec.rb | 2 +- spec/unit/rest/node_indexes_spec.rb | 2 +- spec/unit/rest/paths_spec.rb | 39 +++++++++++++++++++ 8 files changed, 54 insertions(+), 14 deletions(-) rename lib/neography/{rest_helpers.rb => rest/helpers.rb} (100%) rename lib/neography/{rest_paths.rb => rest/paths.rb} (80%) create mode 100644 spec/unit/rest/paths_spec.rb diff --git a/lib/neography.rb b/lib/neography.rb index ecefbea..03c7f1b 100644 --- a/lib/neography.rb +++ b/lib/neography.rb @@ -30,8 +30,9 @@ def find_and_require_user_defined_code require 'neography/config' -require 'neography/rest_helpers' -require 'neography/rest_paths' +require 'neography/rest/helpers' +require 'neography/rest/paths' + require 'neography/rest/nodes' require 'neography/rest/node_properties' require 'neography/rest/node_relationships' diff --git a/lib/neography/connection.rb b/lib/neography/connection.rb index 0b41639..bdadc3b 100644 --- a/lib/neography/connection.rb +++ b/lib/neography/connection.rb @@ -76,19 +76,19 @@ def merge_options(options) end def get(path, options={}) - evaluate_response(HTTParty.get(configuration + URI.encode(path), merge_options(options))) + evaluate_response(HTTParty.get(configuration + path, merge_options(options))) end def post(path, options={}) - evaluate_response(HTTParty.post(configuration + URI.encode(path), merge_options(options))) + evaluate_response(HTTParty.post(configuration + path, merge_options(options))) end def put(path, options={}) - evaluate_response(HTTParty.put(configuration + URI.encode(path), merge_options(options))) + evaluate_response(HTTParty.put(configuration + path, merge_options(options))) end def delete(path, options={}) - evaluate_response(HTTParty.delete(configuration + URI.encode(path), merge_options(options))) + evaluate_response(HTTParty.delete(configuration + path, merge_options(options))) end def evaluate_response(response) diff --git a/lib/neography/rest_helpers.rb b/lib/neography/rest/helpers.rb similarity index 100% rename from lib/neography/rest_helpers.rb rename to lib/neography/rest/helpers.rb diff --git a/lib/neography/rest/node_indexes.rb b/lib/neography/rest/node_indexes.rb index 049463f..ce8e8fd 100644 --- a/lib/neography/rest/node_indexes.rb +++ b/lib/neography/rest/node_indexes.rb @@ -13,10 +13,6 @@ class NodeIndexes add_path :key_value, "/index/node/:index/:key/:value" add_path :query, "/index/node/:index?query=:query" - # this should be the same as the :key_value path above - # was changed to ?query="..." to fix bug with spaces and slashes - add_path :key_value2, "/index/node/:index/:key?query=\":value\"" - def initialize(connection) @connection = connection end @@ -78,7 +74,7 @@ def get(index, key, value) # TODO FIX BUG %20 def find_by_value(index, key, value) - @connection.get(key_value2_path(:index => index, :key => key, :value => value)) || [] + @connection.get(key_value_path(:index => index, :key => key, :value => value)) || [] end def find_by_query(index, query) diff --git a/lib/neography/rest_paths.rb b/lib/neography/rest/paths.rb similarity index 80% rename from lib/neography/rest_paths.rb rename to lib/neography/rest/paths.rb index ba73c9e..bd25b59 100644 --- a/lib/neography/rest_paths.rb +++ b/lib/neography/rest/paths.rb @@ -8,10 +8,14 @@ def self.included(mod) def build_path(path, attributes) path.gsub(/:([\w_]*)/) do - attributes[$1.to_sym].to_s + encode(attributes[$1.to_sym].to_s) end end + def encode(value) + URI.encode(value).gsub("/","%2F") + end + module ClassMethods def add_path(key, path) define_method :"#{key}_path" do |*attributes| diff --git a/spec/integration/rest_index_spec.rb b/spec/integration/rest_index_spec.rb index 45dcb02..bbf8424 100644 --- a/spec/integration/rest_index_spec.rb +++ b/spec/integration/rest_index_spec.rb @@ -190,7 +190,7 @@ @neo.remove_node_from_index("test_node_index", key, value, new_node) end - xit "can get a node index with a slash in the value" do + it "can get a node index with a slash in the value" do new_node = @neo.create_node key = generate_text(6) value = generate_text + "/" + generate_text diff --git a/spec/unit/rest/node_indexes_spec.rb b/spec/unit/rest/node_indexes_spec.rb index 157e6c3..10d5382 100644 --- a/spec/unit/rest/node_indexes_spec.rb +++ b/spec/unit/rest/node_indexes_spec.rb @@ -72,7 +72,7 @@ class Rest end it "finds by key and value" do - connection.should_receive(:get).with("/index/node/some_index/some_key?query=\"some_value\"") + connection.should_receive(:get).with("/index/node/some_index/some_key/some_value") subject.find_by_value("some_index", "some_key", "some_value") end diff --git a/spec/unit/rest/paths_spec.rb b/spec/unit/rest/paths_spec.rb new file mode 100644 index 0000000..a30bfae --- /dev/null +++ b/spec/unit/rest/paths_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +module Neography + class Rest + + class Dummy + include Paths + + add_path :one, "/node/:id" + add_path :two, "/node/:id/properties/:property" + end + + describe Dummy do + + it { should respond_to(:one_path) } + it { should respond_to(:two_path) } + + it "replaces a key" do + subject.one_path(:id => 42).should == "/node/42" + end + + it "replaces multiple keys" do + subject.two_path(:id => 42, :property => "foo").should == "/node/42/properties/foo" + end + + it "url encodes spaces" do + subject.one_path(:id => "with space").should == "/node/with%20space" + end + + # URI.encode does not escape slashes (and rightly so), but should escape these keys + it "url encodes slashes" do + subject.one_path(:id => "with/slash").should == "/node/with%2Fslash" + end + + end + + end +end +