You can use Cypher in your own user-defined function or procedure.
package mypackage;
import org.neo4j.graphdb.*;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;
public class MyFunctionClass
{
@Context
public GraphDatabaseService db;
@UserFunction
@Description("mypackage.myfunction('arg1') - do a thing")
public Node myfunction(
@Name("arg1") String arg1
){
try (
Transaction tx = db.beginTx();
Result result = db.execute("MATCH (n:Node) RETURN n LIMIT 10")
) {
while ( result.hasNext()) {
Map<String,Object> row = result.next();
for ( Map.Entry<String,Object> column : row.entrySet() ) {
Object node = column.getValue();
// here you'll have every match.
// note, things get trickier when you want to process multiple result columns.
}
}
tx.success();
} catch (Throwable e) {
throw new CypherException("Cypher.query failed: "+query, e);
}
}
}
@tony.chiboucas oh wow that's amazing. I thought that I'll have to initialize db myself. Can you tell me more about the @Context magic that's happening?
What are the other things which one can get with @Context?
The @Context is auto-wired dependency injection. It only works well with GraphDatabaseService. There are other parts of the Neo4j core you can get at with static methods and singletons, but beware, they don't work the way you'd expect.
Take a poke at my code, it's still a bit of a mess, but there's some gems in there:
might be a better idea than using the db directly. This is because if the calling code starts a transaction, and then your user-defined function (i don't know if this is a problem for procedures) starts another transaction then it causes some sort of transaction-nesting problem for neo4j
Do you have a way to execute Cypher, or query the database, that way? When I tried something like this, it only works if your function is standalone, and does not need to interact with anything other that the arguments it is provided.
I certainly agree that such functions are ideal, and IFF you can define your functions that way, with a Cypher friendly return, code and use of the function is much simpler.
Yes, both... maybe a sample code-block? It's a bit of a chore unraveling how to do those things from Neo4j documentation.
Unfortunately my usecase is rather simplistic, so either I have user-defined functions where I'm not using cypher at all or one where I only have one cypher query and I'm post processing the results in a way I find easier than doing in purely cypher-defined functions (e.g. apoc.custom.asFunction).
So I can't comment on if doing "other things" may work. As for executing cypher without injecting DB, I have amended your example to show what I meant.
package mypackage;
import org.neo4j.graphdb.*;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;
public class MyFunctionClass
{
@Context
public Transaction tx;
@UserFunction
@Description("mypackage.myfunction('arg1') - do a thing")
public Node myfunction(
@Name("arg1") String arg1
){
try (
Result result = tx.execute("MATCH (n:Node) RETURN n LIMIT 10")
) {
while ( result.hasNext()) {
Map<String,Object> row = result.next();
for ( Map.Entry<String,Object> column : row.entrySet() ) {
Object node = column.getValue();
// here you'll have every match.
// note, things get trickier when you want to process multiple result columns.
}
}
} catch (Throwable e) {
throw new CypherException("Cypher.query failed: "+query, e);
}
}
}
I hope this helps. I'm certainly no expert in this area, but if doing things this way is resulting in an error for you, it'll be interesting to see the error message and minimum sample code that produces that error. And perhaps I or someone else could help!