Match where AND on the same relationship - neo4j

Given the following schema, the node in the middle is called Business and the sattelite ones are called BusinessType. The relationship is one to many as one Business has many BusinessType nodes. What I'm trying to do is get all Business nodes where the BusinessType equals a set of codes. The problem is that I'm getting back Business nodes which only have one part of the criteria. For example:
MATCH (business:Business {uuid: 'f872e7d4-1105-11ea-9461-4cedfb791f65'})-[r:BUSINESS_HAS_TYPE]->(bt:BusinessType)
return *
returns the result bellow.
MATCH (business:Business)
MATCH (business)-[r:BUSINESS_HAS_TYPE]->(bt:BusinessType)
WHERE bt.slug = 'rail-doors' AND bt.slug = 'pergolas'
RETURN business
What i'm after in the query above is to get all Business nodes where their BusinessType matches 'rail-doors' AND 'pergolas'. This returns null for some reason though clearly there's a potential match as shown from the previous query.
I also tried using ['rail-doors','pergolas'] but that's clearly wrong cause it returns any Business that has either one of the two.
Any input is welcome

A single node cannot have 2 different values for the same property at the same time. You want a query that looks for each desired value across all related :BusinessType nodes.
Try this query, instead:
MATCH (business:Business)-[:BUSINESS_HAS_TYPE]->(bt:BusinessType)
WITH business, COLLECT(bt.slug) AS slugs
WHERE ALL(x IN ['rail-doors', 'pergolas'] WHERE x IN slugs)
RETURN business

Related

depth in nodes of the same label in custom query

