Identify paths between two nodes in neo4j - graph

I have two paths in a graph: A-B-C-D and A-B-E-F. I would like to assign identification numbers to those paths, i.e. A-B-C-D would be 1 and A-B-E-F would be 2.
Is it possible? If yes, how?

You mean like a persistent path ID? This isn't directly featured, but you can do it in a Query in Cypher.
If you want somthing persistent, you can always use an index, so create a Relationship index that would store the Relationships of Path 1 under the key/value of Path:1.
EDIT: After getting more information, here's a use case using the index:
It would be up to you to define this in the Index. Here is what you do:
Node a = db.createNode();
Node b = db.createNode();
Node c = db.createNode();
Node d = db.createNode();
Node e = db.createNode();
Node f = db.createNode();
Relationship aTob = a.createRelationshipTo(b, DynamicRelationshipType.withName("RELATIONSHIP"));
Relationship bToc = b.createRelationshipTo(c, DynamicRelationshipType.withName("RELATIONSHIP"));
Relationship cTod = c.createRelationshipTo(d, DynamicRelationshipType.withName("RELATIONSHIP"));
Relationship bToe = b.createRelationshipTo(e, DynamicRelationshipType.withName("RELATIONSHIP"));
Relationship eTof = e.createRelationshipTo(f, DynamicRelationshipType.withName("RELATIONSHIP"));
Index<Relationship> relationshipIndex = db.index().forRelationships("PathIndex");
String pathRId = UUID.randomUUID().toString();
String pathMId = UUID.randomUUID().toString();
relationshipIndex.add(aTob, "PathId", pathRId);
relationshipIndex.add(bToc, "PathId", pathRId);
relationshipIndex.add(cTod, "PathId", pathRId);
relationshipIndex.add(aTob, "PathId", pathMId);
relationshipIndex.add(bToe, "PathId", pathMId);
relationshipIndex.add(eTof, "PathId", pathMId);
Then when you want to find a path, you would search by the ID. You would be responsible for maintaining the Set Id in the index, here I use UUID, but you can use something more representative of your information. The relationships would not be in any repeatable order when returned from the Index.

Related

Java GraphTraversal output Gremlin query

How to output Gremlin query from a Java GraphTraversal object? The default output (graphTraversal.toString()) looks like [HasStep([~label.eq(brand), name.eq(Nike), status.within([VALID])])] which is not easy to read.
Gremlin provides the GroovyTranslator class to help with that. Here is an example.
// Simple traversal we can use for testing a few things
Traversal t =
g.V().has("airport","region","US-TX").
local(values("code","city").
fold());
// Generate the text form of the query from a Traversal
String query;
query = GroovyTranslator.of("g").
translate(t.asAdmin().getBytecode());
System.out.println("\nResults from GroovyTranslator on a traversal");
System.out.println(query);
This is taken from a set of examples located here: https://github.com/krlawrence/graph/blob/master/sample-code/RemoteWriteText.java
You can use getByteCode() method on a DefaultGraphTraversal to get output gremlin query.
For example, consider the following graph
Graph graph = TinkerGraph.open();
Vertex a = graph.addVertex(label, "person", "name", "Alex", "Age", "23");
Vertex b = graph.addVertex(label, "person", "name", "Jennifer", "Age", "20");
Vertex c = graph.addVertex(label, "person", "name", "Sophia", "Age", "22");
a.addEdge("friends_with", b);
a.addEdge("friends_with", c);
Get a graph Traversal as following:
GraphTraversalSource gts = graph.traversal();
GraphTraversal graphTraversal =
gts.V().has("name","Alex").outE("friends_with").inV().has("age", P.lt(20));
Now you can get your traversal as a String as:
String traversalAsString = graphTraversal.asAdmin().getBytecode().toString();
It gives you output as:
[[], [V(), has(name, Alex), outE(friends_with), inV(), has(age, lt(20))]]
It is much more readable, almost like the one you have provided as the query. You can now modify/parse the string to get the actual query if you want like replacing [,], adding joining them with . like in actual query.

sqlalchemy self-referencing many-to-many with "select" as association table

