Match Command in Qlikview - neo4j

I have created only one graph in Neo4j using the create statement. Now I want to retrieve data from the same graph using the MATCH command. It is giving me the correct count of all the nodes and relationships. Now I have run the same create query again without any changes and now if I retrieve the count using the same MATCH then I'm getting the double value.If run it again then triple value. If I give the return statement in the create statement then the count is correct, if I give it separately then the count is getting multiplied.
Why it is so.
And how to get the correct count if I run the same graph multiple times.
LOAD CSV FROM "file:D:\\Neo4j\\demo2dbcopy.csv" AS emp
MERGE (root:SubGraph {source: emp[7]})
MERGE (cname:Cmpname {name: emp[0]})
MERGE (clusters:ClustName { name: emp [1]})
MERGE (dom:domains { name: emp [2]})
MERGE (Hd:Head { name: emp [3]})
MERGE (DelHd:DeliveryHead { name: emp [4]})
MERGE (Mgr:Managers { name: emp [5]})
MERGE (Emp:Employees { name: emp [6]})
CREATE (root)<-[:PART_OF_SUB_GRAPH]-(cname)-[:has]->(clusters),(clusters)-[:contains]->(dom)<-[:headedby]-(Hd),
(Hd)-[:equals]->(DelHd),
(DelHd)-[:assistedby]->(Mgr),
(Mgr)-[:Dividesinto]->(Emp)
return root,cname,clusters,dom,Hd,count(Hd);
and the MATCH statement is
MATCH (root:SubGraph {source: "demo2db"})<--(cname:Cmpname)-->(clusters:ClustName)-->(dom:domains {name:"BIDW"})<-[:headedby]-(Hd:Head) RETURN dom.name, count (Hd);
Thanks in advance...

The create command is
LOAD CSV FROM "file:D:\\Neo4j\\demo2dbcopy.csv" AS emp
MERGE (root:SubGraph {source: emp[7]})
MERGE (cname:Cmpname {name: emp[0]})
MERGE (clusters:ClustName { name: emp [1]})
MERGE (dom:domains { name: emp [2]})
MERGE (Hd:Head { name: emp [3]})
MERGE (DelHd:DeliveryHead { name: emp [4]})
MERGE (Mgr:Managers { name: emp [5]})
MERGE (Emp:Employees { name: emp [6]})
CREATE (root)<-[:PART_OF_SUB_GRAPH]-(cname)-[:has]->(clusters),(clusters)-[:contains]->(dom)<-[:headedby]-(Hd),
(Hd)-[:equals]->(DelHd),
(DelHd)-[:assistedby]->(Mgr),
(Mgr)-[:Dividesinto]->(Emp)
return root,cname,clusters,dom,Hd,count(Hd);
and the MATCH statement is
MATCH (root:SubGraph {source: "demo2db"})<--(cname:Cmpname)-->(clusters:ClustName)-->(dom:domains {name:"BIDW"})<-[:headedby]-(Hd:Head) RETURN count (Hd);

Related

Neo4J Cypher: create relationship only if destination node exists

