Need to MATCH instead of MERGE - twitter

My Neo4j instance is populated with a complete Alumni data set from a local University. A small % (15K) of the (248K) nodes (n:Alumni) include a twitter user_name. My goal is to use a GITHUB app (TWIITER to NEO4J) that loads and links twitter screenNames in order to obtain relationship data. My issue is that the query below (which is contain in the java script from the GITHUB app) Cypher creates unwanted Follower/Following nodes that do not match the Alumni screen_name. I would like to write a query that only allows relationship to be created from the app. Let me know if this is clearer....
MERGE (targetNode:Person {screen_name: {screenName}})
FOREACH (friend in {userList} |
MERGE (friendNode:Person {screen_name: friend.screen_name})
ON CREATE SET friendNode=friend
MERGE (targetNode)-[:FOLLOWS]->(friendNode))
This was my idea - I set the MERGE to MATCH Alumni
MATCH (targetNode:Alumni {screen_name: {screenName}})
WHERE targetNode:Alumni.screen_name = {screen_name}
FOREACH (friend in {userList} |
MATCH (friendNode:Alumni {screen_name: friend.screen_name})
SET friendNode=friend
MERGE (targetNode)-[:FOLLOWS]->(friendNode))
Thanks for any thoughts...
var multiline = require('multiline');
exports.upsertOne = multiline(function(){/*
MERGE (n:Person {screen_name: {screenName}})
ON CREATE SET n+={userMap}
RETURN n
*/});
/*
* Parameters are:
*
* screenName - string, the handle of the person to attach followers to
* userList - array of objects, people to merge and create relationships
*/
exports.upsertManyAndFollows = multiline(function() {/*
MATCH (followeeNode:Alumni {screen_name: {screenName}})
UNWIND {userList} as follower
MATCH (followerNode:Alumni {screen_name: follower.screen_name})
SET followerNode=follower
MERGE (followerNode)-[:FOLLOWS]->(followeeNode)
*/});
/*
* Parameters are:
*
* screenName - string, the handle of the person to attach followers to
* userList - array of objects, people to merge and create relationships
*/
exports.upsertManyAndFriends = multiline(function() {/*
MATCH (targetNode:Person {screen_name: {screenName}})
UNWIND {userList} as friend
MATCH (friendNode:Person {screen_name: friend.screen_name})
SET friendNode=friend
MERGE (targetNode)-[:FOLLOWS]->(friendNode)
*/});
/*
* Parameters are:
*
* screenName - string, the handle of the person that is at the center of the graph.
*
*/

You might try this instead, though make sure you use the correct screen name (you have both {screenName} and {screen_name} in your query, so one of these is wrong).
Provided your :Alumni nodes are already created, this should work.
MATCH (targetNode:Alumni {screen_name: {screenName}})
UNWIND {userList} as friend
MATCH (friendNode:Alumni {screen_name: friend.screen_name})
SET friendNode=friend
MERGE (targetNode)-[:FOLLOWS]->(friendNode)

Related

How to return nodes that have only one given relationship

I have nodes that represent documents, and nodes that represent entities. Entities can be referenced in document, if so, they are linked together with a relationship like that :
(doc)<-[:IS_REFERENCED_IN]-(entity)
The same entity can be referenced in several documents, and a document can reference several entities.
I'd like to delete, for a given document, every entity that are referenced in this given document only.
I thought of two different ways to do this.
The first one uses java to make a foreach and would basically be something like that :
List<Entity> entities = MATCH (d:Document {id:0})<-[:IS_REFERENCED_IN]-(e:Entity) return e
for (Entity entity : entities){
MATCH (e:Entity)-[r:IS_REFERENCED_IN]->(d:Document) WITH *, count(r) as nb_document_linked WHERE nb_document_linked = 1 DELETE e
}
This method would work but i'd like not to use a foreach or java code to make it. I'd like to do it in one cypher query.
The second one uses only one cypher query but doesn't work. It's something like that :
MATCH (d:Document {id:0})<-[:IS_REFERENCED_IN]-(e:Entity)-[r:IS_REFERENCED_IN]->(d:Document) WITH *, count(r) as nb_document_linked WHERE nb_document_linked = 1 DELETE e
The problem here is that nb_document_linked is not unique for every entity, it is a unique variable for all the entities, which mean it'll count every relationship of every entity, which i don't want.
So how could I make a kind of a foreach in my cypher query to make it work?
Sorry for my english, I hope the question is clear, if you need any information please ask me.
You can do something like:
MATCH (d:Document{key:1})<-[:IS_REFERENCED_IN]-(e:Entity)
WITH e
MATCH (d:Document)<-[:IS_REFERENCED_IN]-(e)
WITH COUNT (d) AS countD, e
WHERE countD=1
DETACH DELETE e
Which you can see working on this sample data:
MERGE (a:Document {key: 1})
MERGE (b:Document {key: 2})
MERGE (c:Document {key: 3})
MERGE (d:Entity{key: 4})
MERGE (e:Entity{key: 5})
MERGE (f:Entity{key: 6})
MERGE (g:Entity{key: 7})
MERGE (h:Entity{key: 8})
MERGE (i:Entity{key: 9})
MERGE (j:Entity{key: 10})
MERGE (k:Entity{key: 11})
MERGE (l:Entity{key: 12})
MERGE (m:Entity{key: 13})
MERGE (d)-[:IS_REFERENCED_IN]-(a)
MERGE (e)-[:IS_REFERENCED_IN]-(a)
MERGE (f)-[:IS_REFERENCED_IN]-(a)
MERGE (g)-[:IS_REFERENCED_IN]-(a)
MERGE (d)-[:IS_REFERENCED_IN]-(b)
MERGE (e)-[:IS_REFERENCED_IN]-(b)
MERGE (f)-[:IS_REFERENCED_IN]-(c)
MERGE (g)-[:IS_REFERENCED_IN]-(c)
MERGE (j)-[:IS_REFERENCED_IN]-(a)
MERGE (h)-[:IS_REFERENCED_IN]-(a)
MERGE (i)-[:IS_REFERENCED_IN]-(a)
MERGE (g)-[:IS_REFERENCED_IN]-(c)
MERGE (k)-[:IS_REFERENCED_IN]-(c)
MERGE (l)-[:IS_REFERENCED_IN]-(c)
MERGE (m)-[:IS_REFERENCED_IN]-(c)
On which it removes 3 Entities.
The first MATCH finds the entities that are attached to your input doc, and the second MATCH finds the number of documents that each of these entities is connected to.

