Cypher query to exclude results based upon relationship - neo4j

I'm trying to out how to exclude nodes from a query. My graph consists of users, skills, skill scoring, questions and endo
user has skills
skill has scorings in relation with questions ((skill)-->(scorings)-->(questions))
endo is a relationship for users and skills scorings ((user)-->(endos)-->(scorings))
I would like to find all question for user, but exclude those questions who user has already endo relationship
I thought I could do :
MATCH (u:`User`),
u<-[rel1:`USERS`]-(s:`Skill`),
s-[rel2:`SKILLS`]->(k:`SkillScoring`),
k-[rel3:`ANSWER`]->(q:`Question`),
u<-[rel4:`ENDO`]-(e:`Endo`)
WHERE NOT((e)-->(u)) AND (u.id='1')
RETURN u, e, k, q
UPDATE:
endo nodes are connected like this.
blue is user node
purple is journaled endorsement node (created at)
green is skill scoring node
In fact the relationship "ENDORSEMENT" has a node (journalised) which connects the skill scoring nodes
UPDATE:
When I execute this query, it returns me the question in connection with the user
MATCH (u:User),
u<-[rel1:USERS]-(s:SoftSkill),
s-[rel2:SOFT_SKILLS]->(k:SkillScoring),
k-[rel3:ANSWER]->(q:Question),
u<-[:ENDO]-()<-[:ENDO]->(k)
WHERE u.id='1'
RETURN q, u
by cons when I execute this query to exclude the question, the query returns me questions but also question that I don't want
MATCH (u:User),
u<-[rel1:USERS]-(s:SoftSkill),
s-[rel2:SOFT_SKILLS]->(k:SkillScoring),
k-[rel3:ANSWER]->(q:Question)
WHERE u.id='1' AND NOT u<-[:ENDO]-()<-[:ENDO]->(k)
RETURN q, u
What's wrong? Any suggestions?
Thanks

Your query first says that u has :ENDO relationship from e, then that e has no relationship to u, but that can't be.
How are endo nodes connected to scorings/questions in those cases you want to exclude? Could you try something like
MATCH (u:User)-[rel1:USERS]->(s:Skill)-[rel2:SKILLS]->(k:SkillScoring)-[rel3:ANSWER]->(q:Question)
WHERE u.id = '1' AND NOT u<-[:ENDO]-()-[:??]->k
and fill in the relationship type at ?? for how the endo node (anonymous node () above) is connected?
If you want you can create an example graph at http://console.neo4j.org, that would help make your intention clear.
Also, it's fine to use backticks around labels and relationships, but you don't need to unless they contain whitespace or some other uncommon characters.

Related

Cypher pattern for getting self related nodes

