Consider this example: I have one Author node, and several Book nodes. I want to create the WROTE relationship between the Author and several Book nodes in a single cypher Query statement. Also, the only way I have to look up the nodes for both the Author and all the Book nodes is by their node ID.
Here's what I tried:
MATCH (a:Author) WHERE id(a) = '31'
MATCH (b0:Book) WHERE id(b0) = '32'
MATCH (b1:Book) WHERE id(b1) = '33'
CREATE (b0)<-[:WROTE {order : '0'}]-(a)
CREATE (b1)<-[:WROTE {order : '1'}]-(a)
However, it doesn't seem to work.
Thanks.
Neo4js native ids are stored as numbers, so do not match by string. Also Cypher has IN clause that lets you match arrays, so you can simplify you query to this
MATCH (a:Author) where id(a)=31
MATCH (b:Book) where id(b) in [32,33]
CREATE (a)-[:WROTE]->(b)
Found the issue to be that I shouldn't be using the ' when matching node IDs!
Related
So this is a very basic question. I am trying to make a cypher query that creates a node and connects it to multiple nodes.
As an example, let's say I have a database with towns and cars. I want to create a query that:
creates people, and
connects them with the town they live in and any cars they may own.
So here goes:
Here's one way I tried this query (I have WHERE clauses that specify which town and which cars, but to simplify):
MATCH (t: Town)
OPTIONAL MATCH (c: Car)
MERGE a = ((c) <-[:OWNS_CAR]- (p:Person {name: "John"}) -[:LIVES_IN]-> (t))
RETURN a
But this returns multiple people named John - one for each car he owns!
In two queries:
MATCH (t:Town)
MERGE a = ((p:Person {name: "John"}) -[:LIVES_IN]-> (t))
MATCH (p:Person {name: "John"})
OPTIONAL MATCH (c:Car)
MERGE a = ((p) -[:OWNS_CAR]-> (c))
This gives me the result I want, but I was wondering if I could do this in 1 query. I don't like the idea that I have to find John again! Any suggestions?
It took me a bit to wrap my head around why MERGE sometimes creates duplicate nodes when I didn't intend that. This article helped me.
The basic insight is that it would be best to merge the Person node first before you match the towns and cars. That way you won't get a new Person node for each relationship pattern.
If Person nodes are uniquely identified by their name properties, a unique constraint would prevent you from creating duplicates even if you run a mistaken query.
If a person can have multiple cars and residences in multiple towns, you also want to avoid a cartesian product of cars and towns in your result set before you do the merge. Try using the table output in Neo4j Browser to see how many rows are getting returned before you do the MERGE to create relationships.
Here's how I would approach your query.
MERGE (p:Person {name:"John"})
WITH p
OPTIONAL MATCH (c:Car)
WHERE c.licensePlate in ["xyz123", "999aaa"]
WITH p, COLLECT(c) as cars
OPTIONAL MATCH (t:Town)
WHERE t.name in ["Lexington", "Concord"]
WITH p, cars, COLLECT(t) as towns
FOREACH(car in cars | MERGE (p)-[:OWNS]->(car))
FOREACH(town in towns | MERGE (p)-[:LIVES_IN]->(town))
RETURN p, towns, cars
I know that you can match on multiple relationships in Neo4j, like this example in the docs:
MATCH (wallstreet {title: 'Wall Street'})<-[:ACTED_IN|:DIRECTED]-(person)
RETURN person.name
which returns nodes with an ACTED_IN or DIRECTED relationship to 'Wall Street'.
However, is there a way to get the type of the relationship in this query? That is, I would like to return not only the name, but also which relationship applies to him/her, in order to see if it was the ACTED_IN, or the DIRECTED relationship that caused the result to be output.
You can do the equivalent here:
MATCH (:Person {name: 'Oliver Stone'})-[r]->(movie)
RETURN type(r)
but that's just matching on any relationship. I would like to do this, but only with the two relationships specified in the clause.
Thanks
You no longer need additional colons in between valid edge types you are querying. otherwise you can use the variable just like you did in the unspecific edge case:
MATCH (:Movie{title: 'The Matrix'})<-[r:ACTED_IN|DIRECTED]-(person)
RETURN type(r), person.name
I am trying to figure out how to limit a shortest path query in cypher so that it only connects "Person" nodes containing a specific property.
Here is my query:
MATCH p = shortestPath( (from:Person {id: 1})-[*]-(to:Person {id: 2})) RETURN p
I would like to limit it so that when it connects from one Person node to another Person node, the Person node has to contain a property called "job" and a value of "engineer."
Could you help me construct the query? Thanks!
Your requirements are not very clear, but if you simply want one of the people to have an id of 1 and the other person to be an engineer, you would use this:
MATCH p = shortestPath( (from:Person {id: 1})-[*]-(to:Person {job: "engineer"}))
RETURN p;
This kind query should be much faster if you also created indexes for the id and job properties of Person.
I am trying to query a graph to return all of the paths with a set of specified relationships.
The graph I have contains the following nodes: Person
The relationships which connect two people are: knows, married
So an example of some data are:
c:Person-[:knows]->b:Person
a:Person-[:married]->c:Person
d:Person-[knows]-> a:Person
In my query I would like to be able to find all paths that contain both relationships 'knows' and 'married'; however, I don't care about the ordering of such relationships in the paths. For example, my query should return the following paths:
1) a:Person-[:married]->c:Person-[:knows]->b:Person
2) d:Person-[:knows]->a:Person-[:married]->c:Person
I tried the following query
MATCH p=(a)-[:KNOWS|MARRIED*1..3]-(b)
RETURN p
However, it returned the paths only having knows relationship or the paths only having married relationship, but not both.
Is there any way of finding the paths I want? Many thanks!
You can try this
MATCH p=(a)-[rel*]-(b)
WHERE type(rel)='KNOWS' OR type(rel)='MARRIED'
RETURN p
It will provide you all paths
MATCH p = (a:Person)-[rels:KNOWS|MARRIED*]->(b:Person)
WITH p, EXTRACT(r IN rels | TYPE(r)) AS types
WHERE 'KNOWS' IN types AND 'MARRIED' IN types
RETURN p
I figured out how to write this query when I am looking for 2 relationships, but not sure how to add more relationships to the query.
Assume you have a book club database with 'reader' and 'book' as nodes. The 'book' nodes have a 'genre' attribute (to define that the book is a Fiction, Non-Fiction, Biography, Reference, etc.) There is a Relationship "HasRead" between 'reader' nodes and 'book' nodes where someone has read a particular book.
If I want to find readers that have read both Fiction AND Non-Fiction books, I could execute this Cypher query:
Start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction')
Match b1-[:HadRead]-r-[:HasRead]-b2
Return r.ReaderName
The key to the above query is the Match clause that has the two book aliases feeding into the r alias for the 'reader' nodes.
Question: How would I write the query to find users that have read Fiction AND Non-Fiction AND Reference books? I'm stuck with how you would write the Match clause when you have more than 2 things you are looking for.
You can have multiple line specified in a single MATCH clause, separated by commas. For example, the following two MATCH clauses are semantically equivalent (and will be evaluated identically by the engine):
//these mean the same thing!
match a--b--c
match a--b, b--c
You can have any number of these matches. So, plugging that into your query, you get this:
start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction'),
b3=node:MyBookIndex('Genre:Reference')
match b1-[:HasRead]-r,
b2-[:HasRead]-r,
b3-[:HasRead]-r
return r.ReaderName
You can user cypher 'with' clause -
start b1=node:MyBookIndex('Genre:Fiction'),
b2=node:MyBookIndex('Genre:Non-Fiction'),
b3=node:MyBookIndex('Genre:Reference')
match b1-[:HasRead]-r-[:HasRead]-b2
with b3, r
match b3-[:HasRead]-r
return r.ReaderName