Spent multiple days trying to figure out why this isn't working. My model is Player-[:PLAYED_WITH_TEAM]->team-[:CONTESTED_IN]->league. Few instances of this relation is as follows
bob-[:PLAYED_WITH_TEAM]->falcons-[:CONTESTED_IN]->ABC League
alice-[:PLAYED_WITH_TEAM]->falcons-[:CONTESTED_IN]->ABC League
bob-[:PLAYED_WITH_TEAM]->falcons-[:CONTESTED_IN]->XYZLeague
Bob played for the same team "Falcons" in two leagues ABC and XYZ. This is the fact i want to capture. Since Bob played for the same team in 2 different leagues, I need to have two PLAYED_WITH_TEAM relations between the same start (Bob) and end (Falcons) nodes.
I am using spring data and have the entities defined. I am able to create 2 such relationships but not more than two using spring data. i.e. if bob played for the same team Falcons for another 3rd league, I am unable to create that 3rd relation. I am not sure where the problem is. Below is my code for creating the new relation. PlayedWith is a RelationshipEntity with Player as the start node and Team as the end node.
private PlayedWith createPlayedWithRelation(League currentLeague, Team team, Player p)
{
System.err.println("Creating PLAYED_WITH_TEAM relation between " + team + " and " + p + " and " + currentLeague);
PlayedWith playedWith = template.createRelationshipBetween(p, team, PlayedWith.class, "PLAYED_WITH_TEAM", true);
playedWith.setDuring(currentLeague.getStartDate());
playedWith.setInLeague(currentLeague);
playedWith.setPlayer(p);
playedWith.setTeam(team);
playedWith.setAsCaptain(p.isCaptain());
team.addPlayer(p);
template.save(playedWith);
return playedWith;
}
PlayedWith
#RelationshipEntity (type = "PLAYED_WITH_TEAM")
public class PlayedWith
{
#GraphId
private Long nodeId;
#StartNode
Player player;
#Fetch
#EndNode
Team team;
}
Let me know if there is an alternate way of storing this scenario.
You do not need to add another relationship between bob and the falcons but between the falcons and the new league like this:
(falcons)-[:CONTESTED_IN]->(NEWLeague)
As bob plays for the falcons and the falcons then contested in ABC League, XYZ League and NEW League bob implicitly plays in those three leagues.
Actually there should only be one :PLAYED_WITH_TEAM relationship between bob and the falcons.
Are you sure that you query is right for getting the amount of :PLAYED_WITH_TEAM relationships between Bob and the falcons?
From the SDN reference documentation:
Note
Spring Data Neo4j ensures by default that there is only one
relationship of a given type between any two given entities. This can
be circumvented by using the createRelationshipBetween() method with
the allowDuplicates parameter on repositories or entities.
Related
I'm really struggling getting my head around neo4j and was hoping someone might be able to help point me in the right direction with the below.
Basically, I have a list of what can be referred to as events; the event can be said to describe a patient entering and leaving a room.
Each event has a unique identifier; it also has an identifier for the student in question along with start and end times (e.g. the student entered the room at 12:00 and left at 12:05) and an identifier for the room.
The event and data might look along the lines of the below, columns separated by a pipe delimiter
ID|SID|ROOM|ENTERS|LEAVES
1|1|BLUE|1/01/2015 11:00|4/01/2015 10:19
2|2|GREEN|1/01/2015 12:11|1/01/2015 12:11
3|2|YELLOW|1/01/2015 12:11|1/01/2015 12:20
4|2|BLUE|1/01/2015 12:20|5/01/2015 10:48
5|3|GREEN|1/01/2015 18:41|1/01/2015 18:41
6|3|YELLOW|1/01/2015 18:41|1/01/2015 21:00
7|3|BLUE|1/01/2015 21:00|9/01/2015 9:30
8|4|BLUE|1/01/2015 19:30|3/01/2015 11:00
9|5|GREEN|2/01/2015 19:08|2/01/2015 19:08
10|5|ORANGE|2/01/2015 19:08|3/01/2015 2:43
11|5|PURPLE|3/01/2015 2:43|4/01/2015 16:44
12|6|GREEN|3/01/2015 11:52|3/01/2015 11:52
13|6|YELLOW|3/01/2015 11:52|3/01/2015 17:45
14|6|RED|3/01/2015 17:45|7/01/2015 10:00
Questions that might be asked could be:
what rooms have student x visited and in what order
what does the movement of students between rooms look like - to which room does students go to when they leave room y
That sounds simple enough but I'm tying myself into knots.
I started off creating unique constraints for both student and room
create constraint on (student: Student) assert student.id is unique
I then did the same for room.
I then loaded student as
using periodic commit 1000 load csv with headers from 'file://c:/event.csv' as line merge (s:Student {id: line.SID});
I also did the same for room and visits.
I have absolutely no idea how to create the relationships though to be able to answer the above questions though. Each event lists the time the student enters and leaves the room but not the room the student went to. Starting with the extract, should the extract be changed so that it contains the room the student left for? If someone could help talk through how I need to think of the relationships that needs to be created, that would be very much appreciated.
Cheers
As the popular saying goes, there is more than one way to skin an Ouphe - or thwart a mage. One way you could do it (which makes for the simplest modeling imo) is as follows :
CREATE CONSTRAINT ON (s:Student) ASSERT s.studentID IS UNIQUE;
CREATE CONSTRAINT ON (r:Room) ASSERT r.roomID IS UNIQUE;
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///dorm.csv" as line fieldterminator '|'
MERGE (s:Student {studentID: line.SID})
MERGE (r:Room {roomID: line.ROOM})
CREATE (s)-[:VISIT {starttime: apoc.date.parse(line.ENTERS,'s',"dd/MM/yyyy HH:mm"), endtime: apoc.date.parse(line.LEAVES,'s',"dd/MM/yyyy HH:mm")}]->(r);
# What rooms has student x visited and in what order
MATCH (s:Student {studentID: "2"})-[v:VISIT]->(r:Room)
RETURN r.roomID,v.starttime ORDER BY v.starttime;
# What does the movement of students between rooms look like - to which room does students go to when they leave room y
MATCH (s:Student)-[v:VISIT]->(r:Room {roomID: "GREEN"})
WITH s, v
MATCH (s)-[v2:VISIT]->(r2:Room)
WHERE v2.starttime > v.endtime
RETURN s.studentID, r2.roomID, v2.starttime ORDER BY s.studentID, v2.starttime;
So actually you would only have Student and Room as nodes and a student VISITing a room would make up the relationship (with the enter/leave times as properties of that relationship). Now, as you can see that might not be ideal for your second query (although it does work). One alternative is to have a Visit node and chain it (as timeline events) to both Students and Rooms. There's plenty of examples around on how to do that.
Hope this helps,
Tom
Just migrated a Spring Data Neo4j 3 project to version 4 (4.0.0.RELEASE, Neo4j 2.2.5 Community Server) and coming up against an issue with a simple one-way object relationship not being updated as expected. This relationship does NOT utilise a RelationshipEntity.
With nodes A, B1 and B2 already in the datastore and an existing relationship A -> B1, changing A's B target node to B2 and saving A gives B1 <- A -> B2. The new relationship is created but the old relationship to B1 is not removed.
expected resulting state ::
actual resulting state
A defines a relationship to B, but B does not define a relationship to A (because B to A might end up being one to too many).
...imports omitted
#NodeEntity
public class TypeA {
#GraphId
private Long id;
#Relationship(type = "TYPEB", direction = Relationship.OUTGOING)
private TypeB b;
...getters and setters omitted
}
And B
...imports omitted
#NodeEntity
public class TypeB {
#GraphId
private Long id;
private String identifier;
public TypeB() {}
public TypeB(String identifier) {
this.identifier = identifier;
}
...getters and setters omitted
}
It took a long time to debug this (despite consistent failures in the application) because it seemed impossible to write a failing integration test (running against a real Neo4j server). In fact, I've not been able to write a failing test when the objects A, B1 and B2 are created at test runtime. But when the three nodes and relationship are already present in the datastore when a test is run (the realistic situation when running the application) then a B1 <- A -> B2 outcome is seen. The basic structure of the test is below (complete code can be provided).
// Find the TypeA node
Iterable<TypeA> As = typeARepository.findAll();
TypeA a = Iterables.get(As, 0);
// Find the TypeB node using its String identifier
TypeB b1 = typeBRepository.findByIdentifier(ONE);
assertNotNull(b1);
assertEquals(ONE, b1.getIdentifier());
// check that this is the TypeB node that is related to a
assertEquals(b1.getId(), a.getB().getId());
// Find the other TypeB node using its String identifier
TypeB b2 = typeBRepository.findByIdentifier(TWO);
assertNotNull(b2);
assertEquals(TWO, b2.getIdentifier());
// now create a relationship between a and this TypeB node instead
a.setB(b2);
// and save a
TypeA savedUpdatedA = typeARepository.save(a);
Repositories are created programmatically at test runtime. And using the GraphAware RestTest library to validate the data store subgraph before and after running the repository save calls.
Setting:
<logger name="org.neo4j.ogm" level="DEBUG" />
It can be seen that the Cypher sent to the Neo4j server does not include a delete call for the starting A -> B1 relationship on saving a when the nodes are already present in the datastore. It does when the nodes are created at test runtime. So I'm thinking that there is an issue somewhere around the Neo4jSession whereby the existing relationship is not flagged for deletion - but it is when the same session was used to create the objects in the first place (in the test).
Has anyone had similar issues using or migrating to SDN 4? I cannot recall seeing this with SDN 3. It's not something I looked very hard at, it just seemed to work, but obviously SDN 4 is a complete rewrite.
I believe this is now fixed. You'll need to use the latest snapshot version of the OGM, 1.1.4-SNAPSHOT, rather than the 1.1.3 release.
i've seen in grails doc something about join fetch :
"This works well for single-ended associations, but you need to be
careful with one-to-manys. Queries will work as you'd expect right up
to the moment you add a limit to the number of results you want. At
that point, you will likely end up with fewer results than you were
expecting. The reason for this is quite technical but ultimately the
problem arises from GORM using a left outer join."
i don't see why a left outer join can raise a problem in a none single-end association like one-to-many for example.
can you give me an example ?
if i take the example given by Joshua
Class Person {
String name
static hasMany = [numbers:PhoneNumber]
static mapping = {
nubmers fetch : 'join'
}
}
Class PhoneNumber{
static belongsTo = [owner : Person]
}
//for testing
def person = Person.get(1)
result in:
select name,... from
person left outer join phone_number
on person.id = phone_number.owner_id
where person.id=1;
Can you give me a query(in gorm) which can show me the problem ?
thanks
If I understand your question correctly then it's easiest to explain with a simple example. Let's say we have a domain class Person with many PhoneNumber classes associated with it. Thus a LEFT JOIN to fetch this data will result in a SQL result like this:
PersonId, Name, PhoneNumberId, TelNumber
-----------------------------------------
1, Joe, 1, 123-123-1234
1, Joe, 2, 222-222-2222
1, Joe, 3, 333-333-3333
2, Jane, 4, 000-000-000
This would be four records returned from the datasource, but only two instances of the Person domain with their associated phone numbers. However, if you limit this query to a maximum of 2 records you will in fact only get one instance of the Person domain (Joe in this example) and his first two phone numbers.
Hope that helps you understand the underlying issue.
P.S. This is an entirely theoretical example, the actual query results from GORM would have different column names and such due to Hibernate.
Update
An example GORM based query is quite simple to demonstrate:
def people = Person.list([max: 2])
You might expect the above to give you two person instances, but in reality because of the left join you will only end up with one instance. Turning on SQL logging will show you the actual query being executed and you will notice it's using a LEFT join.
Assume that I am implementing a user friend relationship on Neo4j. I am using spring-neo4j for modeling.
Users are related to each other as friend. We can take a users friends and this is first level relationship. Is there a way to take friends friends which is second level relationship and third level relationship in Neo4J, especially spring-neo4j?.
Thank You!
Edited:
Could relation level be given dynamically? I have a piece that find every one on all levels.
MATCH u1-[:friend*]->(u2)
WHERE u1.userId IN ["userId1"]
RETURN u2
My edited question is that can I just give a relation level and get the result. For example, I am giving 2 and trying to get 2nd level friends. If I gave 3 it should return 3th level friends. Could this be dynamically give?
Not exactly sure what is the specific question here but this is how you could do that using spring-data-neo4j
#NodeEntity
public class User
{
#RelatedTo(type="FRIEND")
Set<User> friends;
private Iterable<Person> friends;
#Query(value = "start n=node({self}) match (u)-[r]->(friend)-[r]->(friendOfFriend) where r.type = 'FRIEND' return friendOfFriend")
Iterable<User> friendsOfFriends;
}
Anwser of edited part of my question is ;
MATCH u1-[:friend*relationLevel]->(u2)
WHERE u1.userId IN ["userId1"]
RETURN u2
relationLevel is the level that I want. If it is 2, result will be only for 2nd level relation.
And it is probably in spring,
#Query("start user1=node({0}) match (user1)-[r:friend*{1}]->(user2) where has(user2.userId) return user2.userId")
Iterable<String> getRelatedUserIdsByRelationLevel(GraphUser graphUser,int relationLevel);
I'm using the following code to generate unique relationships
public Relationship createRelationshipTypeWithUniqueFactory(Index<Relationship> index,
String indexableKey, final String indexableValue,
final RelationshipType type, final Node start, final Node end) {
UniqueFactory<Relationship> factory = new UniqueFactory.UniqueRelationshipFactory(index) {
#Override
protected Relationship create(Map<String, Object> properties) {
Relationship r = start.createRelationshipTo(end, type);
return r;
}
};
return factory.getOrCreate(indexableKey, indexableValue);
}
The factory requires me to pass a key and value (for the Index) so it can enforce the uniqueness of the relationship.
However, i'm not sure on how to build up the key + value.
Lets assume the following:
A - [knows] -> B (A knows B) where A and B are identified by the property email address
I want this relation ship to be unique so that A has at most 1 KNOWN relationship with B of this type.
Also, this should prevent B from creating an additional relationship to A (as the KNOWS relationship is bi-directional).
What would the best value to choose? One of the e-mail addresses (for example the lexical sorted most important of the two)?
Can anyone shed any light on this?
Cheers,
Erik
You can use Cyphers CREATE UNIQUE feature, creating the relationship only if it doesn't exist no regarding the direction upon a second run, see http://console.neo4j.org/r/a4kc2k
START n=node:node_auto_index(name='Neo'), t= node:node_auto_index(name='The Architect')
CREATE UNIQUE n-[r:SPEAKES_WITH]-t
RETURN n AS Neo,r