Given that I'm very new to Neo4j. I have a schema which looks like the below image:
Here Has nodes are different for example Passport, Merchant, Driving License, etc. and also these nodes are describing the customer node (looking for future scope of filtering customers based on these nodes).
SIMILAR is a self-relation meaning there exists a customer with ID:1 is related to another customer with ID:2 with a score of 2800.
I have the following questions:
Is this a good schema given the condition of the future scope I mentioned above, or getting all the properties in a single customer node is viable? (Different nodes may have array of items as well, for example: ()-[:HAS]->(Phone) having {active: "+91-1231241", historic_phone_numbers: ["+91-121213", "+91-1231421"]})
I want to get the customer along with describing nodes in relation to other customers. For that, I tried the below query (w/o number of relation more than 1):
// With number_of_relation > 1
MATCH (searched:Customer)-[r:SIMILAR]->(matched:Customer)
WHERE r.score > 2700
WITH searched, COLLECT(matched.customer_id) AS MatchedList, count(r) as cnt
WHERE cnt > 1
UNWIND MatchedList AS matchedCustomer
MATCH (person:Customer {customer_id: matchedCustomer})-[:HAS|:LIVES_IN|:IS_EMPLOYED_BY]->(related)
RETURN searched, person, related
Result what I got is below, notice one customer node not having its describing nodes:
// without number_of_relation > 1
// second attempt - for a sample customer_id
MATCH (matched)<-[r:SIMILAR]-(c)-[:HAS|:LIVES_IN|:IS_EMPLOYED_BY]->(b)
WHERE size(keys(b)) > 0
AND c.customer_id = "1b093559-a39b-4f95-889b-a215cac698dc"
AND r.score > 2700
RETURN b AS props, c AS src_cust, r AS relation, matched
Result I got are below, notice related nodes are not having their describing nodes:
If I had two describing nodes with some property (some may have a list) upon which I wanted to query and build the expected graph specified in point 2 above, how can I do that?
I want the database to find a similar customer given the describing nodes. Example: A customer {name: "Dave"} has phone {active_number: "+91-12345"} is similar to customer {name: "Mike"} has phone {active_number: "+91-12345"}. How can get started with this?
If something is unclear, please ask. I can explain with examples.
[EDITED]
Yes, the schema seems fine, except that you should not use the same HAS relationship type between different node label pairs.
The main problem with your first query is that its top MATCH clause uses a directional relationship pattern, ()-->(), which does not allow all Customer nodes to have a chance to be the searched node (because some nodes may only be at the tail end of SIMILAR relationships). This tweaked query should work better:
MATCH (searched:Customer)-[r:SIMILAR]-(matched:Customer)
WHERE r.score > 2700
WITH searched, COLLECT(matched) AS matchedList
WHERE SIZE(matchedList) > 1
UNWIND matchedList AS person
MATCH (person)-[:HAS|LIVES_IN|IS_EMPLOYED_BY]->(pDesc)
WITH searched, person, COLLECT(pDesc) AS personDescribers
MATCH (searched)-[:HAS|LIVES_IN|IS_EMPLOYED_BY]->(sDesc)
RETURN searched, person, personDescribers, COLLECT(sDesc) AS searchedDescribers
It's not clear what you want are trying to do.
To get all Customers who have the same phone number:
MATCH (c:Customer)-[:HAS_PHONE]-(p:Phone)
WHERE p.activeNumber = '+91-12345'
WITH p.activeNumber AS phoneNumber, COLLECT(c) AS customers
WHERE SIZE(customers) > 1
RETURN phoneNumber, customers

NEO4J nodes under a node filter by relationship

How can we add a relationship to the query.
Say A-[C01]-B-[C02]-D and A-[C01]-B-[C03]-E
C01 C02 C03 are relationship codes I want to get output
B E
because I want only nodes that can be reached unbroken by C01 or C03
How can I get this result in Cypher?
You may want to clarify, what you're asking for seems like a very simple case of matching. You may want to provide some more info, such as node labels and how you're matching to your start nodes, since without these we have to make things up for example code.
MATCH (a:Thing)
WHERE a.ID = 123
WITH a
MATCH (a)-[:C01|C03*]->(b:Thing)
RETURN b
The key here is specifying multiple relationship types to traverse, using * for multiplicity, so it will match on all nodes that can be reached by any chain of those relationships.

Grouping nodes by their relationship

