Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot add relations to unpersisted nodes #816

Closed
ctembreull opened this issue May 29, 2015 · 5 comments
Closed

Cannot add relations to unpersisted nodes #816

ctembreull opened this issue May 29, 2015 · 5 comments

Comments

@ctembreull
Copy link

I have the following use case, a bridge class between a Person and an Event called an Invitation. This is all scratch code, so please try to see the intent and not the actual sketchy Ruby I'm laying down.

class Invitation
  include Neo4j::ActiveNode
  has_one :in, :inviter, type: :invited_by, model_class: Person
  has_one :in, :invitee, type: :invitation_for, model_class: Person
  has_one :out, :event, type: :invitation_to, model_class: Event

  property :attending, type: Boolean, default: false
end

If I want to ensure that the :inviter has permission to send the invitation, it seems I should handle that as a validation - something like,

validates :inviter_can_invite
def inviter_can_invite
  errors.add(:inviter) unless event.inviters.include? inviter
end

The problem is that the event can never be valid because none of the relations can be established until the node is saved - results in a Neo4j::ActiveNode::HasN::NonPersistedNodeError. But the invitation should never be saved unless the relations can be established and checked. Any idea how to manage this? Is there a best-practice or convention around this? I'd even settle for an anecdotal success story heard third-hand from a semi-reliable source.

@subvertallchris
Copy link
Contributor

My opinions on this sort of thing:

  • The process of creating nodes should be separate from the process of creating relationships. If something is trying to create a relationship with non-persisted nodes, you have a problem.
  • If there's any kind of logic that goes into managing relationships, it belongs in an ActiveRel model, not tacked onto an ActiveNode model.

Soooo... what I'd do here is:

  • Define three ActiveRel classes: InvitedBy, InvitationFor, and InvitationTo. Each is responsible for its own pieces of the puzzle. Maybe you don't need one between the Invitation and Event?
  • Start a new transaction.
  • Find/create inviter node.
  • Find/create invitee node.
  • Find/create invitation node.
  • Find/create event node.
  • Create relationships using the given ActiveRel classes.
  • If everything saved correctly, close the transaction; otherwise, mark the transaction failed, raise an error in the app, fix the problem.

The key here is that the process of creating/saving/finding nodes is completely separate from the process of creating the relationships. You can put that same callback and others in the ActiveRel classes, but you should be able to trust that the nodes are persisted because that isn't the relationship model's problem. If there is a problem, the transaction protects you from putting anything in an unhealthy state, and you should catch that problem early on so there are no surprises.

How's that? I'll be signed into our Gitter room all day tomorrow, feel free to pop in if you want to work through this or anything else. http://gitter.im/neo4jrb/neo4j.

@subvertallchris
Copy link
Contributor

BTW, you can find lots of info about ActiveRel at https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3AActiveRel.

@beno
Copy link

beno commented Jun 25, 2015

This is biting me too when trying to implement authorization logic. I am new to Neo4j, so not all the concepts of the graph have sunk in yet, but having everything created inside a transaction and then check if I could have done that breaks my brain a bit.

author = Author.create(name:'Author')
book = Book.new(title:'Title', author:author)
book.title # => 'Title'
book.author # => nil - ???, assignment should raise an error at least

@mattdharmon
Copy link

@beno I think the book.author is transactional since author is a relational reference and not a property, it couldn't find the relationship since book.save has not triggered.

I could be wrong about this.

@subvertallchris
Copy link
Contributor

Basic support for association creation with unpersisted nodes was added recently. It will clear up the initial issue, so I'm going to close this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants