I am using neo4j 3.2.x. What I am try to do is write a query that will update relation between two nodes (User and Gender) and return single User node with InterestedInGender property pulled from relation as array. There is a problem with the below query as right now it returns multiple records with single value for interestedInGender. Relations are created properly but when returning data it is returning multiple records. I just want to return User node. Is there any way we can fix this query to just return single user node.
MATCH (u:User {_id:"1234"})
MATCH (ig:Gender) WHERE ig.value IN ["male", "female"]
WITH u, ig
OPTIONAL MATCH (u)-[igr:INTERESTED_IN_GENDER]->()
DELETE igr
with u, ig
MERGE (u)-[:INTERESTED_IN_GENDER]->(ig)
RETURN u{
._id,
interestedInGender: [(u)-[:INTERESTED_IN_GENDER]->(ig:Gender) | ig.value]
}
The reason you're getting multiple records (rows) is because your ig match to gender matches to two :Gender nodes...two rows where both rows have the same u node, but different ig nodes. That cardinality remains throughout the rest of your query, and so you get two rows back.
You need to shrink the cardinality of u back down to 1 after you MERGE the relationship, so add this after your MERGE but before your RETURN:
WITH distinct u
I am querying neo4j with cypher and am getting two rows per match:
MATCH (g:Group {Name: "Goliath_Treasury237"})-[w:MEMBER]->(a:Account)<-[y:ACCOUNT]-(p:Person)-[MANAGER*0..1]->(b:Person)
WHERE NOT p.staffID = b.staffID
MATCH (g:Group {Name: "Goliath_Treasury237"})-[j:MEMBER]->(v:Account)<-[x:ACCOUNT]-(p:Person)-[f:DEP*0..1]->(d:Department)
RETURN p.GivenName, p.Surname, p.staffID, p.CorpT, b.staffID, d.Name
I'm trying to get the information for both the department and the boss of a person which I'm struggling with declaratively unless I do the double match. This returns two rows for each match where a person has a boss, one with their ID as their bosses ID and one with the correct bosses id. For people without a boss I get one row back but the bosses id is their own.
If I remove the variable length path for boss then I get one row for each individual but no row where someone doesn't have a boss.
I'm at a loss now, any help would be great!
There's several things we can fix here.
For one, you don't need the second match to start over from the beginning, you can reuse the same p node without needing all the stuff before. You also don't need to have a variable on every node and relationship if you're not going to use or return it in some way.
You can use an OPTIONAL MATCH when you don't want the match to filter out rows, newly-introduced variables in the OPTIONAL MATCH will be null if the match fails.
Something like this should work:
MATCH (:Group {Name: "Goliath_Treasury237"})-[:MEMBER]->(:Account)<-[:ACCOUNT]-(p:Person)
OPTIONAL MATCH (p)-[MANAGER]->(b:Person)
WHERE NOT p.staffID = b.staffID
OPTIONAL MATCH (p)-[:DEP]->(d:Department)
RETURN p.GivenName, p.Surname, p.staffID, p.CorpT, b.staffID, d.Name
I'm writing a query that searches for recipes. I want to return the current users interactions (likes etc) with the recipes that matches the query.
This query returns all the interactions for the current user properly:
MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
RETURN reaction
However when I add the match to my existing search query, the reaction variable is null for every record:
MATCH (recipe:Recipe)
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
WHERE ALL(
ingredient IN ['tomato', 'banana']
WHERE (recipe)-[:CONTAINS]->(:Ingredient {name: ingredient})
)
AND ALL(
category IN ['smoothie']
WHERE (recipe)-[:IS]->(:Category {name: category})
)
RETURN DISTINCT recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favourite} AS interactions,
collect( DISTINCT {name: i.name, amount: a.amount}) AS ingredients,
collect( DISTINCT {name: c.name}) AS categories
The query I use for getting a single recipe by id works as it should though:
MATCH (recipe:Recipe {cuid: {recipeCuid}})
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: {beholderCuid}})
RETURN recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favorite} AS interactions,
collect( DISTINCT {name: i.name, amount: a.amount}) AS ingredients,
collect( DISTINCT {name: c.name}) AS categories
Any pointers towards what I'm doing wrong?
Gabor's query is a good step forward, as it moves your WHERE to after the WITH instead of keeping it with the OPTIONAL MATCH...that was the primary reason for your inability to get the correct results.
However, the query still needs some efficiency improvements. For one, multiple MATCHes or OPTIONAL MATCHes in a row, especially those that will return multiple rows (ingredients, categories), will impact the efficiency of the rest of your MATCHes or OPTIONAL MATCHes if you don't run your aggregations immediately.
For example, for a single recipe with 3 ingredients and 2 categories, that's 3 x 2 = 6 rows emitted by the time your first two OPTIONAL MATCHes are done, meaning the rest of your OPTIONAL MATCHes need to execute across all 6 of those rows, but your intent is for them only to execute once per recipe, not multiple times.
That's why aggregating as soon as possible is useful, as it can reduce the number of rows per recipe to one, instead of multiple (recipe with a single ingredient and a single category, for each combination of recipe and ingredient and category).
Additionally, you're only filtering down (based on ingredients and categories) only after you've matched on all the rest, meaning you're running many OPTIONAL MATCHes upon rows that will definitely be filtered out. That's wasted work and wasted db hits. Better to do your filtering as soon as possible, and THEN run the additional OPTIONAL MATCHes you need on rows that you know you will be returning.
Lastly, since you only seem to want rows back for recipes with certain ingredients and only certain categories, we should use a MATCH to ingredients and categories, not OPTIONAL MATCHes.
I'd recommend something like this for an improved query:
MATCH (cat:Category)
WHERE cat.name IN ['smoothie']
WITH COLLECT(cat) as desiredCategories
MATCH (i:Ingredient)
WHERE i.name IN ['tomato', 'banana']
WITH desiredCategories, COLLECT(i) as desiredIngredients
MATCH (recipe:Recipe)
WHERE ALL(
category IN desiredCategories
WHERE (recipe)-[:IS]->(category)
)
AND ALL(
ingredient IN desiredIngredients
WHERE (recipe)-[:CONTAINS]->(ingredient)
)
WITH recipe
MATCH (recipe)-[:IS]->(c:Category)
WITH recipe, COLLECT(c) as categories
MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
WITH recipe, categories, COLLECT({name: i.name, amount: a.amount}) as ingredients
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
// only one author, so okay to use optional matches back to back
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
RETURN recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favourite} AS interactions,
ingredients,
categories
You should be able to see that by running our COLLECTS() right after we do the matches returning multiple rows, we keep the built-up rows to 1 per recipe along with the collections (a collection is a single row, vs multiple rows when uncollected).
You should also be able to see that since we filter out recipes that don't have the desired categories or ingredients early, the OPTIONAL MATCHes at the end for author and reaction will only run for recipes that have the desired categories and ingredients, instead of running uselessly for recipes that will be filtered out later.
EDIT
I noticed, late, that there's a problem with the way you're checking for categories and ingredients, you can't compare the nodes directly with the names in your arrays. You likely have a name property on :Ingredient and :Category nodes, and we should be using that to match on the necessary ingredients and categories, and then filter the recipes near the start so we're only working with the recipes with those categories and ingredients. That also lets us avoid matching and collecting categories and ingredients until we've done the filtering. I've updated the query accordingly.
Try to collect the ingredients and categories to collections, and perform the check on those collections. The RETURN clause is a bit simplified, but let's see if this works in the first place.
MATCH (recipe:Recipe)
OPTIONAL MATCH (recipe)-[a:CONTAINS]->(i:Ingredient)
OPTIONAL MATCH (recipe)-[:IS]->(c:Category)
OPTIONAL MATCH (recipe)<-[:AUTHORED]-(u:User)
OPTIONAL MATCH (recipe)<-[reaction:REACTS]-(beholder:User {cuid: 'some-id'})
WITH recipe, collect(i) AS ingredients, collect(c) AS categories, u, reaction, beholder
WHERE ALL(
ingredient IN ['tomato', 'banana']
WHERE ingredient in ingredients
)
AND ALL(
category IN ['smoothie']
WHERE category in categories
)
RETURN DISTINCT recipe,
{username: u.username, cuid: u.cuid} AS author,
{love: reaction.love, favorite: reaction.favourite} AS interactions,
ingredients,
categories
Is there a better way to do this than 3 separate queries?
MATCH (you:User {name: "Alexander"})-[:LIKES]->(youLike:User)
RETURN youLike
MATCH (likesYou:User)-[:LIKES]->(you:User {name: "Alexander"})
RETURN likesYou
MATCH (mutualLike:User)-[:LIKES]->(you:User {name: "Alexander"})-[:LIKES]->(mutualLike:User)
RETURN mutualLike
Here is a shot at a single query.
Essentially, find yourself first, optionally find people that you like and collect them, optionally find people that like you and collect them, then return both collections and the intersection of the two.
By matching the node that identifies you and reusing it you match yourself once instead of three times.
Using the collection filter function allowsyou two find the intersection of the two :LIKES populations without rematching those nodes.
The OPTIONAL keyword allows the query to continue if either :LIKES population is empty.
MATCH (you:User {name: "Alexander"})
WITH you
OPTIONAL MATCH(you)-[:LIKES]->(youLike:User)
WITH you, collect(youLike) as youLike
OPTIONAL MATCH (likesYou:User)-[:LIKES]->(you)
WITH you, youLike, collect(likesYou) as likesYou
RETURN you
, youLike
, likesYou
, filter(n in youLike where n in likesYou) as mutualLike
I have "users" who owns "items", users are also friends w/ each other. I am trying to construct a cypher query to return all items I own PLUS those my friends own in a single query. I can do them individually but can't figure out how to do it in a single query.
RELATIONSHIPS:
(u:user)-[:OWNS]-(i:items)
(u:user)-[:FRIEND]-(f:user)
Let's say I have just two users in my DB and 100 items. Out of 100, the first person owns (1-5) 5 items and the 2nd person owns another 5 items(6-10). These two users are also friends.
I get 5 items if I do:
MATCH (uer1)-[:OWNS]->(i:items) return i
I get another 5 items if I do:
MATCH (uer1)-[:FRIEND]->(f)-[:OWNS]->(i:items) return i
But I need to combine them both for a given user(user1) so I can return all 10 items in a single shot. How to do that?
You have two (or more options)
Union
MATCH (user:User {name:"Raja"})-[:OWNS]->(i:Item) return i
UNION
MATCH (user:User {name:"Raja"})-[:FRIEND]->(f)-[:OWNS]->(i:Item) return i
Variable length paths
MATCH (user:User {name:"Raja"})-[:FRIEND*0..1]->(f)-[:OWNS]->(i:Item) return i
in this case we look at friends of the distance 0 (the user itself) to one (first degree friends)
The first option might be faster
The second is more versatile.