I have a graph that's laid out from left-to-right. There are some elements of this graph, however, that I want to have positioned relative to another node. For example, if I have this graph:
digraph "Test" {
rankdir = "LR"
A -> B
B -> C
D -> B
note -> B
note [ shape="house" ]
};
It renders like this:
However, I'd like the "note" node to always be positioned directly underneath the node to which it's pointing, like this (manually created) graph:
I've tried experimenting with a subgraph with a different rankdir and fiddling with rank and constraint attributes, but have been unsuccessful in getting this to work, as I've only been playing around with DOT for a couple of days.
You can enumerate the nodes before defining the edges, and then constrain node B to the same rank as node note by putting them in a subgraph:
digraph "Test" {
rankdir = "LR"
A;D;
{rank=same; note; B;}
C;
A -> B
B -> C
D -> B
B -> note [dir=back]
note [ shape="house" ]
};
Please note that in order to have node note below node B, I had to reverse the edge direction and add dir=back to have the arrow drawn correctly.
A general technique for moving nodes around is to create invisible edges. In your case, you could create an edge from A to note, mark it invisible, and then mark the edge from note to B as non-constraining:
A -> note [style="invis"];
note -> B [constraint=false];
Related
I have a graph which is made of many instances of the same pattern (or subgraph).
The subgraph of interest is pictured below.
The relationship cardinality between the nodes are:
s -> c (one-many)
c -> p (many-many)
p -> aid (one-many)
p -> rA (one-one)
p -> rB (one-one)
p -> o (many-one)
The goal is to return a list of all instances of this subgraph or pattern as shown below
[
{
s-1,
c-1,
p-1,
aid-1,
o-1,
rA-1,
rB-1
},
{
s-2,
c-2,
p-2,
aid-2,
o-2,
rA-2,
rB-2
},
{
... so on and so forth
}
]
How do I query my graph to return this response?
I have tried using a combination of and() and or() as shown below, but that did not capture the entire subpattern as desired.
g.V().hasLabel('severity').as('s').out('severity').as('c').out('affecting').as('p')
.and(
out('ownedBy').as('o'),
out('rA').as('rA'),
out('rB').as('rB'),
out('package_to_aid').as('aid')
)
.project('p', 'c', 's', 'o', 'rA', 'r', 'aid').
by(valueMap()).
by(__.in('affecting').values('cve_id')).
by(__.in('affecting').in('severity').values('severity')).
by(out('ownedBy').values('name')).
by(out('rA').valueMap()).
by(out('rB').valueMap()).
by(out('package_to_aid').values('aid')).
I know I can use a series of out() and in() steps to traverse a non-branching path (for example the nodes: s->c->p), however I am struggling with capturing/traversing paths that branch out (for example, the node p and its 3 children nodes: rA, rB, and o)
I looked at Union() but I am unable to make it work either.
I am unable to find examples of similar queries online. Does Gremlin allow this sort of traversal, or do I have to remodel my graph as a Linked-list for this to work?
ps. I am doing this on Cosmos where Match() step is not supported
I have written the following query which returns all paths possible from the specified node.
g.V(<some_id>).repeat(bothE().bothV().simplePath()).emit().dedup().path()
If we had a simple graph of 3 nodes in this structure:
A -- edge_1 -> B -- edge_2 -> C
this query would return two paths:
A, edge_1, B
A, edge_1, B, edge_2, C
However, I only want to return all paths that terminate (i.e. there is no further nodes to traverse), which in this example would only return
A, edge_1, B, edge_2, C
Is this possible?
You can try to do the repeat without the emit step and use until to stop at the end of the path.
g.V().hasLabel('A').repeat(bothE().bothV().
simplePath()).
until(bothE().simplePath().
count().is(eq(0))).dedup().path()
example: https://gremlify.com/jqr8y7p24wb
In a CosmosDB Graph collection, I'm trying to find all nodes of type typeA that do not have any "live" edges pointing to nodes of type typeB.
Some edges may be "soft-deleted" (i.e. g.E().has('softDeleted', true) ). These edges should be ignored.
This is what I tried:
g.V().hasLabel('typeA')
-> returns 10 nodes of type "typeA", as expected
g.V().hasLabel('typeA')
.outE().not(has('softDelete', true))
.inV().hasLabel('typeB')
-> returns 2 nodes of type "typeB", as expected
g.V().hasLabel('typeA')
.where( // same condition as above, inside a 'where' clause
outE().not(has('softDelete', true))
.inV().hasLabel('typeB')
.count().is(eq(0)) // " where there are no such edges"
)
-> returns all 10 nodes of type "typeA" again ?!
In the query above, applying the where clause seems to not filter anything.
The following query will find all typeA vertices that do not have an edge to a typeB vertex. Only edges that have no softDelete property or that edges that have softDelete set to false will be considered, other edges will be ignored.
g.V().hasLabel('typeA').
not(outE().or(hasNot('softDelete'),
has ('softDelete', false)).
inV().hasLabel('typeB'))
I have this following type of graph which I get using this query:
MATCH (p:Person)-[:REPORTS_TO *]->(c:Person) WHERE p.name="F"
WITH COLLECT (c) + p AS all
UNWIND all as p MATCH (p)-[:REPORTS_TO]-(c)
RETURN p,c;
Use-Case:
1. I want to find what level is a node at with respect to node F?
Example :
Node `D`, `E` are direct child of `F`, hence they are at level 1
Node `A,B,C` are childs of `D` (which is child of `F`) hence level 2
Node `X` is child of `A' (which is at level 2), hence level 3
and so onnnnn....
I tried to solve this by introducing a variable i and increment it with each iteration (but it didn't worked).
MATCH (p:Person)-[:REPORTS_TO *]->(c:Person) WHERE p.name="F"
WITH COLLECT (c) + p AS all ,i:int=0
UNWIND all as p MATCH (p)-[:REPORTS_TO]->(c)
RETURN p,c, i=i+1;
2. Given two nodes find relation between then
e.g Find relation between F and X?
Expected answer = 3 (as it is at level 3)
How should I proceed to solve these use-cases?
Note : Graphical response from Neo4j server isn't necessarily needed , Json response will also be fine.
UC1 Use Path and length(p) function
MATCH p=(root:Person)-[:REPORTS_TO *]->(child:Person)
WHERE root.name="F"
RETURN nodes(p)[-2], nodes(p)[-1],length(p)
This will find all paths from root node, and return pairs of second to last and last nodes + level you want.
nodes(p) - list of nodes on path p
[-2] - second node from the end of the list
UC2: use shortestPath function:
MATCH (p1:Person),(p2:Person)
WHERE p1.name = '..' AND p2.name = '...'
MATCH p=shortestPath((p2)-[:REPORTS_TO*]->(p2))
RETURN length(p)
Let's say I have a directed graph with nodes of types A and B. Nodes of type A can be connected to nodes of type A or B, and nodes of type B have no outgoing connections.
Also, nodes B has a boolean property. I need to find all nodes A that:
have no direct or inderect connections to nodes B
if node A has conection to at least 1 node B, it should be returned only if all connected nodes B has a property set to true.
Or in other words, I need to find all A which are not connected to any B with property set to false.
I was trying to do it with query:
OPTIONAL MATCH (a:A)-[*]->(b:B)
WITH a,b, collect(b) as bc
WITH a,b,COLLECT(bc) AS coll
UNWIND coll as unwinded
WITH a,b,unwinded
WHERE ALL (x IN unwinded WHERE x.prop = true)
return a
But it returns me A if they have at least 1 b with prop=true. What am I doing wrong?
Thank you!
This should return all A nodes that have no connected B nodes with a false prop:
MATCH (a:A)
OPTIONAL MATCH (a)-[*]->(b:B { prop: false })
WITH a, COLLECT(b) AS bs
WHERE SIZE(bs)= 0
RETURN a;
Here is a console showing this query.
[UPDATE]
As suggested by #InverseFalcon, the above query can be greatly simplified:
MATCH (a:A)
WHERE NOT (a)-[*]->(:B {prop:false})
RETURN a;
Does this work?
MATCH (a:A)
OPTIONAL MATCH (a)-[*]->(b:B {prop: false})
WITH a, b
WHERE b IS NULL
RETURN a