Skip to content

Instantly share code, notes, and snippets.

@andreasronge
Created July 6, 2013 09:47
Show Gist options
  • Save andreasronge/5939419 to your computer and use it in GitHub Desktop.
Save andreasronge/5939419 to your computer and use it in GitHub Desktop.
Spike on neo4j 2.0 featrues
require 'java'
require 'forwardable'
require 'fileutils'
# Load Neo4j Jars for the jars folder
# Put the neo4j 2.0 jars there
jar_folder = File.expand_path('../jars', __FILE__)
jars = Dir.new(jar_folder).entries.find_all { |x| x =~ /\.jar$/ }
jars.each { |jar| require File.expand_path(jar, jar_folder) }
# See end of this file for example of usage
module Neo4j
class Transaction
def self.new(instance = Database.instance)
instance.begin_tx
end
def self.run(run_in_tx=true)
raise ArgumentError.new("Expected a block to run in Transaction.run") unless block_given?
return yield(nil) unless run_in_tx
begin
tx = Neo4j::Transaction.new
ret = yield tx
tx.success
rescue Exception => e
if e.respond_to?(:cause) && e.cause
puts "Java Exception in a transaction, cause: #{e.cause}"
e.cause.print_stack_trace
end
tx.failure unless tx.nil?
raise
ensure
tx.finish unless tx.nil?
end
ret
end
end
module TxMethods
def tx_methods(*methods)
methods.each do |method|
tx_method = "#{method}_in_tx"
send(:alias_method, tx_method, method)
send(:define_method, method) do |*args|
Transaction.run(Database.instance.auto_commit?) { send(tx_method, *args) }
end
end
end
end
# wrapps Neo4j ResourceIterator, automatically close it
class ResourceIterator
include Enumerable
def initialize(iterable)
@_iterable = iterable
end
def each
iterator = @_iterable.iterator
while (iterator.has_next)
yield iterator.next
end
ensure
iterator.close
end
end
class Label
extend TxMethods
attr_reader :name
JAVA_CLASS = Java::OrgNeo4jGraphdb::DynamicLabel
def initialize(name)
@name = name.to_s
end
def to_s
@name
end
def find_nodes(key, value, db = Database.instance)
ResourceIterator.new db.find_nodes_by_label_and_property(to_java, key, value)
end
def to_java
self.class.to_java(@name)
end
def index(*properties)
db = properties.last.kind_of?(Database) ? properties.pop : Database.instance
index_creator = db.schema.index_for(to_java)
# we can also use the PropertyConstraintCreator here
properties.inject(index_creator) {|creator, key| creator.on(key.to_s)}.create
end
tx_methods :index
class << self
def to_java(labels)
if labels.is_a?(Array)
labels.inject([]) { |result, label| result << JAVA_CLASS.label(label.to_s) }.to_java(JAVA_CLASS)
else
JAVA_CLASS.label(labels.to_s)
end
end
end
end
module Properties
extend TxMethods
def []=(key,value)
set_property(key.to_s, value)
end
tx_methods :[]=
def [](key)
get_property(key.to_s)
end
end
module Labels
def labels
get_labels.map{|x| Label.new(x.name) }
end
end
class Node
# include these modules only for documentation purpose
extend TxMethods
include Properties
include Labels
class << self
extend TxMethods
# Returns a new neo4j Node.
# The return node is actually an Java object of type Java::OrgNeo4jGraphdb::Node java object
# which has been extended (see the included mixins for Neo4j::Node).
def new(properties = {}, *labels_or_db)
db = labels_or_db.last.kind_of?(Database) ? labels_or_db.pop : Database.instance
_java_node = db.create_node(Label.to_java(labels_or_db))
properties.each_pair {|k,v| _java_node.set_property(k,v)}
_java_node
end
tx_methods :new
# This method is used to extend a Java Neo4j class so that it includes the same mixins as this class.
def extend_java_class(java_clazz)
java_clazz.class_eval do
include Properties
include Labels
end
end
end
extend_java_class(Java::OrgNeo4jKernelImplCore::NodeProxy)
end
class Database
extend Forwardable
def_delegator :@graph_db, :schema
def_delegator :@graph_db, :begin_tx
def_delegator :@graph_db, :create_node
def_delegator :@graph_db, :findNodesByLabelAndProperty, :find_nodes_by_label_and_property
def initialize(path, config = {})
factory = Java::OrgNeo4jGraphdbFactory::GraphDatabaseFactory.new
if (config[:delete_existing_db])
FileUtils.rm_rf path
end
@graph_db = factory.newEmbeddedDatabase(path)
@auto_commit = !!config[:auto_commit]
@@instance ||= self
end
def auto_commit?
@auto_commit
end
def self.instance
@@instance
end
end
# Create a new database, which will wrap method with transactions
db = Database.new('hej', auto_commit: true, delete_existing_db: true)
# Create a new label with an index on property name
red = Label.new(:red)
red.index(:name)
# how should we specify constraints like unique ?
# red.index(:name).unique!
# red.index_unique(:name)
# red.index(:name, constraints: [:unique])
# red.index do
# property :name, contraints: :unique
# end
# labels argument can be either, string, symbol or Label objects (anything responding to 'to_s')
node = Node.new({name: 'andreas'}, red, :green)
puts "Created node #{node[:name]} with labels #{node.labels.map(&:name).join(', ')}"
# Find nodes using the label
red.find_nodes(:name, "andreas").each do |node|
# notice that we do not wrap the java object, but instead extend the Java class with ruby methods
# prints out: FOUND andreas class Java::OrgNeo4jKernelImplCore::NodeProxy with labels red, green
puts "FOUND #{node[:name]} class #{node.class} with labels #{node.labels.map(&:name).join(', ')}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment