Trinity
Why Trinity?
While working on Liquigraph, we realized that having to keep several branches per Neo4j version was a pain.
This was mainly due to the fact there was no JDBC driver supporting both Neo4j v2 and Neo4j v3.
Moreover, we did not need a full-fledged JDBC implementation, we just needed to run Cypher queries.
And then, Trinity was born! With a simple API in mind, some build and implementation tricks to work around Neo4j incompatibilities, we achieved what we wanted: having a single way to run Cypher with any Neo4j setup.
Why the name?
-
Because badass women for the win!
-
And also because Twitter!
Minimum Viable Snippet
Bolt
package in.da.matrix;
import org.liquigraph.trinity.bolt.BoltClient;
import org.neo4j.driver.v1.AuthToken;
import org.neo4j.driver.v1.AuthTokens;
import org.neo4j.driver.v1.GraphDatabase;
import java.util.List;
public class TrinityBoltExample {
public static void main(String[] args) {
AuthToken authentication = AuthTokens.basic("neo4j", "toto");
try (BoltClient trinity =
new BoltClient(GraphDatabase.driver("bolt://localhost:7687", authentication))) {
trinity.runSingleTransaction("MATCH (n:Crew) RETURN COUNT(n)")
.consume(
(List<Fault> errors) -> {
// do something amazingly useful
},
(List<Data> data) -> {
// do something usefully amazing with it
}
);
}
}
}
HTTP
package in.da.matrix;
import org.liquigraph.trinity.Data;
import org.liquigraph.trinity.Either;
import org.liquigraph.trinity.Fault;
import org.liquigraph.trinity.http.BasicAuthenticator;
import org.liquigraph.trinity.http.HttpClient;
import java.util.List;
import okhttp3.OkHttpClient;
public class TrinityHttpExample {
public static void main(String... args) {
// if auth is disabled, just use: new HttpClient("http://localhost:7474")
HttpClient trinity = new HttpClient(
"http://localhost:7474",
okHttpClient("neo4j", "s3cr3t")
);
Either<List<Fault>, List<Data>> either =
trinity.runSingleTransaction("MATCH (n:Crew) RETURN COUNT(n)");
if (either.isRight()) {
List<Data> data = either.getRight();
// do something usefully amazing with it
}
else {
List<Fault> errors = either.getLeft();
// do something amazingly useful
}
}
private static OkHttpClient okHttpClient(String username, String password) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder = builder.authenticator(new BasicAuthenticator(username, password));
return builder.build();
}
}
Embedded for Neo4j v3
package in.da.matrix;
import org.liquigraph.trinity.neo4jv3.EmbeddedClient;
import org.liquigraph.trinity.Data;
import org.liquigraph.trinity.Either;
import org.liquigraph.trinity.Fault;
import java.nio.file.Paths;
import java.util.List;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
public class TrinityEmbeddedV3Example {
public static void main(String... args) {
File path = Paths.get("some", "path").toFile();
EmbeddedClient trinity = new EmbeddedClient(graphDatabase(path));
Either<List<Fault>, List<Data>> either =
trinity.runSingleTransaction("MATCH (n:Crew) RETURN COUNT(n)");
if (either.isRight()) {
List<Data> data = either.getRight();
// do something usefully amazing with it
}
else {
List<Fault> errors = either.getLeft();
// do something amazingly useful
}
}
private static GraphDatabaseService graphDatabase(File path) {
return new GraphDatabaseFactory().newEmbeddedDatabase(path);
}
}
Embedded for Neo4j v2
package in.da.matrix;
import org.liquigraph.trinity.neo4jv2.EmbeddedClient;
import org.liquigraph.trinity.Data;
import org.liquigraph.trinity.Either;
import org.liquigraph.trinity.Fault;
import java.util.List;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
public class TrinityEmbeddedV2Example {
public static void main(String... args) {
EmbeddedClient trinity = new EmbeddedClient(graphDatabase("/some/path"));
Either<List<Fault>, List<Data>> either =
trinity.runSingleTransaction("MATCH (n:Crew) RETURN COUNT(n)");
if (either.isRight()) {
List<Data> data = either.getRight();
// do something usefully amazing with it
}
else {
List<Fault> errors = either.getLeft();
// do something amazingly useful
}
}
private static GraphDatabaseService graphDatabase(String path) {
return new GraphDatabaseFactory().newEmbeddedDatabase(path);
}
}
Download
Application developers
Picking the right implementation is just a matter of selecting the right artifact:
Artifact coordinates | Version of Neo4j | JRE prerequisites |
---|---|---|
|
2.0.0 to latest 2.x |
JRE 7 or later |
|
3.0.0 to latest 3.x |
JRE 8 or later |
|
2.0.0 to latest 3.x |
JRE 7 or later |
|
3.0.0 to latest 3.x |
JRE 8 or later |
Note
|
Trinity <dependency>
<groupId>org.liquigraph.trinity</groupId>
<artifactId>trinity-embedded-2x</artifactId>
<version><!-- CURRENT VERSION --></version>
</dependency>
|
Library developers
If you develop a library that needs to support several Neo4j setups as well, you can pick any of the two bundles:
-
Neo4j 2:
<dependency> <groupId>org.liquigraph.trinity</groupId> <artifactId>trinity-neo4j-v2</artifactId> <version><!-- CURRENT VERSION --></version> </dependency>
-
Neo4j 3:
<dependency> <groupId>org.liquigraph.trinity</groupId> <artifactId>trinity-neo4j-v3</artifactId> <version><!-- CURRENT VERSION --></version> </dependency>
Note
|
These two bundles cannot be included together, as they require different versions of Neo4j and JRE. |
Please note that Trinity instance discovery is implemented via the good old Java Service Provider Interfaces.
To retrieve a Trinity instance, one just needs to include one of the two bundles and use org.liquigraph.trinity.CypherClientLookup
like in the example below:
package in.da.matrix;
import org.liquigraph.trinity.CypherClientLookup;
import org.liquigraph.trinity.CypherTransport;
import org.liquigraph.trinity.OngoingTransaction;
import org.liquigraph.trinity.Optional; // for trinity-neo4j-v2
import java.util.Optional; // for trinity-neo4j-v3
import java.util.Properties;
public class TrinityHttpDiscoveryExample {
public static void main(String... args) {
CypherClientLookup lookup = new CypherClientLookup();
Optional<CypherClient<OngoingTransaction>> maybeTrinity = lookup.getInstance(
CypherTransport.HTTP,
httpProperties()
);
// trinity-neo4j-v3 users can rely on the primitive Java 8 Optional
// they are encouraged to use instead maybeTrinity.ifPresent(trinity -> ...)
if (maybeTrinity.isPresent()) {
CypherClient<OngoingTransaction> trinity = maybeTrinity.get();
// then you can run Cypher queries as in the above examples
}
}
private static Properties httpProperties() {
Properties props = new Properties();
props.setProperty("cypher.http.baseurl", "http://localhost:7474");
props.setProperty("cypher.http.username", "neo4j");
props.setProperty("cypher.http.password", "s3cr3t");
return props;
}
}