I try to write a function in Java that finds nodes in my Neo4j graph database with same names and labels.
@UserFunction(value = "boris.example")
@Description("finds nodes with same name")
public ResourceIterator<Node> passName(@Name("nodeId") long nodeId)
{
Node nodeX = null;
Node node = db.getNodeById( nodeId ); //Find node with spcific ID
Object nameVal = node.getProperties("name"); //Get its name
Label label = Label.label("VersionableObject"); //Decl. of label
// Find nodes by label and name
ResourceIterator<Node> nodes = db.findNodes(label, "name", nameVal);
nodes.close();
return nodes;
}
This function causes following exception an refuses to start Neo4j:
"Argument ResourceIterator<Node>
at position 0 in passName
with
type Label
cannot be converted to a Neo4j type: Don't know how to map org.neo4j.graphdb.ResourceIterator
to the Neo4j Type System."
Then I tried to convert ResourceIterator to Stream using
Stream<Node> nodesStream = nodes.stream();
Same Error - Neo4j can't map Stream..
ResourceIterator<Node> nodes = db.findNodes(label, "name", nameVal);
while (nodes.hasNext()) {
nodeX = nodes.next();
}
nodes.close();
return nodeX;
Output in Neo4j empty, even though it should find 3 nodes.
Converting the Stream to Array or List did not work as well.
Finding one node and displaying itself or its properties works, e.g.:
Node node = db.getNodeById( nodeId );
Map<String, Object> propertyMap = node.getProperties("name");
EDIT
After changing @UserFunction to @Procedure with mode.READ:
package org.neo4j.example.procedure_template;
import org.neo4j.graphdb.ResourceIterator;
import java.util.stream.Stream;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.logging.Log;
import org.neo4j.procedure.*;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
public class FindNode {
@Context
public GraphDatabaseService db;
@Context
public Log log;
public FindNode() {
}
@Procedure(value = "boris.getAllNodesWithProperty", mode = Mode.READ)
@Description("boris.getAllNodesWithProperty - finds Node by ID and return defined Property")
public Stream<Node> passName(@Name("nodeId") long nodeId)
{
Node node = db.getNodeById( nodeId );
Object nameVal = node.getProperties("name");
Label label = Label.label("VersionableObject");
ResourceIterator<Node> nodes = db.findNodes(label, "name", nameVal);
Stream<Node> nodesStream = nodes.stream();
return nodesStream;
}
}
In this case i get: "Procedures with zero output fields must be declared as VOID".
Any ideas? Thank you!
In Neo4j you can extend Cypher by creating some custom
user defined function
orprocedure
.A
user defined function
is just a convertor, it's readonly, and return a single type that can be :long, Long, double, Double, boolean, Boolean, String, Node, Relationship, Path, Map<String, Object, or List<T>
A
procedure
can takes arguments, perform operations on the database, and return results (as a Stream).In your code, you have defined a
user function
, and I think what you want is aprocedure
. That's why you have the cast error fromResourceIterator
to theNeo4j Type System
.You have to make those changes :
@UserFunction(value = "boris.example")
by@Procedure(value = "boris.example", mode = Mode.READ)
Stream<Node>
Cheers.
Edit
A procedure must return a
pojo
or a simple type. So you have to wrap yourNode
in a pojo like this :