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.
Related
I'm starting out with Neo4J to create a graph of users and their relationships. At the moment there is a single 'KNOWS' relationship between users i.e.
What I want to do now is specify properties on the relationship specifically for each of the users. For example, "interest" which indicates how much a user is interested in the other user. Can I specify this for each user on a single KNOWS relationship or would I need to create two relationships between the users and set the attribute on each of the relationships?
Any help would be appreciated.
Can I specify this (property: interest) for each user on a single KNOWS relationship or would I need to create two relationships between the users and set the attribute on each of the relationships?
You will need two relationships.
You could do it with one but then you have to keep two properties in the relationship and information about which property goes with which node. Much easier with two relationships.
From comment:
Can I keep them as bi-directional or would I need to use directional
in this case?
Relationships are always directional. It is only when you query that the concept of bi-directional appears, but that is not really bi-directional, it is without direction, e.g. (a)-[r]-(b). So you would use (a)-[r]->(b) and (b)-[r]->(a) or (a)<-[r]-(b). If you query with the direction, then you know how to apply the relationship property.
I typically do more of my work with Java as an embedded application instead of Cypher and it pays to use directional queries as it makes for less code to do the associations.
Note
Since your case is so simple, just try various methods and see what works. Remember to keep track of how long the quires take and if necessary add indexes. Also use the query profiling tool to make sure you are making effective queries.
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).
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).
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.