diff --git a/janusgraph-dist/src/assembly/descriptor/archive.xml b/janusgraph-dist/src/assembly/descriptor/archive.xml index c6be10d7d7..ec8830c51b 100644 --- a/janusgraph-dist/src/assembly/descriptor/archive.xml +++ b/janusgraph-dist/src/assembly/descriptor/archive.xml @@ -12,4 +12,15 @@ ${assembly.descriptor.dir}/htmldocs.component.xml ${assembly.descriptor.dir}/readmes.component.xml + + + + ${top.level.basedir}/janusgraph-examples + /examples + + **/target/** + + + + diff --git a/janusgraph-examples/README.md b/janusgraph-examples/README.md new file mode 100644 index 0000000000..7890429e20 --- /dev/null +++ b/janusgraph-examples/README.md @@ -0,0 +1,39 @@ +# JanusGraph Examples + +The JanusGraph examples show the basics of how to configure and construct +a graph application. It uses [Apache Maven](https://maven.apache.org) to +manage the numerous dependencies required to build the application. The common +application will: + +* Open and initialize the graph +* Define the schema +* Build the graph +* Run traversal queries to get data from the graph +* Make updates to the graph +* Close the graph + +By using different graph configurations, the same example code can run against +the various supported storage and indexing backends. + +## Prerequisites + +* Java 8 Developer Kit, update 40 or higher +* Apache Maven, version 3.3 or higher + +## Building the Examples + +``` +mvn clean install +``` + +## Running the Examples + +Refer to the directions in each sub-directory. + +* [Common](example-common/README.md) +* [BerkeleyJE](example-berkeleyje/README.md) +* [Cassandra](example-cassandra/README.md) +* [CQL](example-cql/README.md) +* [HBase](example-hbase/README.md) +* [RemoteGraph](example-remotegraph/README.md) +* [TinkerGraph](example-tinkergraph/README.md) diff --git a/janusgraph-examples/example-berkeleyje/.gitignore b/janusgraph-examples/example-berkeleyje/.gitignore new file mode 100644 index 0000000000..f87a0f249c --- /dev/null +++ b/janusgraph-examples/example-berkeleyje/.gitignore @@ -0,0 +1 @@ +jgex/ diff --git a/janusgraph-examples/example-berkeleyje/README.md b/janusgraph-examples/example-berkeleyje/README.md new file mode 100644 index 0000000000..7a62c4bfdc --- /dev/null +++ b/janusgraph-examples/example-berkeleyje/README.md @@ -0,0 +1,48 @@ +# BerkeleyJE Storage, Lucene Index + +## About BerkeleyJE and Lucene + +[Oracle Berkeley DB Java Edition](http://www.oracle.com/technetwork/database/berkeleydb/overview/index-093405.html) +is an embedded database, so it runs within your application rather than as +a standalone server. By including the `janusgraph-berkeleyje` dependency, +the required jar files are pulled in. The data is stored in a directory on +the file system. + +[Apache Lucene](http://lucene.apache.org/) is an embedded index, so it runs +within your application rather than as a standalone server. By including the +`janusgraph-lucene` dependency, the required jar files are pulled in. The +data is stored in a directory on the file system. + +## JanusGraph configuration + +[`jgex-berkeleyje.properties`](conf/jgex-berkeleyje.properties) contains +the directory locations for BerkeleyJE and Lucene. + +Refer to the JanusGraph [configuration reference](http://docs.janusgraph.org/latest/config-ref.html) +for additional properties. + +## Running the example + +Use [Apache Maven](http://maven.apache.org/) and the +[exec-maven-plugin](http://www.mojohaus.org/exec-maven-plugin/java-mojo.html) +to pull in the required jar files onto the runtime classpath. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-berkeleyje + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dexec.args="conf/jgex-berkeleyje.properties" +``` + +## Drop the graph + +Make sure to stop the application before dropping the graph. The configuration +uses the application name `jgex` as the root directory for the BerkeleyJE +and Lucene directories. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-berkeleyje + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dexec.args="conf/jgex-berkeleyje.properties drop" + +$ rm -rf jgex/ +``` diff --git a/janusgraph-examples/example-berkeleyje/conf/jgex-berkeleyje.properties b/janusgraph-examples/example-berkeleyje/conf/jgex-berkeleyje.properties new file mode 100644 index 0000000000..1a1e88a644 --- /dev/null +++ b/janusgraph-examples/example-berkeleyje/conf/jgex-berkeleyje.properties @@ -0,0 +1,7 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=berkeleyje +storage.directory=jgex/berkeleyje + +index.jgex.backend=lucene +index.jgex.directory=jgex/lucene diff --git a/janusgraph-examples/example-berkeleyje/pom.xml b/janusgraph-examples/example-berkeleyje/pom.xml new file mode 100644 index 0000000000..d355eda854 --- /dev/null +++ b/janusgraph-examples/example-berkeleyje/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-berkeleyje + pom + Example-BerkeleyJE: BerkeleyJE Storage, Lucene Index + http://janusgraph.org + + + + + org.janusgraph + example-common + ${project.version} + runtime + + + org.janusgraph + janusgraph-berkeleyje + ${project.version} + runtime + + + org.janusgraph + janusgraph-lucene + ${project.version} + runtime + + + + diff --git a/janusgraph-examples/example-cassandra/README.md b/janusgraph-examples/example-cassandra/README.md new file mode 100644 index 0000000000..aeea5e6955 --- /dev/null +++ b/janusgraph-examples/example-cassandra/README.md @@ -0,0 +1,53 @@ +# Cassandra Thrift Storage, Elasticsearch Index + +## About Cassandra and Elasticsearch + +[Apache Cassandra](http://cassandra.apache.org/) is a distributed database +designed for scalability and high availability. Cassandra supports two +protocols for communications, Thrift (legacy RPC protocol) and CQL (native +protocol). Depending on the Cassandra version, Thrift may not be started by +default. Make sure that [Thrift is started](http://docs.datastax.com/en/cassandra/2.1/cassandra/tools/toolsStatusThrift.html) +when using this example. + +[Elasticsearch](https://www.elastic.co/products/elasticsearch) is a scalable, +distributed search engine. + +> Check the JanusGraph [version compatibility](http://docs.janusgraph.org/latest/version-compat.html) +to ensure you select versions of Cassandra and Elasticsearch compatible with +this JanusGraph release. + +## JanusGraph configuration + +* [`jgex-cassandra.properties`](conf/jgex-cassandra.properties) contains the +Cassandra and Elasticsearch server locations. By providing different values +for `storage.cassandra.keyspace` and `index.jgex.index-name`, you can store +multiple graphs on the same Cassandra and Elasticsearch servers. Refer to +the JanusGraph [configuration reference](http://docs.janusgraph.org/latest/config-ref.html) +for additional properties. + +* [`logback.xml`](conf/logback.xml) configures logging with [Logback](https://logback.qos.ch/), +which is the logger used by Cassandra. The example configuration logs to the +console and adjusts the logging level for some noisier packages. Refer to +the Logback [manual](https://logback.qos.ch/manual/index.html) for additional +details. + +## Run the example + +Use [Apache Maven](http://maven.apache.org/) and the [exec-maven-plugin](http://www.mojohaus.org/exec-maven-plugin/java-mojo.html) +to pull in the required jar files onto the runtime classpath. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-cassandra + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-cassandra.properties" +``` + +## Drop the graph + +Make sure to stop the application before dropping the graph. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-cassandra + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-cassandra.properties drop" +``` diff --git a/janusgraph-examples/example-cassandra/conf/jgex-cassandra.properties b/janusgraph-examples/example-cassandra/conf/jgex-cassandra.properties new file mode 100644 index 0000000000..98204b76ca --- /dev/null +++ b/janusgraph-examples/example-cassandra/conf/jgex-cassandra.properties @@ -0,0 +1,9 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=cassandra +storage.cassandra.keyspace=jgex +storage.hostname=127.0.0.1 + +index.jgex.backend=elasticsearch +index.jgex.index-name=jgex +index.jgex.hostname=127.0.0.1 diff --git a/janusgraph-examples/example-cassandra/conf/logback.xml b/janusgraph-examples/example-cassandra/conf/logback.xml new file mode 100644 index 0000000000..a6e60f82a6 --- /dev/null +++ b/janusgraph-examples/example-cassandra/conf/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss} %-5level %logger - %msg%n + + + + + + + diff --git a/janusgraph-examples/example-cassandra/pom.xml b/janusgraph-examples/example-cassandra/pom.xml new file mode 100644 index 0000000000..81f858dbff --- /dev/null +++ b/janusgraph-examples/example-cassandra/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-cassandra + pom + Example-Cassandra: C* Thrift Storage, ES Index + http://janusgraph.org + + + + + org.janusgraph + example-common + ${project.version} + runtime + + + org.janusgraph + janusgraph-cassandra + ${project.version} + runtime + + + org.janusgraph + janusgraph-es + ${project.version} + runtime + + + + diff --git a/janusgraph-examples/example-common/README.md b/janusgraph-examples/example-common/README.md new file mode 100644 index 0000000000..c929353f88 --- /dev/null +++ b/janusgraph-examples/example-common/README.md @@ -0,0 +1,24 @@ +# Common Example + +## About the common example + +`GraphApp` is an abstract class that defines a basic structure for a graph +application. It contains methods for configuring a graph instance, defining +a graph schema, creating a graph structure, and querying a graph. + +`JanusGraphApp` is a subclass of `GraphApp` using JanusGraph-specific methods +to create the schema. + +## In-Memory configuration + +[`jgex-inmemory.properties`](conf/jgex-inmemory.properties) contains the +settings for the JanusGraph [in-memory storage backend](http://docs.janusgraph.org/latest/inmemorystorage.html). +This backend is primarily for testing purposes. + +## Running the example + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-common + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dexec.args="conf/jgex-inmemory.properties" +``` diff --git a/janusgraph-examples/example-common/conf/jgex-inmemory.properties b/janusgraph-examples/example-common/conf/jgex-inmemory.properties new file mode 100644 index 0000000000..9608404cfb --- /dev/null +++ b/janusgraph-examples/example-common/conf/jgex-inmemory.properties @@ -0,0 +1,3 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=inmemory diff --git a/janusgraph-examples/example-common/pom.xml b/janusgraph-examples/example-common/pom.xml new file mode 100644 index 0000000000..27e864144a --- /dev/null +++ b/janusgraph-examples/example-common/pom.xml @@ -0,0 +1,22 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-common + Example-Common: Common Graph Code for Examples + http://janusgraph.org + + + + org.janusgraph + janusgraph-core + ${project.version} + + + + diff --git a/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/GraphApp.java b/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/GraphApp.java new file mode 100644 index 0000000000..0ac284e884 --- /dev/null +++ b/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/GraphApp.java @@ -0,0 +1,326 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.GraphFactory; +import org.janusgraph.core.attribute.Geoshape; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GraphApp { + private static final Logger LOGGER = LoggerFactory.getLogger(GraphApp.class); + + protected String propFileName; + protected Configuration conf; + protected Graph graph; + protected GraphTraversalSource g; + protected boolean supportsTransactions; + protected boolean supportsSchema; + protected boolean supportsGeoshape; + + /** + * Constructs a graph app using the given properties. + * @param fileName location of the properties file + */ + public GraphApp(final String fileName) { + propFileName = fileName; + } + + /** + * Opens the graph instance. If the graph instance does not exist, a new + * graph instance is initialized. + */ + public GraphTraversalSource openGraph() throws ConfigurationException { + LOGGER.info("opening graph"); + conf = new PropertiesConfiguration(propFileName); + graph = GraphFactory.open(conf); + g = graph.traversal(); + return g; + } + + /** + * Closes the graph instance. + */ + public void closeGraph() throws Exception { + LOGGER.info("closing graph"); + try { + if (g != null) { + g.close(); + } + if (graph != null) { + graph.close(); + } + } finally { + g = null; + graph = null; + } + } + + /** + * Drops the graph instance. The default implementation does nothing. + */ + public void dropGraph() throws Exception { + } + + /** + * Creates the graph schema. The default implementation does nothing. + */ + public void createSchema() { + } + + /** + * Adds the vertices, edges, and properties to the graph. + */ + public void createElements() { + try { + // naive check if the graph was previously created + if (g.V().has("name", "saturn").hasNext()) { + if (supportsTransactions) { + g.tx().rollback(); + } + return; + } + LOGGER.info("creating elements"); + + // see GraphOfTheGodsFactory.java + + Vertex saturn = g.addV("titan").property("name", "saturn").property("age", 10000).next(); + Vertex sky = g.addV("location").property("name", "sky").next(); + Vertex sea = g.addV("location").property("name", "sea").next(); + Vertex jupiter = g.addV("god").property("name", "jupiter").property("age", 5000).next(); + Vertex neptune = g.addV("god").property("name", "neptune").property("age", 4500).next(); + Vertex hercules = g.addV("demigod").property("name", "hercules").property("age", 30).next(); + Vertex alcmene = g.addV("human").property("name", "alcmene").property("age", 45).next(); + Vertex pluto = g.addV("god").property("name", "pluto").property("age", 4000).next(); + Vertex nemean = g.addV("monster").property("name", "nemean").next(); + Vertex hydra = g.addV("monster").property("name", "hydra").next(); + Vertex cerberus = g.addV("monster").property("name", "cerberus").next(); + Vertex tartarus = g.addV("location").property("name", "tartarus").next(); + + g.V(jupiter).as("a").V(saturn).addE("father").from("a").next(); + g.V(jupiter).as("a").V(sky).addE("lives").property("reason", "loves fresh breezes").from("a").next(); + g.V(jupiter).as("a").V(neptune).addE("brother").from("a").next(); + g.V(jupiter).as("a").V(pluto).addE("brother").from("a").next(); + + g.V(neptune).as("a").V(sea).addE("lives").property("reason", "loves waves").from("a").next(); + g.V(neptune).as("a").V(jupiter).addE("brother").from("a").next(); + g.V(neptune).as("a").V(pluto).addE("brother").from("a").next(); + + g.V(hercules).as("a").V(jupiter).addE("father").from("a").next(); + g.V(hercules).as("a").V(alcmene).addE("mother").from("a").next(); + + if (supportsGeoshape) { + g.V(hercules).as("a").V(nemean).addE("battled").property("time", 1) + .property("place", Geoshape.point(38.1f, 23.7f)).from("a").next(); + g.V(hercules).as("a").V(hydra).addE("battled").property("time", 2) + .property("place", Geoshape.point(37.7f, 23.9f)).from("a").next(); + g.V(hercules).as("a").V(cerberus).addE("battled").property("time", 12) + .property("place", Geoshape.point(39f, 22f)).from("a").next(); + } else { + g.V(hercules).as("a").V(nemean).addE("battled").property("time", 1) + .property("place", getGeoFloatArray(38.1f, 23.7f)).from("a").next(); + g.V(hercules).as("a").V(hydra).addE("battled").property("time", 2) + .property("place", getGeoFloatArray(37.7f, 23.9f)).from("a").next(); + g.V(hercules).as("a").V(cerberus).addE("battled").property("time", 12) + .property("place", getGeoFloatArray(39f, 22f)).from("a").next(); + } + + g.V(pluto).as("a").V(jupiter).addE("brother").from("a").next(); + g.V(pluto).as("a").V(neptune).addE("brother").from("a").next(); + g.V(pluto).as("a").V(tartarus).addE("lives").property("reason", "no fear of death").from("a").next(); + g.V(pluto).as("a").V(cerberus).addE("pet").from("a").next(); + + g.V(cerberus).as("a").V(tartarus).addE("lives").from("a").next(); + + if (supportsTransactions) { + g.tx().commit(); + } + + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + if (supportsTransactions) { + g.tx().rollback(); + } + } + } + + /** + * Returns the geographical coordinates as a float array. + */ + protected float[] getGeoFloatArray(final float lat, final float lon) { + final float[] fa = { lat, lon }; + return fa; + } + + /** + * Runs some traversal queries to get data from the graph. + */ + public void readElements() { + try { + if (g == null) { + return; + } + + LOGGER.info("reading elements"); + + // look up vertex by name can use a composite index in JanusGraph + Optional> v = g.V().has("name", "jupiter").valueMap(true).tryNext(); + if (v.isPresent()) { + LOGGER.info(v.get().toString()); + } else { + LOGGER.warn("jupiter not found"); + } + + // look up an incident edge + Optional> edge = g.V().has("name", "hercules").outE("battled").as("e").inV() + .has("name", "hydra").select("e").valueMap(true).tryNext(); + if (edge.isPresent()) { + LOGGER.info(edge.get().toString()); + } else { + LOGGER.warn("hercules battled hydra not found"); + } + + // numerical range query can use a mixed index in JanusGraph + List list = g.V().has("age", P.gte(5000)).values("age").toList(); + LOGGER.info(list.toString()); + + // pluto might be deleted + boolean plutoExists = g.V().has("name", "pluto").hasNext(); + if (plutoExists) { + LOGGER.info("pluto exists"); + } else { + LOGGER.warn("pluto not found"); + } + + // look up jupiter's brothers + List brothers = g.V().has("name", "jupiter").both("brother").values("name").dedup().toList(); + LOGGER.info("jupiter's brothers: " + brothers.toString()); + + } finally { + // the default behavior automatically starts a transaction for + // any graph interaction, so it is best to finish the transaction + // even for read-only graph query operations + if (supportsTransactions) { + g.tx().rollback(); + } + } + } + + /** + * Makes an update to the existing graph structure. Does not create any + * new vertices or edges. + */ + public void updateElements() { + try { + if (g == null) { + return; + } + LOGGER.info("updating elements"); + final long ts = System.currentTimeMillis(); + g.V().has("name", "jupiter").property("ts", ts).iterate(); + if (supportsTransactions) { + g.tx().commit(); + } + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + if (supportsTransactions) { + g.tx().rollback(); + } + } + } + + /** + * Deletes elements from the graph structure. When a vertex is deleted, + * its incident edges are also deleted. + */ + public void deleteElements() { + try { + if (g == null) { + return; + } + LOGGER.info("deleting elements"); + // note that this will succeed whether or not pluto exists + g.V().has("name", "pluto").drop().iterate(); + if (supportsTransactions) { + g.tx().commit(); + } + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + if (supportsTransactions) { + g.tx().rollback(); + } + } + } + + /** + * Run the entire application: + * 1. Open and initialize the graph + * 2. Define the schema + * 3. Build the graph + * 4. Run traversal queries to get data from the graph + * 5. Make updates to the graph + * 6. Close the graph + */ + public void runApp() { + try { + // open and initialize the graph + openGraph(); + + // define the schema before loading data + if (supportsSchema) { + createSchema(); + } + + // build the graph structure + createElements(); + // read to see they were made + readElements(); + + for (int i = 0; i < 3; i++) { + try { + Thread.sleep((long) (Math.random() * 500) + 500); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + // update some graph elements with changes + updateElements(); + // read to see the changes were made + readElements(); + } + + // delete some graph elements + deleteElements(); + // read to see the changes were made + readElements(); + + // close the graph + closeGraph(); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + } + } + +} diff --git a/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/JanusGraphApp.java b/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/JanusGraphApp.java new file mode 100644 index 0000000000..c751c96f15 --- /dev/null +++ b/janusgraph-examples/example-common/src/main/java/org/janusgraph/example/JanusGraphApp.java @@ -0,0 +1,244 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphFactory; +import org.janusgraph.core.Multiplicity; +import org.janusgraph.core.RelationType; +import org.janusgraph.core.attribute.Geoshape; +import org.janusgraph.core.schema.JanusGraphManagement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JanusGraphApp extends GraphApp { + private static final Logger LOGGER = LoggerFactory.getLogger(JanusGraphApp.class); + + protected static final String APP_NAME = "jgex"; + protected static final String MIXED_INDEX_CONFIG_NAME = "jgex"; + + // Storage backends + + protected static final String BERKELEYJE = "berkeleyje"; + protected static final String CASSANDRA = "cassandra"; + protected static final String CQL = "cql"; + protected static final String HBASE = "hbase"; + protected static final String INMEMORY = "inmemory"; + + // Index backends + + protected static final String LUCENE = "lucene"; + protected static final String ELASTICSEARCH = "elasticsearch"; + protected static final String SOLR = "solr"; + + protected boolean useMixedIndex; + protected String mixedIndexConfigName; + + /** + * Constructs a graph app using the given properties. + * @param fileName location of the properties file + */ + public JanusGraphApp(final String fileName) { + super(fileName); + this.supportsSchema = true; + this.supportsTransactions = true; + this.supportsGeoshape = true; + this.useMixedIndex = true; + this.mixedIndexConfigName = MIXED_INDEX_CONFIG_NAME; + } + + @Override + public GraphTraversalSource openGraph() throws ConfigurationException { + super.openGraph(); + useMixedIndex = useMixedIndex && conf.containsKey("index." + mixedIndexConfigName + ".backend"); + return g; + } + + @Override + public void dropGraph() throws Exception { + if (graph != null) { + JanusGraphFactory.drop(getJanusGraph()); + } + } + + @Override + public void createElements() { + super.createElements(); + if (useMixedIndex) { + try { + // mixed indexes typically have a delayed refresh interval + Thread.sleep(10000); + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + } + } + } + + /** + * Returns the JanusGraph instance. + */ + protected JanusGraph getJanusGraph() { + return (JanusGraph) graph; + } + + @Override + public void createSchema() { + final JanusGraphManagement mgmt = getJanusGraph().openManagement(); + try { + // naive check if the schema was previously created + if (mgmt.getRelationTypes(RelationType.class).iterator().hasNext()) { + mgmt.rollback(); + return; + } + LOGGER.info("creating schema"); + createProperties(mgmt); + createVertexLabels(mgmt); + createEdgeLabels(mgmt); + createCompositeIndexes(mgmt); + createMixedIndexes(mgmt); + mgmt.commit(); + } catch (Exception e) { + mgmt.rollback(); + } + } + + /** + * Creates the vertex labels. + */ + protected void createVertexLabels(final JanusGraphManagement mgmt) { + mgmt.makeVertexLabel("titan").make(); + mgmt.makeVertexLabel("location").make(); + mgmt.makeVertexLabel("god").make(); + mgmt.makeVertexLabel("demigod").make(); + mgmt.makeVertexLabel("human").make(); + mgmt.makeVertexLabel("monster").make(); + } + + /** + * Creates the edge labels. + */ + protected void createEdgeLabels(final JanusGraphManagement mgmt) { + mgmt.makeEdgeLabel("father").multiplicity(Multiplicity.MANY2ONE).make(); + mgmt.makeEdgeLabel("mother").multiplicity(Multiplicity.MANY2ONE).make(); + mgmt.makeEdgeLabel("lives").signature(mgmt.getPropertyKey("reason")).make(); + mgmt.makeEdgeLabel("pet").make(); + mgmt.makeEdgeLabel("brother").make(); + mgmt.makeEdgeLabel("battled").make(); + } + + /** + * Creates the properties for vertices, edges, and meta-properties. + */ + protected void createProperties(final JanusGraphManagement mgmt) { + mgmt.makePropertyKey("name").dataType(String.class).make(); + mgmt.makePropertyKey("age").dataType(Integer.class).make(); + mgmt.makePropertyKey("time").dataType(Integer.class).make(); + mgmt.makePropertyKey("reason").dataType(String.class).make(); + mgmt.makePropertyKey("place").dataType(Geoshape.class).make(); + } + + /** + * Creates the composite indexes. A composite index is best used for + * exact match lookups. + */ + protected void createCompositeIndexes(final JanusGraphManagement mgmt) { + mgmt.buildIndex("nameIndex", Vertex.class).addKey(mgmt.getPropertyKey("name")).buildCompositeIndex(); + } + + /** + * Creates the mixed indexes. A mixed index requires that an external + * indexing backend is configured on the graph instance. A mixed index + * is best for full text search, numerical range, and geospatial queries. + */ + protected void createMixedIndexes(final JanusGraphManagement mgmt) { + if (useMixedIndex) { + mgmt.buildIndex("vAge", Vertex.class).addKey(mgmt.getPropertyKey("age")) + .buildMixedIndex(mixedIndexConfigName); + mgmt.buildIndex("eReasonPlace", Edge.class).addKey(mgmt.getPropertyKey("reason")) + .addKey(mgmt.getPropertyKey("place")).buildMixedIndex(mixedIndexConfigName); + } + } + + /** + * Returns a string representation of the schema generation code. This + * request string is submitted to the Gremlin Server via a client + * connection to create the schema on the graph instance running on the + * server. + */ + protected String createSchemaRequest() { + final StringBuilder s = new StringBuilder(); + + s.append("JanusGraphManagement mgmt = graph.openManagement(); "); + s.append("boolean created = false; "); + + // naive check if the schema was previously created + s.append( + "if (mgmt.getRelationTypes(RelationType.class).iterator().hasNext()) { mgmt.rollback(); created = false; } else { "); + + // properties + s.append("PropertyKey name = mgmt.makePropertyKey(\"name\").dataType(String.class).make(); "); + s.append("PropertyKey age = mgmt.makePropertyKey(\"age\").dataType(Integer.class).make(); "); + s.append("PropertyKey time = mgmt.makePropertyKey(\"time\").dataType(Integer.class).make(); "); + s.append("PropertyKey reason = mgmt.makePropertyKey(\"reason\").dataType(String.class).make(); "); + s.append("PropertyKey place = mgmt.makePropertyKey(\"place\").dataType(Geoshape.class).make(); "); + + // vertex labels + s.append("mgmt.makeVertexLabel(\"titan\").make(); "); + s.append("mgmt.makeVertexLabel(\"location\").make(); "); + s.append("mgmt.makeVertexLabel(\"god\").make(); "); + s.append("mgmt.makeVertexLabel(\"demigod\").make(); "); + s.append("mgmt.makeVertexLabel(\"human\").make(); "); + s.append("mgmt.makeVertexLabel(\"monster\").make(); "); + + // edge labels + s.append("mgmt.makeEdgeLabel(\"father\").multiplicity(Multiplicity.MANY2ONE).make(); "); + s.append("mgmt.makeEdgeLabel(\"mother\").multiplicity(Multiplicity.MANY2ONE).make(); "); + s.append("mgmt.makeEdgeLabel(\"lives\").signature(reason).make(); "); + s.append("mgmt.makeEdgeLabel(\"pet\").make(); "); + s.append("mgmt.makeEdgeLabel(\"brother\").make(); "); + s.append("mgmt.makeEdgeLabel(\"battled\").make(); "); + + // composite indexes + s.append("mgmt.buildIndex(\"nameIndex\", Vertex.class).addKey(name).buildCompositeIndex(); "); + + // mixed indexes + if (useMixedIndex) { + s.append("mgmt.buildIndex(\"vAge\", Vertex.class).addKey(age).buildMixedIndex(\"" + mixedIndexConfigName + + "\"); "); + s.append("mgmt.buildIndex(\"eReasonPlace\", Edge.class).addKey(reason).addKey(place).buildMixedIndex(\"" + + mixedIndexConfigName + "\"); "); + } + + s.append("mgmt.commit(); created = true; }"); + + return s.toString(); + } + + public static void main(String[] args) throws Exception { + final String fileName = (args != null && args.length > 0) ? args[0] : null; + final boolean drop = (args != null && args.length > 1) ? "drop".equalsIgnoreCase(args[1]) : false; + final JanusGraphApp app = new JanusGraphApp(fileName); + if (drop) { + app.openGraph(); + app.dropGraph(); + } else { + app.runApp(); + } + } +} diff --git a/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/GraphAppTest.java b/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/GraphAppTest.java new file mode 100644 index 0000000000..b5ce2dc544 --- /dev/null +++ b/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/GraphAppTest.java @@ -0,0 +1,114 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class GraphAppTest { + protected static final String CONF_FILE = "conf/jgex-inmemory.properties"; + + protected static GraphApp app; + protected static GraphTraversalSource g; + + @BeforeClass + public static void setUpClass() throws ConfigurationException { + app = new GraphApp(CONF_FILE); + g = app.openGraph(); + } + + @Before + public void setUp() { + g.V().drop().iterate(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + if (app != null) { + app.closeGraph(); + } + app = null; + } + + @Test + public void openGraph() throws ConfigurationException { + assertNotNull(g); + } + + @Test(expected = ConfigurationException.class) + public void openGraphNullConfig() throws ConfigurationException { + new GraphApp(null).openGraph(); + } + + @Test(expected = ConfigurationException.class) + public void openGraphConfigNotFound() throws ConfigurationException { + new GraphApp("conf/foobar").openGraph(); + } + + @Test + public void createElements() throws ConfigurationException { + app.createElements(); + + assertEquals(12L, ((Long) g.V().count().next()).longValue()); + assertEquals(1L, ((Long) g.V().hasLabel("titan").count().next()).longValue()); + assertEquals(1L, ((Long) g.V().hasLabel("demigod").count().next()).longValue()); + assertEquals(1L, ((Long) g.V().hasLabel("human").count().next()).longValue()); + assertEquals(3L, ((Long) g.V().hasLabel("location").count().next()).longValue()); + assertEquals(3L, ((Long) g.V().hasLabel("god").count().next()).longValue()); + assertEquals(3L, ((Long) g.V().hasLabel("monster").count().next()).longValue()); + + assertEquals(17L, ((Long) g.E().count().next()).longValue()); + assertEquals(2L, ((Long) g.E().hasLabel("father").count().next()).longValue()); + assertEquals(1L, ((Long) g.E().hasLabel("mother").count().next()).longValue()); + assertEquals(6L, ((Long) g.E().hasLabel("brother").count().next()).longValue()); + assertEquals(1L, ((Long) g.E().hasLabel("pet").count().next()).longValue()); + assertEquals(4L, ((Long) g.E().hasLabel("lives").count().next()).longValue()); + assertEquals(3L, ((Long) g.E().hasLabel("battled").count().next()).longValue()); + final float[] place = (float[]) g.V().has("name", "hercules").outE("battled").has("time", 12).values("place") + .next(); + assertNotNull(place); + assertEquals(2, place.length); + assertEquals(Float.valueOf(39f), Float.valueOf(place[0])); + assertEquals(Float.valueOf(22f), Float.valueOf(place[1])); + } + + @Test + public void updateElements() throws ConfigurationException { + app.createElements(); + assertFalse(g.V().has("name", "jupiter").has("ts").hasNext()); + app.updateElements(); + final long ts1 = (long) g.V().has("name", "jupiter").values("ts").next(); + app.updateElements(); + final long ts2 = (long) g.V().has("name", "jupiter").values("ts").next(); + assertTrue(ts2 > ts1); + } + + @Test + public void deleteElements() { + app.createElements(); + app.deleteElements(); + assertFalse(g.V().has("name", "pluto").hasNext()); + assertFalse(g.V().has("name", "jupiter").both("brother").has("name", "pluto").hasNext()); + } +} diff --git a/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/JanusGraphAppTest.java b/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/JanusGraphAppTest.java new file mode 100644 index 0000000000..355e231510 --- /dev/null +++ b/janusgraph-examples/example-common/src/test/java/org/janusgraph/example/JanusGraphAppTest.java @@ -0,0 +1,86 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.janusgraph.core.Cardinality; +import org.janusgraph.core.EdgeLabel; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphVertex; +import org.janusgraph.core.Multiplicity; +import org.janusgraph.core.PropertyKey; +import org.janusgraph.core.attribute.Geoshape; +import org.janusgraph.core.schema.JanusGraphIndex; +import org.janusgraph.core.schema.JanusGraphManagement; +import org.junit.Test; + +public class JanusGraphAppTest { + protected static final String CONF_FILE = "conf/jgex-inmemory.properties"; + + @Test + public void createSchema() throws ConfigurationException { + final JanusGraphApp app = new JanusGraphApp(CONF_FILE); + final GraphTraversalSource g = app.openGraph(); + app.createSchema(); + final JanusGraph janusGraph = (JanusGraph) g.getGraph(); + final JanusGraphManagement mgmt = janusGraph.openManagement(); + + final List vertexLabels = StreamSupport.stream(mgmt.getVertexLabels().spliterator(), false) + .map(l -> l.name()).collect(Collectors.toList()); + final List expectedVertexLabels = Stream.of("titan", "location", "god", "demigod", "human", "monster") + .collect(Collectors.toList()); + assertTrue(vertexLabels.containsAll(expectedVertexLabels)); + + final List edgeLabels = StreamSupport + .stream(mgmt.getRelationTypes(EdgeLabel.class).spliterator(), false).map(l -> l.name()) + .collect(Collectors.toList()); + final List expectedEdgeLabels = Stream.of("father", "mother", "brother", "pet", "lives", "battled") + .collect(Collectors.toList()); + assertTrue(edgeLabels.containsAll(expectedEdgeLabels)); + + final EdgeLabel father = mgmt.getEdgeLabel("father"); + assertTrue(father.isDirected()); + assertFalse(father.isUnidirected()); + assertEquals(Multiplicity.MANY2ONE, father.multiplicity()); + + final List propertyKeys = StreamSupport + .stream(mgmt.getRelationTypes(PropertyKey.class).spliterator(), false).map(l -> l.name()) + .collect(Collectors.toList()); + final List expectedPropertyKeys = Stream.of("name", "age", "time", "place", "reason") + .collect(Collectors.toList()); + assertTrue(propertyKeys.containsAll(expectedPropertyKeys)); + + final PropertyKey place = mgmt.getPropertyKey("place"); + assertEquals(Cardinality.SINGLE, place.cardinality()); + assertEquals(Geoshape.class, place.dataType()); + + final JanusGraphIndex nameIndex = mgmt.getGraphIndex("nameIndex"); + assertTrue(nameIndex.isCompositeIndex()); + assertTrue(nameIndex.getIndexedElement().equals(JanusGraphVertex.class)); + final PropertyKey[] nameIndexKeys = nameIndex.getFieldKeys(); + assertEquals(1, nameIndexKeys.length); + assertEquals("name", nameIndexKeys[0].name()); + } +} diff --git a/janusgraph-examples/example-common/src/test/resources/log4j.properties b/janusgraph-examples/example-common/src/test/resources/log4j.properties new file mode 100644 index 0000000000..45af6eb2ab --- /dev/null +++ b/janusgraph-examples/example-common/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n + +# Set root logger level to the designated level and its only appender to A1. +log4j.rootLogger=INFO, A1 diff --git a/janusgraph-examples/example-cql/README.md b/janusgraph-examples/example-cql/README.md new file mode 100644 index 0000000000..661fc4ab24 --- /dev/null +++ b/janusgraph-examples/example-cql/README.md @@ -0,0 +1,51 @@ +# Cassandra CQL Storage, Elasticsearch Index + +## About Cassandra and Elasticsearch + +[Apache Cassandra](http://cassandra.apache.org/) is a distributed database +designed for scalability and high availability. Cassandra supports two +protocols for communications, Thrift (legacy RPC protocol) and CQL (native +protocol). + +[Elasticsearch](https://www.elastic.co/products/elasticsearch) is a scalable, +distributed search engine. + +> Check the JanusGraph [version compatibility](http://docs.janusgraph.org/latest/version-compat.html) +to ensure you select versions of Cassandra and Elasticsearch compatible with +this JanusGraph release. + +## JanusGraph configuration + +* [`jgex-cql.properties`](conf/jgex-cql.properties) contains the Cassandra +and Elasticsearch server locations. By providing different values for +`storage.cql.keyspace` and `index.jgex.index-name`, you can store multiple +graphs on the same Cassandra and Elasticsearch servers. Refer to the JanusGraph +[configuration reference](http://docs.janusgraph.org/latest/config-ref.html) +for additional properties. + +* [`logback.xml`](conf/logback.xml) configures logging with [Logback](https://logback.qos.ch/), +which is the logger used by Cassandra. The example configuration logs to the +console and adjusts the logging level for some noisier packages. Refer to +the Logback [manual](https://logback.qos.ch/manual/index.html) for additional +details. + +## Run the example + +Use [Apache Maven](http://maven.apache.org/) and the [exec-maven-plugin](http://www.mojohaus.org/exec-maven-plugin/java-mojo.html) +to pull in the required jar files onto the runtime classpath. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-cql + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-cql.properties" +``` + +## Drop the graph + +Make sure to stop the application before dropping the graph. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-cql + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-cql.properties drop" +``` diff --git a/janusgraph-examples/example-cql/conf/jgex-cql.properties b/janusgraph-examples/example-cql/conf/jgex-cql.properties new file mode 100644 index 0000000000..77c2965a18 --- /dev/null +++ b/janusgraph-examples/example-cql/conf/jgex-cql.properties @@ -0,0 +1,9 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=cql +storage.cql.keyspace=jgex +storage.hostname=127.0.0.1 + +index.jgex.backend=elasticsearch +index.jgex.index-name=jgex +index.jgex.hostname=127.0.0.1 diff --git a/janusgraph-examples/example-cql/conf/logback.xml b/janusgraph-examples/example-cql/conf/logback.xml new file mode 100644 index 0000000000..2bb203a6b0 --- /dev/null +++ b/janusgraph-examples/example-cql/conf/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss} %-5level %logger - %msg%n + + + + + + + diff --git a/janusgraph-examples/example-cql/pom.xml b/janusgraph-examples/example-cql/pom.xml new file mode 100644 index 0000000000..87a905aef6 --- /dev/null +++ b/janusgraph-examples/example-cql/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-cql + pom + Example-Cql: C* CQL Storage, ES Index + http://janusgraph.org + + + + + org.janusgraph + example-common + ${project.version} + runtime + + + org.janusgraph + janusgraph-cql + ${project.version} + runtime + + + org.janusgraph + janusgraph-es + ${project.version} + runtime + + + + diff --git a/janusgraph-examples/example-hbase/README.md b/janusgraph-examples/example-hbase/README.md new file mode 100644 index 0000000000..76133c786b --- /dev/null +++ b/janusgraph-examples/example-hbase/README.md @@ -0,0 +1,172 @@ +# HBase Storage, Solr Index + +## About HBase and Solr + +[Apache HBase](http://hbase.apache.org/) is a scalable, distributed big data +store. + +[Apache Solr](http://lucene.apache.org/solr/) is a scalable, distributed +search engine. + +> Check the JanusGraph [version compatibility](http://docs.janusgraph.org/latest/version-compat.html) +to ensure you select versions of HBase and Solr compatible with this +JanusGraph release. + +## JanusGraph configuration + +* Be aware that Solr has two configuration options: SolrCloud or HTTP. With +either option, there is manual configuration required for the Solr cores (config +sets). Refer to the JanusGraph [Solr documentation](http://docs.janusgraph.org/latest/solr.html) +for additional details. + + * [`jgex-hbase-solr-cloud.properties`](conf/jgex-hbase-solr-cloud.properties) + contains the HBase and SolrCloud server locations. + + * [`jgex-hbase-solr-http.properties`](conf/jgex-hbase-solr-http.properties) + contains the HBase and Solr HTTP server locations + +* By providing different values for `storage.hbase.table` and `index.jgex.index-name`, +you can store multiple graphs on the same HBase and Solr servers. Refer to +the JanusGraph [configuration reference](http://docs.janusgraph.org/latest/config-ref.html) +for additional properties. + +* [`logback.xml`](conf/logback.xml) configures logging with [Logback](https://logback.qos.ch/). +The example configuration logs to the console and adjusts the logging level +for some noisier packages. Refer to the Logback [manual](https://logback.qos.ch/manual/index.html) +for additional details. + +## Run the example + +Use [Apache Maven](http://maven.apache.org/) and the [exec-maven-plugin](http://www.mojohaus.org/exec-maven-plugin/java-mojo.html) +to pull in the required jar files onto the runtime classpath. + + +## HBase and SolrCloud + +### Upload the configset to Zookeeper + +Using a configset makes it possible to reuse the same configuration for new +cores. The configset is stored in Zookeeper under `/configs/jgex` where the +name `jgex` matches the properties file value for `index.jgex.solr.configset`. +Make sure the Zookeeper url matches the properties value for `index.jgex.solr.zookeeper-url`. + +``` +$ cd $SOLR_HOME + +$ server/scripts/cloud-scripts/zkcli.sh -z 127.0.0.1:9983 -cmd upconfig -d $JANUSGRAPH_HOME/conf/solr -n jgex +``` + +### Run the program + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-hbase + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-hbase-solr-cloud.properties" +``` + +### Drop the graph + +Make sure to stop the application before dropping the graph. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-hbase + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-hbase-solr-cloud.properties drop" +``` + +### Remove the configset from Zookeeper + +The configset is stored in Zookeeper under `/configs/jgex` where the name +`jgex` matches the properties file value for `index.jgex.solr.configset`. +Make sure the Zookeeper url matches the properties value for `index.jgex.solr.zookeeper-url`. + +``` +$ cd $SOLR_HOME + +$ server/scripts/cloud-scripts/zkcli.sh -z 127.0.0.1:9983 -cmd clear /configs/jgex +``` + + +## HBase and Solr HTTP + +### Create the Solr cores + +The core names match the `vAge` or `eReasonPlace` values when `mgmt.buildIndex()` +defines the mixed indexes in `JanusGraphApp.createMixedIndexes()` + +``` +$ cd $SOLR_HOME + +$ bin/solr create_core -d $JANUSGRAPH_HOME/conf/solr -c vAge + +Copying configuration to new core instance directory: +/usr/lib/solr-5.5.4/server/solr/vAge + +Creating new core 'vAge' using command: +http://localhost:8983/solr/admin/cores?action=CREATE&name=vAge&instanceDir=vAge + +{ + "responseHeader":{ + "status":0, + "QTime":577}, + "core":"vAge"} + +$ bin/solr create_core -d $JANUSGRAPH_HOME/conf/solr -c eReasonPlace + +Copying configuration to new core instance directory: +/usr/lib/solr-5.5.4/server/solr/eReasonPlace + +Creating new core 'eReasonPlace' using command: +http://localhost:8983/solr/admin/cores?action=CREATE&name=eReasonPlace&instanceDir=eReasonPlace + +{ + "responseHeader":{ + "status":0, + "QTime":116}, + "core":"eReasonPlace"} +``` + +### Run the program + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-hbase + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-hbase-solr-http.properties" +``` + +### Drop the graph + +Make sure to stop the application before dropping the graph. + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-hbase + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.JanusGraphApp" -Dlogback.configurationFile="conf/logback.xml" -Dexec.args="conf/jgex-hbase-solr-http.properties drop" +``` + +### Drop the Solr cores + +The core names match the `vAge` or `eReasonPlace` values when `mgmt.buildIndex()` +defines the mixed indexes in `JanusGraphApp.createMixedIndexes()` + +``` +$ cd $SOLR_HOME + +$ bin/solr delete -c vAge + +Deleting core 'vAge' using command: +http://localhost:8983/solr/admin/cores?action=UNLOAD&core=vAge&deleteIndex=true&deleteDataDir=true&deleteInstanceDir=true + +{"responseHeader":{ + "status":0, + "QTime":7}} + +$ bin/solr delete -c eReasonPlace + +Deleting core 'eReasonPlace' using command: +http://localhost:8983/solr/admin/cores?action=UNLOAD&core=eReasonPlace&deleteIndex=true&deleteDataDir=true&deleteInstanceDir=true + +{"responseHeader":{ + "status":0, + "QTime":9}} +``` diff --git a/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-cloud.properties b/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-cloud.properties new file mode 100644 index 0000000000..f1ef6ee676 --- /dev/null +++ b/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-cloud.properties @@ -0,0 +1,20 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=hbase +storage.hbase.table=jgex +storage.hostname=127.0.0.1 +#storage.hbase.ext.hbase.zookeeper.property.clientPort=2181 +# Some HBase installations may have this set to another value, +# such as /hbase-unsecure or /hbase-secure +#storage.hbase.ext.zookeeper.znode.parent=/hbase + +index.jgex.backend=solr +index.jgex.index-name=jgex +index.jgex.hostname=127.0.0.1 + +index.jgex.solr.mode=cloud +index.jgex.solr.configset=jgex +# Make sure the port matches the Zookeeper that SolrCloud is using. +# If using Solr's embedded Zookeeper, the default port is 9983. +# If using an external Zookeeper, the default port is 2181. +index.jgex.solr.zookeeper-url=127.0.0.1:9983 diff --git a/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-http.properties b/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-http.properties new file mode 100644 index 0000000000..4c58e0cb69 --- /dev/null +++ b/janusgraph-examples/example-hbase/conf/jgex-hbase-solr-http.properties @@ -0,0 +1,16 @@ +gremlin.graph=org.janusgraph.core.JanusGraphFactory + +storage.backend=hbase +storage.hbase.table=jgex +storage.hostname=127.0.0.1 +#storage.hbase.ext.hbase.zookeeper.property.clientPort=2181 +# Some HBase installations may have this set to another value, +# such as /hbase-unsecure or /hbase-secure +#storage.hbase.ext.zookeeper.znode.parent=/hbase + +index.jgex.backend=solr +index.jgex.index-name=jgex +index.jgex.hostname=127.0.0.1 + +index.jgex.solr.mode=http +index.jgex.solr.http-urls=http://127.0.0.1:8983/solr diff --git a/janusgraph-examples/example-hbase/conf/logback.xml b/janusgraph-examples/example-hbase/conf/logback.xml new file mode 100644 index 0000000000..9221a400b3 --- /dev/null +++ b/janusgraph-examples/example-hbase/conf/logback.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss} %-5level %logger - %msg%n + + + + + + + + + + + diff --git a/janusgraph-examples/example-hbase/pom.xml b/janusgraph-examples/example-hbase/pom.xml new file mode 100644 index 0000000000..ce4e0b8e10 --- /dev/null +++ b/janusgraph-examples/example-hbase/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-hbase + pom + Example-HBase: HBase Storage, Solr Index + http://janusgraph.org + + + + + org.janusgraph + example-common + ${project.version} + runtime + + + org.janusgraph + janusgraph-hbase + ${project.version} + runtime + + + org.apache.hbase + hbase-client + ${hbase100.version} + runtime + + + org.janusgraph + janusgraph-solr + ${project.version} + runtime + + + + diff --git a/janusgraph-examples/example-remotegraph/README.md b/janusgraph-examples/example-remotegraph/README.md new file mode 100644 index 0000000000..ce545d1322 --- /dev/null +++ b/janusgraph-examples/example-remotegraph/README.md @@ -0,0 +1,75 @@ +# Remote Graph + +## About Remote Graph + +[Gremlin Server](http://tinkerpop.apache.org/docs/3.2.6/reference/#gremlin-server) +allows a JanusGraph instance to run on a standalone server. This enables +multiple clients to connect to the same JanusGraph instance. It also allows +clients to connect via non-JVM languages. By default, the Gremlin Server +communicates over WebSocket. + +A Java program can connect to the Gremlin Server in two ways: +* [Gremlin Driver](http://tinkerpop.apache.org/docs/3.2.6/reference/#connecting-via-java) +client. Sumbit scripts as a `String` for evaluation on the server. +* [Remote Graph](http://tinkerpop.apache.org/docs/3.2.6/reference/#connecting-via-remotegraph) +uses the same client connection but enables you to compose queries as code. + +## JanusGraph configuration + +### Gremlin Server configuration + +* Select a specific storage backend to use, then copy its configuration into +the `$JANUSGRAPH_HOME/conf/gremlin-server/` directory + +* Update the `$JANUSGRAPH_HOME/conf/gremlin-server/gremlin-server.yaml` to +use the configuration file + + ``` + graphs: { + graph: conf/gremlin-server/jgex-cql.properties + } + ``` + +* Note the default `$JANUSGRAPH_HOME/conf/gremlin-server/gremlin-server.yaml` +uses the script `scripts/empty-graph.groovy` to define the graph traversal source +`g` for the graph `g: graph.traversal()` + +### Remote Graph configuration + +* [`jgex-remote.properties`](conf/jgex-remote.properties) contains the remote +connection class, the Gremlin Driver configuration file location, and the graph +traversal source name. Refer to the [Remote Graph documentation](http://tinkerpop.apache.org/docs/3.2.6/reference/#connecting-via-remotegraph) +for additional details. + +* [`remote-objects.yaml`](conf/remote-objects.yaml) contains server location +and serializer options. The default serializer is Gryo, and it is important +that the server is configured with the same serializer. Refer to the +[Gremlin Driver documentation](http://tinkerpop.apache.org/docs/3.2.6/reference/#_configuration) +for additional properties. + +## Running the example + +### Starting the Gremlin Server + +Start the Gremlin Server with the preferred storage configuration. The graph +instance will be created if it did not previously exist. Make sure to check +the server logs for errors. + +``` +$ cd $JANUSGRAPH_HOME + +$ bin/gremlin-server.sh conf/gremlin-server/gremlin-server.yaml +``` + +### Running the remote graph example + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-remotegraph + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.RemoteGraphApp" -Dexec.args="conf/jgex-remote.properties" +``` + +## Drop the graph + +Make sure to stop the Gremlin Server before dropping the graph. Follow the +directions for the specific storage backend. diff --git a/janusgraph-examples/example-remotegraph/conf/jgex-remote.properties b/janusgraph-examples/example-remotegraph/conf/jgex-remote.properties new file mode 100644 index 0000000000..6963e9b128 --- /dev/null +++ b/janusgraph-examples/example-remotegraph/conf/jgex-remote.properties @@ -0,0 +1,5 @@ +gremlin.remote.remoteConnectionClass=org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection +# cluster file has the remote server configuration +gremlin.remote.driver.clusterFile=conf/remote-objects.yaml +# source name is the global graph traversal source defined on the server +gremlin.remote.driver.sourceName=g diff --git a/janusgraph-examples/example-remotegraph/conf/remote-objects.yaml b/janusgraph-examples/example-remotegraph/conf/remote-objects.yaml new file mode 100644 index 0000000000..e68afdfafb --- /dev/null +++ b/janusgraph-examples/example-remotegraph/conf/remote-objects.yaml @@ -0,0 +1,8 @@ +hosts: [127.0.0.1] +port: 8182 +serializer: { + className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, + config: { + ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] + } +} diff --git a/janusgraph-examples/example-remotegraph/pom.xml b/janusgraph-examples/example-remotegraph/pom.xml new file mode 100644 index 0000000000..5feb9b2690 --- /dev/null +++ b/janusgraph-examples/example-remotegraph/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-remotegraph + Example-RemoteGraph: Example with RemoteGraph + http://janusgraph.org + + + + org.janusgraph + example-common + ${project.version} + + + org.apache.tinkerpop + gremlin-driver + ${tinkerpop.version} + + + org.apache.tinkerpop + gremlin-server + ${tinkerpop.version} + test + + + + diff --git a/janusgraph-examples/example-remotegraph/src/main/java/org/janusgraph/example/RemoteGraphApp.java b/janusgraph-examples/example-remotegraph/src/main/java/org/janusgraph/example/RemoteGraphApp.java new file mode 100644 index 0000000000..899af41bb5 --- /dev/null +++ b/janusgraph-examples/example-remotegraph/src/main/java/org/janusgraph/example/RemoteGraphApp.java @@ -0,0 +1,196 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import java.util.stream.Stream; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.apache.tinkerpop.gremlin.driver.ResultSet; +import org.apache.tinkerpop.gremlin.process.traversal.Bindings; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.attribute.Geoshape; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteGraphApp extends JanusGraphApp { + private static final Logger LOGGER = LoggerFactory.getLogger(RemoteGraphApp.class); + + // used for bindings + private static final String NAME = "name"; + private static final String AGE = "age"; + private static final String TIME = "time"; + private static final String REASON = "reason"; + private static final String PLACE = "place"; + private static final String LABEL = "label"; + private static final String OUT_V = "outV"; + private static final String IN_V = "inV"; + + protected JanusGraph janusgraph; + protected Cluster cluster; + protected Client client; + protected Configuration conf; + + /** + * Constructs a graph app using the given properties. + * @param fileName location of the properties file + */ + public RemoteGraphApp(final String fileName) { + super(fileName); + // the server auto-commits per request, so the application code doesn't + // need to explicitly commit transactions + this.supportsTransactions = false; + } + + @Override + public GraphTraversalSource openGraph() throws ConfigurationException { + LOGGER.info("opening graph"); + conf = new PropertiesConfiguration(propFileName); + + // using the remote driver for schema + try { + cluster = Cluster.open(conf.getString("gremlin.remote.driver.clusterFile")); + client = cluster.connect(); + } catch (Exception e) { + throw new ConfigurationException(e); + } + + // using the remote graph for queries + graph = EmptyGraph.instance(); + g = graph.traversal().withRemote(conf); + return g; + } + + @Override + public void createElements() { + LOGGER.info("creating elements"); + + // Use bindings to allow the Gremlin Server to cache traversals that + // will be reused with different parameters. This minimizes the + // number of scripts that need to be compiled and cached on the server. + // http://tinkerpop.apache.org/docs/3.2.6/reference/#parameterized-scripts + final Bindings b = Bindings.instance(); + + // see GraphOfTheGodsFactory.java + + Vertex saturn = g.addV(b.of(LABEL, "titan")).property(NAME, b.of(NAME, "saturn")) + .property(AGE, b.of(AGE, 10000)).next(); + Vertex sky = g.addV(b.of(LABEL, "location")).property(NAME, b.of(NAME, "sky")).next(); + Vertex sea = g.addV(b.of(LABEL, "location")).property(NAME, b.of(NAME, "sea")).next(); + Vertex jupiter = g.addV(b.of(LABEL, "god")).property(NAME, b.of(NAME, "jupiter")).property(AGE, b.of(AGE, 5000)) + .next(); + Vertex neptune = g.addV(b.of(LABEL, "god")).property(NAME, b.of(NAME, "neptune")).property(AGE, b.of(AGE, 4500)) + .next(); + Vertex hercules = g.addV(b.of(LABEL, "demigod")).property(NAME, b.of(NAME, "hercules")) + .property(AGE, b.of(AGE, 30)).next(); + Vertex alcmene = g.addV(b.of(LABEL, "human")).property(NAME, b.of(NAME, "alcmene")).property(AGE, b.of(AGE, 45)) + .next(); + Vertex pluto = g.addV(b.of(LABEL, "god")).property(NAME, b.of(NAME, "pluto")).property(AGE, b.of(AGE, 4000)) + .next(); + Vertex nemean = g.addV(b.of(LABEL, "monster")).property(NAME, b.of(NAME, "nemean")).next(); + Vertex hydra = g.addV(b.of(LABEL, "monster")).property(NAME, b.of(NAME, "hydra")).next(); + Vertex cerberus = g.addV(b.of(LABEL, "monster")).property(NAME, b.of(NAME, "cerberus")).next(); + Vertex tartarus = g.addV(b.of(LABEL, "location")).property(NAME, b.of(NAME, "tartarus")).next(); + + g.V(b.of(OUT_V, jupiter)).as("a").V(b.of(IN_V, saturn)).addE(b.of(LABEL, "father")).from("a").next(); + g.V(b.of(OUT_V, jupiter)).as("a").V(b.of(IN_V, sky)).addE(b.of(LABEL, "lives")) + .property(REASON, b.of(REASON, "loves fresh breezes")).from("a").next(); + g.V(b.of(OUT_V, jupiter)).as("a").V(b.of(IN_V, neptune)).addE(b.of(LABEL, "brother")).from("a").next(); + g.V(b.of(OUT_V, jupiter)).as("a").V(b.of(IN_V, pluto)).addE(b.of(LABEL, "brother")).from("a").next(); + + g.V(b.of(OUT_V, neptune)).as("a").V(b.of(IN_V, sea)).addE(b.of(LABEL, "lives")) + .property(REASON, b.of(REASON, "loves waves")).from("a").next(); + g.V(b.of(OUT_V, neptune)).as("a").V(b.of(IN_V, jupiter)).addE(b.of(LABEL, "brother")).from("a").next(); + g.V(b.of(OUT_V, neptune)).as("a").V(b.of(IN_V, pluto)).addE(b.of(LABEL, "brother")).from("a").next(); + + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, jupiter)).addE(b.of(LABEL, "father")).from("a").next(); + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, alcmene)).addE(b.of(LABEL, "mother")).from("a").next(); + + if (supportsGeoshape) { + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, nemean)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 1)).property(PLACE, b.of(PLACE, Geoshape.point(38.1f, 23.7f))).from("a") + .next(); + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, hydra)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 2)).property(PLACE, b.of(PLACE, Geoshape.point(37.7f, 23.9f))).from("a") + .next(); + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, cerberus)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 12)).property(PLACE, b.of(PLACE, Geoshape.point(39f, 22f))).from("a") + .next(); + } else { + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, nemean)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 1)).property(PLACE, b.of(PLACE, getGeoFloatArray(38.1f, 23.7f))) + .from("a").next(); + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, hydra)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 2)).property(PLACE, b.of(PLACE, getGeoFloatArray(37.7f, 23.9f))) + .from("a").next(); + g.V(b.of(OUT_V, hercules)).as("a").V(b.of(IN_V, cerberus)).addE(b.of(LABEL, "battled")) + .property(TIME, b.of(TIME, 12)).property(PLACE, b.of(PLACE, getGeoFloatArray(39f, 22f))).from("a") + .next(); + } + + g.V(b.of(OUT_V, pluto)).as("a").V(b.of(IN_V, jupiter)).addE(b.of(LABEL, "brother")).from("a").next(); + g.V(b.of(OUT_V, pluto)).as("a").V(b.of(IN_V, neptune)).addE(b.of(LABEL, "brother")).from("a").next(); + g.V(b.of(OUT_V, pluto)).as("a").V(b.of(IN_V, tartarus)).addE(b.of(LABEL, "lives")) + .property(REASON, b.of(REASON, "no fear of death")).from("a").next(); + g.V(b.of(OUT_V, pluto)).as("a").V(b.of(IN_V, cerberus)).addE(b.of(LABEL, "pet")).from("a").next(); + + g.V(b.of(OUT_V, cerberus)).as("a").V(b.of(IN_V, tartarus)).addE(b.of(LABEL, "lives")).from("a").next(); + } + + @Override + public void closeGraph() throws Exception { + LOGGER.info("closing graph"); + try { + if (g != null) { + // this closes the remote, no need to close the empty graph + g.close(); + } + if (cluster != null) { + // the cluster closes all of its clients + cluster.close(); + } + } finally { + g = null; + graph = null; + client = null; + cluster = null; + } + } + + @Override + public void createSchema() { + LOGGER.info("creating schema"); + // get the schema request as a string + final String req = createSchemaRequest(); + // submit the request to the server + final ResultSet resultSet = client.submit(req); + // drain the results completely + Stream futureList = resultSet.stream(); + futureList.map(result -> result.toString()).forEach(LOGGER::info); + } + + public static void main(String[] args) { + final String fileName = (args != null && args.length > 0) ? args[0] : null; + final RemoteGraphApp app = new RemoteGraphApp(fileName); + app.runApp(); + } +} diff --git a/janusgraph-examples/example-tinkergraph/README.md b/janusgraph-examples/example-tinkergraph/README.md new file mode 100644 index 0000000000..295aaed9ff --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/README.md @@ -0,0 +1,26 @@ +# TinkerGraph + +## About TinkerGraph + +[TinkerGraph](http://tinkerpop.apache.org/docs/3.2.6/reference/#tinkergraph-gremlin) +is the in-memory reference implementation of the Apache TinkerPop Graph API. +It can be useful with small graphs to prototype out a graph structure and +queries against it. It can also be useful as a comparison when debugging to +help determine whether a particular issue is in the graph traversal logic +or whether it is a JanusGraph-specific issue. + +## TinkerGraph configuration + +[`jgex-tinkergraph.properties`](conf/jgex-tinkergraph.properties) contains +the id manager settings for the graph. + +Refer to the TinkerGraph [configuration reference](http://tinkerpop.apache.org/docs/3.2.6/reference/#_configuration_3) +for additional properties. + +## Running the example + +``` +$ cd $JANUSGRAPH_HOME/janusgraph-examples/example-tinkergraph + +$ mvn exec:java -Dexec.mainClass="org.janusgraph.example.TinkerGraphApp" -Dexec.args="conf/jgex-tinkergraph.properties" +``` diff --git a/janusgraph-examples/example-tinkergraph/conf/jgex-tinkergraph.properties b/janusgraph-examples/example-tinkergraph/conf/jgex-tinkergraph.properties new file mode 100644 index 0000000000..cf06ea7ec2 --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/conf/jgex-tinkergraph.properties @@ -0,0 +1,6 @@ +gremlin.graph=org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph + +# DefaultIdManager types: INTEGER, LONG, UUID, ANY +gremlin.tinkergraph.edgeIdManager=INTEGER +gremlin.tinkergraph.vertexIdManager=INTEGER +gremlin.tinkergraph.vertexPropertyIdManager=INTEGER diff --git a/janusgraph-examples/example-tinkergraph/pom.xml b/janusgraph-examples/example-tinkergraph/pom.xml new file mode 100644 index 0000000000..d9a150bfc7 --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/pom.xml @@ -0,0 +1,27 @@ + + 4.0.0 + + org.janusgraph + janusgraph-examples + 0.2.0-SNAPSHOT + ../pom.xml + + example-tinkergraph + Example-TinkerGraph: Example with TinkerGraph + http://janusgraph.org + + + + org.janusgraph + example-common + ${project.version} + + + org.apache.tinkerpop + tinkergraph-gremlin + ${tinkerpop.version} + + + + diff --git a/janusgraph-examples/example-tinkergraph/src/main/java/org/janusgraph/example/TinkerGraphApp.java b/janusgraph-examples/example-tinkergraph/src/main/java/org/janusgraph/example/TinkerGraphApp.java new file mode 100644 index 0000000000..2cc2de3190 --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/src/main/java/org/janusgraph/example/TinkerGraphApp.java @@ -0,0 +1,51 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TinkerGraphApp extends GraphApp { + private static final Logger LOGGER = LoggerFactory.getLogger(TinkerGraphApp.class); + + /** + * Constructs a graph app using the given properties. + * @param fileName location of the properties file + */ + public TinkerGraphApp(final String fileName) { + super(fileName); + this.supportsSchema = true; + this.supportsTransactions = false; + this.supportsGeoshape = false; + } + + @Override + public void createSchema() { + LOGGER.info("creating schema"); + final TinkerGraph tinkerGraph = (TinkerGraph) graph; + // naive check if the schema was previously created + if (!tinkerGraph.getIndexedKeys(Vertex.class).iterator().hasNext()) { + tinkerGraph.createIndex("name", Vertex.class); + } + } + + public static void main(String[] args) { + final String fileName = (args != null && args.length > 0) ? args[0] : null; + final TinkerGraphApp app = new TinkerGraphApp(fileName); + app.runApp(); + } +} diff --git a/janusgraph-examples/example-tinkergraph/src/test/java/org/janusgraph/example/TinkerGraphAppTest.java b/janusgraph-examples/example-tinkergraph/src/test/java/org/janusgraph/example/TinkerGraphAppTest.java new file mode 100644 index 0000000000..f9c6e6b9f7 --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/src/test/java/org/janusgraph/example/TinkerGraphAppTest.java @@ -0,0 +1,44 @@ +// Copyright 2017 JanusGraph Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.janusgraph.example; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Set; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerEdge; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; +import org.junit.Test; + +public class TinkerGraphAppTest { + protected static final String CONF_FILE = "conf/jgex-tinkergraph.properties"; + + @Test + public void createSchema() throws ConfigurationException { + final TinkerGraphApp app = new TinkerGraphApp(CONF_FILE); + final GraphTraversalSource g = app.openGraph(); + app.createSchema(); + final TinkerGraph tinkerGraph = (TinkerGraph) g.getGraph(); + final Set vertexIndexes = tinkerGraph.getIndexedKeys(TinkerVertex.class); + assertEquals(1, vertexIndexes.size()); + assertEquals("name", vertexIndexes.toArray()[0]); + final Set edgeIndexes = tinkerGraph.getIndexedKeys(TinkerEdge.class); + assertTrue(edgeIndexes.isEmpty()); + } +} diff --git a/janusgraph-examples/example-tinkergraph/src/test/resources/log4j.properties b/janusgraph-examples/example-tinkergraph/src/test/resources/log4j.properties new file mode 100644 index 0000000000..45af6eb2ab --- /dev/null +++ b/janusgraph-examples/example-tinkergraph/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +# A1 is set to be a ConsoleAppender. +log4j.appender.A1=org.apache.log4j.ConsoleAppender + +# A1 uses PatternLayout. +log4j.appender.A1.layout=org.apache.log4j.PatternLayout +log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n + +# Set root logger level to the designated level and its only appender to A1. +log4j.rootLogger=INFO, A1 diff --git a/janusgraph-examples/pom.xml b/janusgraph-examples/pom.xml new file mode 100644 index 0000000000..45ac5ffe8b --- /dev/null +++ b/janusgraph-examples/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + + org.janusgraph + janusgraph + 0.2.0-SNAPSHOT + ../pom.xml + + janusgraph-examples + pom + JanusGraph-Examples: Examples for JanusGraph + http://janusgraph.org + + + example-common + example-berkeleyje + example-cassandra + example-cql + example-hbase + example-remotegraph + example-tinkergraph + + + + + org.janusgraph + janusgraph-test + ${project.version} + test + + + + diff --git a/pom.xml b/pom.xml index 51af770398..9a15194372 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,7 @@ janusgraph-dist janusgraph-doc janusgraph-solr + janusgraph-examples