Neo4j: Relationship direction cypher - neo4j

For the cypher -
match (m)-[r]-(n) where m.name = 'XYZ' return n.name, type(r), m.name
n.name type(r) m.name
XYZ belongs_to Ordering Status
XYZ runs_on_queue inbound
XYZ runs_on_db DBxc
In this case, Ordering Status is a business service that "owns" XYZ & the relation is defined as follows:
CREATE (XYZ)-[:belongs_to]->(Order)
Type(r) only gives the relation but not the direction of the relation. Is this still the optimal way to get the direction - I also noticed a comment on not being available for Cypher
Neo4j Cypher Get Relationship Direction
Thanks.

Not as a function, but you can do this:
MATCH (m)-[r]-(n)
RETURN m.name, TYPE(r), n.name,
CASE WHEN STARTNODE(r) = m THEN 'outgoing' ELSE 'incoming' END AS direction

Related

In Neo4j Graph Data Science, can I create graph by cypher with custom type name?

I create graph from tag nodes, where the relationships in the created graph are not relationships from original database, but some paths which represent tags are tagged on the same videos.
CALL gds.graph.create.cypher(
"tags",
"MATCH (t:Tag) RETURN id(t) AS id, labels(t) AS labels",
"
MATCH (t:Tag)
WITH collect(t) as nodes
UNWIND nodes as n
UNWIND nodes as m
WITH n, m WHERE id(n) < id(m)
MATCH (n)-[:TAGGED_ON]->(v:Video)<-[:TAGGED_ON]-(m)
RETURN id(n) AS source, id(m) AS target, count(v) AS weight
"
)
And then I do this to export to new database:
CALL gds.graph.export("tags", {dbName: "tags"})
CREATE DATABASE tags
However, the relationships in the new tags database will all named __ALL__.
In the relationshipQuery part of graph creation, docs of neo4j say
Optionally, a type column can be specified to represent relationship type.
However, all examples I found about this are type(r) AS type, where this cannot work on my case. I still can rename the relationships in the new tags database by this:
MATCH ()-[r]->()
WITH collect(r) AS rels
CALL apoc.refactor.rename.type("__ALL__", "TAGGED_ON_THE_SAME_VIDEO", rels)
YIELD committedOperations
RETURN committedOperations
But can I use the custom relationship name at the gds graph creation time? I tried something like "TAGGED_ON_THE_SAME_VIDEO" AS type but this cannot work.
I tried something like "TAGGED_ON_THE_SAME_VIDEO" AS type but this
cannot work.
Why this cannot work? I've tested it and it works just fine. Try the following query to project the graph:
CALL gds.graph.create.cypher(
"tags",
"MATCH (t:Tag) RETURN id(t) AS id, labels(t) AS labels",
"
MATCH (t:Tag)
WITH collect(t) as nodes
UNWIND nodes as n
UNWIND nodes as m
WITH n, m WHERE id(n) < id(m)
MATCH (n)-[:TAGGED_ON]->(v:Video)<-[:TAGGED_ON]-(m)
RETURN id(n) AS source, id(m) AS target, count(v) AS weight, 'TAGGED_ON_THE_SAME_VIDEO' as type
"
)

neo4j cypher query for filtering paths