I am trying to load a csv file that and as part of import try to create a relationship if the foreign key fields are resolved.
OPTIONAL MATCH (d1:Destination1 { id: line[2], name: line[3] })
OPTIONAL MATCH (d2:Destination2 { id: line[4], name: line[5] })
MERGE (n:Source {id: line[0])-[:MY_RELATIONSHIP]->(d1)
MERGE (n:Source {id: line[0])-[:MY_RELATIONSHIP]->(d2)
this query fails when either of d1 or d2 are not resolved. Is there a way to have a IF THEN option to create a link only if the destination exists. I see even WHERE clause is not allowed in the MERGE statement.
UPDATE 1
Looks like I over simplified my query, to give better context, here is what I am planning to achieve:
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
MATCH (g1:Grade { number: line[6]})
MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2)
I notice that if the query fails to find either g1 or g2 it fully fails to update other relationships. I had to resort to Optional Match but then the Merge fails
If the only thing you are doing is creating a relationship between existing nodes, then simply change the OPTIONAL MATCH to MATCH clause
MATCH (d1:Destination1 { id: line[2], name: line[3] })
MATCH (d2:Destination2 { id: line[4], name: line[5] })
....
You can think of it as this, if a MATCH clause fails to retrieve any data, then the query execution stops for that particular row. In a sense it is doing the same as:
OPTIONAL MATCH (d1:Destination1 { id: line[2], name: line[3] })
OPTIONAL MATCH (d2:Destination2 { id: line[4], name: line[5] })
WITH d1, d2
WHERE d1 IS NOT NULL and d2 IS NOT NULL
....
If you need to do other things as well, you can resort to FOREACH tricks or subqueries.
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
FOREACH (_ in CASE WHEN g2 is not null and g1 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2))
FOREACH trick has been around since forewer (https://data-xtractor.com/blog/databases/neo4j-cypher-hacks/#4_Conditional_Execution_with_8230FOREACH)
Maybe a more clean version would be to use a subquery. I think the following should work:
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
WITH n, g1, g2
CALL {
WITH n, g1, g2
WITH *
WHERE g1 IS NOT NULL and g2 IS NOT NULL
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2)}
You can also obviously create two FOREACH subqueries or two subqueries if you want to link to grade1 if it exists but grade 2 does not exists.
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
FOREACH (_ in CASE WHEN g1 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1))
FOREACH (_ in CASE WHEN g2 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2))

Adding relationships between an array of array of objects in Neo4j Cypher

I have three arrays of objects as shown below:
const org = [
{
id: "orgId1",
name: "first organization"
},
{
id: "orgId2",
name: "second organization"
}
]
const location = [
{
id: "loc1",
name: "Texas"
},
{
id: "loc2",
name: "New York"
}
]
const contacts = [
{
id: "contact1",
name: "James"
},
{
id: "contact2",
name: "John"
}
]
What is the optimal way to add relationships between them? Note that the arrays are of the same length.
I need a Cypher query that can loop through a range from 0 to orgs.length, and add corresponding relationships between each element at i. e.g org[i], contacts[i], location[i]
I tried the following, but it gives me an explosive combination where the first org maps to all the entries in location array and contact array, when I want is a one-to-one mapping.
UNWIND $orgs as orgs
UNWIND $locations as locs
UNWIND $contacts as contacts
FOREACH (i IN range(0, size(orgs) - 1)
| MERGE (:Organization { id: orgs[i].id })-[r:LOCATED_AT]->(:Location {id: locs[i].id})
| MERGE (:Organization { id: orgs[i].id })-[r:CONTACT_AT]->(:Contact {id: contacts[i].id})
)
Any help would be appreciated. Thanks in advance.
I don't think you need to UNWIND all arrays
WITH $orgs AS orgs,
$locs AS locs,
$contacts AS contacts
UNWIND $orgs as orgs
FOREACH (i IN range(0, size(orgs) - 1) |
MERGE (org:Organization { id: orgs[i].id })
MERGE (loc:Location {id: locs[i].id})
MERGE (contact:Contact {id: contacts[i].id})
MERGE (org)-[:LOCATED_AT]->(loc)
MERGE (org)-[:CONTACT_AT]->(contact)
)
should do it
A solution was posted in the official Neo4j site. I'm sharing here as well.
UNWIND range(0, size($orgs) - 1) as i
with i
MERGE (o:Organization { id: $orgs[i].id })-[r:LOCATED_AT]->(l:Location {id: $locs[i].id})
with o, i
MERGE (o)-[r2:CONTACT_AT]->(l2:Contact {id: $contacts[i].id})
Link to original answer

How to return a list of composite objects in Cypher?