Merging Nodes in Neo4j

I am trying merge two neo4j graphs using CYPHER. The first one is the example of Countries and their Capitals. The second one is a sample example I created.
WITH "https://gist.githubusercontent.com/jimmycrequer/7aa867900d0cf0b9588d4354f09cb286/raw/countries.json" AS url
CALL apoc.load.json(url) YIELD value AS v
MERGE (c:Country {name: v.name})
SET c.population = v.population, c.area = v.area
CREATE (capital:City {name: v.capital})
CREATE (c)<-[:IS_CAPITAL_OF]-(capital)
FOREACH (n IN v.neighbors |
MERGE (neighbor:Country {name: n})
MERGE (c)-[:IS_NEIGHBOR_OF]-(neighbor)
)
To this, I'm trying to add my graph
//Manufacturers
MERGE (BMW:Manufacturer {name:"BMW" , headquarters :"Germany" , employees :100306,factories:25 ,revenue:95.8 ,production:1668982 ,sales: 1688982 })
MERGE(Germany:Country)-[:MANUFACTURERS]->(BMW)
The Node Germany has the following properties
id:103, area:357022, name:Germany, population:8288000
When, I try to look for the final output. I see there is an empty blank node created for the relationship [:MANUFACTURERS] and a node BMW is created.
Change your second query a bit. Just because you name the node variable Germany, Neo4j doesnt know you want to match the country with the name property Germany.
And in most cases you should merge or match nodes first and only then add tje relationship between the two
MERGE (BMW:Manufacturer {name:"BMW" , headquarters :"Germany" , employees :100306,factories:25 ,revenue:95.8 ,production:1668982 ,sales: 1688982 })
MERGE (Germany:Country{name:'Germany})
MERGE (Germany)-[:MANUFACTURERS]->(BMW)

Cypher: Adding properties to relationship as distinct values

What I'm trying to do is to write a query - I already made it a webservice(working on local machine, so I get the name and people as parameters) - which connects people who share the same hobbies and set the hobbies as the relationship property as an array.
My first attempt was;
MERGE (aa:Person{name:$name})
WITH aa, $people as people
FOREACH (person IN people |
MERGE (bb:Person{name:person.name})
MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
ON MATCH SET r.hobbies = r.hobbies + person.hobby
ON CREATE SET r.hobbies = [person.hobby])
However this caused duplicated property elements like ["swimming","swimming"]
I'm trying to set only unique properties. Then I tried the following query;
MERGE (aa:Person{name:$name})
WITH aa, $people as people FOREACH (person IN people | MERGE (bb:Person{name:person.name}) MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
WITH r, COALESCE(r.hobbies, []) + person.hobby AS hobbies
UNWIND hobbies as unwindedHobbies
WITH r, collect(distinct, unwindedHobbies) AS unique
set r.as = unique)
However now it gives me syntax error;
errorMessage = "[Neo.ClientError.Statement.SyntaxError] Invalid use of WITH inside FOREACH
Any help is appreciated.
This should work:
MERGE (aa:Person {name: $name})
WITH aa
UNWIND $people AS person
MERGE (bb:Person {name: person.name})
MERGE (bb)-[r:SHARESSAMEHOBBY]-(aa)
WITH r, person, CASE
WHEN NOT EXISTS(r.hobbies) THEN {new: true}
WHEN NOT (person.hobby IN r.hobbies) THEN {add: true}
END AS todo
FOREACH(ignored IN todo.new | SET r.hobbies = [person.hobby])
FOREACH(ignored IN todo.add | SET r.hobbies = r.hobbies + person.hobby);
You actually had 2 issues, and the above query addresses both:
If a SHARESSAMEHOBBY relationship already existed in the opposite direction (from aa to bb), the following MERGE clause would have caused the unnecessary creation of a second SHARESSAMEHOBBY relationship (from bb to aa):
MERGE (bb)-[r:SHARESSAMEHOBBY]->(aa)
To avoid this, you should have used a non-directional relationship pattern (which is is permitted by MERGE, but not CREATE) to match a relationship in either direction, like this:
MERGE (bb)-[r:SHARESSAMEHOBBY]-(aa)
You needed to determine whether it is necessary to initialize a new hobbies list or to add the person.hobby value to an existing r.hobbies list that did not already have that value. The above query uses a CASE clause to assign to todo either NULL, or a map with a key indicating what additional work to do. It then uses a FOREACH clause to execute each thing to do, as appropriate.

neo4j match node in array order by input

I am trying to implement https://neo4j.com/blog/moving-relationships-neo4j/ pointer functionality for using it as a team order machine.See http://imgur.com/a/MViF0 for a model. I am using this cypher query.
MERGE (list:LIST)
WITH list
MATCH (u) WHERE ID(u) IN [421, 419, 420]
MERGE (team:TEAM{name: u.name})
MERGE (team)-[:PARTOF]->(list)
WITH collect(team)as elems,list
FOREACH (n IN RANGE(0, LENGTH(elems)-2) |
FOREACH (prec IN [elems[n]] |
FOREACH (next IN [elems[n+1]] |
MERGE (prec)-[:NEXT]->(next))))
with list
MATCH (elem:TEAM) WHERE NOT (elem)<-[:NEXT]-()
MERGE (list)-[:POINTER]->(elem)
Now this works quite nicely, but I have only one problem. This line:
MATCH (u) WHERE ID(u) IN [421, 419, 420]
returns my original teams ordered by id, but I would like to define my order by the pattern in the [421,419,420] pattern, like a function that
return * order by my array input.
Keep in mind that it should work for any amount of teams,this is just an example. And that my original team node isn't labeled a team but something else, so we make a duplicate every time. Any input appreciated, thanks.
Try to use the statement "unwind":
MERGE (list:LIST)
WITH list
UNWIND [421, 419, 420] as uid
MATCH (u) WHERE id(u) = uid
MERGE (team:TEAM{name: u.name})
...
[Update] Of course, it is possible to know the order manually for each node:
MERGE (list:LIST)
WITH list, [3871013, 3871011, 3871012] as ids
MATCH (u) WHERE ID(u) IN ids
WITH list, u,
FILTER(x in RANGE(0,size(ids)-1) WHERE ids[x] = id(u)) as orderIndex
ORDER BY orderIndex[0] // Sort by node position in the array of identifiers
MERGE (team:TEAM{name: u.name})
...

Cant use Merge inside foreach for existed nodes

I am expecting he following query to create nodes (only if exits) and relations by a given source node (1) and a list(2) this way:
MERGE (p1:C9{userId: '1'}) WITH p1, [{userId:"2"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
Thats the outcome:
Now if I am executing this query again by switching the node id's this way:
MERGE (p1:C9{userId: '2'}) WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users | MERGE
((p1)-[r1:follow]->(:C9 {userId: user.userId})))
We got this:
neo4j duplicated for me the node with id=1. I want it to merge in case of existed nodes.
I expected to see only two nodes connected to each other by merging existed nodes.
any idea what I should fix?
Thanks,
ray.
I normally avoid FOREACH when I can use an UNWIND, so I would start with something like this:
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p1)-[r1:follow]->(:C9 {userId: user.userId})
Sometimes you also want to separate your node creation from your relationship creation. If you do both at the same time, I think that Neo4j can think that you want a unique combination of node (with properties) and relationship.
MERGE (p1:C9 {userId: '1'})
WITH p1, [{userId:"2"}] AS users
UNWIND users AS user
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
You can use MERGE within FOREACH.
But you have to understand the semantics of MERGE. It tries to MATCH a full pattern and if it does not find it it will fully CREATE that pattern.
You in your case you try to find a pattern within the context of p1 and not globally and if not found it will create it within the context of p1.
So if you change your query to:
MERGE (p1:C9{userId: '2'})
WITH p1, [{userId:"1"}] AS users
FOREACH (user IN users |
MERGE (p2:C9 {userId: user.userId})
MERGE (p1)-[r1:follow]->(p2)
)
I.e. create p2 first and then MERGE the relationship, it will work.

Resources