I have in my database Company with:
#Relationship(type = "COLLECTS_FROM")
private Set<Company> subCompanies
What I'm trying to achieve is to create two queries:
get the company with limited depth 2 for nodes of the same type (so company which collects from different companies, which collects from different companies)
get the company with full depth for nodes of the same type (similar to previous, but just all the way down without any limitations.
I had a few attempts to solve this and here are those:
first approach was to use findById(ID,2) and findById(ID,999999), but this query has a huge performance drop and I ran into StackOverflow exception, that's why I'm trying to create my own methods similar to what I found here.
second approach was to use custom query (which were actually working in case of different node types):
#Query("Match (company:Company {name:$name})-[relation:COLLECTS_FROM*0..2]->(secondCompany:Company) RETURN company,relation,secondCompany")
Company customerWithCustomDepth(String name);
Unfortunately while using the same node types I ran into the IncorrectResultSizeDataAccessException: Incorrect result size: expected at most 1 and when I changed return type to the list 3 records were returned (actually the first one of them is what I would like to get, but it seems like specifying secondCompany in the return statement populates nodes that I don't want to have in my return type.
on the third approach I did similar to the second, but it also returned 3 rows (the first one is what I wanted).
#Query("Match p = (company:Company {name:$name})-[relation:COLLECTS_FROM*0..2]->(secondCompany:Company) RETURN p")
and fourth one once again 3 companies, instead of a single one (the first one is what I wanted)
#Query("MATCH (company:Company {name:$name} ) WITH company MATCH p=(company)-[COLLECTS_FROM*0..2]->(m) RETURN p")
And that's basically it. I feel like I'm close, but a little detail is missing. Maybe instead of trying to fix the query, I should somehow limit the result to get the first row from the Query? Any help will be really appreciated.
update after receiving #cybersam answer:
The general purpose of this query is to create a tree graph, that's why I would like to get a company with their sub-companies inside (and companies in the sub-companies), not only the parent one.
I could use second, or third approach and just grab the first element on the server side:
result.stream()
.findFirst()
.orElseThrow();
but I believe it should be able to do it on the service side.
To return the Company with the specified name if it is connected by a path of 1 to 2 COLLECTS_FROM relationships to descendant Company nodes:
#Query("MATCH p=(c:Company {name:$name})-[:COLLECTS_FROM*..2]->(:Company) WHERE ALL(n IN NODES(p) WHERE 'Company' IN LABELS(n)) RETURN c")
Company customerWithCustomDepth(String name);
I assume that you do not want to get a result if there are no such descendants, which is why the variable-length relationship pattern does not use *0..2.
Note: If you want to test for paths of exactly length 2, change ..2 to just 2.
The corresponding query for paths of any length can be obtained by just changing ..2 to ... However, variable-length relationship pattern with no upper bound are not recommended, since they can take "forever" to run or run out of memory.

Why is a single Neo4j relationship shown twice in Cypher query results?

Let's consider a trivial graph with a directed relationship:
CREATE
(`0` :Car {value:"Ford"})
, (`1` :Car {value:"Subaru"})
, (`0`)-[:`DOCUMENT` {value:"DOC-1"}]->(`1`);
The following query MATCH (n1:Car)-[r:DOCUMENT]-(n2:Car) RETURN * returns:
╒══════════════════╤══════════════════╤═════════════════╕
│"n1" │"n2" │"r" │
╞══════════════════╪══════════════════╪═════════════════╡
│{"value":"Subaru"}│{"value":"Ford"} │{"value":"DOC-1"}│
├──────────────────┼──────────────────┼─────────────────┤
│{"value":"Ford"} │{"value":"Subaru"}│{"value":"DOC-1"}│
└──────────────────┴──────────────────┴─────────────────┘
The graph defines only a Ford->Subaru relationship, why are two relationships?
How to interpret the reversed one (line 1; not specified in the CREATE) statement?
Note: This is a follow-up to Convert multiple relationships between 2 nodes to a single one with weight asked by me earlier. I solved my problem, but I'm not convinced my answer is the best solution.
Your MATCH statement here doesn't specify the direction, therefore there are two possible paths that will match the pattern (remember that the ordering of nodes in the path is important and distinguishes paths from each other), thus your two answers.
If you specify the direction of the relationship instead you'll find there is only one possible path that matches:
MATCH (n1:Car)-[r:DOCUMENT]->(n2:Car)
RETURN *
As for the question of why we get two paths back when we omit the direction, remember that paths are order-sensitive: two paths that have the same elements but with a different order of the elements are different paths.
To help put this into perspective, consider the following two queries:
# Query 1
MATCH (n1:Car)-[r:DOCUMENT]-(n2:Car)
WHERE n1.value = 'Ford'
RETURN *
╒══════════════════╤══════════════════╤═════════════════╕
│"n1" │"n2" │"r" │
╞══════════════════╪══════════════════╪═════════════════╡
│{"value":"Ford"} │{"value":"Subaru"}│{"value":"DOC-1"}│
└──────────────────┴──────────────────┴─────────────────┘
# Query 2
MATCH (n1:Car)-[r:DOCUMENT]-(n2:Car)
WHERE n1.value = 'Subaru'
RETURN *
╒══════════════════╤══════════════════╤═════════════════╕
│"n1" │"n2" │"r" │
╞══════════════════╪══════════════════╪═════════════════╡
│{"value":"Subaru"}│{"value":"Ford"} │{"value":"DOC-1"}│
└──────────────────┴──────────────────┴─────────────────┘
Conceptually (and also used by the planner, in absence of indexes), to get to each of the above results you start off with the results of the full match as in your description, then filter to the only one which meets the given criteria.
The results above would not be consistent with the original directionless match query if that original query only returned a single row instead of two.
Additional information from the OP
It will take a while to wrap my head around it, but it does work this way and here's a piece of documentation to confirm it's by design:
When your pattern contains a bound relationship, and that relationship pattern doesn’t specify direction, Cypher will try to match the relationship in both directions.
MATCH (a)-[r]-(b)
WHERE id(r)= 0
RETURN a,b
This returns the two connected nodes, once as the start node, and once as the end node.

Find nodes in Neo4j that are at the end of all paths and not in another

We are in a POC for Neo4j. The use case is a dashboard where we only bring back opportunities for a seller that they are qualified for and have not already taken an action on. Currently there are 3 criteria and we are looking to add two more. The corresponding SQL is 3 pages so we are looking at a better way as when we add the next criteria, 2 more nodes paths in Neo, will be a bear in SQL. When I run the query below I get back a different amount of rows than the SQL. the buys returned must be at the end of all 3 paths and not be in the 4th. I hope you can point out where I went wrong. If this is a good query then I have a data problem.
Here is the query:
//oportunities dashboard
MATCH (s:SellerRep)-[:SELLS]->(subCat:ProductSubCategory)<-[:IS_FOR_SUBCAT]-(b:Buy)
MATCH (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg)-[:HAS_SELLER_TYPE]->(st:SellerType)<-[:IS_FOR_ST]-(b:Buy)
MATCH (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg)-[:IS_IN_SC]->(sc:SellerCommunity)<-[:IS_FOR_SC]-(b:Buy)
WHERE NOT (s:SellerRep)-[:PLACED_BID]->(:Bid)-[:IS_FOR_BUY]->(b:Buy)
AND s.sellerRepId = 217722 and b.currBuyStatus = 'Open'
RETURN b.buyNumber, b.buyDesc, st.sellerType, sc.communtiyName, subCat.subCategoryName+' - '+subCat.desc as sub_cat
If it helps, here is the data model:
POC Data model
Thanks for any help.
A WHERE clause only filters the immediately preceding MATCH clause.
Since you placed your WHERE clause after the third MATCH clause, the first 2 MATCH clauses are not bound to a specific SellerRep or Buy node, and are therefore bringing in more ProductSubCategory and SellerType nodes than you intended.
The following query is probably closer to what you intended:
MATCH (s:SellerRep)-[:SELLS]->(subCat:ProductSubCategory)<-[:IS_FOR_SUBCAT]-(b:Buy)
WHERE s.sellerRepId = 217722 AND b.currBuyStatus = 'Open' AND NOT (s:SellerRep)-[:PLACED_BID]->(:Bid)-[:IS_FOR_BUY]->(b:Buy)
MATCH (s)-[:SELLS_FOR]->(o:SellerOrg)-[:HAS_SELLER_TYPE]->(st:SellerType)<-[:IS_FOR_ST]-(b)
MATCH (o)-[:IS_IN_SC]->(sc:SellerCommunity)<-[:IS_FOR_SC]-(b)
RETURN b.buyNumber, b.buyDesc, st.sellerType, sc.communtiyName, subCat.subCategoryName+' - '+subCat.desc as sub_cat
NOTE: Your second and third MATCH clauses both started with (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg). I simplified the same logic by having my third MATCH clause just start with (o). Hopefully, you actually intended to force both clauses to refer to the same SellerOrg node.

Cypher Query not returning nonexistent relationships

I have a graph database where there are user and interest nodes which are connected by IS_INTERESTED relationship. I want to find interests which are not selected by a user. I wrote this query and it is not working
OPTIONAL MATCH (u:User{userId : 1})-[r:IS_INTERESTED] -(i:Interest)
WHERE r is NULL
Return i.name as interest
According to answers to similar questions on SO (like this one), the above query is supposed to work.However,in this case it returns null. But when running the following query it works as expected:
MATCH (u:User{userId : 1}), (i:Interest)
WHERE NOT (u) -[:IS_INTERESTED] -(i)
return i.name as interest
The reason I don't want to run the above query is because Neo4j gives a warning:
This query builds a cartesian product between disconnected patterns.
If a part of a query contains multiple disconnected patterns, this
will build a cartesian product between all those parts. This may
produce a large amount of data and slow down query processing. While
occasionally intended, it may often be possible to reformulate the
query that avoids the use of this cross product, perhaps by adding a
relationship between the different parts or by using OPTIONAL MATCH
(identifier is: (i))
What am I doing wrong in the first query where I use OPTIONAL MATCH to find nonexistent relationships?
1) MATCH is looking for the pattern as a whole, and if can not find it in its entirety - does not return anything.
2) I think that this query will be effective:
// Take all user interests
MATCH (u:User{userId: 1})-[r:IS_INTERESTED]-(i:Interest)
WITH collect(i) as interests
// Check what interests are not included
MATCH (ni:Interest) WHERE NOT ni IN interests
RETURN ni.name
When your OPTIONAL MATCH query does not find a match, then both r AND i must be NULL. After all, since there is no relationship, there is no way get the nodes that it points to.
A WHERE directly after the OPTIONAL MATCH is pulled into the evaluation.
If you want to post-filter you have to use a WITH in between.
MATCH (u:User{userId : 1})
OPTIONAL MATCH (u)-[r:IS_INTERESTED] -(i:Interest)
WITH r,i
WHERE r is NULL
Return i.name as interest

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.

Resources