lib/neoid/relationship.rb in neoid-0.0.51 vs lib/neoid/relationship.rb in neoid-0.1
- old
+ new
@@ -1,83 +1,123 @@
module Neoid
module Relationship
+ # this is a proxy that delays loading of start_node and end_node from Neo4j until accessed.
+ # the original Neography Relatioship loaded them on initialization
+ class RelationshipLazyProxy < ::Neography::Relationship
+ def start_node
+ @start_node_from_db ||= @start_node = Neography::Node.load(@start_node, Neoid.db)
+ end
+
+ def end_node
+ @end_node_from_db ||= @end_node = Neography::Node.load(@end_node, Neoid.db)
+ end
+ end
+
+ def self.from_hash(hash)
+ relationship = RelationshipLazyProxy.new(hash)
+
+ relationship
+ end
+
+
+ module ClassMethods
+ def delete_command
+ :delete_relationship
+ end
+ end
+
module InstanceMethods
def neo_find_by_id
- Neoid.db.get_relationship_index(self.class.neo_index_name, :ar_id, self.id)
- rescue Neography::NotFoundException
- nil
+ results = Neoid.db.get_relationship_auto_index(Neoid::UNIQUE_ID_KEY, self.neo_unique_id)
+ relationship = results.present? ? Neoid::Relationship.from_hash(results[0]) : nil
+ relationship
end
- def neo_create
+ def _neo_save
return unless Neoid.enabled?
- @_neo_destroyed = false
options = self.class.neoid_config.relationship_options
- start_node = self.send(options[:start_node])
- end_node = self.send(options[:end_node])
+ start_item = self.send(options[:start_node])
+ end_item = self.send(options[:end_node])
- return unless start_node && end_node
+ return unless start_item && end_item
- data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id)
+ # initialize nodes
+ start_item.neo_node
+ end_item.neo_node
+
+ data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id, Neoid::UNIQUE_ID_KEY => self.neo_unique_id)
data.reject! { |k, v| v.nil? }
-
- relationship = Neography::Relationship.create(
- options[:type].is_a?(Proc) ? options[:type].call(self) : options[:type],
- start_node.neo_node,
- end_node.neo_node,
- data
- )
-
- Neoid.db.add_relationship_to_index(self.class.neo_index_name, :ar_id, self.id, relationship)
- Neoid::logger.info "Relationship#neo_create #{self.class.name} #{self.id}, index = #{self.class.neo_index_name}"
+ rel_type = options[:type].is_a?(Proc) ? options[:type].call(self) : options[:type]
+
+ gremlin_query = <<-GREMLIN
+ idx = g.idx('relationship_auto_index');
+ q = null;
+ if (idx) q = idx.get(unique_id_key, unique_id);
+
+ relationship = null;
+ if (q && q.hasNext()) {
+ relationship = q.next();
+ relationship_data.each {
+ if (relationship.getProperty(it.key) != it.value) {
+ relationship.setProperty(it.key, it.value);
+ }
+ }
+ } else {
+ node_index = g.idx('node_auto_index');
+ start_node = node_index.get(unique_id_key, start_node_unique_id).next();
+ end_node = node_index.get(unique_id_key, end_node_unique_id).next();
+
+ relationship = g.addEdge(start_node, end_node, rel_type, relationship_data);
+ }
+
+ relationship
+ GREMLIN
+
+ script_vars = {
+ unique_id_key: Neoid::UNIQUE_ID_KEY,
+ relationship_data: data,
+ unique_id: self.neo_unique_id,
+ start_node_unique_id: start_item.neo_unique_id,
+ end_node_unique_id: end_item.neo_unique_id,
+ rel_type: rel_type
+ }
+
+ Neoid::logger.info "Relationship#neo_save #{self.class.name} #{self.id}"
+ relationship = Neoid.execute_script_or_add_to_batch gremlin_query, script_vars do |value|
+ Neoid::Relationship.from_hash(value)
+ end
+
relationship
end
- def neo_load(relationship)
- Neography::Relationship.load(relationship)
+ def neo_load(hash)
+ Neoid::Relationship.from_hash(hash)
end
-
- def neo_destroy
- return if @_neo_destroyed
- @_neo_destroyed = true
- return unless neo_relationship
- Neoid.db.remove_relationship_from_index(self.class.neo_index_name, neo_relationship)
- neo_relationship.del
- _reset_neo_representation
- Neoid::logger.info "Relationship#neo_destroy #{self.class.name} #{self.id}, index = #{self.class.neo_index_name}"
-
- true
- end
-
- def neo_update
- Neoid.db.set_relationship_properties(neo_relationship, self.to_neo) if neo_relationship
- end
-
def neo_relationship
_neo_representation
end
end
def self.included(receiver)
receiver.send :include, Neoid::ModelAdditions
receiver.send :include, InstanceMethods
+ receiver.extend ClassMethods
- receiver.after_create :neo_create
- receiver.after_destroy :neo_destroy
- receiver.after_update :neo_update
+ initialize_relationship receiver if Neoid.env_loaded
- if Neoid.env_loaded
- initialize_relationship receiver
- else
- Neoid.relationship_models << receiver
- end
+ Neoid.relationship_models << receiver
end
+ def self.meta_data
+ @meta_data ||= {}
+ end
+
def self.initialize_relationship(rel_model)
rel_model.reflect_on_all_associations(:belongs_to).each do |belongs_to|
return if belongs_to.options[:polymorphic]
# e.g. all has_many on User class
@@ -88,11 +128,10 @@
next unless this_has_many
# e.g. User has_many :likes, after_remove: ...
full_callback_name = "after_remove_for_#{this_has_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_remove if belongs_to.klass.method_defined?(full_callback_name)
- # belongs_to.klass.send(:has_many, this_has_many.name, this_has_many.options.merge(after_remove: :neo_after_relationship_remove))
# has_many (with through) on the side of the relationship that removes a relationship. e.g. User has_many :movies, through :likes
many_to_many = all_has_many.find { |o| o.options[:through] == this_has_many.name }
next unless many_to_many
@@ -102,27 +141,23 @@
foreign_key_of_owner = many_to_many.through_reflection.foreign_key
# movie_id
foreign_key_of_record = many_to_many.source_reflection.foreign_key
- (Neoid.config[:relationship_meta_data] ||= {}).tap do |data|
+ (Neoid::Relationship.meta_data ||= {}).tap do |data|
(data[belongs_to.klass.name.to_s] ||= {}).tap do |model_data|
model_data[many_to_many.klass.name.to_s] = [rel_model.name.to_s, foreign_key_of_owner, foreign_key_of_record]
end
end
- # puts Neoid.config[:relationship_meta_data].inspect
-
# e.g. User has_many :movies, through: :likes, before_remove: ...
full_callback_name = "before_remove_for_#{many_to_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_before_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
- # belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(before_remove: :neo_after_relationship_remove))
# e.g. User has_many :movies, through: :likes, after_remove: ...
full_callback_name = "after_remove_for_#{many_to_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
- # belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(after_remove: :neo_after_relationship_remove))
end
end
end
end