I am new to Neo4j so I quite stuck with looping through some values.
I have a list of skill to skill strings
let data = [
'big_data, business_intelligence',
'big_data, data_collection',
'big_data, economic_growth',
'big_data, economy'
]
And I want to create or update the relation between left side with right side
for (let item of data) {
CreateSkillToSkillRelation(item);
}
const CreateSkillToSkillRelation = async (relation) => {
let mainSkill = relation.split(",")[0];
let secundarySkill = relation.split(",")[1];
try {
// Check if the relationship exists
let { records } = await session.run(
"MATCH(main:SKILL {name:$mainSkill}) -[relation:SKILL_TO_SKILL]-> (secundary:SKILL {name:$secundarySkill}) RETURN relation",
{ mainSkill, secundarySkill }
);
let count =
records[0]?._fields[0].properties.count > 0
? records[0]._fields[0].properties.count + 1
: 1;
// If count is greater then 1 then lets update the counter
if (count > 1) {
await session.run(
"MATCH(main:SKILL {name:$mainSkill}) -[relation:SKILL_TO_SKILL]-> (secundary:SKILL {name:$secundarySkill}) SET relation.count = $count RETURN main, secundary",
{
mainSkill,
secundarySkill,
count,
}
);
}
// Otherwise the skill relation is not created so lets create one
else {
await session.run(
"CREATE(main:SKILL {name:$mainSkill}) -[:SKILL_TO_SKILL {count:$count}]-> (secundary:SKILL {name:$secundarySkill}) RETURN main, secundary",
{
mainSkill,
secundarySkill,
count,
}
);
}
await session.close();
} catch (error) {
console.log(error);
}
};
But every time when I run this I get the following error Neo4jError: Queries cannot be run directly on a session with an open transaction; either run from within the transaction or use a different session.
Any idea how can I solve this?
for (let item of data) {
CreateSkillToSkillRelation(item);
}
Is not awaiting the promises you create and so you are basically trying to run all of these promises concurrently against a single session which only supports a single concurrent transaction.
You should create a session in each call of CreateSkillToSkillRelation or await each call to it using a single session.
Though note you close the session at the end of CreateSkillToSkillRelation but only on success, might I suggest moving await session.close(); into a finally block.
The answer of the colleague @just_another_dotnet_dev is absolutely correct: you run asynchronous functions in a loop, and close the session in one of them.
The Cipher language is very rich, and you can use it to do everything that you tried to do with a loop in Javascript. Something like this, using UNWIND and MERGE:
const CreateSkillToSkillRelations = async (data) => {
const session = driver.session();
try {
let { records } = await session.run(
`WITH split(row, ',') as rels
WITH trim(rels[0]) as mainSkill,
trim(rels[1]) as secundarySkill
MERGE (main:SKILL {name: mainSkill})
-[relation:SKILL_TO_SKILL]->
(secundary:SKILL {name: secundarySkill})
ON CREATE SET relation.count = 1
ON MATCH SET relation.count = relation.count + 1
RETURN main, relation, secundary`,
{ data }
);
} catch (error) {
console.log(error);
} finally {
await session.close()
}
};