Is it possible to create a relationship to a relationship in Neo4j?
The use-case goes something like this:
I have a bunch of questions, like "What movie should we see?"
Each question can have many options like "Movie1", "Movie2", etc.
For each question I want a user to be able to vote for their favorite option.
The graph would preferably look something like this:
(:Question {name:"What movie?"})-[:Option]->(:Movie {name:"Movie1"})
^
|
[:Vote]
|
(:User)
I realize that one way I could solve this is with the following:
(:Question)-[:Option]->(:Movie)<-[:Vote]-(:User)
But then if I decide to remove the Movie as an Option in the future, I don't get to take advantage of DETACH and will have to manage removing the Vote relationship myself. Not to mention, if the Movie belongs to multiple categories, I have to keep track of which Question->Movie relationship it belongs to (probably with some sort of ID). It just seems very messy...
Is is possible to create a relationship to a relationship? Or am I going to have to manually enforce referential integrity?
Is is possible to create a relationship to a relationship?
No. This is not possible. According the docs:
A relationship connects two nodes, and is guaranteed to have a valid
source and target node.
That is: the start and end point of a relationship should be a node.
I believe you should do some changes in your data model. For example:
Maybe the Option can be a node and not a relationship. Make more sense, not? This way:
(:Category)-[:HAS]->(:Option)
Also, the Vote can be a node too, and not a relationship... This way, when the user makes (a relationship, ahn?) a vote, this vote node will reference the option and the category that it is relative to.
(:Category)-[:HAS]->(:Option)
\ /
[:FOR_CATEGORY][:FOR_OPTION]
\ /
(:Vote)
|
[:MAKES]
|
(:User)
If, for example, you need to delete a Option and consequently the :Votes related to it you can do something like:
MATCH (o:Option {id:10})<-[:FOR_OPTION]-(v:Vote)
DETACH DELETE o
DETACH DELETE v
Make any sense? Sorry for my ASCII art. :)
I'm investigating this. The only reasonable way I can find is to assign an identifier to the relationship (:HAS in your case) and then use it in the pointing relationship (:VOTE).
Neo4j has internal IDs for this (see the ID() function), but I usually prefer to try to assign a semantically meaningful ID (like a person's national insurance number, a page URL or, in case of relations, the hash code of its concatenated endpoint's identifers).
Related
I've already understood that in order to get unidirectional mapping I simply can ignore direction when writing queries. However, I have a question about inserting data.
UserA -[friend]-> UserB
UserA <-[friend]- UserB
Should I avoid create secondary friend relation in other direction? If so, how can I achieve this?
I mean, how to write in cypher "creation relation only of there is no same relation in other direction"?
You want to MERGE the relationship. Unlike CREATE, you can omit the direction of a merged relationship:
MERGE (userA)-[:FRIEND]-(userB)
This will check for an existing relationship in either direction, and if none exists it will create one with an arbitrary direction.
As for whether to create a second relationship in the opposite direction, avoid that if you can, unless it has a distinct meaning compared to the first relationship.
For example, a person can be in love with another person, but that doesn't mean the other person loves them back, a second relationship would be needed to cement that.
Let`s use the movie DB as an example.
If I would insert all people that worked on a movie in the DB, it would be difficult to find relationship names for everyone. Would it be a problem to have entities like: sound_designer, sound_engineer, set_designer, set_assistance, cable_guy, etc with the same relationship "WORKS_IN" to a Movie entity. Is it possible? Is it a good solution? Would I have problems? Are there alternatives?
Gabor's answer in the comments is a good one, there are no problems with nodes of differing labels having relationships of the same type to the same node.
Multi-labeling nodes with their role isn't a bad idea, however that assumes that a person's role is constant throughout the years captured by the graph, which may not hold true. Or rather, the labels would capture what roles they have been in their entire history, but what specific role they played within a particular movie is likely something you want as a property of the relationship itself, like a role property. Which might even be a list, if a person might have multiple roles for the same movie, similar to actors playing a part (where there is a roles list property on :ACTED_IN relationships).
I'm modeling legal cases with Attorneys, Firms, Judges, Cases and Parties and I'm trying to model the relationship between an Attorney and a Party on a Case. The trouble is that my current model doesn't scope the relationship to a particular case, that is, once an Attorney has a REPRESENTS relationship to a Party, then she is always associated with that Party, even on unrelated Cases. I know that relationships can only have two nodes on them, so how do I model this without creating a SQL-like join table? That is, I want this (even though I can't have it):
(Attorney)-[REPRESENTS]->(Party+Case)
Here's a simplified sketch of my models:
(Attorney {email:, ...})
-[REPRESENTS]->(Party)
-[MEMBER_OF]->(Firm)
(Party {name:, ...})
-[PARTY_IN {role: <plaintiff, defenadant, ...>}]->(Case)
(Firm {email_domain:, ...})
(Case {title:, case_number:, court_house:, ...)
(Judge {name:,...})
-[PRESIDING_OVER]->(Case)
Look into the use of intermediate nodes. Not a perfect example, but this might help you think through the data model.
http://www.markhneedham.com/blog/2013/10/22/neo4j-modelling-hyper-edges-in-a-property-graph/
The idea is that you might want to create a relationship node that connects the Case, Party, and Attorney
Seems like you may want to break out your PARTY_IN roles as their own nodes.
So you might have something like this:
(:Party)-[:PARTY_AS]->(:Defendant)-[:IN]->(:Case)
(:Attorney)-[:REPRESENTS]->(:Defendant)-[:IN]->(:Case)
You can either use separate labels for :Defendant, :Plaintiff, etc (recommended), or have a more generalized (:Role) with a type field.
If you wanted a query to give you the parties and attorneys for a case, you might use something like:
MATCH (case:Case)
WHERE case.id = 123
WITH case
MATCH (party:Party)-[:PARTY_AS]->(role)-[:IN]->(case)
WITH party, role
MATCH (attorney:Attorney)-[:REPRESENTS]->(role)
RETURN LABELS(role) AS role, COLLECT(attorney) AS attorneys, party
(using collect here as multiple attorneys may represent a party in a case)
Consider Person nodes and Item nodes.
What is the best way to prevent having both 'Purchased' type relationships and 'Bought' type relationships in the graph that have the same meaning, but are simply named differently?
E.g. if we end up with our graph in a state like:
(Alice) -[Bought] -> (Pickles)
(Bob) -[Purchased]-> (Pickles)
and I want to know everyone who has bought a jar of pickles. Clearly someone made a mistake when creating one of these relationships. How do I prevent that class of mistake?
Limit the relationships a user can create to a specific set of names, and don't allow any other relationship names.
Maybe it is a long shot but worth trying...
I have the following relation User1-[:MATCHED]-User2, I want to allow other users to give feedback (Like) on that relation, I am guessing that the obvious answer is to define new node from type Match which will be created for every two matched users and then relate to that node with LIKE relation from each user who liked the match.
I am trying to think about other way to model that in the graph without the overhead of creating new node for each match...
Can a relation relate to other nodes except the start/end nodes?
Any help will be appreciated thanks.
Neo4j does not support hypergraphs or relationships to relationships. Modelling your MATCHED relationship with a node is probably the way to go.
An alternative is to reference the relationship id from another node:
User1-[MATCHED]->User2 (where MATCHED has the id xyz)
User3-[LIKES]->Relationship(relId = xyz)
The "Relationship" node would contain the id of the MATCHED relationship as a property. This relId property would need to be indexed to find all LIKES of a given MATCHED relationship.
This solution is not well suited for traversals though.