Make relationship based on other relationships in neo4j? - neo4j

suppose i have following relationships stored in neo4j.
A->B,A->D,C->B,C->E
Here A, C are of same label nodes and B, E also are of same label nodes.
What is the cypher query to count how many nodes A and C have in common?
Based on that I want to make to a relationship between A and C. I would like to add a relationship rank between them and give it some value say 0.5 because 1 node common. What would that query look like?

To return the number of common nodes between A and C match a pattern that has A at one and C a the other with an intermediary node. Then count the occurrences of the intermediary node.
match (:TypeOne {name: 'A'})--(common)--(:TypeOne {name: 'C'})
return count(common)
If you want to create a relationship directly between A and C as a result of the match then use merge or create with the A and C nodes. And use set to add a value to the newly created relationship.
Something like this should satisfy your requirements.
match (a:TypeOne {name: 'A'})--(common)--(c:TypeOne {name: 'C'})
with a, c, count(common) as in_common
merge (a)-[rel:COMMON_WITH]->(c)
set rel.value = in_common * 0.5
return *

Related

Creating relation with the values of another node instead of using that node

How to store values in a relation of neo4j in Cypher Query Language?
Example: I have 3 nodes A,B,C. 'A' should relate with c using the values/properties of 'B'. Without using Node B separately we should use its values in the relation of A->C
Something like this will create a new FOO relationship with the properties of the B node. I made up a data model, since you did not provide yours.
MATCH (a:A {name: 'a'}), (b:B {name: 'b'}), (c:C {name: 'c'})
CREATE (a)-[rel:FOO]->(c)
SET rel = b;
RETURN a, b, c, rel;
If you wanted to also delete the b node, you can add a DELETE b clause right before the RETURN (and remove b from the RETURN clause).

Neo4j duplicate relationship

I have duplicate relationships between nodes e.g:
(Author)-[:CONNECTED_TO {weight: 1}]->(Coauthor)
(Author)-[:CONNECTED_TO {weight: 1}]->(Coauthor)
(Author)-[:CONNECTED_TO {weight: 1}]->(Coauthor)
and I want to merge these relations into one relation of the form: A->{weight: 3} B for my whole graph.
I tried something like the following; (I'm reading the data from a csv file)
MATCH (a:Author {authorid: csvLine.author_id}),(b:Coauthor { coauthorid: csvLine.coauthor_id})
CREATE UNIQUE (a)-[r:CONNECTED_TO]-(b)
SET r.weight = coalesce(r.weight, 0) + 1
But when I start this query, ıt creates duplicate coauthor nodes. The weight will update. It seems like this:
(Author)-[r:CONNECTED_TO]->(Coauthor)
( It creates 3 same coauthor nodes for the author)
If you need to fix it after the fact, you could aggregate all of the relationships and the weight between each set of applicable nodes. Then update the first relationship with the new aggregated number. Then with the collection of relationships delete the second through the last. Perform the update only where there is more than one relationship. Something like this...
MATCH (a:Author {name: 'A'})-[r:CONNECTED_TO]->(b:CoAuthor {name: 'B'})
// aggregate the relationships and limit it to those with more than 1
WITH a, b, collect(r) AS rels, sum(r.weight) AS new_weight
WHERE size(rels) > 1
// update the first relationship with the new total weight
SET (rels[0]).weight = new_weight
// bring the aggregated data forward
WITH a, b, rels, new_weight
// delete the relationships 1..n
UNWIND range(1,size(rels)-1) AS idx
DELETE rels[idx]
If you are doing it for the whole graph and the graph is expansive you may want to perm the update it in batches using limit or some other control mechanism.
MATCH (a:Author)-[r:CONNECTED_TO]->(b:CoAuthor)
WITH a, b, collect(r) AS rels, sum(r.weight) AS new_weight
LIMIT 100
WHERE size(rels) > 1
SET (rels[0]).weight = new_weight
WITH a, b, rels, new_weight
UNWIND range(1,size(rels)-1) AS idx
DELETE rels[idx]
If you want to eliminate the problem when loading...
MATCH (a:Author {authorid: csvLine.author_id}),(b:Coauthor { coauthorid: csvLine.coauthor_id})
MERGE (a)-[r:CONNECTED_TO]->(b)
ON CREATE SET r.weight = 1
ON MATCH SET r.weight = coalesce(r.weight, 0) + 1
Side Note: not really knowing your data model, I would consider modelling CoAuthor as Author as they are likely authors in their own right. It is probably only in the context of a particular project they would be considered a coauthor.

Give a specific pattern from a node during clause

I have the nodes: (a:charlie), (b:economy), and (c:bicycle) . I want to create this pattern:
create (a:charlie)-[x:wants_make]->(b:economy)->[y:by_using]->(c:bicycle)
But it gives me cartesian product. I already thought to skip the creation of the node (b) giving to relation [x:want_make]a property. But node (b) has many other relations in the same context(economic context). What I want to get the pattern above.
Any suggestion?
If your query looks like this:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b)-[:by_using]->(c);
then it is saying both of these things:
Create a wants_make relationship between every charlie node and every economy node.
Create a by_using relationship between every economy node and every bicycle node.
So, if the number of charlie, economy, and bicycle nodes are C, E, and B -- this results in (C * E * B) merges, which is a Cartesian product of a Cartesian product.
Also, your data model seems to be wrong. For example, it seems much more reasonable to have a Person label instead of a charlie label.
A more reasonable query might look something like this:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:wants_make]->(b:Economy)
MERGE (b)-[:by_using]->(c);
This query avoids Cartesian products by being more specific about the first and last nodes in the path, and it also avoids creating nodes and relationships that already exist.
And, going even further, you might want to combine wants_make, Economy, and by_using into a single economizes_by_using relationship:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:economizes_by_using]->(c);
You might need to break up your query a bit:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b), (b)->[:by_using]->(c)

