Neo4j: Find all possible combinations - neo4j

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.

Related

Neo4j APOC A* with conditions

I have successfully used the following to get the shortest path using A* in the APOC library.
apoc.algo.aStar("A", "B", 'Link', 'Length','X','Y') YIELD path, weight
apoc.algo.aStar("A", "B", 'Link', {weight:'Length',default:1, x:'X',y:'Y'}) YIELD path, weight
How would I go about adding a filter so that it only uses edges where "Value" is true. The documentation doesn't provide an example.
public class Node{
public long Id {get;set;}
public string Name {get;set;}
public long X {get;set;}
public long Y {get;set;}
}
public class Link{
public bool Value {get;set;}
public long Length {get;set;}
}
There is no example because this feature is not available.
So you have three choices :
add a Length value very high on relationships where "Value" is true
modify your model by adding the property "Value" in the relationship type (ie to have two types : Link_On and Link_value_Off), so you can use the apoc procedure.
create your own A* procedure by taking example on the one from APOC (source code here)

How to fetch pattern variable with node and relationships (if present)

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.

Can't delete node using Neo4j + Spring Data for Neo

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

How do I use spring-data-neo4j with spatial indexes and cypher?

I want to use a spatial index in Neo4j with the spring-data-neo4j framework. Additionally I want to query the index using cypher. The database is embedded.
I'm a bit clueless as to how to get it to all wire together.
With a domain object like this,
#NodeEntity
class Junction {
#GraphId Long id;
#Indexed(indexType = IndexType.POINT, indexName = "junctionLocations") Point wkt;
}
SDN ought to be maintaining the index for me. It appears so, as I can do spatial queries using the repository:
interface JunctionGraph extends GraphRepository<Junction>, SpatialRepository<Junction> {}
with
junctionGraph.findWithinBoundingBox("junctionLocations", new Box(lowerBound.point, upperBound.point))
However, I understand that to query this index using cypher (via a #Query in the repository) this spatial index configuration won't work. I think that this is because each node needs to be manually added to the spatial index (or at least a proxy for the node). That means that adding this to JunctionGraph:
#Query("START n=node:junctionLocations('withinDistance:[{0}, {1}, {2}]') MATCH n-[*]->(i:Item) return i")
Collection<Item> getItemsWithin(double lat, double lon, double radius)
doesn't work.
Does anyone have a working recipe? It appears to be a bit black magic to me, and I'm unsure what the best way to proceed within SDN is.
It works, you just have to create the whole query string outside and pass it as parameter, placeholders within string constants are not substituted.
#Query("START n=node:junctionLocations({0}) MATCH n-[*]->(i:Item) return i")
Collection<Item> getItemsWithin(String query)
you have to do the replacement yourself, .e.g. using String.format
String.format("withinDistance:[%f, %f, %f]",lat,lon,radius)

Index key/value for relationship with unique factory. What to choose?

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

Resources