Problem description
I'm using sqlalchemy (v1.2) declarative, and I have a simple class Node with an id and a label. I would like to build a self-referencing many-to-many relationship where the association table is not a database table, but a dynamic select statement. This statement selects from two joined aliases of Node and returns rows of the form (left_id, right_id), defining the relationship. The code I have so far works if I access the relationship through an instance object, but when I try to filter by the relationship the joins are messed up.
The "classical" self-referential many-to-many relation
For reference, let's start with the example from the documentation on Self-Referential Many-to-Many Relationship, which uses an association table:
node_to_node = Table(
"node_to_node", Base.metadata,
Column("left_node_id", Integer, ForeignKey("node.id"), primary_key=True),
Column("right_node_id", Integer, ForeignKey("node.id"), primary_key=True)
)
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
label = Column(String, unique=True)
right_nodes = relationship(
"Node",
secondary=node_to_node,
primaryjoin=id == node_to_node.c.left_node_id,
secondaryjoin=id == node_to_node.c.right_node_id,
backref="left_nodes"
)
def __repr__(self):
return "Node(id={}, Label={})".format(self.id, self.label)
Joining Node to itself through this relationship:
>>> NodeAlias = aliased(Node)
>>> print(session.query(Node).join(NodeAlias, Node.right_nodes))
SELECT node.id AS node_id, node.label AS node_label
FROM node JOIN node_to_node AS node_to_node_1
ON node.id = node_to_node_1.left_node_id
JOIN node AS node_1
ON node_1.id = node_to_node_1.right_node_id
Everything looks well.
The many-to-many relation through an association select statement
As an example we implement a relationship next_two_nodes which connects a node to the two nodes with id+1 and id+2 (if existent). The complete code for testing.
Here is a function which generates the select statement for the "dynamic" association table:
_next_two_nodes = None
def next_two_nodes_select():
global _next_two_nodes
if _next_two_nodes is None:
_leftside = aliased(Node, name="leftside")
_rightside = aliased(Node, name="rightside")
_next_two_nodes = select(
[_leftside.id.label("left_node_id"),
_rightside.id.label("right_node_id")]
).select_from(
join(
_leftside, _rightside,
or_(
_leftside.id + 1 == _rightside.id,
_leftside.id + 2 == _rightside.id
)
)
).alias()
return _next_two_nodes
Note that the function caches the result in a global variable, so that successive calls always return the same object instead of using new aliases. Here is my attempt to use this select in a relationship:
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
label = Column(String, unique=True)
next_two_nodes = relationship(
"Node", secondary=next_two_nodes_select,
primaryjoin=(lambda: foreign(Node.id)
== remote(next_two_nodes_select().c.left_node_id)),
secondaryjoin=(lambda: foreign(next_two_nodes_select().c.right_node_id)
== remote(Node.id)),
backref="previous_two_nodes",
viewonly=True
)
def __repr__(self):
return "Node(id={}, Label={})".format(self.id, self.label)
Some test data:
nodes = [
Node(id=1, label="Node1"),
Node(id=2, label="Node2"),
Node(id=3, label="Node3"),
Node(id=4, label="Node4")
]
session.add_all(nodes)
session.commit()
Accessing the relationship through an instance works as expected:
>>> node = session.query(Node).filter_by(id=2).one()
>>> node.next_two_nodes
[Node(id=3, Label=Node3), Node(id=4, Label=Node4)]
>>> node.previous_two_nodes
[Node(id=1, Label=Node1)]
However, filtering on the relationship does not give the expected result:
>>> session.query(Node).join(NodeAlias, Node.next_two_nodes).filter(NodeAlias.id == 3).all()
[Node(id=1, Label=Node1),
Node(id=2, Label=Node2),
Node(id=3, Label=Node3),
Node(id=4, Label=Node4)]
I would expect only Node1 and Node2 to be returned. And indeed, the SQL statement of the join is wrong:
>>> print(session.query(Node).join(NodeAlias, Node.next_two_nodes))
SELECT node.id AS node_id, node.label AS node_label
FROM node JOIN (SELECT leftside.id AS left_node_id, rightside.id AS right_node_id
FROM node AS leftside JOIN node AS rightside
ON leftside.id + 1 = rightside.id OR leftside.id + 2 = rightside.id) AS anon_1
ON anon_1.left_node_id = anon_1.left_node_id
JOIN node AS node_1 ON anon_1.right_node_id = node_1.id
Comparing with the working example above, instead of ON anon_1.left_node_id = anon_1.left_node_id it should clearly read ON node.id = anon_1.left_node_id. My primaryjoin seems to be wrong, but I cannot figure out how to connect the last dots.
After more debugging I found that "Clause Adaption" is replacing my ON clause. I'm not sure about the details, but for some reasen sqlalchemy thinks that I am referring to the node.id from the select rather than from the original Node table. The only way I found to suppress clause adaption was to select in text form:
select(
[literal_column("leftside.id").label("left_node_id"),
literal_column("rightside.id").label("right_node_id")]
)...
This way the relationship to Node is broken and filtering works as expected. It feels like a hack with unforeseeable side effects, maybe someone knows a cleaner way...

jpa 2 criteria with map key and value