I am using neo4j to store data with nodes having 1 of 2 labels :Person and Organization. All nodes have a property :name
All the relationships are labeled LinkedTo and have a property :score. There might be multiple relations between one pair of Person and Organization nodes
I have used path queries to search paths between these nodes like:
MATCH (n:Person) WHERE n.name =~ "(?i)person1"
MATCH (m:Organization) WHERE m.name =~ "(?i)organization1"
WITH m,n
MATCH p = (m)-[*1..4]-(n)
RETURN p ORDER BY length(p) LIMIT 10
This returns all paths (upto 10)
Now I want to find specific paths, with all relations involved having a score=1. Not sure how to achieve this, I started with MATCH p = (m)-[f*1..4]-(n) but it got a deprecation warning. So after some googling and trials and errors, I came up with this:
MATCH (n:Person) WHERE n.name =~ "(?i)person1"
MATCH (m:Organization) WHERE m.name =~ "(?i)organization1"
WITH m,n
MATCH p = (m)-[*1..4]-(n)
WITH filter(x IN relationships(p) WHERE x.score=1) AS f
ORDER BY length(p)
UNWIND f AS ff
MATCH (a)-[ff]-(b)
RETURN a,b,ff LIMIT 10
But this is not correct and not clean and is giving me relationships and nodes that are not needed in the path.
This might be a basic cypher query but I am just a beginner and need help with this. :)
From what I have understand you are searching this query :
MATCH p = (m:Organization)-[rels*1..4]-(n:Person)
WHERE
n.name =~ "(?i)person1" AND
m.name =~ "(?i)organization1" AND
all(r IN rels WHERE r.score=1)
RETURN p
ORDER BY length(p)
Due to all(r IN rels WHERE r.score=1), Neo4j will just expand relationships in the path that have an attribute score set to 1.
Also you should note that you are using a regex condition for the node n & m, and this operator can't use indexes !
If your goal is to have a case-insensitive search, I advise you to create a sanitize field (ex _name) to store the name in lower-case, and to replace your regex with a CONTAINS or STARTS WITH

Find nodes (n) with :OWNER relations to nodes (a), (b), and (c) and no other nodes

My Neo4j 3.2 database has nodes (n) which may have :OWNER relations to other nodes. I want to find all the nodes (n) with :OWNER relations specifically to nodes (a), (b), and (c) and specifically not to any other nodes.
I would have thought that this would be fairly easily accomplished with
MATCH (n), (o)
WHERE (
(n)-[:OWNER]->(o) AND o.uuid IN $owner_ids
AND NOT ((n)-[:OWNER]->(o) AND NOT o.uuid IN $owner_ids)
RETURN (n)
But it doesn't work. This query incorrectly returns nodes (n) with :OWNER relations to (a), (b), (c), and (d). I've also tried
MATCH (n), (o)
WHERE (n)-[:OWNER]->(o) AND o.uuid IN $owner_ids
WITH (n),(o)
WHERE NOT ((n)-[:OWNER]->(o) AND NOT o.uuid IN $owner_ids)
RETURN (n)
As well as what feels like a million other permutations to no avail. Any suggestions are greatly appreciated!
UPDATE
The above is a simplified scenario. As requested in a comment, an example closer to reality is:
MATCH (a)<-[:ANSWER]-(:Person {uuid: $person_id}), (o)
WHERE (exists((o)<-[:OWNER]-(:Owner)<-[:OWNER]-(:Form)-[:ANSWER]->(a)) AND o.uuid IN $owner_ids)
AND NOT (exists((o)<-[:OWNER]-(:Owner)<-[:OWNER]-(:Form)-[:ANSWER]->(a)) AND NOT o.uuid IN $owner_ids)
RETURN (a)
ANSWER
The full answer is
MATCH (o)<-[:OWNER]-(:Owner)<-[:OWNER]-(:Form)-[:ANSWER]->(a)<-[:ANSWER]-(:Person {uuid: $person_id})
WHERE o.uuid IN $owner_ids
WITH (a), count(o) as cnt
WHERE cnt = size(()<-[:OWNER]-(:Owner)<-[:OWNER]-(:Form)-[:ANSWER]->(a))
RETURN (a)
Assuming you add labels to your graph (let's use :Node for now, though it's not clear from your description if all nodes should be the same or if some should use different labels), and that you have a unique constraint on :Node(uuid) for quick lookup, this should work:
MATCH (n:Node)-[:OWNER]->(o:Node)
WHERE o.uuid IN $owner_ids
WITH n, count(o) as cnt
WHERE cnt = size((n)-[:OWNER]->())
RETURN n
Your query had a cartesian product between n and o (the cross product of all the nodes of your graph with each other), which won't perform well. You need to specify the relationship in the MATCH, not the WHERE.
As for the rest of the query, we're getting, for each n, the count of o nodes (those with the ids in question), and ensuring that the number of :OWNER relationships for each n is equal to that count. If it's greater, then there are :OWNER relationships to other nodes, so those are filtered out.
The size() function we're using, since we aren't specifying anything for the end node, is efficient at getting relationship counts.
If your requirement was to have all three :OWNER relationships present (not just any subset of them), then I'd use this query:
WITH ['a', 'b', 'c'] AS ids
MATCH (n:Node)-[:OWNER]->(o:Node)
WITH n,
COUNT(CASE WHEN o.uuid IN ids THEN 1 END) AS matches_found,
size(ids) AS matches_desired,
count(o) AS total_relationships
WHERE matches_found = matches_desired
AND matches_found = total_relationships
RETURN n
ORDER BY n.uuid

cypher NOT IN query with Optional Match

NOT RELEVANT - SKIP TO Important Edit.
I have the following query:
MATCH (n)
WHERE (n:person) AND n.id in ['af97ab48544b'] // id is our system identifier
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m)
WHERE (m:person OR m:dog OR m:cat)
RETURN n,r,m
This query returns all the persons, dogs and cats that have a relationship with a specific person. I would like to turn it over to receive all the nodes & relationships that NOT includes in this query results.
If it was SQL it would be
select * from graph where id NOT IN (my_query)
I think that the OPTIONAL MATCH is the problematic part. I How can I do it?
Any advice?
Thanks.
-- Important Edit --
Hey guys, sorry for changing my question but my requirements has been changed. I need to get the entire graph (all nodes and relationships) connected and disconnected except specific nodes by ids. The following query is working but only for single id, in case of more ids it isn't working.
MATCH (n) WHERE (n:person)
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m) WHERE (m:person OR m:dog OR m:cat)
WITH n,r,m
MATCH (excludeNode) WHERE excludeNode.id IN ['af97ab48544b']
WITH n,r,m,excludeNode WHERE NOT n.id = excludeNode.id AND (NOT m.id = excludeNode.id OR m is null)
RETURN n,m,r
Alternatively I tried simpler query:
MATCH (n) WHERE (n:person) AND NOT n.id IN ['af97ab48544b'] return n
But this one does not returns the relationships (remember I need disconnected nodes also).
How can I get the entire graph exclude specific nodes? That includes nodes and relationships, connected nodes and disconnected as well.
try this:
match (n) where not n.id = 'id to remove' optional match (n)-[r]-(m)
where not n.id in ['id to remove'] and not m.id in ['id to remove']
return n,r,m
You've gotta switch the 'perspective' of your query... start by looping over every node, then prune the ones that connect to your person.
MATCH (bad:person) WHERE bad.id IN ['af97ab48544b']
WITH COLLECT(bad) AS bads
MATCH path = (n:person) - [r:friend|:connected|:owner] -> (m)
WHERE n._id = '' AND (m:person OR m:cat OR m:dog) AND NOT ANY(bad IN bads WHERE bad IN NODES(path))
RETURN path
That said, this is a problem much more suited to SQL than to a graph. Any time you have to loop over every node with a label, you're in relational territory, the graph will be less efficient.

