I'm quite new to Neo4J, so pardon if my question looks silly, but I'm wondering if it is possible to do a fallback match if no value (null) is found in my original query.
This is how the graph looks like:
User1 - linked -> Channel 1
User1 - linked -> Channel 2
Channel1 - has -> property1_channel_1, null , property3_channel_1
Channel2 - has -> property1_channel_2, property2_channel_2, property3_channel_2
In this example I'd expect this response to be returned when I query for data User1, Channel1:
property1_channel_1, property2_channel_2, property3_channel_1
Is that possible?
Edit:
How can I structure my query so that if a property - let's say quantity, in one of my products is missing to get its default value from the same product in BASE. Is that possible? In the example the trousers in the BASE is complete entity, but the trousers in the EN channel is incomplete.
I guess something like this
// get all the channels the user is linked to
MATCH (u:User)-[:LINK]->(c:Channel)
WHERE u.name='User1'
// put them in the right order
WITH c ORDER BY c.name
// merge the property maps using apoc.map.mergeList()
RETURN apoc.map.mergeList(COLLECT(properties(c)) AS properties
Related
My current graph monitors board members at a company through time.
However, I'm only interested in currently employed directors. This can be observed because director nodes connect to company nodes through an employment path which includes an end date (r.to) when the director is no longer employed at the firm. If he is currently employed, there will be no end date(null as per below picture). Therefore, I would like to filter the path not containing an end date. I am not sure if the value is an empty string, a null value, or other types so I've been trying different ways without much success. Thanks for any tips!
Current formula
MATCH (c2:Company)-[r2:MANAGED]-(d:Director)-[r:MANAGED]-(c:Company {ticker:'COMS'})
WHERE r.to Is null
RETURN c,d,c2
Unless the response from the Neo4j browser was edited, it looks like the value of r.to is not null or empty, but the string None.
This query will help verify if this is the case:
MATCH (d:Director)-[r:MANAGED]-(c:Company {ticker:'COMS'})
RETURN DISTINCT r.to ORDER by r.to DESC
Absence of the property will show a null in the tabular response. Any other value is a real value of that property. If None shows up, then your query would be
MATCH (c2:Company)-[r2:MANAGED]-(d:Director)-[r:MANAGED]-(c:Company {ticker:'COMS'})
WHERE r.to="None"
RETURN c,d,c2
I'm trying to get a "Friend Recommendation" query. The nodes have the next sequence
(Node) - [FRIEND] - (Node) - [INFO] - (P_info)
where every node have a INFOrelation with a P_info node asociated. I can get a list of recommended friends of a Node but i need to include the P_info keys into recommended friends keys to return all together.
This is my query at the moment:
match (person:Account{_id:"185860469"})
match (person)-[:FRIEND]-()-[:FRIEND]-(potentialFriend)
where not (person)-[:FRIEND]-(potentialFriend)
match (potentialFriend)-[:INFO]-(information:P_info)
with person,potentialFriend, COUNT(*) AS friendsInCommon,information
where friendsInCommon > 5
return {user:person,recommend:collect(potentialFriend)},{info:information}
but the information of "info" is not asociated with "potentialFriend" at the response.
I want to do something like this:
return {user:person,collect(potentialFriend,information)} but i don't know if it's possible, cypher says:
Too many parameters for function 'collect'
thanks in advance.
I did it, just adding an additional WITH.I leave the answer in case someone helps
WITH person,{friend_id:potentialFriend._id,friend_name:information.name} AS Recommended_friend
RETURN person._id,collect(Recommended_friend)
this will return an unique response with person id and an array with all the recommended friends for him.
I think this is a long question for what's likely a simple answer. However I thought it wise to include the full context in case there's something wrong with my query logic (excuse the formatting if it's off - I've renamed the vars and it may be malformed, I need help with the theory and not the structure)
An organisation can have a sub office
(o:Organisation)-[:sub_office]->(an:Organisation)
Or a head office
(o)-[:head_office]->(ho:Organisation)
Persons in different sub offices can be employees or ex-employee
EX1
(o)-[:employee]->(p:Person{name:'person1'})<-[:ex_employee]-(an)
Persons can be related to other people through the management relationships. These management links can be variable length.
EX2
(o)-[:employee]->(p:Person{name:'person2'})-[:managed]->(p:Person{name:'person3'})<-[:ex_employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person4'})-[:managed]->(p:Person{name:'NOT_RETURNED1'})-[:managed]->(p:Person{name:'person5'})<-[:employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person6'})<-[:managed]-(p:Person{name:'NOT_RETURNED2'})<-[:managed]-(p:Person{name:'person8'})<-[:employee]-(an)
(o)-[:ex_employee]->(p:Person{name:'person9'})-[:managed]->(p:Person{name:'NOT_RETURNED4'})-[:managed]->(p:Person{name:'NOT_RETURNED5'})<-[:managed]-(p:Person{name:'person11'})<-[:employee]-(an)
....
I'm querying:
-organisation,
-sub office,
-how they're related
These are all working fine (I think...)
The issues I'm having is with returning Persons associated with the orgs (employees or ex employees) and their relationships to the organisation but only if they are connected to the other organisation directly (as in EX1) or through a managed chain (all of EX2 - I've tried to make it clearer by marking the Persons who won't be returned by the query as name 'NOT_RETURNED')
I've created the following:
MATCH (queryOrganisation:Organisation{name:'BigCorp'})-[orgRel]-(relatedOrganisation:Organisation)
WITH queryOrganisation, orgRel, relatedOrganisation
MATCH (queryOrganisation)-[employmentRel]->(queryPerson:Person)
OPTIONAL MATCH (queryPerson)<-[relatedOrgRel]-(relatedOrganisation)
OPTIONAL MATCH (queryPerson)-[:managed*1..]-(relatedPerson:Person)<-[relatedOrgRel]-(relatedOrganisation)
WITH queryOrganisation, orgRel, relatedOrganisation, employmentRel, queryPerson, relatedOrgRel, relatedPerson
WHERE NOT queryOrganisation.name = relatedOrganisation.name
RETURN ID(queryOrganisation) as queryOrganisationID,
ID(startNode(orgRel))as startNodeId, type(orgRel)as orgRel, ID(endNode(orgRel))as endNodeId,
ID(relatedOrganisation)as relatedOrganisationId, relatedOrganisation.name as relatedOrganisationName
COLLECT({
queryPerson:{endpoint:{ID:ID(queryPerson)}, endpointrelationship: type(employmentRel)},
relatedPerson:{endpoint:{ID:coalesce(ID(relatedPerson),ID(queryPerson))}, endpointrelationship:type(relatedOrgRel)}
}) as rels
I would have expected all the collected results to look like:
{
"startEmp":{
"ID":2715,
"startrelationship":"employee"
},
"relatedEmp":{
"ID":2722,
"endrelationship":"ex employee"
}
}
However the directly connected node results (same node ID) appear like:
{
"startEmp":{
"ID":2716,
"startrelationship":"employee"
},
"relatedEmp":{
"ID":2716,
"endrelationship":null
}
}
Why is that null appearing for type(relatedOrgRel)? Am I misunderstanding whats happening in the OPTIONAL MATCH and the relatedOrgRel gets overwritten by null during the second OPTIONAL MATCH? If so, how can I remedy?
Thanks
No, the OPTIONAL MATCHes cannot overwrite variables that are already defined.
I think the cause of the problems is when your second OPTIONAL MATCH doesn't match anything, but this is partially covered up by the COALESCE used in the collecting of persons in your return hides some of the conseque:
...
relatedPerson:{endpoint:{ID:coalesce(ID(relatedPerson),ID(queryPerson))}, endpointrelationship:type(relatedOrgRel)}
...
If relatedPerson is null, as it will be if your second OPTIONAL MATCH fails, then you're falling back to the id of queryPerson, but since you're not using a COALESCE for relatedOrgRel, this will still be null. You'll need a COALESCE here, or otherwise you'll need to figure out a better way to deal with the null variables in your OPTIONAL MATCHES in cases where they fail.
I have a possibly bone-headed question, but I'm just starting out with Neo4j, and I hope someone can help me out with learning Cypher syntax, which I've just started learning and evaluating.
I have two User nodes, and a single NewsPost node. Both users LIKE the NewsPost. I'm able to construct a Cypher query to count the likes for the post, but I'm wondering if it's also possible to check if the current user has liked the post in the same query.
What I have so far for a Cypher query is
match (p:NewsPost)<-[r:LIKES]-(u:User)
where id(p) = 1
return p, count(*)
Which returns the post and like count, but I can't figure out the other part of "has the current user liked this post". I know you're not supposed to filter on <id>, but I learned that after the fact and I'll go back and fix it later.
So first, is it possible to answer the "has the current user liked this post" question in the same query? And if so, how do I modify my query to do that?
The smallest change to your query that adds a true/false test for a particular user liking the news post would be
MATCH (p:NewsPost)<-[r:LIKES]-(u:User)
WHERE ID(p) = 1
RETURN p, count(r), 0 < size(p<-[:LIKES]-(:User {email:"michael#nero.com"}))
This returns, in addition to your query, the comparison of 0 being less than the size of the path from the news post node via an incoming likes relationship to a user node with email address michael#nero.com. If there is no such path you get false, if there is one or more such paths you get true.
If that does what you want you can go ahead and change the query a little, for instance use RETURN ... AS ... to get nicer result identifiers, and so on.
What you are looking for is Case.
In your database you should have something unique for each user (id property, email or maybe login, I don't know), so you have to match this user, and then match the relation to the post you want, using case you can return a boolean.
Example:
Optional Match (u:User{login:"Michael"})-[r:LIKES]-(p:newPost{id:1})
return CASE WHEN r IS NULL THEN false ELSE true END as userLikesTopic
If you want to get the relation directly (to get a property in it as example) you can remove the CASE part and directly return r, if it does not exist, null will be returned from the query.
I'm tracking if a user has liked and or voted on a object in a list of objects others posted.. I can get either likes and votes, but not both. (A person can both like and vote on an object and these options are not mutually exclusive).
To simply this problem let me describe it in relational terms (left joins used - object is ALWAYS returned, liker and voter data is only returned if a record of that type exists)
[object]+ -> liker
+ -> voter
What I'd like to return is:
objectID likerID voterID
2343 null 88
2345 11 null
2382 44 1256
2400 null null
Yet every which way I've sliced I cannot get it to come out like that . Either row 2400 is skipped (I've tried every combination of where), or values are even shifted from likerID to the voterID column (bug?).
Here is a sample of the cypher:
start objects=node(158)
match contestant-[:POSTED]->object_node-[:POSTED_OBJECT]->objects<-[?:POSTED_OBJECT]-object_node_a<-[?:LIKES]-liker
, objects<-[?:POSTED_OBJECT]-object_node_b<-[?:VOTES]-voter
return id(object, id(liker), id(voter)
It doesn't work even if I try where id(object_node_a) = id(object_node_b)...
If I just try to get a liker it works.. same with voter.. but when I try to do both.. bombs..
I've tried using where , etc but ultimately I never get the full list of objects - it either trims down the list based upon matches, or gives me the Cartesian product which distinct does not resolve.
SQL EXAMPLE: LEFT JOIN
I'm a sql guy so let me explain it this way - I have a objects table on the left, and I want to left join it to a liker table and a voter table, and return both the liker id and voter id on a single row along with the object data. All the object records will be returned regardless if there is a voter or liker record.
[object]+ -> liker
+ -> voter
IS THIS EVEN POSSIBLE?
Is it possible to do this via cypher?
Hopefully I haven't misunderstood. To get
objectID likerID voterID
2343 null 88
2345 11 null
2382 44 1256
2400 null null
i.e. all objects and the ID of those that liked it and voted for it, this query should do it-
start o=<lookup for objects>
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o
return o,ID(ul),ID(uv)
This will return objects that no votes and likes, both votes and likes and either one. Note that if you have multiple users voting for the same object as is likely, then your object row will repeat for each user. You might want to do something like
start o=<lookup for objects>
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o
return o,collect(ID(ul)),collect(ID(uv))
to still get a row per object but a collection of user IDS for votes and likes.
To include the person that posted the object as well:
start o=node(4,5,6,7)
match ul-[like?:LIKED]->o, uv-[vote?:VOTED]->o, c-[:POSTED_OBJECT]->o
return o,ID(ul),ID(uv),ID(c)
I created a tiny sample to play with: http://console.neo4j.org/r/in8g4w