Neo4j- Avoid multiple relationships getting created between the same nodes in cypher?

I want to restrict the relationships getting created , if already created between the existing nodes. Right now its getting created multiple times? Is there any command similar to 'merge' for restricting duplicate relationships?Thanks for any suggestion
User MERGE instead of CREATE while creating relationships.
MERGE (Product)-[:listed_under_category]->(level3)
MERGE (level3)-[:child_of]->(level2)
hey i think the mix up but i need more information. Is that merge, merges the the whole statement. so if he does not find a "match" for the whole statement the database "creates" the whole statement.
So when you type something like merge (a)-[]->(b)-[]->(q)
if you already have (a)-[]->(b)
he will recreate (a)-[]->(b)-[]->(q) and you have in the db (a)-[]->(b)-[]->(q) , (a)-[]->(b)
but if you type merge (a)-[]->(b) merge(b)-[]->(q)
and you know that you have the nodes. Neo4j merge will create the relations if there are no relations. and match if there are relations
If I understand your question, CREATE UNIQUE may be the solution you are looking for. For example:
create (a:Ex {name: 'A'})-[r:inside]->(b:Ex {name: 'B'}) return a, b, r
sets up your original (a)-[]->(b) relationship. Then to extend the relationship in the manner you proposed ....
match (a)-[:inside]->(b) where a.name = 'A' and b.name = 'B'
create unique (a)-[:inside]->(b)-[:inside]->(q:Ex {name: 'Q'})
return a, b, q
CREATE UNIQUE also works if the relationships are different:
match (a)-[:inside]->(b) where a.name = 'A' and b.name = 'B'
create unique (a)-[:inside]->(b)-[:under]->(q:Ex {name: 'Q'})
return a, b, q
The MERGE clause also avoids duplicating relationships.

Find a node which is connected to all start nodes

i'm trying to model a cypher query for the following scenario:
I have 3 start nodes A, B, C and i'm trying to find n nodes D which are related to all three start nodes. At the end I will reduce the weight property of the relationships and choose the node with the highest weight.
Thanks in advance for helping me out!
How about something like this?
MATCH (a:Node {name: "A"})-[r1]-(d)
, (b:Node {name: "B"})-[r2]-(d)
, (c:Node {name: "C"})-[r3]-(d)
RETURN d.name
, r1.weight + r2.weight + r3.weight AS Weight
ORDER BY Weight DESC
LIMIT 1
Only return d's that match all of a, b, c; add up the weights of those relative relationships; sort descending by weight; and pick off the first.

Resources