increase weight between relationship on finding match - cypher

I have a query in cypher , I create a relationship between :
MATCH match (u:user {id : "10662"})-[r:has_profile]->(p:profile)
, (u2:user)-[r2:has_profile]->(p2:profile)
WHERE
p.user_id <> p2.user_id
AND abs(u2.date_birth - u.date_birth) >= 94608000000
merge u-[r:matches_with { weight: rand() }]->u2
RETURN collect(u2.id) as id;
In this particular situation, I am trying to create a relationship between u and u2 only on a match, when a previous relationship between u and u2 is found. It will just increment the weight in the relationship.
And I am trying to return the matches based on weight.
Can someone please suggest an approach for incrementing the weight on finding a match, with this query. Thank you.
Here is a query that looks for all matches_with relationships starting from a specific user, increments the weight of the relationship, and returns the results sorted by weight.
MATCH (u:user { id : "10662" })-[r:matches_with]->(u2)
SET r.weight = r.weight + 1
RETURN u, r, u2
ORDER BY r.weight;
I found a mechanism , by which you can alter the weight..
I added the merge clause before the return statement:
MATCH (u:user { id : "10662" })-[r:matches_with]->(u2)
merge (u)-[m:matches_with]->(u2)
SET m.weight=
CASE WHEN NOT (HAS (m.weight))
THEN toFloat(0.125)
ELSE LAST(m.weight)/2
END
RETURN u.id, m, u2.id
ORDER BY m.weight;
Thank you #cybersam.

Resources