In my entity I have this field
#ElementCollection
#CollectionTable
#MapKeyColumn(name = "SERVER_ID")
#Column(name = "IS_SYNC")
private Map<String, Boolean> serverSyncs = new HashMap<>();
I'm trying to get all entities of my table that do not have an entry with the key equals to "serverId" (passed as parameter in my function) or that have an entry but the value is false.
This is what I've done for now
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<T> criteriaQuery = builder.createQuery(clazz);
Root<T> root = criteriaQuery.from(clazz);
MapJoin<T, String, Boolean> mapRoot = root.joinMap("serverSyncs");
List<T> result = session.createQuery(
criteriaQuery.where(
builder.or(
mapRoot.isNull(),
builder.not(mapRoot.key().in(serverId)),
builder.and(
mapRoot.key().in(serverId),
mapRoot.value().in(false)
)
)
)
).list();
The thing is I get this error on my query
Could not locate CollectionPersister for role : ca.tecsar.core.model.AbstractServerEntity.serverSyncs
Question is : how can I achieve what I want with JPA 2.0 Criteria?
Example of what I need to retrieve
id|SERVER_ID|IS_SYNC
1|0000000001|true
1|0000000002|false
2|0000000003|false
If I ask for SERVER_ID = 3,I should get entity 1 and 2
If I ask for SERVER_ID = 2,I should get entity 1
If I ask for SERVER_ID = 1,I should get nothing
So I couldn't do it with JPA 2 Criteria but I've succeeded with a SQL Query. I have a table named PUNCH and the map table is PUNCH_SERVERSYNCS.
SELECT p.PUNCHID
FROM PUNCH p
LEFT JOIN PUNCH_SERVERSYNCS pss
ON p.PUNCHID = pss.PUNCH_PUNCHID
WHERE (pss.IS_SYNC = false AND pss.SERVER_ID = 'Server2')
OR NOT EXISTS (SELECT p.PUNCHID FROM PUNCH_SERVERSYNCS pss2 WHERE
pss2.PUNCH_PUNCHID = p.PUNCHID AND pss2.SERVER_ID = 'Server2')
GROUP BY p.PUNCHID

Add, Update, Delete from a ndb.KeyProperty() - Google Cloud Datastore NDB

Here are my many-to-many relationship models:
class ModelA(ndb.Model):
name = ndb.StringProperty(required=true)
model_b = ndb.KeyProperty(kind=ModelB,repeated=True)
class ModelB(ndb.Model):
name = ndb.StringProperty(required=true)
model_a = ndb.KeyProperty(kind=ModelA,repeated=True)
My question is, how do I add/update/delete a single (or many) KeyProperty from let's say model_b?
I managed to do it like this:
pos = ModelA.model_b.index(ndb.Key('ModelB',213)) # Get position from list
ModelA.model_b.pop(pos) # Remove from list
ModelA.put() # Update

Cassandra - CqlEngine - using collection

I want to know how I can work with collection in cqlengine
I can insert value to list but just one value so I can't append some value to my list
I want to do this:
In CQL3:
UPDATE users
SET top_places = [ 'the shire' ] + top_places WHERE user_id = 'frodo';
In CqlEngine:
connection.setup(['127.0.0.1:9160'])
TestModel.create(id=1,field1 = [2])
this code will add 2 to my list but when I insert new value it replace by old value in list.
The only help in Cqlengine :
https://cqlengine.readthedocs.org/en/latest/topics/columns.html#collection-type-columns
And I want to know that how I can Read collection field by cqlengine.
Is it an dictionary in my django project? how I can use it?!!
Please help.
Thanks
Looking at your example it's a list.
Given a table based on the Cassandra CQL documentation:
CREATE TABLE plays (
id text PRIMARY KEY,
game text,
players int,
scores list<int>
)
You have to declare model like this:
class Plays(Model):
id = columns.Text(primary_key=True)
game = columns.Text()
players = columns.Integer()
scores = columns.List(columns.Integer())
You can create a new entry like this (omitting the code how to connect):
Plays.create(id = '123-afde', game = 'quake', players = 3, scores = [1, 2, 3])
Then to update the list of scores one does:
play = Plays.objects.filter(id = '123-afde').get()
play.scores.append(20) # <- this will add a new entry at the end of the list
play.save() # <- this will propagate the update to Cassandra - don't forget it
Now if you query your data with the CQL client you should see new values:
id | game | players | scores
----------+-------+---------+---------------
123-afde | quake | 3 | [1, 2, 3, 20]
To get the values in python you can simply use an index of an array:
print "Length is %(len)s and 3rd element is %(val)d" %\
{ "len" : len(play.scores), "val": play.scores[2] }

Resources