I've got a simple graph database which contains two types.
BlogPost
Tag
A BlogPost can have many tags.
I've written a BlogPost and given it 4 tags. I'd like to search for other BlogPosts which contain at least these four tags.
I've tried
MATCH (b:BlogPost{id='156'})-[:tagged]->(original_tag)
WITH b, collect(original_tag) AS original_tags
MATCH (b2)-[:tagged]-(second_blog_tag)
WITH b, original_tags, collect(second_blog_tag) AS second_blog_tags, b2
WHERE original_tags IN second_blog_tags
RETURN b2.id
However the line
WHERE original_tags IN second_blog_tags
is wrong. Can anyone offer any assistance? Is my thinking about using collects correct or is there a better way?
Try this:
MATCH (b:BlogPost{id='156'})-[:tagged]->(original_tag)
WITH b, collect(original_tag) AS original_tags
MATCH (b2)-[:tagged]-(second_blog_tag)
WITH b, original_tags, collect(second_blog_tag) AS second_blog_tags, b2
WHERE ALL(tag IN original_tags WHERE tag IN second_blog_tags)
RETURN b2.id
you can also try:
MATCH (b:BlogPost{id='156'})-[:tagged]->(original_tag)<-[:tagged]-(b2:BlogPost)
WITH b, b2, count(distinct original_tag) as tagCount
WHERE tagCount = size((b)-[:tagged]->())
RETURN b2.id
Related
I`m using Cypher query language and I need to find nodes between node A and E. (A->B->C->D->E)
Next query returns all nodes including A and E, but i need to exclude them, to have B, C, D nodes. How can I filter my query result?
MATCH p= (A:City{name: 'City1'})-[:LINKED*]->(E:City{name: "City5"}) return nodes(p)
There are a number of ways you might do this, but a simple option is to just index into the node list using:
return nodes(p)[1..-1]
When doing a Cypher query to retrieve a specific subgraph with automorphisms, let's say
MATCH (a)-[:X]-(b)-[:X]-(c),
RETURN a, b, c
It seems that the default behaviour is to return every retrieved subgraph and all their automorphisms.
In that exemple, if (u)-[:X]-(v)-[:X]-(w) is a graph matching the pattern, the output will be u,v,w but also w,v,u, which consist in the same graph.
Is there a way to retrieve each subgraph only once ?
EDIT: It would be great if Cypher have a feature to do that in the search, using some kind of symmetry breaking condition as it would reduce the computing time. If that is not the case, how would you post-process to find the desired output ?
In the query you are making, (a)-[r:X]-(b) and (a)-[t:X]-(c) refer to a similar pattern. Since (b) and (c) can be interchanged. What is the need to repeat matching twice? MATCH (a)-[r:X]-(b) RETURN a, r, b returns all the subgraphs you are looking for.
EDIT
You can do something as follows to find the nodes, which are having two relations of type X.
MATCH (a)-[r:X]-(b) WHERE size((a)-[:X]-()) = 2 RETURN a, r, b
For these kind of mirrored patterns, we can add a restriction on the internal graph ids so only one of the two paths is kept:
MATCH (a)-[:X]-(b)-[:X]-(c)
WHERE id(a) < id(c)
RETURN a, b, c
This will also prevent the case where a = c.
I would like to find the name node which trade with three fruits only.
I tried to use the following code in neo4j.
match (s:good)-[r:TRADES]-(n:Name)-[:TRADES]-(p:good)
WHERE (s.good = 'Apple' or s.good='Orange') and p.stock ='Grapes'
return s,n,p
where it returns the query as below.
However, I just want the following. Just the one who trade Grapes, Orange and Apple only.
I don't know which part of the cypher is incorrect. thank you for helping
We have a knowledge base article on match intersection, what you're trying to do here, however your other restriction is that these are the only 3 connected nodes, so we have some additional work to do.
Using the first approach in the article, we just have to add an additional predicate to ensure the degree of :TRADES relationships equals the size of the collection:
WITH ['Apple', 'Orange', 'Grapes'] as names
MATCH (g:good)<-[:TRADES]-(n:Name)
WHERE g.good in names AND size((n)-[:TRADES]->()) = size(names)
WITH n, size(names) as inputCnt, count(DISTINCT g) as cnt
WHERE cnt = inputCnt
RETURN n
I have a graph which looks like this:
Here is the link to the graph in the neo4j console:
http://console.neo4j.org/?id=av3001
Basically, you have two branching paths, of variable length. I want to match the two paths between orange node and yellow nodes. I want to return one row of data for each path, including all traversed nodes. I also want to be able to include different WHERE clauses on different intermediate nodes.
At the end, i need to have a table of data, like this:
a - b - c - d
neo - morpheus - null - leo
neo - morpheus - trinity - cypher
How could i do that?
I have tried using OPTIONAL MATCH, but i can't get the two rows separately.
I have tried using variable length path, which returns the two paths but doesn't allow me to access and filter intermediate nodes. Plus it returns a list, and not a table of data.
I've seen this question:
Cypher - matching two different possible paths and return both
It's on the same subject but the example is very complex, a more generic solution to this simpler problem is what i'm looking for.
You can define what your end node by using WHERE statement. So in your case end node has no outgoing relationship. Not sure why you expect a null on return as you said neo - morpheus - null - leo
MATCH p=(n:Person{name:"Neo"})-[*]->(end) where not (end)-->()
RETURN extract(x IN nodes(p) | x.name)
Edit:
may not the the best option as I am not sure how to do this programmatically. If I use UNWIND I get back only one row. So this is a dummy solution
MATCH p=(n{name:"Neo"})-[*]->(end) where not (end)-->()
with nodes(p) as list
return list[0].name,list[1].name,list[2].name,list[3].name
You can use Cypher to match a path like this MATCH p=(:a)-[*]->(:d) RETURN p, and p will be a list of nodes/relationships in the path in the order it was traversed. You can apply WHERE to filter the path just like with node matching, and apply any list functions you need to it.
I will add these examples too
// Where on path
MATCH p=(:a)-[*]-(:d) WHERE NONE(n in NODES(p) WHERE n.name="Trinity") WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Spit path into columns
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Match path, filter on label
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN FILTER(n in p WHERE "a" in LABELS(n)) as a, FILTER(n in p WHERE "b" in LABELS(n)) as b, FILTER(n in p WHERE "c" in LABELS(n)) as c, FILTER(n in p WHERE "d" in LABELS(n)) as d
Unfortunately, you HAVE to explicitly set some logic for each column. You can't make dynamic columns (that I know of). In your table example, what is the rule for which column gets 'null'? In the last example, I set each column to be the set of nodes of a label.
I.m.o. you're asking for extensive post-processing of the results of a simply query (give me all the paths starting from Neo). I say this because :
You state you need to be able to specify specific WHERE clauses for each path (but you don't specify which clauses for which path ... indicating this might be a dynamic thing ?)
You don't know the size of the longest path beforehand ... but you still want the result to be a same-size-for-all-results table. And would any null columns then always be just before the end node ? Why (for that makes no real sense other then convenience) ?
...
Therefore (and again i.m.o.) you need to process the results in a (Java or whatever you prefer) program. There you'll have full control over the resultset and be able to slice and dice as you wish. Cypher (exactly like SQL in fact) can only do so much and it seems that you're going beyond that.
Hope this helps,
Regards,
Tom
P.S. This may seem like an easy opt-out, but look at how simple your query is as compared to the constructs that have to be wrought trying to answer your logic. So ... separate the concerns.
I have a cypher query that is something like:
MATCH(e:ZOOT {id:100})
OPTIONAL MATCH(e)-[a1]->(X:QAZ)
OPTIONAL MATCH(e)-[a2]->(Y:WSX)
WHERE a1 is not null or a2 is not null
RETURN e, a1, a2
What I want is for rows producing neither a1 or a2 to be filtered away.
Yet my statement is returning rows in all cases, even of a1 and a2 are both null.
How does WHERE really work?
edit - clarification added to query
The reason you're seeing confusing results is because you're assuming the WHERE applies to the entire result before being pumped out by the RETURN. However, this isn't the case.
From the documentation on Cypher Structure:
WHERE: Not a clause in its own right, but rather part of MATCH,
OPTIONAL MATCH and WITH. Adds constraints to a pattern, or filters the
intermediate result passing through WITH.
So if I place parentheses to show how the clauses group together, it would look like this:
MATCH (e:ZOOT {id:100})
OPTIONAL MATCH(e)-[a1]->(X:QAZ)
(OPTIONAL MATCH(e)-[a2]->(Y:WSX)
WHERE a1 is not null or a2 is not null)
RETURN e, a1, a2
Your WHERE is only applying to that OPTIONAL MATCH (that particular OPTIONAL MATCH will only be included if a1 is not null or a2 is not null), which wasn't your intention. You want to apply it to the whole thing, so the easiest way to do that is to separate the queries with a WITH like so:
MATCH (e:ZOOT {id:100})
OPTIONAL MATCH(e)-[a1]->(:QAZ)
OPTIONAL MATCH(e)-[a2]->(:WSX)
WITH e, a1, a2
WHERE a1 is not null or a2 is not null
RETURN e, a1, a2
There's a way to optimize this query a bit if you're not really interested in the relationships themselves, and just want to know if your :ZOOT node has a match to either a QAZ node or a WSX node. You can use EXISTS() like so:
MATCH (e:ZOOT {id:100})
WHERE EXISTS((e)-->(:QAZ)) OR EXISTS((e)-->(:WSX))
RETURN e
Note that since you didn't provide the relationship type or use the X and Y variables bound to your end nodes, I'm assuming you aren't interested in them; I've removed them to avoid confusion from anyone reading your query.
you can change your query to this
match (e)-[r:a1|a2]-(x) return e,r,x
as for the WHERE clause i hate to state the obvious but read this :)