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.
Related
I have a following SDN 4 node entity:
#NodeEntity
public class Product {
#Index(unique = false)
private String name;
...
}
Inside of this entity I have added name property and declared an index.
Right now I'm going to implement case insensitive search by product name.
I have created a SDN 4 repository method:
#Query("MATCH (p:Product) WHERE LOWER(d.name) = LOWER({name}) RETURN p")
Product findByName(#Param("name") String name);
In order to search a product I use following Cypher: LOWER(d.name) = LOWER({name})
I think the index doesn't effectively work in this case because I lowercase the strings.
What is a proper way in Neo4j/SDN 4 to make index working here ?
If you do not need to store the name in the original case, then convert the name to lowercase before storing it in the DB.
If you do need the name in the original case, then you could add an extra property (say, "lower_name") that stores the lowercased name. You can index that property and use it for indexed comparisons.
A third choice is to use legacy indexing, which is much more complex to use and no longer favored. However, it does support case-insensitive indexing (see the second example on this page).
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.
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
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
I Have the following two classes:
class Person{
String name
static hasMany = [
items: Item
]
}
class Item{
String name
}
Multiple Persons can also have the same Item. I'm trying to get a list of all Persons that have a specific item in their collection. I.E. Both Person A and B have Item A in their list so return them both. Unfortunately their is no findAllByCollectionContains() the closest is is findAllByCollection() which requires an exact set.
I've been trying executeQuery to give me a but more control and haven't come up with anything yet.
Example of what I have tried:
Person.executeQuery("select name from Person p where ? in p.items",[item])
Any suggestions?
You have to join the items collection, then you can query on it easily
Person.executeQuery("select name from Person p join p.items as i where i = ?",[item])