Skip to content

Instantly share code, notes, and snippets.

@tjad
Created February 9, 2017 15:46
Show Gist options
  • Save tjad/fb779fcbada040b7e4ff455d48875f0c to your computer and use it in GitHub Desktop.
Save tjad/fb779fcbada040b7e4ff455d48875f0c to your computer and use it in GitHub Desktop.
Short hand syntax for retrieving association object graph
#to recieve the respective associations
Person.with_associations( {brothers: {:children [:daughters, :sons] }})
#Outputs all associations as an object graph structure defined above by each associations depth
class Person
include Neo4j::ActiveNode
#associations to other Person objects
has_many :out, :brothers, type: :HAS_BROTHER
has_many :out, :children, type: :HAS_CHILD
has_many :out, :daughters, type: :HAS_DAUGHTER
has_many :out, :sons, type: :HAS_SON
end
def with_associations(assoc_conf)
object_name = self.name.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
# Construct paths to represent the query paths for information to be eagerly loaded
paths = []
# For reconstructing objects
path_directive_class_table = {}
path_constructor = lambda do |current_path, path_conf|
if path_conf.instance_of? Hash
path_conf.each do |k, v|
path_constructor.call(current_path+[k], v)
end
elsif path_conf.instance_of? Array
path_conf.each do |path|
paths << current_path + [path]
end
elsif path_conf.instance_of? Symbol # Append to current path and add to paths local variable
current_path << path_conf
paths << current_path
else
raise Exception.new("Unhandled path type")
end
end
path_constructor.call([], assoc_conf)
# Construct cypher queries
match_statements = []
path_directive_class_table[object_name.to_sym] = self
paths.each do |path|
assoc_class = self
query_string = '('+object_name+':'+self.name+')'
path.each do |path_directive|
relationship_type = assoc_class.associations[path_directive].relationship_type
assoc_class = assoc_class.associations[path_directive].target_class
path_directive_class_table[path_directive] = assoc_class
query_string += '-[:' + relationship_type.to_s + ']->(' + path_directive.to_s + ':' + assoc_class.name + ')'
end
match_statements << query_string
end
assoc_chain = [object_name] + paths.first
# Perform queries to gather information from database for eager loading
matches = Neo4j::Session.current.query.match(match_statements.join(','))
.pluck(assoc_chain)
# Embed the nodes correctly within the object in order to reconstruct the model
link_nodes = lambda do |match, parent, path|
unless path.empty?
path_member = path.shift
parent[path_member] = link_nodes.call(match, match[assoc_chain.find_index(path_member)].attributes, path)
end
parent
end
matches.map do |match|
link_nodes.call(match, {}, assoc_chain.dup)[assoc_chain.first]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment