Hello! My user defined function isn't working. I get the following error when I test it:
org.neo4j.driver.exceptions.ClientException: Failed to invoke function `org.enkelt.findSettingsOfNod`e: Caused by: java.util.NoSuchElementException
The error occurs when the following line is executed in the test class:
Node node = session.run("MATCH (umbrella:umbrella) RETURN " +
"org.enkelt.findSettingsOfNode(umbrella) AS node").single().get("node").asNode();
I can't seem to figure out why. It seems to me that I've done everything correctly. I've denoted the name correctly, included the correct class with the embedded database instance. I've already written and tested 4 procedures/functions this way and they all work fine. What's the problem?
This is the procedure class.
Class: FindSettingsOfNode
package org.enkelt;
import org.neo4j.graphdb.*;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class FindSettingsOfNode {
// This function finds a node based on path.
static final Label rootLabel = Label.label("root");
static final String childRelName = "CHILD";
static final Label umbrellaLabel = Label.label("umbrella");
static final Label impulseLabel = Label.label("impulse");
static final Label mtriggerLabel = Label.label("mtrigger");
static final Label triggerLabel = Label.label("trigger");
@Context
public Transaction tx;
@Context
public Log log;
@UserFunction(value = "org.enkelt.findSettingsOfNode")
@Description("Description")
public Node findSettingsOfNode(@Name("Node") Node node) {
String settingsName = getNodeSettingsName(node);
return getSettingsNode(node, settingsName);
}
public Node getSettingsNode(Node node, String settingsName) {
Iterator<Relationship> settingsIterator = node.getRelationships(
Direction.OUTGOING, RelationshipType.withName(settingsName)).iterator();
if (settingsIterator.hasNext()) {
return settingsIterator.next().getEndNode();
} else {
Iterator<Relationship> parentIterator = node.getRelationships(
Direction.OUTGOING, RelationshipType.withName(childRelName)).iterator();
Node parent = parentIterator.next().getEndNode();
return getSettingsNode(parent, settingsName);
}
}
public String getNodeSettingsName(Node node) {
ArrayList<Label> validNames = new ArrayList<>(List.of(rootLabel, umbrellaLabel, impulseLabel,
mtriggerLabel, triggerLabel));
Iterator<Label> labels = node.getLabels().iterator();
if (!labels.hasNext()) {
throw new RuntimeException("Invalid node");
}
Label label = labels.next();
if (! validNames.contains(label)) {
throw new RuntimeException("Invalid node");
} else {
return label.toString();
}
}
}
And this is the test class.
Class: FindSettingsOfNodeClass
package org.enkelt;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.*;
import org.neo4j.driver.*;
import org.neo4j.driver.types.Node;
import org.neo4j.graphdb.Label;
import org.neo4j.harness.Neo4j;
import org.neo4j.harness.Neo4jBuilders;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class FindSettingsOfNodeTest {
private Driver driver;
private Neo4j embeddedDatabaseServer;
@BeforeAll
void initializeNeo4j() {
this.embeddedDatabaseServer \= Neo4jBuilders.newInProcessBuilder()
.withDisabledServer()
.withFunction(FindSettingsOfNode.class)
.build();
this.driver \= GraphDatabase.driver(embeddedDatabaseServer.boltURI());
}
@AfterAll
void closeDriver() {
this.driver.close();
this.embeddedDatabaseServer.close();
}
@AfterEach
void cleanDb() {
try (Session session = driver.session()) {
session.run("MATCH (n) DETACH DELETE n");
}
}
@Test
void testUmbrellaSettings() {
String queryString = "CREATE (root:root {name:'root'})" +
"CREATE (umbrella:umbrella {name:'umbrella'})" +
"CREATE (impulse:impulse {name:'impulse'})" +
"CREATE (mtrigger:mtrigger {name:'mtrigger'})" +
"CREATE (umbrellaSettings:umbrellaSettings {name:'default'})" +
"CREATE (impulseSettings:impulseSettings {name:'default'})" +
"CREATE (mtriggerSettings:mtriggerSettings {name:'default'})" +
"CREATE (root)-\[:CHILD\]->(umbrella)-\[:CHILD\]->(impulse)-\[:CHILD\]->(mtrigger)," +
"(root)-\[:UMBRELLASETTINGS\]->(umbrellaSettings)," +
"(root)-\[:IMPULSESETTINGS\]->(impulseSettings)," +
"(root)-\[:MTRIGGERSETTINGS\]->(mtriggerSettings)";
try (Session session = driver.session()) {
session.run(queryString);
Node node = session.run("MATCH (umbrella:umbrella) RETURN " +
"org.enkelt.findSettingsOfNode(umbrella) AS node").single().get("node").asNode();
assertTrue(node.hasLabel("umbrellaSettings"));
}
}
}