I have added delete functionality for simple kind of node in my application
public class Vote extends BaseEntity {
#GraphId
private Long id;
#RelatedTo(type = VOTED_FOR, direction = Direction.OUTGOING)
private Decision decision;
#RelatedTo(type = VOTED_ON, direction = Direction.OUTGOING)
private Criterion criterion;
#RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
private User author;
Also, some other node entities having incoming relation to Vote
Delete query looks like this
"MATCH ()-[r]-(v:Vote) WHERE id(v) = {voteId} DELETE r,v"
When you doing delete all goes fine, no exception, when i trying to find all vote nodes from node entity referenced this vote before delete, result is as expected all votes except one i deleted are returned
But when i trying to find deleted vote by id i get not expected exception
java.lang.IllegalStateException: No primary SDN label exists .. (i.e one starting with _)
Find work ok for other nodes, Probably it is related to SDN I don't know
Seems like I don't know how properly delete node or something, please help
Related
Suppose, we have the following data in neo4j DB ->
The java entity representation is as follows ->
#NodeEntity
public class Place {
#Id
#GeneratedValue
private Long id;
private String name;
}
#NodeEntity
public class Monument {
#Id
#GeneratedValue
private Long id;
private String name;
}
#NodeEntity
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "VISITED", direction = Relationship.OUTGOING)
private Monument monument;
#Relationship(type = "STAYS", direction = Relationship.OUTGOING)
private Place place;
}
Now, I want to fetch all persons also populating the linked place and monument, if present. This means, the cypher query will not only provide me List< Person> as result and the Monument and Place object should also be linked with each Person object, if the links are available (and otherwise null).
To clarify it further, for Person 'Ron', I should be able to see the monument he visited and the place he stays, without performing any more queries to fetch the relationships. Similarly, for Person 'April', I should be able to see where she stays, but will not know which monument she visited because there is no link there.
With my basic knowledge in Cypher Query language, I have tried but could not get the desired result.
If I provide both the relations in the query and fetch the corresponding pattern variable, I only get the Person 'Ron' in the result.
MATCH
p=(place:Place)<-[STAYS]-(person:Person)-[VISITED]->(monument:Monument) RETURN p
If I only provide the relation 'STAYS', I get 'Ron' and 'April'.
MATCH p=(person:Person)-[STAYS]->(place:Place) RETURN p
If I query without the relationships, I only get the Person object and the monument and place are not linked [getMonument() and getPlace() is null even for Person 'Ron'].
MATCH p=(person:Person) RETURN p
I could not find a query where I get all of these.
You need to put the relations into optional matches, like this:
MATCH (person:Person)
OPTIONAL MATCH (person)-[:VISITED]->(monument)
OPTIONAL MATCH (person)-[:STAYS]->(place)
return person, place, monument
Otherwise, neo4j treats the relations in your query 1) as required, that's why 'Ron' will be the only result.
I'm currently struggling with a problem regarding combinatorics.
For a prototype I wanted to try neo4j-ogm.
This is the domain model I designed so far:
#NodeEntity
public class Operand {
#GraphId
private Long graphId;
}
#NodeEntity
public class Option extends Operand {
private Long id;
private String name;
#Relationship(type = "CONDITIONED_BY")
private List<Rule> rules = new ArrayList<>();
}
#NodeEntity
public class Operation extends Operand {
#Relationship(type = "COMPOSITION", direction = Relationship.INCOMING)
private Rule composition;
private Operation superOperation;
private Boolean not;
private List<Operand> operands = new ArrayList<>();
}
public class AndOperation extends Operation {
// Relationships named accordingly "AND"
}
public class OrOperation extends Operation {
// Relationships named accordingly "OR"
}
#NodeEntity
public class Rule {
#GraphId
private Long graphId;
#Relationship(type = "COMPOSITION")
private Operation composition;
#Relationship(type = "CONDITIONED_BY", direction = Relationship.INCOMING)
private Option option;
}
This is a snippet of my graph:
representing something like (167079 ^ ...) & (167155 ^ ...)
Is it possible to form a query in cypher resulting in all possible combinations?
167079, 167155
167079, 167092
...
I found this resource dealing with a distantly related problem so far.
Do you think this is a proper use case for neo4j?
Do you propose any changes in the domain model?
Do you suggest any other technology?
Edit:
The example graph only shows a small part of my original graph, I'd have to calculate the permutation with various depth and various encapsulated operations.
Yes, we can get this by collecting on each side the equation (two collections) and then unwinding each collection.
Assuming the two collections are passed in as parameters:
UNWIND {first} as first
UNWIND {second} as second
RETURN first, second
This should give you a cartesian product between elements of both collections.
EDIT
If know know how many AND branches you have (let's say 2 for example), you can form your query like this:
MATCH (first)<-[:OR*0..]-()<-[:AND]-(root)-[:AND]->()-[:OR*0..]->(second)
WHERE id(root) = 168153
RETURN first, second
(edit, the match itself will generate the cartesian product for you here)
As for operations where you don't know how many AND branches you have, I don't believe you can accomplish this using this approach with just Cypher, as I don't believe dynamic columns are supported.
You might be able to do this using collections, though the operations would be tricky.
A custom procedure to perform cross products given two collections would be very helpful in this case. If you could collect all collections of nodes along AND branches, you could run REDUCE() on that, applying the cross product procedure with each collection.
As far as more complex trees with more operations, I don't believe you'll be able to do this through Cypher. I'd highly recommend a custom procedure, where you'll have far more control and be able to use conditional logic, recursion, and method calls.
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.
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.
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