If I wanted to return a composite object, based on an exisiting node, I know that I could to this: (thanks to this post)
MATCH(...)-[:HAS_DB]->(db: Database {name: "my_database")
WITH { name: db.name,
format: db.format,
} AS database
RETURN database;
This would return an object based on but not exactly my Database node.
However, I would like to return composite objects for a collection of nodes, not just for one node:
I tried this, but it seems like FOREACH is only appropriate for List<T>.
MATCH(...)-[:HAS_DB]->(databases: Database)
FOREACH (db IN databases |
RETURN {
name: db.name,
format: db.format
}
)
How could I do this?
MATCH(...)-[:HAS_DB]->(db: Database {name: "my_database")
WITH DISTINCT db
Return COLLECT( { name: db.name,
format: db.format,
}) AS database

Neo4j - Build Prioritization Rule

We are trying to build a recommendation engine for knowledge management system (KMS) using Neo4j. The idea is to get the published articles that are relevant to a user based on certain criteria and then prioritize the articles to decide the sequence of articles.
For Example - There are a total of 100 articles published in my KMS, out of those 50 are relevant to me based on the metadata properties attached to every article (I have queries that can get me this information), but, I only have a placeholder to show 10 articles.
For this, I have a prioritization matrix that decides the priority of all the 50 articles relevant to me and get the 10 (highly prioritized) that I want to show to the user.
Prioritization matrix is as follows -
Background - Every article will have certain actions - comment, share, like and download. And every colleague will have its own organizational hierarchy like manager, peers, manager of manager etc.
What is required - Every time an action is performed on the article, a score is attached to it. If the action is performed by my manager, it will have a higher score as compared to the action performed by my peer.
Here is the score list:
Actions (Score)-
like (1),
comment (2),
share (5),
download (3)
Association (Multiplication factor) -
Peer (2),
Manager (3),
MOM (3)
Article score formula - ActionScore * AssociationFactor
Scenario -
Article 1 (Liked by my manager and commented by 3 peers) - Score is 1*3 + 3(2*2) = 15
Article 2 (3 Comments added by MOM, liked by 5 peers) - Score is 3(2*3) + 5(1*2) = 28
Article 3 (Commented by 4 peers, liked by MOM and liked by 2 peers) - Score is 4(2*2) + 1*3 + 2(1*2) = 23
According to the score chart, the sequence of my articles should be -
Article 2
Article 3
Article 1
I need to calculate these scores on the fly against all the articles relevant to a colleague and then, return them according to the score attached. How can this be done in Neo4j?
Editted -
Here is the sample code to create two articles, similarly we can create more articles with the desired metadata -
CREATE (knowledge:Article { name: 'Article1' }),(bu:BU { name: 'ARS' }),(date:Date { name: 'Date' }),(practice:Practice { name: 'Practice' }),(category:Category { name: 'Category' }),(place:Place { name: 'Place' }),(subcat:Subcategory { name: 'Subacategory' }),(geo:Geography { name: 'US' }),(lang:Language { name: 'Language' }),(trans:Translation { name: 'Translation' }),(keyword:Keyword { name: 'Keyword' }),(colleague1:Colleague { name: 'GS',collid:'567890',BU:'ASC',Country:'India' }),(colleague2:Colleague { name: 'DN',collid:'765432',BU:'ASC',Country:'India' }),(colleague3:Colleague { name: 'PK',collid:'324567',BU:'ASC',Country:'India' }),(colleague4:Colleague { name: 'PMM',collid:'717865',BU:'ARS',Country:'US' }),(knowledge)-[:ASSOCIATED_TO]->(bu),(knowledge)-[:PUBLISHED_ON]->(date),(knowledge)-[:UPDATED_ON]->(date),(knowledge)-[:LIKED_ON]->(date),(knowledge)-[:COMMENTED_ON]->(date),(knowledge)-[:PUBLISHED_IN]->(place),(knowledge)-[:LOCATED_IN]->(geo),(knowledge)-[:IS_OF_SUBTYPE]->(subcat),(knowledge)-[:HAS_ORIGINAL_LANG]->(lang),(knowledge)-[:TRANSLATED_TO]->(trans),(trans)-[:IN_LANGUAGE]->(lang),(knowledge)-[:IS_SUBASSOCIATED_TO]->(practice),(knowledge)-[:IS_OF_TYPE]->(category),(subcat)-[:IS_PART_OF]->(category),(knowledge)-[:WRITTEN_BY]->(colleague1),(knowledge)-[:CONTACT_TO]->(colleague1),(knowledge)-[:UPDATED_BY]->(colleague3),(knowledge)-[:LIKED_BY]->(colleague1),(knowledge)-[:COMMENTED_BY]->(colleague1),(colleague1)-[:IS_A_MANAGER_OF]->(colleague2),(colleague1)-[:FOLLOWS]->(colleague2),(colleague1)-[:IS_A_MANAGER_OF]->(colleague4),(colleague1)-[:IS_A_MANAGER_OF]->(colleague3)
MERGE (col1:Colleague { name: 'MK', collid:'98762',BU:'ASC',Country:'US'})
MERGE (col2:Colleague { name: 'AA', collid:'01234567',BU:'ASC',Country:'US'})
MERGE (col3:Colleague { name: 'PM', collid:'45722',BU:'ASC',Country:'US'})
MATCH (col2:Colleague {name:'AA'}),(col1:Colleague { name: 'PM'})
CREATE (col2)-[:IS_A_MANAGER_OF]->(col1)
MATCH (col2:Colleague {name:'PM'}),(col1:Colleague { name: 'MK'})
CREATE (col2)-[:IS_A_MANAGER_OF]->(col1)
MATCH (col2:Colleague {name:'PM'}),(col1:Colleague { name: 'GS'})
CREATE (col2)-[:IS_A_MANAGER_OF]->(col1)
MATCH (colleague:Colleague { name: 'DN' })
MATCH (coll:Colleague { name: 'PK' })
MERGE (knowledge:Article { name: 'Article2' })
MERGE (bu:BU { name: 'ASC' })
MERGE (date:Date { name: 'Date' })
MERGE (practice:Practice { name: 'Practice' })
MERGE (category:Category { name: 'Category' })
MERGE (place:Place { name: 'Place' })
MERGE (subcat:Subcategory { name: 'Subacategory' })
MERGE (geo:Geography { name: 'India' })
MERGE (lang:Language { name: 'English' })
MERGE (trans:Translation { name: 'Translation' })
MERGE (keyword:Keyword { name: 'Keyword' })
MERGE (knowledge)-[:ASSOCIATED_TO]->(bu)
MERGE (knowledge)-[:PUBLISHED_ON]->(date)
MERGE (knowledge)-[:UPDATED_ON]->(date)
MERGE (knowledge)-[:LIKED_ON]->(date)
MERGE (knowledge)-[:COMMENTED_ON]->(date)
MERGE (knowledge)-[:PUBLISHED_IN]->(place)
MERGE (knowledge)-[:LOCATED_IN]->(geo)
MERGE (knowledge)-[:IS_OF_SUBTYPE]->(subcat)
MERGE (knowledge)-[:HAS_ORIGINAL_LANG]->(lang)
MERGE (knowledge)-[:TRANSLATED_TO]->(trans)
MERGE (trans)-[:IN_LANGUAGE]->(lang)
MERGE (knowledge)-[:IS_SUBASSOCIATED_TO]->(practice)
MERGE (knowledge)-[:IS_OF_TYPE]->(category)
MERGE (subcat)-[:IS_PART_OF]->(category)
MERGE (knowledge)-[:WRITTEN_BY]->(colleague)
MERGE (knowledge)-[:CONTACT_TO]->(colleague)
MERGE (knowledge)-[:UPDATED_BY]->(colleague)
MERGE (knowledge)-[:HAVING]->(keyword)
MERGE (knowledge)-[:LIKED_BY]->(coll)
MERGE (knowledge)-[:COMMENTED_BY]->(coll)
Query to get the org chart of the user -
MATCH (me:Colleague)
WHERE me.collid = '567890'
Return me.name as ColleagueName,me.collid as ColleagueID,0 as Level,true as MyData
Union
MATCH (s:Colleague)<-[:IS_A_MANAGER_OF]-(me)
WHERE me.collid = '567890'
Return s.name as ColleagueName,s.collid as ColleagueID,-1 as Level,false as MyData
Union
Match (peers:Colleague)<-[:IS_A_MANAGER_OF]-(mang:Colleague)-[:IS_A_MANAGER_OF]->(me:Colleague)
Where me.collid = '567890'
Return peers.name as ColleagueName,peers.collid as ColleagueID,0 as Level,false as MyData
Union
MATCH path = (m:Colleague)-[:IS_A_MANAGER_OF*]->(me)
WHERE me.collid = '567890'
Return m.name as ColleagueName,m.collid as ColleagueID,length(path) as Level,false as MyData
Below is the criteria to retrieve articles.
Criteria is to get all articles written by my manager and peers-
Match (n:Article)-[:WRITTEN_BY]->(mang:Colleague)-[:IS_A_MANAGER_OF]->(me:Colleague)
Where me.collid='717865'
Return n.name as ArticleName,mang.name as Author,me.name as DataForColleague
Union
Match (n:Article)-[:WRITTEN_BY]->(peers:Colleague)<-[:IS_A_MANAGER_OF]-(mang:Colleague)-[:IS_A_MANAGER_OF]->(me:Colleague)
Where me.collid='717865'
Return n.name as ArticleName,me.name as DataForColleague,peers.name as Author
Here, say as an example, if we had a total of 1000 articles (instead of 2 in this example) and the above query would return 50 articles, then, I would need a prioritization logic as explained above to prioritize the articles and get the top 10 articles on the basis of scores calculated.

match property on nodes in path of cypher shortestPath

For simplicity sake, let's say I have a graph like this (using actors/movies as an example):
Nodes:
Actor(id:1)
Actor(id:2)
Actor(id:3)
Movie(id:4,rating:'PG-13')
Movie(id:5,rating:'PG-13')
Relationships:
Actor(id:1) APPEARS_IN Movie(id:4)
Actor(id:2) APPEARS_IN Movie(id:4)
Actor(id:2) APPEARS_IN Movie(id:5)
Actor(id:3) APPEARS_IN Movie(id:5)
Cypher to create the sample graph:
create (a1:Actor {id: 1, name: 'Actor 1'})
create (a2:Actor {id: 2, name: 'Actor 2'})
create (a3:Actor {id: 3, name: 'Actor 3'})
create (m1:Movie {id: 4, rating:'PG-13', name: 'Movie 1' } )
create (m2:Movie {id: 5, rating:'PG-13', name: 'Movie 2' } )
create (a1)-[:APPEARS_IN]->(m1)
create (a2)-[:APPEARS_IN]->(m1)
create (a2)-[:APPEARS_IN]->(m2)
create (a3)-[:APPEARS_IN]->(m2)
return *
So now we have want to find the shortest path between Actor(id:1) and Actor(id:3). That's easy enough, we might try a cypher query like:
MATCH p=shortestPath((a:Actor { id: 1 })-[:APPEARS_IN*0..14]-(b:Actor { id: 3 })) RETURN NODES(p)
And we would get a result.
This is where my question comes in How can I put a requirement on the nodes in between (specifically the movie nodes) to only include rating:'R' as part of its path?
This query will only return the nodes of the shortest path between the two actors where all of the movies are R rated.
It filters out the Movie nodes and then checks to make sure that the rating of each movie node in the collection is R.
MATCH p=shortestPath((a:Actor { id: 1 })-[:APPEARS_IN*0..14]-(b:Actor { id: 3 }))
WHERE all ( movie in filter( m in nodes(p) where 'Movie' in labels(m)) where movie.rating = 'R')
RETURN nodes(p)

Resources