I've built a simple graph of one node type and two relationship types: IS and ISNOT. IS relationships means that the node pair belongs to the same group, and obviouslly ISNOT represents the not belonging rel.
When I have to get the groups of related nodes I run the following query:
"MATCH (a:Item)-[r1:IS*1..20]-(b:Item) RETURN a,b"
So this returns a lot of a is b results and I added some code to group them after.
What I'd like is to group them modifying the query above, but given my rookie level I haven't yet figured it out. What I'd like is to get one row per group like:
(node1, node3, node5)
(node2,node4,node6)
(node7,node8)
I assume what you call groups are nodes present in a path where all these nodes are connected with a :IS relationship.
I think this query is what you want :
MATCH p=(a:Item)-[r1:IS*1..20]-(b:Item)
RETURN nodes(p) as nodes
Where p is a path describing your pattern, then you return all the nodes present in the path in a collection.
Note that, a simple graph (http://console.neo4j.org/r/ukblc0) :
(item1)-[:IS]-(item2)-[:IS]-(item3)
will return already 6 paths, because you use undericted relationships in the pattern, so there are two possible paths between item1 and item2 for eg.

Neo4j: Cypher Query With Variable Length and Condition on Node Labels

What I'm looking for
With variable length relationships (see here in the neo4j manual), it is possible to have a variable number of relationships with a certain label between two nodes.
# Cypher
match (g1:Group)-[:sub_group*]->(g2:Group) return g1, g2
I'm looking for the same thing with nodes, i.e. a way to query for two nodes with a variable number of nodes in between, but with a label condition on the nodes rather than the relationships:
# Looking for something like this in Cypher:
match (g1:Group)-->(:Group*)-->(g2:Group) return g1, g2
Example
I would use this mechanism, for example, to find all (direct or indirect) members of a group within a group structure.
# Looking for somthing like this in Cypher:
match (group:Group)-->(:Group*)-->(member:User) return member
Take, for example, this structure:
group1:Group
|-------> group2:Group -------> user1:User
|-------> group3:Group
|--------> page1:Page -----> group4:Group -----> user2:User
In this example, user1 is a member of group1 and group2, but user2 is only member of group4, not member of the other groups, because a non-Group labeled node is in between.
Abstraction
A more abstract pattern would be a kind of repeat operator |...|* in Cypher:
# Looking for repeat operator in Cypher:
match (g1:Group)|-[:is_subgroup_of]->(:Group)|*-[:is_member_of]->(member:User)
return member
Does anyone know of such a repeat operator? Thanks!
Possible Solution
One solution I've found, is to use a condition on the nodes using where, but I hope, there is a better (and shorter) soluation out there!
# Cypher
match path = (member:User)<-[*]-(g:Group{id:1})
where all(node in tail(nodes(path)) where ('Group' in labels(node)))
return member
Explanation
In the above query, all(node in tail(nodes(path)) where ('Group' in labels(node))) is one single where condition, which consists of the following key parts:
all: ALL(x in coll where pred): TRUE if pred is TRUE for all values in
coll
nodes(path): NODES(path): Returns the nodes in path
tail(): TAIL(coll): coll except first element–––I'm using this, because the first node is a User, not a Group.
Reference
See Cypher Cheat Sheet.
How about this:
MATCH (:Group {id:1})<-[:IS_SUBGROUP_OF|:IS_MEMBER_OF*]-(u:User)
RETURN DISTINCT u
This will:
find all subtrees of the group with ID 1
only traverse the relationships IS_GROUP_OF and IS_MEMBER_OF in incoming direction (meaning sub-groups or users that belong to group with ID or one of its sub-groups)
only return nodes which have a IS_MEMBER_OF relationship to a group in the subtree
and discard duplicate results (users who belong to more than one of the groups in the tree would otherwise appear multiple times)
I know this relies on relationships types rather than node labels, but IMHO this is a more graphy approach.
Let me know if this would work or not.

neo4j cypher multi relationship between nodes

for example:
a-[r]->b, there are multi r between the two nodes, each r.userId is unique.
(eg: a-[r:R {userId:"user1"}]->b, (a-[r:R{userId:"user2"}]->b,
and the same for a-[r]->c
And the situation is a-[r]->b has a relationship: r.userId = amdin, but a-[r]->c doesn't have this relationship.
how can i only return c.
i try to create cypher:
"MATCH (a:SomeLabel)-[r:SomeR]->(any:SomeLabel) "
"WHERE id(a)=0 AND r.userId <> \"admin\" "
"RETURN any";
but this will also return b ,because a->b has other relationship: r.userId=xxxx
how can i write the cypher to return nodes not inculde user.Id="admin"......
If you not clearly understand what i say,please let me know....i need your help for this case..thanks
I draw a picture below, multi relationship named sr but with different properties (userId is unique),
and i want to find all nodes that related to node A, but not contains sr {userId:admin}, i add a red underline there. So as in the picture, node B has the relationship sr {userId:admin}, so i only want to return node C, no node B
For showing simple representations of graph problems, graphgists are really helpful as people can explore the data.
I've created one based on your description: http://gist.neo4j.org/?94ef056e41153b116e4f
To your problem, you can collect all usernames involved in the relationships per pair of nodes and filter based on those:
MATCH (a { name:'A' })-[r:sr]->b
WITH a,b, collect(r.name) AS usernames
WHERE NOT 'admin' IN usernames
RETURN a, b
Your question is pretty unclear. My interpretation is that you want to find nodes c that are not connected to a node a with a relationship of type R.
You basically want to do a negative match aka search for a pattern that does not exist. Negative patterns can be retrieved using a where not:
MATCH (a:SomeLabel), (c:SomeLabel)
WHERE ID(a)=0 AND NOT (a)-[:R]->(c)
RETURN c
This returns a list of all SomeLabel nodes not being connected to a.
See http://docs.neo4j.org/chunked/stable/query-where.html#query-where-patterns in the reference manual.

Resources