├── .travis.yml
├── .gitignore
├── src
├── test
│ └── java
│ │ └── scripts
│ │ └── ScriptsTest.java
└── main
│ └── java
│ └── scripts
│ └── Scripts.java
├── README.adoc
├── pom.xml
└── LICENSE
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | jdk:
4 | - oraclejdk8
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | \#*
3 | target
4 | out
5 | .project
6 | .classpath
7 | .settings
8 | .externalToolBuilders/
9 | .scala_dependencies
10 | .factorypath
11 | .cache
12 | .cache-main
13 | .cache-tests
14 | *.iws
15 | *.ipr
16 | *.iml
17 | .idea
18 | .DS_Store
19 | .shell_history
20 | .mailmap
21 | .java-version
22 | .cache-main
23 | .cache-tests
24 | Thumbs.db
25 | .cache-main
26 | .cache-tests
27 | neo4j-home
28 | dependency-reduced-pom.xml
29 |
--------------------------------------------------------------------------------
/src/test/java/scripts/ScriptsTest.java:
--------------------------------------------------------------------------------
1 | package scripts;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 |
6 | import org.neo4j.driver.v1.*;
7 | import org.neo4j.harness.junit.Neo4jRule;
8 |
9 | import static java.util.Arrays.asList;
10 |
11 | import static org.hamcrest.MatcherAssert.assertThat;
12 | import static org.hamcrest.core.IsEqual.equalTo;
13 | import static org.neo4j.driver.v1.Values.parameters;
14 |
15 | public class ScriptsTest
16 | {
17 | @Rule
18 | public Neo4jRule neo4j = new Neo4jRule().withConfig("dbms.security.procedures.unrestricted","scripts.*").withProcedure( Scripts.class ).withFunction( Scripts.class );
19 |
20 | @Test
21 | public void shouldAllowCreatingAndRunningJSProcedures() throws Throwable
22 | {
23 | try( Driver driver = GraphDatabase.driver( neo4j.boltURI() ,
24 | Config.build().withEncryptionLevel(Config.EncryptionLevel.NONE).toConfig()) )
25 | {
26 |
27 | try (Session session = driver.session()) {
28 |
29 | long nodeId = session.run("CREATE (p:User {name:'Brookreson'}) RETURN id(p)")
30 | .single()
31 | .get(0).asLong();
32 |
33 |
34 | session.run("CALL scripts.function({name}, {code})", parameters("name", "users", "code", "function users() { return collection(db.findNodes(label('User'))); }"));
35 |
36 | StatementResult result = session.run("CALL scripts.run('users',null)");
37 | Value value = result.single().get("value");
38 | System.out.println(value.asObject());
39 | assertThat(value.asNode().id(), equalTo(nodeId));
40 | result = session.run("RETURN scripts.run('users') AS value");
41 | value = result.single().get("value");
42 | System.out.println(value.asObject());
43 | assertThat(value.get(0).asNode().id(), equalTo(nodeId));
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Neo4j Script Procedures
2 |
3 | image:https://travis-ci.org/neo4j-contrib/neo4j-script-procedures.svg?branch=3.1["Build Status", link="https://travis-ci.org/neo4j-contrib/neo4j-script-procedures"]
4 |
5 | This project demonstrates how to create dynamic procedures and functions on top of Neo4j's built in infrastructure.
6 |
7 | You can create JavaScript functions in your database that can be later run using the 'nashorn' engine.
8 |
9 | [Note]
10 | This project requires Neo4j 3.x
11 |
12 | == Installation
13 |
14 | 1. Download the jar from the http://github.com/neo4j-contrib/neo4j-script-procedures/releases/latest[latest release]
15 | 2. Copy it into your `$NEO4J_HOME/plugins` directory.
16 | 3. Add `dbms.security.procedures.unrestricted=scripts.*` to `$NEO4J_HOME/conf/neo4j.conf`
17 | 4. Restart your server.
18 |
19 | == Operations
20 |
21 | The operations are:
22 |
23 | .create function
24 | [source,cypher]
25 | ----
26 | CALL scripts.function({name}, {code})
27 |
28 | {"name" : "users",
29 | "code" : "function users() { return collection(db.findNodes(label('User'))) }"}
30 | ----
31 |
32 | .run function as procedure
33 | [source,cypher]
34 | ----
35 | CALL scripts.run({name}[, {params}])
36 |
37 | CALL scripts.run('users', null)
38 | CALL scripts.run('users')
39 |
40 | -> returns one user per row
41 | ----
42 |
43 | .run function as function
44 | [source,cypher]
45 | ----
46 | RETURN scripts.run({name}[, {params}])
47 |
48 | RETURN scripts.run('users', null) as users
49 |
50 | or
51 |
52 | RETURN scripts.run('users') as users
53 |
54 | -> returns a list of users
55 | ----
56 |
57 | .list functions
58 | [source,cypher]
59 | ----
60 | CALL scripts.list()
61 | ----
62 |
63 | .helper functions
64 | [source,javascript]
65 | ----
66 | function label(s) { return org.neo4j.graphdb.Label.label(s); }
67 |
68 | function type(s) { return org.neo4j.graphdb.RelationshipType.withName(s); }
69 |
70 | function collection(it) { r=[]; while (it.hasNext()) r.push(it.next()); return Java.to(r); }
71 | ----
72 |
73 | == Building it yourself
74 |
75 | This project uses maven, to build a jar-file with the procedure in this
76 | project, simply package the project with maven:
77 |
78 | mvn clean package
79 |
80 | This will produce a jar-file,`target/neo4j-script-procedures-*-SNAPSHOT.jar`, that can be copied in the `$NEO4J_HOME/plugins` directory of your Neo4j instance.
81 |
82 | == License
83 |
84 | Apache License V2, see LICENSE
85 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 |
6 | org.neo4j.procedures
7 | neo4j-script-procedures
8 | 3.4.0
9 |
10 | jar
11 | Neo4j Script Procedures
12 |
13 |
14 | UTF-8
15 | 1.8
16 | ${encoding}
17 | ${encoding}
18 | ${java.version}
19 | ${java.version}
20 | 3.4.10
21 |
22 |
23 |
24 |
25 | org.neo4j
26 | neo4j
27 | ${neo4j.version}
28 | provided
29 |
30 |
31 |
32 | net.biville.florent
33 | neo4j-sproc-compiler
34 | 1.1
35 | true
36 | provided
37 |
38 |
39 |
40 | org.neo4j.test
41 | neo4j-harness
42 | ${neo4j.version}
43 | test
44 |
45 |
46 |
47 | org.neo4j.driver
48 | neo4j-java-driver
49 | 1.7.0
50 | test
51 |
52 |
53 |
54 | junit
55 | junit
56 | 4.12
57 | test
58 |
59 |
60 |
61 |
62 |
63 |
64 | maven-shade-plugin
65 | 2.4.3
66 |
67 |
68 | package
69 |
70 | shade
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/main/java/scripts/Scripts.java:
--------------------------------------------------------------------------------
1 | package scripts;
2 |
3 | import java.util.List;
4 | import java.util.stream.Stream;
5 | import java.util.stream.StreamSupport;
6 |
7 | import org.neo4j.graphdb.GraphDatabaseService;
8 | import org.neo4j.kernel.internal.GraphDatabaseAPI;
9 | import org.neo4j.kernel.impl.core.EmbeddedProxySPI;
10 | import org.neo4j.kernel.impl.core.GraphProperties;
11 | import org.neo4j.kernel.impl.core.GraphPropertiesProxy;
12 | import org.neo4j.logging.Log;
13 | import org.neo4j.procedure.Context;
14 | import org.neo4j.procedure.Name;
15 | import org.neo4j.procedure.Mode;
16 | import org.neo4j.procedure.PerformsWrites;
17 | import org.neo4j.procedure.Procedure;
18 | import org.neo4j.procedure.UserFunction;
19 |
20 | import javax.script.Invocable;
21 | import javax.script.ScriptEngine;
22 | import javax.script.ScriptEngineManager;
23 | import javax.script.ScriptException;
24 |
25 | import static org.neo4j.helpers.collection.MapUtil.stringMap;
26 |
27 | public class Scripts {
28 |
29 | private static String PREFIX = "script.function.";
30 | public static final Object[] NO_OBJECTS = new Object[0];
31 |
32 | @Context
33 | public GraphDatabaseAPI db;
34 |
35 | @Context
36 | public Log log;
37 |
38 | private static final GraphPropertiesProxy NO_GRAPH_PROPERTIES = new GraphPropertiesProxy(null);
39 | private static GraphProperties graphProperties = NO_GRAPH_PROPERTIES;
40 |
41 | private static ThreadLocal engine = new ThreadLocal<>();
42 |
43 | private GraphProperties graphProperties() {
44 | if (graphProperties == NO_GRAPH_PROPERTIES)
45 | graphProperties = this.db.getDependencyResolver().resolveDependency(EmbeddedProxySPI.class).newGraphPropertiesProxy();
46 | return graphProperties;
47 | }
48 | private ScriptEngine getEngine() {
49 | if (engine.get() == null) {
50 | ScriptEngine js = new ScriptEngineManager().getEngineByName("nashorn");
51 | addFunctions(js,
52 | "function label(s) org.neo4j.graphdb.Label.label(s)",
53 | "function type(s) org.neo4j.graphdb.RelationshipType.withName(s)",
54 | "function collection(it) { r=[]; while (it.hasNext()) r.push(it.next()); return Java.to(r); }");
55 | engine.set(js);
56 | }
57 | return engine.get();
58 | }
59 |
60 | private void addFunctions(ScriptEngine js, String...script) {
61 | try {
62 | for (String s : script) js.eval(s);
63 | } catch (ScriptException e) {
64 | throw new RuntimeException(e);
65 | }
66 | }
67 |
68 | @UserFunction("scripts.run")
69 | public Object runFunction(@Name("name") String name, @Name(value="params",defaultValue="[]") List