cancel
Showing results for 
Search instead for 
Did you mean: 

Join the community at Nodes 2022, our free virtual event on November 16 - 17.

Error: database instance doesn't find user defined procedure

olofdar1
Node

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"));
}


}
}

 

1 ACCEPTED SOLUTION

olofdar1
Node

Solved: I googled a bit carelessly and assumed that the problem was that the function wasn't found (which is the case for many others with the same error message) but the problem is in fact that the recursive function doesn't return correctly because of small error in it.

View solution in original post

1 REPLY 1

olofdar1
Node

Solved: I googled a bit carelessly and assumed that the problem was that the function wasn't found (which is the case for many others with the same error message) but the problem is in fact that the recursive function doesn't return correctly because of small error in it.