I’m desperately trying to understand how to consolidate nodes in Neo4J for a more streamlined relationship.
I have a dataset with four columns: Person_1_ID, Person_1_Name, Person_2_ID, Person_2_Name
How do you consolidate IDs when they exist in two columns so that you only show one node?
For example:
My dataset looks like this:
If I do a simple match and merge like this:
LOAD CSV WITH HEADERS FROM 'file:///users.csv' AS row
MATCH (Person_1:Person_1 {Person_1_ID: row.Person_1_ID})
MATCH (Person_2:Person_2 {Person_2_ID: row.Person_2_ID })
MERGE (Person_2)-[pr:REFERRING]->(Person_1);
It produces this:
I'm trying to merge on names/IDs so that the relationships look like this:
Desperately trying to understand how you merge nodes properly here so that the relationships are consolidated. Any guidance and code example is greatly appreciated!
Doug
You have a couple of issues:
In your initial node import, don't use Person_1 and Person_2 node labels. Just stick with a single node label Person. The same goes for person id node property. If I were you, I would just delete existing graph and use the following Cypher to produce the desired results:
LOAD CSV WITH HEADERS FROM 'file:///users.csv' AS row
MERGE (Person_1:Person {id: row.Person_1_ID})
MERGE (Person_2:Person {id: row.Person_2_ID })
MERGE (Person_2)-[pr:REFERRING]->(Person_1);
Related
Apologies as I am new to neo4j and struggling with what I imagine is a very simple example.
I would like to model an org chart which I have stored as a csv like so
id,name,manager_id
1,allan,2
2,bob,4
3,john,2
4,sam,
5,Jim,2
Note that Bob has 3 direct reports and Bob reports into Sam who doesn't report into anyone.
I would like to produce a graph which shows the management chain. I have tried the following, but it produces relationships which are disjoint from the people:
LOAD CSV WITH HEADERS FROM "file///employees.csv" AS csvLine
CREATE (p:Person {id: csvLine.id, name: csvLine.name})
CREATE (p)-[:MANAGED_BY {manager: csvLine.manager_id}]->(p)
This query creates a bunch of self-referencing relationships. Is there anyway to populate the graph with one command over the single csv? I must be missing something and any help is appreciated. Thanks
I think this is what you are looking for.
In your query tou are creating a relationship between p and p thus the self referencing relationships.
I added a coalesce statement to deal with people that do not have a manager_id value. THis way Sam can report to himself.
LOAD CSV WITH HEADERS FROM "file:///employees.csv" AS csvLine
// create or match the person in the left column
MERGE (p:Person {id: csvLine.id })
// if they are created then assign their name
ON CREATE SET p.name = csvLine.name
// create or match the person/manager in the right column
MERGE (p1:Person {id: coalesce(csvLine.manager_id, csvLine.id) })
// create the reporting relationship
CREATE (p)-[:MANAGED_BY]->(p1)
i'm trying to make a graph database from an edgelist and i'm kind of new with neo4j so i have this problem. First of all, the edgelist i got is like this:
geneId geneSymbol diseaseId diseaseName score
10 NAT2 C0005695 Bladder Neoplasm 0.245871429880008
10 NAT2 C0013182 Drug Allergy 0.202681755307501
100 ADA C0002170 Alopecia 0.2
100 ADA C0002880 Autoimmune hemolytic anemia 0.2
100 ADA C0004096 Asthma 0.21105290517153
i have a lot of relationships like that (165k) between gen and diseases associated.
I want to make a bipartite network in which nodes are gen or diseases, so i upload the data like this:
LOAD CSV WITH HEADERS FROM "file:///path/curated_gene_disease_associations.tsv" as row FIELDTERMINATOR '\t'
MERGE (g:Gene{geneId:row.geneId})
ON CREATE SET g.geneSymbol = row.geneSymbol
MERGE (d:Disease{diseaseId:row.diseaseId})
ON CREATE SET d.diseaseName = row.diseaseName
after a while (which is way longer than what it takes in R to upload the nodes using igraph), it's done and i got the nodes, i used MERGE because i don't want to repeat the gen/disease. The problem is that i don't know how to make the relationships, i've searched and they always use something like
MATCH (g:Gene {geneId: toInt(row.geneId)}), (d:Disease {diseaseId: toInt(row.geneId)})
CREATE (g)-[:RELATED_TO]->(d);
But when i run it it says that there are no changes. I've seen the neo4j tutorial but when they do the relations they don't work with edgelists so maybe the problem is when i merge the nodes so they don't repeat. I'd appreciate any help!
Looks like there might be two problems with your relationship query:
1) You're inserting (probably) as a string type (no toInt), and doing the MATCH query as an integer type (with toInt).
2) You're MATCHing the Disease node on row.geneId, not row.diseaseId.
Try the following modification:
MATCH (g:Gene {geneId: row.geneId}), (d:Disease {diseaseId: row.diseaseId})
CREATE (g)-[:RELATED_TO]->(d);
#DanielKitchener's answer seems to address your main question.
With respect to the slowness of creating the nodes, you should create indexes (or uniqueness constraints, which automatically create indexes as well) on these label/property pairs:
:Gene(geneId)
:Disease(diseaseId)
For example, execute these 2 statements separately:
CREATE INDEX ON :Gene(geneId);
CREATE INDEX ON :Disease(diseaseId);
Once the DB has those indexes, your MERGE clauses should be much faster, as they would not have to scan through all existing Gene or Disease nodes to find possible matches.
I have the following file A.csv
"NODE","PREDECESSORS"
"1",""
"2","1"
"3","1;2"
I want to create with the nodes: 1,2,3 and its relationships 1->2->3 and 1->3
I have already tried to do so:
LOAD CSV WITH HEADERS FROM 'file:///A.csv' AS line
CREATE (:Task { NODE: line.NODE, PREDECESSORS: SPLIT(line.PREDECESSORS ';')})
FOREACH (value IN line.PREDECESSORS |
MERGE (PREDECESSORS:value)-[r:RELATIONSHIP]->(NODE) )
But it does not work, that is, it does not create any relationship.
Please, might you help me?
The problem is in your MERGE:
MERGE (PREDECESSORS:value)-[r:RELATIONSHIP]->(NODE)
This is merging a :value labeled node and assigning it to the variable PREDECESSORS, which can't be what you want to do.
A better approach would be not save the predecessor data in the node, just use that to match on the relevant nodes and create the relationships.
It will also help to have an index on :Task(NODE) so your matches to the predecessors are quick.
Remember also that cypher queries do not process the entire query for each row, but rather each operation in the query is processed for each row, so once the CREATE executes, all nodes will be created, there's no need to use MERGE the predecessor nodes.
Try something like this:
LOAD CSV WITH HEADERS FROM 'file:///A.csv' AS line
CREATE (node:Task { NODE: line.NODE})
WITH node, SPLIT(line.PREDECESSORS, ';') as predecessors
MATCH (p:Task)
WHERE p.NODE in predecessors
MERGE (p)-[:RELATIONSHIP]->(node)
Build a D3 viewer, I'd like to get all the nodes connected to one, and all the links connecting these nodes. In fact, the same thing as in the default neo4j viewer....
For example, I have
CREATE (a:Person {name:'a'})
CREATE (b:Person {name:'b'})
CREATE (c:Person {name:'c'})
CREATE (d:Person {name:'d'})
CREATE (a)-[:KNOWS]->(b)
CREATE (a)-[:KNOWS]->(c)
CREATE (b)-[:KNOWS]->(c)
CREATE (c)-[:KNOWS]->(d)
I can issue some ugly cypher query to give to the POST call, but it does not scale with medium size graphs (~100k nodes, 3M relationships).
Looking, at the posted queries in the neo4j browser, it looks like there are 2 successive one:
A) get the connected nodes
MATCH (p:Person {name:'a'})-[l:KNOWS]-(q:Person) RETURN p,q
B) get the links existing between these nodes
"START a = node(185282,185283,185284), b = node(185282,185283,185284)
MATCH a -[r]-> b
RETURN r;"
My two questions:
Is there a clean way to get list of unique nodes?
Is this two steps method the preferred one?
Again, the data can be a bit heavy, so let's keep that in mind
Thanks for the help
Alex
You can pipe the results of the first query to the second one using WITH to get all connections between Persons which the 'a' Person knows:
MATCH (p:Person)-[l:KNOWS]-(q:Person)
WHERE p.name = "a"
WITH p,q
MATCH (p)-[r]-(q)
RETURN p,r,q
I'm modeling a "tag cloud" with the graph:
(t:Tag {name:'cypher'})-[:IN]->(g:TagGroup)<-[:TAGGED]-(x)
IE: A named tag is part of a "TagGroup", to which zero or more nodes are "TAGGED". I chose this design as I want the ability to combine two or more named tags (e.g. "cypher" and "neo4j") so that both (Tag)s are [IN]the new (TagGroup) and the new (TagGroup) is the endpoint for the union of all nodes that were previously [TAGGED].
My only (not very pleasing) attempt is:
match (t:Tag {name:'cypher'})-[i:IN]->(g:TagGroup),
(t2:Tag {name:'neo4j'})-[:IN]->(g2:TagGroup)<-[y:TAGGED]-(x)
create (t2)-[:IN]->(g)
create unique (g)<-[:TAGGED]-(x)
with g2 as g2
match (g2)<-[r]->() delete g2,r
My main issues is that it only combines two nodes, and doesn't feel very efficient (although I have no alternatives to compare it with). Ideally I'd be able to combine an arbitrary set of (Tag)s by name.
Any ideas if this can be done with Cypher, and if so, how?
You can use labels instead of creating separate tag groups.
eg. if tag neo4j and cypher come under tag group say XYZ then
MERGE (a:Tag {name: "neo4j"})-[:TAGGED]->(x)
MERGE (b:Tag {name: "cypher"})-[:TAGGED]->(x)
set a :XYZ , b :XYZ
So next time you want tags of a particular group TAGGED to a particular post x
MATCH (a:Tag:XYZ)-[:TAGGED]->(x) return a.name