diff --git a/README.md b/README.md index 3361132..3bb0958 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ An implementation of the [Apache TinkerPop OLTP Provider](https://tinkerpop.apac ## Compatibility This Provider supports: -* Apache TinkerPop 3.3 +* Apache TinkerPop 3.7 * ArangoDB 3.11+ (via ArangoDB Java Driver 7.17.0). ## ArangoDB @@ -101,21 +101,24 @@ graph.close(); ## A note on element IDs The provider implementation supports user supplied IDs, i.e. provide an id property for graph -elements, but currently we only support String ids, that is: +elements, but we only support String ids, that is: ``` Vertex v1 = g.addV("person").property(T.id, "1"); ``` -will create a vertex with id "1". However, implementation wise, in ArangoDB we are only allowed to manipulate the documents `name`, not its `id`. For this reason, providing a TinkerPop vertex id (`T.id`) actually sets the vertex's ArangoDB `name`. As a result, retrieving the vertex by the given id will fail: +will create a vertex with id "1". However, implementation wise, in ArangoDB we are only allowed to manipulate the +documents `name`, not its `id`. For this reason, providing a TinkerPop vertex id (`T.id`) actually sets the vertex's +ArangoDB `name`. As a result, retrieving the vertex by the given id will fail: ``` Vertex v2 = g.V("1"); assert v2 == null; ``` -Since we know that documents IDs are created by concatenating (with a slash) the document's collection and its name, then we can find the vertex like so: +Since we know that documents IDs are created by concatenating (with a slash) the document's collection and its name, +then we can find the vertex like so: ``` Vertex v2 = g.V("person/1"); diff --git a/docker/jwtHeader b/docker/jwtHeader index e69de29..153e1b8 100644 --- a/docker/jwtHeader +++ b/docker/jwtHeader @@ -0,0 +1 @@ +Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmFuZ29kYiIsInNlcnZlcl9pZCI6ImZvbyJ9.QmuhPHkmRPJuHGxsEqggHGRyVXikV44tb5YU_yWEvEM diff --git a/docker/jwtSecret b/docker/jwtSecret index e69de29..ea75728 100644 --- a/docker/jwtSecret +++ b/docker/jwtSecret @@ -0,0 +1 @@ +Averysecretword diff --git a/docker/server.pem b/docker/server.pem index e69de29..c97c302 100644 --- a/docker/server.pem +++ b/docker/server.pem @@ -0,0 +1,60 @@ +Bag Attributes + friendlyName: arangotest + localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1WiDnd4+uCmMG +539ZNZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMAL +X9euSseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7Nzmvdo +gNXoJQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiR +dJVPwUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf +2MGO64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzb +f8KnRY4PAgMBAAECggEAKi1d/bdW2TldMpvgiFTm15zLjHCpllbKBWFqRj3T9+X7 +Duo6Nh9ehopD0YDDe2DNhYr3DsH4sLjUWVDfDpAhutMsU1wlBzmOuC+EuRv/CeDB +4DFr+0sgCwlti+YAtwWcR05SF7A0Ai0GYW2lUipbtbFSBSjCfM08BlPDsPCRhdM8 +DhBn3S45aP7oC8BdhG/etg+DfXW+/nyNwEcMCYG97bzXNjzYpCQjo/bTHdh2UPYM +4WEAqFzZ5jir8LVS3v7GqpqPmk6FnHJOJpfpOSZoPqnfpIw7SVlNsXHvDaHGcgYZ +Xec7rLQlBuv4RZU7OlGJpK2Ng5kvS9q3nfqqn7YIMQKBgQDqSsYnE+k6CnrSpa2W +B9W/+PChITgkA46XBUUjAueJ7yVZQQEOzl0VI6RoVBp3t66eO8uM9omO8/ogHXku +Ei9UUIIfH4BsSP7G5A06UC/FgReDxwBfbRuS+lupnmc348vPDkFlJZ4hDgWflNev +7tpUbljSAqUea1VhdBy146V4qwKBgQDGJ6iL1+A9uUM+1UklOAPpPhTQ8ZQDRCj7 +7IMVcbzWYvCMuVNXzOWuiz+VYr3IGCJZIbxbFDOHxGF4XKJnk0vm1qhQQME0PtAF +i1jIfsxpj8KKJl9Uad+XLQCYRV8mIZlhsd/ErRJuz6FyqevKH3nFIb0ggF3x2d06 +odTHuj4ILQKBgCUsI/BDSne4/e+59aaeK52/w33tJVkhb1gqr+N0LIRH+ycEF0Tg +HQijlQwwe9qOvBfC6PK+kuipcP/zbSyQGg5Ij7ycZOXJVxL7T9X2rv2pE7AGvNpn +Fz7klfJ9fWbyr310h4+ivkoETYQaO3ZgcSeAMntvi/8djHhf0cZSDgjtAoGBAKvQ +TUNcHjJGxfjgRLkB1dpSmwgEv7sJSaQOkiZw5TTauwq50nsJzYlHcg1cfYPW8Ulp +iAFNBdVNwNn1MFgwjpqMO4rCawObBxIXnhbSYvmQzjStSvFNj7JsMdzWIcdVUMI1 +0fmdu6LbY3ihvzIVkqcMNwnMZCjFKB6jnXTElu7NAoGAS0gNPD/bfzWAhZBBYp9/ +SLGOvjHKrSVWGwDiqdAGuh6xg+1C3F+XpiITP6d3Wv3PCJ/Gia5isQPSMaXG+xTt +6huBgFlksHqr0tsQA9dcgGW7BDr5VhRq5/WinaLhGGy1R+i2zbDmQXgHbCO+RH/s +bD9F4LZ3RoXmGHLW0IUggPw= +-----END PRIVATE KEY----- +Bag Attributes + friendlyName: arangotest + localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 +subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost + +issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost + +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIEeDCzXzANBgkqhkiG9w0BAQsFADBuMRAwDgYDVQQGEwdV +bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD +VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv +c3QwHhcNMjAxMTAxMTg1MTE5WhcNMzAxMDMwMTg1MTE5WjBuMRAwDgYDVQQGEwdV +bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD +VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1WiDnd4+uCmMG539Z +NZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMALX9eu +SseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7NzmvdogNXo +JQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiRdJVP +wUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf2MGO +64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzbf8Kn +RY4PAgMBAAGjITAfMB0GA1UdDgQWBBTBrv9Awynt3C5IbaCNyOW5v4DNkTANBgkq +hkiG9w0BAQsFAAOCAQEAIm9rPvDkYpmzpSIhR3VXG9Y71gxRDrqkEeLsMoEyqGnw +/zx1bDCNeGg2PncLlW6zTIipEBooixIE9U7KxHgZxBy0Et6EEWvIUmnr6F4F+dbT +D050GHlcZ7eOeqYTPYeQC502G1Fo4tdNi4lDP9L9XZpf7Q1QimRH2qaLS03ZFZa2 +tY7ah/RQqZL8Dkxx8/zc25sgTHVpxoK853glBVBs/ENMiyGJWmAXQayewY3EPt/9 +wGwV4KmU3dPDleQeXSUGPUISeQxFjy+jCw21pYviWVJTNBA9l5ny3GhEmcnOT/gQ +HCvVRLyGLMbaMZ4JrPwb+aAtBgrgeiK4xeSMMvrbhw== +-----END CERTIFICATE----- diff --git a/docker/start_db.sh b/docker/start_db.sh index 002d7a1..39adb06 100755 --- a/docker/start_db.sh +++ b/docker/start_db.sh @@ -66,7 +66,8 @@ docker run -d \ --starter.address="${GW}" \ --docker.image="${DOCKER_IMAGE}" \ --starter.local --starter.mode=${STARTER_MODE} --all.log.level=debug --all.log.output=+ --log.verbose \ - --all.server.descriptors-minimum=1024 --all.javascript.allow-admin-execute=true + --all.server.descriptors-minimum=1024 --all.javascript.allow-admin-execute=true \ + --all.query.require-with=true --all.server.options-api=admin wait_server() { diff --git a/jautodoc_templates.xml b/jautodoc_templates.xml deleted file mode 100644 index ae8de96..0000000 --- a/jautodoc_templates.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index c778fe0..c86a549 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ 7.17.0 3.7.3 4.13.1 - 1.2.13 + 1.3.15 3.4 @@ -80,7 +80,7 @@ org.mockito mockito-core - 2.13.0 + 4.11.0 test diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java index 6f5d391..1e04497 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java @@ -79,7 +79,7 @@ public ArangoDBBaseDocument(String key, String label, ArangoDBGraph graph) { this._key = key; this.label = label; this.graph = graph; - this.collection = graph.getPrefixedCollectioName(label); + this.collection = graph.getPrefixedCollectionName(label); } /** diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseEdge.java deleted file mode 100644 index 99cddf2..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseEdge.java +++ /dev/null @@ -1,105 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.client; - -import com.arangodb.serde.InternalFrom; -import com.arangodb.serde.InternalTo; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -/** - * The ArangoDB BaseEdge provides the internal fields required for the driver to correctly - * serialize and deserialize edges. - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -public abstract class ArangoDBBaseEdge extends ArangoDBBaseDocument { - - - /** ArangoDB internal from. */ - - @InternalFrom - private String _from; - - /** ArangoDB internal to. */ - - @InternalTo - private String _to; - - /** - * Constructor used for ArabgoDB JavaBeans de-/serialisation. - */ - - public ArangoDBBaseEdge() { - super(); - } - - /** - * Instantiates a new Arango DB base edge. - * - * @param label the edge label - * @param from_id the from/source vertex id - * @param to_id the to/target vertex id - * @param graph the graph - */ - public ArangoDBBaseEdge(String label, String from_id, String to_id, ArangoDBGraph graph) { - this(null, label, from_id, to_id, graph); - } - - /** - * Instantiates a new Arango DB base edge with a predefined name. - * - * @param key the name to assign to the edge - * @param label the edge label - * @param from_id the from/source vertex id - * @param to_id the to/target vertex id - * @param graph the graph - */ - public ArangoDBBaseEdge(String key, String label, String from_id, String to_id, ArangoDBGraph graph) { - super(key, label, graph); - this._from = from_id; - this._to = to_id; - } - - /** - * Return the id of the from/source vertex. - * - * @return the id of the vertex - */ - public String _from() { - return _from; - } - - /** - * Change the from/source vertex. - * - * @param from the from/source vertex id - */ - public void _from(String from) { - this._from = from; - } - - /** - * Return the id of the to/target vertex. - * - * @return the id of the vertex - */ - public String _to() { - return _to; - } - - /** - * Change the to/target vertex. - * - * @param to the to/target vertex id - */ - public void _to(String to) { - this._to = to; - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index 60771e9..40848b1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -1,46 +1,33 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.client; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.arangodb.*; import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; +import com.arangodb.model.*; +import com.arangodb.tinkerpop.gremlin.persistence.*; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertex; +import com.arangodb.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Graph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.arangodb.ArangoCollection; -import com.arangodb.ArangoCursor; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabase; -import com.arangodb.ArangoGraph; -import com.arangodb.model.AqlQueryOptions; -import com.arangodb.model.GraphCreateOptions; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder.UniqueVertices; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraphVariables; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertex; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.getArangoDirectionFromGremlinDirection; /** * The arangodb graph client class handles the HTTP connection to arangodb and performs database @@ -55,1131 +42,434 @@ public class ArangoDBGraphClient { - /** + /** * Common exceptions to use with an ArangoDB. This class is intended to translate ArangoDB error codes into - * meaningful exceptions with standard messages. ArangoDBException exception is a RuntimeException intended to - * break execution. + * meaningful exceptions with standard messages. ArangoDBException exception is a RuntimeException intended to + * break execution. */ - - public static class ArangoDBExceptions { - - /** The error code regex. Matches response messages from the ArangoDB client */ - public static Pattern ERROR_CODE = Pattern.compile("^Response:\\s\\d+,\\sError:\\s(\\d+)\\s-\\s([a-z\\s]+).+"); - - /** - * Instantiation happens via factory method - */ + public static class ArangoDBExceptions { - private ArangoDBExceptions() { } - /** - * Translate ArangoDB Error code into exception (@see Error codes) - * - * @param ex the ex - * @return The ArangoDBClientException + * The error code regex. Matches response messages from the ArangoDB client */ - public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) { - String errorMessage = ex.getMessage(); - Matcher m = ERROR_CODE.matcher(errorMessage); - if (m.matches()) { - int code = Integer.parseInt(m.group(1)); - String msg = m.group(2); - switch ((int)code/100) { - case 10: // Internal ArangoDB storage errors - return new ArangoDBGraphException(code, String.format("Internal ArangoDB storage error (%s): %s", code, msg), ex); - case 11: - return new ArangoDBGraphException(code, String.format("External ArangoDB storage error (%s): %s", code, msg), ex); - case 12: - return new ArangoDBGraphException(code, String.format("General ArangoDB storage error (%s): %s", code, msg), ex); - case 13: - return new ArangoDBGraphException(code, String.format("Checked ArangoDB storage error (%s): %s", code, msg), ex); - case 14: - return new ArangoDBGraphException(code, String.format("ArangoDB replication/cluster error (%s): %s", code, msg), ex); - case 15: - return new ArangoDBGraphException(code, String.format("ArangoDB query error (%s): %s", code, msg), ex); - case 19: - return new ArangoDBGraphException(code, String.format("Graph / traversal errors (%s): %s", code, msg), ex); - } - } - return new ArangoDBGraphException("General ArangoDB error (unkown error code)", ex); - } - - /** "name to long" Message. */ - - public static String NAME_TO_LONG = "Name is too long: {} bytes (max 64 bytes for labels, 256 for keys)"; + public static Pattern ERROR_CODE = Pattern.compile("^Response:\\s\\d+,\\sError:\\s(\\d+)\\s-\\s([a-z\\s]+).+"); /** - * Gets the naming convention error. - * - * @param cause the cause - * @param details the details - * @return the naming convention error + * Instantiation happens via factory method */ - public static ArangoDBGraphException getNamingConventionError(String cause, String details) { - return new ArangoDBGraphException("The provided label or name name does not satisfy the naming conventions." + - String.format(cause, details)); + private ArangoDBExceptions() { } /** - * Error persisting element property. + * Translate ArangoDB Error code into exception (@see Error codes) * * @param ex the ex - * @return the arango DB graph exception + * @return The ArangoDBClientException */ + // FIXME: match errors on code and error num instead of pattern matching on message string + public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) { + if (ex.getCause() instanceof InterruptedException) { + TraversalInterruptedException ie = new TraversalInterruptedException(); + ie.initCause(ex); + throw ie; + } - public static ArangoDBGraphException errorPersistingElmenentProperty(ArangoDBGraphException ex) { - return new ArangoDBGraphException("Error persisting property in element. ", ex); + String errorMessage = ex.getMessage(); + Matcher m = ERROR_CODE.matcher(errorMessage); + if (m.matches()) { + int code = Integer.parseInt(m.group(1)); + String msg = m.group(2); + switch (code / 100) { + case 10: // Internal ArangoDB storage errors + return new ArangoDBGraphException(code, String.format("Internal ArangoDB storage error (%s): %s", code, msg), ex); + case 11: + return new ArangoDBGraphException(code, String.format("External ArangoDB storage error (%s): %s", code, msg), ex); + case 12: + return new ArangoDBGraphException(code, String.format("General ArangoDB storage error (%s): %s", code, msg), ex); + case 13: + return new ArangoDBGraphException(code, String.format("Checked ArangoDB storage error (%s): %s", code, msg), ex); + case 14: + return new ArangoDBGraphException(code, String.format("ArangoDB replication/cluster error (%s): %s", code, msg), ex); + case 15: + return new ArangoDBGraphException(code, String.format("ArangoDB query error (%s): %s", code, msg), ex); + case 19: + return new ArangoDBGraphException(code, String.format("Graph / traversal errors (%s): %s", code, msg), ex); + } + } + return new ArangoDBGraphException("General ArangoDB error (unkown error code)", ex); } + } - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); - private final ArangoDB driver; - - private final ArangoDatabase db; + private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); - private final int batchSize; + protected final ArangoDatabase db; - private final ArangoDBGraph graph; + private final ArangoDBGraph graph; /** - * Create a simple graph client and connect to the provided db. If the DB does not exist, the driver will try to - * create one. + * Create a simple graph client and connect to the provided db. * - * @param graph the ArangoDB graph that uses this client - * @param properties the ArangoDB configuration properties - * @param dbname the ArangoDB name to connect to or create - * @param batchSize the size of the batch mode chunks - * @throws ArangoDBGraphException If the db does not exist and cannot be created + * @param graph the ArangoDB graph that uses this client + * @param properties the ArangoDB configuration properties + * @param dbname the ArangoDB name to connect to or create + * @throws ArangoDBGraphException If the db does not exist and cannot be created */ - public ArangoDBGraphClient( - ArangoDBGraph graph, - Properties properties, - String dbname, - int batchSize) - throws ArangoDBGraphException { - this(graph, properties, dbname, batchSize, false); + ArangoDBGraph graph, + Properties properties, + String dbname) + throws ArangoDBGraphException { + logger.debug("Initiating the ArangoDb Client"); + this.graph = graph; + db = new ArangoDB.Builder() + .loadProperties(ArangoConfigProperties.fromProperties(properties)) + .build() + .db(dbname); } - - /** - * Create a simple graph client and connect to the provided db. The create flag controls what is the - * behaviour if the db is not found - * - * @param graph the ArangoDB graph that uses this client - * @param properties the ArangoDB configuration properties - * @param dbname the ArangoDB name to connect to or create - * @param batchSize the size of the batch mode chunks - * @param createDatabase if true, the driver will attempt to crate the DB if it does not exist - * @throws ArangoDBGraphException If the db does not exist and cannot be created - */ - - public ArangoDBGraphClient( - ArangoDBGraph graph, - Properties properties, - String dbname, - int batchSize, - boolean createDatabase) - throws ArangoDBGraphException { - logger.info("Initiating the ArangoDb Client"); - this.graph = graph; - driver = new ArangoDB.Builder() - .loadProperties(ArangoConfigProperties.fromProperties(properties)) - .build(); - db = driver.db(dbname); - if (createDatabase) { - if (!db.exists()) { - logger.info("DB not found, attemtping to create it."); - try { - if (!driver.createDatabase(dbname)) { - throw new ArangoDBGraphException("Unable to crate the database " + dbname); - } - } - catch (ArangoDBException ex) { - throw ArangoDBExceptions.getArangoDBException(ex); - } - } - } - else { - boolean exists = false; - try { - exists = db.exists(); - } catch (ArangoDBException ex) { - // Pass - } - if (!exists) { - logger.error("Database does not exist, or the user has no access"); - throw new ArangoDBGraphException(String.format("DB not found or user has no access: {}@{}", - properties.getProperty("arangodb.user"), dbname)); - } - } - this.batchSize = batchSize; - } - /** - * Shutdown the client and free resources. - */ - - public void shutdown() { - logger.debug("Shutdown"); - if (db != null) { - if (db.exists()) { - db.clearQueryCache(); - } - } - } - - /** - * Drop the graph and its related collections. - * - * @param graph the graph to clear - * @throws ArangoDBGraphException if there was an error dropping the graph and its collections - */ - - public void clear(ArangoDBGraph graph) throws ArangoDBGraphException { - logger.info("Clear {}", graph.name()); - deleteGraph(graph.name()); - } - - /** - * Request the version of ArangoDB. - * - * @return the Version number - * @throws ArangoDBGraphException if user has no access to the db - */ - - public String getVersion() throws ArangoDBGraphException { - try { - return db.getVersion().getVersion(); - } catch (ArangoDBException ex) { - throw ArangoDBExceptions.getArangoDBException(ex); - } - } + /** + * Shutdown the client and free resources. + */ - - /** - * Gets the database. - * - * @return the ArangoDB - */ - - public ArangoDatabase getDB() { - return db; - } + public void shutdown() { + logger.debug("Shutdown"); + db.arango().shutdown(); + } - /** - * Test if the db exists. - * - * @return true if the db exists - */ - - public boolean dbExists() { - return db == null ? false: db.exists(); - } - - /** - * Delete the current database accessed by the driver. - * - * @throws ArangoDBGraphException if there was an error - */ - - public void deleteDb() throws ArangoDBGraphException { - logger.info("Delete current db"); - if (db !=null) { - try { - db.drop(); - } catch (ArangoDBException e) { - throw ArangoDBExceptions.getArangoDBException(e); - } - } - } - - /** - * Get a document from the database. The method is generic so we it can be used to retrieve - * vertices, properties or variables. - * - * @param the value type - * @param id the id of the document (should be a valid ArangoDB _id) - * @param collection the collection from which the document is retrieved - * @param docClass the returned document class - * @return the document - * @throws ArangoDBGraphException If there was an error retrieving the document - */ - - public V getDocument( - String id, - String collection, - Class docClass) { - logger.debug("Get document with id {} from {}:{}", id, graph.name(), collection); - V result; - try { - result = db.graph(graph.name()) - .vertexCollection(collection) - .getVertex(id, docClass); - } catch (ArangoDBException e) { - logger.error("Failed to retrieve vertex: {}", e.getErrorMessage()); - throw new ArangoDBGraphException("Failed to retrieve vertex.", e); - } - result.collection(collection); - result.graph(graph); - return result; - } + public ArangoDBGraphVariables getGraphVariables() { + logger.debug("Get graph variables"); + ArangoDBGraphVariables result; + try { + result = db + .collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION) + .getDocument(graph.name(), ArangoDBGraphVariables.class); + } catch (ArangoDBException e) { + logger.error("Failed to retrieve vertex: {}", e.getErrorMessage()); + throw new ArangoDBGraphException("Failed to retrieve vertex.", e); + } + result.collection(result.label); + return result; + } - /** - * Insert a ArangoDBBaseDocument in the graph. The document is updated with the id, rev and key (if not present). - * @param document the document - * @throws ArangoDBGraphException If there was an error inserting the document - */ + /** + * Insert a ArangoDBBaseDocument in the graph. The document is updated with the id, rev and name + * (if not * present) + * + * @param document the document + * @throws ArangoDBGraphException If there was an error inserting the document + */ - public void insertDocument(ArangoDBBaseDocument document) { - logger.debug("Insert document {} in {}", document, graph.name()); - insertDocument(document, document.collection()); - } + public void insertGraphVariables(ArangoDBGraphVariables document) { + logger.debug("Insert graph variables {} in {}", document, graph.name()); + if (document.isPaired()) { + throw new ArangoDBGraphException("Paired docuements can not be inserted, only updated"); + } + ArangoCollection gVars = db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION); + if (!gVars.exists()) { + CollectionEntity ce = gVars.create(); + System.out.println(ce.getStatus()); + } + DocumentCreateEntity vertexEntity; + try { + vertexEntity = gVars.insertDocument(document); + } catch (ArangoDBException e) { + logger.error("Failed to insert document: {}", e.getMessage()); + ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); + if (arangoDBException.getErrorCode() == 1210) { + throw Graph.Exceptions.vertexWithIdAlreadyExists(document._key); + } + throw arangoDBException; + } + document._id(vertexEntity.getId()); + document._rev(vertexEntity.getRev()); + if (document._key() == null) { + document._key(vertexEntity.getKey()); + } + document.setPaired(true); + } - /** - * Delete a document from the graph. - * @param document the document to delete - * @throws ArangoDBGraphException If there was an error deleting the document - */ - - public void deleteDocument(ArangoDBBaseDocument document) { - logger.debug("Delete document {} in {}", document, graph.name()); - try { - db.graph(graph.name()) - .vertexCollection(document.collection()) - .deleteVertex(document._key()); - } catch (ArangoDBException e) { - logger.error("Failed to delete document: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - document.setPaired(false); - } + /** + * Delete a document from the graph. + * + * @param document the document to delete + * @throws ArangoDBGraphException If there was an error deleting the document + */ - /** - * Update the document in the graph. - * @param document the document - * - * @throws ArangoDBGraphException If there was an error updating the document - */ - - public void updateDocument(ArangoDBBaseDocument document) { - logger.debug("Update document {} in {}", document, graph.name()); - VertexUpdateEntity vertexEntity; - try { - vertexEntity = db.graph(graph.name()) - .vertexCollection(document.collection()) - .updateVertex(document._key(), document); - } catch (ArangoDBException e) { - logger.error("Failed to update document: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - logger.info("Document updated, new rev {}", vertexEntity.getRev()); - document._rev(vertexEntity.getRev()); - } - - /** - * Get an edge from the graph. - * - * @param the value type - * @param id the id (name) of the edge - * @param collection the collection from which the edge is retrieved - * @param edgeClass the edge's specialised class - * @return the edge - * @throws ArangoDBGraphException If there was an error retrieving the edge - */ - - public V getEdge( - String id, - String collection, - Class edgeClass) { - logger.debug("Get edge {} from {}:{}", id, graph.name(), graph.getPrefixedCollectioName(collection)); - V result; - try { - result = db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(collection)) - .getEdge(id, edgeClass); - } catch (ArangoDBException e) { - logger.error("Failed to retrieve edge: {}", e.getErrorMessage()); + public void deleteGraphVariables(ArangoDBGraphVariables document) { + logger.debug("Delete variables {} in {}", document, graph.name()); + try { + db.collection(document.collection()) + .deleteDocument(document._key()); + } catch (ArangoDBException e) { + logger.error("Failed to delete document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); - } - result.collection(collection); - result.graph(graph); - return result; - } - - /** - * Insert an edge in the graph. The edge is updated with the id, rev and name (if not - * present) - * @param edge the edge - * @throws ArangoDBGraphException If there was an error inserting the edge - */ + } + document.setPaired(false); + } - public void insertEdge(ArangoDBBaseEdge edge) { - logger.debug("Insert edge {} in {} ", edge, graph.name()); - EdgeEntity insertEntity; - try { - insertEntity = db.graph(graph.name()) - .edgeCollection(edge.collection()) - .insertEdge(edge); - } catch (ArangoDBException e) { - logger.error("Failed to insert edge: {}", e.getErrorMessage()); - ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); - if (arangoDBException.getErrorCode() == 1210) { - throw Graph.Exceptions.edgeWithIdAlreadyExists(edge._key); - } - throw arangoDBException; - } - edge._id(insertEntity.getId()); - edge._key(insertEntity.getKey()); - edge._rev(insertEntity.getRev()); - edge.setPaired(true); - } - - /** - * Delete an edge from the graph. - * @param edge the edge - * @throws ArangoDBGraphException If there was an error deleting the edge - */ + /** + * Update the document in the graph. + * + * @param document the document + * @throws ArangoDBGraphException If there was an error updating the document + */ - public void deleteEdge(ArangoDBBaseEdge edge) { - logger.debug("Delete edge {} in {}", edge, graph.name()); - try { - db.graph(graph.name()) - .edgeCollection(edge.collection()) - .deleteEdge(edge._key()); - } catch (ArangoDBException e) { - logger.error("Failed to delete vertex: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - edge.setPaired(false); - } - - /** - * Update the edge in the graph. - * @param edge the edge - * @throws ArangoDBGraphException If there was an error updating the edge - */ - - public void updateEdge(ArangoDBBaseEdge edge) { - logger.debug("Update edge {} in {}", edge, graph.name()); - EdgeUpdateEntity edgeEntity; - try { - edgeEntity = db.graph(graph.name()) - .edgeCollection(edge.collection()) - .updateEdge(edge._key(), edge); - } catch (ArangoDBException e) { - logger.error("Failed to update vertex: {}", e.getErrorMessage()); + public void updateGraphVariables(ArangoDBGraphVariables document) { + logger.debug("Update variables {} in {}", document, graph.name()); + DocumentUpdateEntity updateEntity; + try { + updateEntity = db.collection(document.collection()) + .updateDocument(document._key(), document); + } catch (ArangoDBException e) { + logger.error("Failed to update document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); - } - logger.info("Edge updated, new rev {}", edgeEntity.getRev()); - edge._rev(edgeEntity.getRev()); - } + } + logger.debug("Document updated, new rev {}", updateEntity.getRev()); + document._rev(updateEntity.getRev()); + } - public ArangoDBGraphVariables getGraphVariables() { - logger.debug("Get graph variables"); - ArangoDBGraphVariables result; - try { - result = db - .collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION) - .getDocument(graph.name(), ArangoDBGraphVariables.class); - } catch (ArangoDBException e) { - logger.error("Failed to retrieve vertex: {}", e.getErrorMessage()); - throw new ArangoDBGraphException("Failed to retrieve vertex.", e); - } - result.collection(result.label); - return result; - } + /** + * Get vertices of a graph. If no ids are provided, get all vertices. + * + * @param ids the ids to match + * @return ArangoDBBaseQuery the query object + */ - /** - * Insert a ArangoDBBaseDocument in the graph. The document is updated with the id, rev and name - * (if not * present) - * @param document the document - * @throws ArangoDBGraphException If there was an error inserting the document - */ + // FIXME: use multi-docs API + public ArangoIterable getGraphVertices(final List ids) { + logger.debug("Get all {} graph vertices, filtered by ids: {}", graph.name(), ids); + List prefixedColNames = graph.vertexCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); + return getGraphDocuments(ids, prefixedColNames, VertexData.class); + } - public void insertGraphVariables(ArangoDBGraphVariables document) { - logger.debug("Insert graph variables {} in {}", document, graph.name()); - if (document.isPaired()) { - throw new ArangoDBGraphException("Paired docuements can not be inserted, only updated"); - } - ArangoCollection gVars = db.collection(document.collection()); - if (!gVars.exists()) { - CollectionEntity ce = db.createCollection(document.collection()); - System.out.println(ce.getStatus()); - } - DocumentCreateEntity vertexEntity; - try { - vertexEntity = gVars.insertDocument(document); - } catch (ArangoDBException e) { - logger.error("Failed to insert document: {}", e.getMessage()); - ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); - if (arangoDBException.getErrorCode() == 1210) { - throw Graph.Exceptions.vertexWithIdAlreadyExists(document._key); - } - throw arangoDBException; - } - document._id(vertexEntity.getId()); - document._rev(vertexEntity.getRev()); - if (document._key() == null) { - document._key(vertexEntity.getKey()); - } - document.setPaired(true); - } + /** + * Get edges of a graph. If no ids are provided, get all edges. + * + * @param ids the ids to match + * @return ArangoDBBaseQuery the query object + */ + // FIXME: use multi-docs API + public ArangoIterable getGraphEdges(List ids) { + logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); + List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); + return getGraphDocuments(ids, prefixedColNames, EdgeData.class); + } - /** - * Delete a document from the graph. - * @param document the document to delete - * @throws ArangoDBGraphException If there was an error deleting the document - */ + private ArangoIterable getGraphDocuments(List ids, List prefixedColNames, Class clazz) { + Map bindVars = new HashMap<>(); + ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); + if (ids.isEmpty()) { + if (prefixedColNames.size() > 1) { + queryBuilder.union(prefixedColNames, "d", bindVars); + } else { + queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); + } + } else { + List prunedIds = ids.stream() + .map(graph::getArangoDBId) + .filter(it -> prefixedColNames.contains(it.getCollection())) + .collect(Collectors.toList()); + queryBuilder.with(prefixedColNames, bindVars).documentsById(prunedIds, "d", bindVars); + } + queryBuilder.ret("d"); + String query = queryBuilder.toString(); + logger.debug("AQL {}", query); + return executeAqlQuery(query, bindVars, null, clazz); + } - public void deleteGraphVariables(ArangoDBGraphVariables document) { - logger.debug("Delete variables {} in {}", document, graph.name()); - try { - db.collection(document.collection()) - .deleteDocument(document._key()); - } catch (ArangoDBException e) { - logger.error("Failed to delete document: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - document.setPaired(false); - } + /** + * Create a new graph. + * + * @param name the name of the new graph + * @param edgeDefinitions the edge definitions for the graph + * @param options additional graph options + * @return the arango graph + * @throws ArangoDBGraphException If the graph can not be created + */ - /** - * Update the document in the graph. - * @param document the document - * - * @throws ArangoDBGraphException If there was an error updating the document - */ + public ArangoGraph createGraph(String name, + List edgeDefinitions, + GraphCreateOptions options) + throws ArangoDBGraphException { + logger.debug("Creating graph {}", name); + try { + logger.debug("Creating graph in database."); + db.createGraph(name, edgeDefinitions, options); + } catch (ArangoDBException e) { + logger.debug("Error creating graph in database.", e); + throw ArangoDBExceptions.getArangoDBException(e); + } + return db.graph(name); + } - public void updateGraphVariables(ArangoDBGraphVariables document) { - logger.debug("Update variables {} in {}", document, graph.name()); - DocumentUpdateEntity updateEntity; - try { - updateEntity = db.collection(document.collection()) - .updateDocument(document._key(), document); - } catch (ArangoDBException e) { - logger.error("Failed to update document: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - logger.info("Document updated, new rev {}", updateEntity.getRev()); - document._rev(updateEntity.getRev()); - } - - /** - * Create a query to get all the edges of a vertex. - * - * @param vertex the vertex - * @param edgeLabels a list of edge labels to follow, empty if all type of edges - * @param direction the direction of the edges - * @return ArangoDBBaseQuery the query object - * @throws ArangoDBException if there is an error executing the query - */ - public ArangoCursor getVertexEdges( - ArangoDBVertex vertex, - List edgeLabels, - Direction direction) - throws ArangoDBException { - logger.debug("Get Vertex's {}:{} Edges, in {}, from collections {}", vertex, direction, graph.name(), edgeLabels); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - ArangoDBQueryBuilder.Direction arangoDirection = ArangoDBUtil.getArangoDirectionFromGremlinDirection(direction); - logger.debug("Creating query"); - queryBuilder.iterateGraph(graph.name(), "v", Optional.of("e"), - Optional.empty(), Optional.empty(), Optional.empty(), - arangoDirection, vertex._id(), bindVars) - .graphOptions(Optional.of(UniqueVertices.NONE), Optional.empty(), true) - .filterSameCollections("e", edgeLabels, bindVars) - .ret("e"); - - String query = queryBuilder.toString(); - return executeAqlQuery(query, bindVars, null, ArangoDBEdge.class); - } - - /** - * Get all neighbours of a document. - * - * @param the document type - * @param document the document - * @param edgeLabelsFilter a list of edge types to follow - * @param direction a direction - * @param propertyFilter filter the neighbours on the given property:value values - * @param resultType the result type - * @return ArangoDBBaseQuery the query object - */ + /** + * Get the ArangoGraph that is linked to the client's graph + * + * @return the graph or null if the graph was not found + */ - public ArangoCursor getDocumentNeighbors( - ArangoDBBaseDocument document, - List edgeLabelsFilter, - Direction direction, - ArangoDBPropertyFilter propertyFilter, - Class resultType) { - logger.debug("Get Document's {}:{} Neighbors, in {}, from collections {}", document, direction, graph.name(), edgeLabelsFilter); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - ArangoDBQueryBuilder.Direction arangoDirection = ArangoDBUtil.getArangoDirectionFromGremlinDirection(direction); - logger.debug("Creating query"); - queryBuilder.iterateGraph(graph.name(), "v", Optional.of("e"), - Optional.empty(), Optional.empty(), Optional.empty(), - arangoDirection, document._id(), bindVars) - .graphOptions(Optional.of(UniqueVertices.GLOBAL), Optional.empty(), true) - .filterSameCollections("e", edgeLabelsFilter, bindVars) - .filterProperties(propertyFilter, "v", bindVars) - .ret("v"); - - String query = queryBuilder.toString(); - return executeAqlQuery(query, bindVars, null, resultType); - } + public ArangoGraph getArangoGraph() { + return db.graph(graph.name()); + } - /** - * Gets the element properties. - * - * @param the generic type - * @param document the document - * @param edgeLabelsFilter a list of edge types to follow - * @param propertyFilter Filter the neighbours on the given property:value values - * @param propertyType the property type - * @return ArangoDBBaseQuery the query object - */ + /** + * Execute AQL query. + * + * @param the generic type of the returned values + * @param query the query string + * @param bindVars the value of the bind parameters + * @param aqlQueryOptions the aql query options + * @param type the type of the result (POJO class, VPackSlice, String for Json, or Collection/List/Map) + * @return the cursor result + * @throws ArangoDBGraphException if executing the query raised an exception + */ - public ArangoCursor getElementProperties( - ArangoDBBaseDocument document, - List edgeLabelsFilter, - ArangoDBPropertyFilter propertyFilter, - Class propertyType) { - logger.debug("Get Vertex's {}:{} Neighbors, in {}, from collections {}", document, graph.name(), edgeLabelsFilter); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - logger.debug("Creating query"); - queryBuilder.iterateGraph(graph.name(), "v", Optional.of("e"), - Optional.empty(), Optional.empty(), Optional.empty(), - ArangoDBQueryBuilder.Direction.OUT, document._id(), bindVars) - .graphOptions(Optional.of(UniqueVertices.GLOBAL), Optional.empty(), true) - .filterSameCollections("e", edgeLabelsFilter, bindVars) - .filterProperties(propertyFilter, "v", bindVars) - .ret("v"); - - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, propertyType); - } - - - /** - * Get vertices of a graph. If no ids are provided, get all vertices. - * - * @param ids the ids to match - * @param collections the collections to search within - * @return ArangoDBBaseQuery the query object - */ + public ArangoCursor executeAqlQuery( + String query, + Map bindVars, + AqlQueryOptions aqlQueryOptions, + final Class type) + throws ArangoDBGraphException { + logger.debug("Executing AQL query ({}) against db, with bind vars: {}", query, bindVars); + try { + return db.query(query, type, bindVars, aqlQueryOptions); + } catch (ArangoDBException e) { + logger.error("Error executing query", e); + throw ArangoDBExceptions.getArangoDBException(e); + } + } - public ArangoCursor getGraphVertices( - final List ids, - final List collections) { - logger.debug("Get all {} graph vertices, filtered by ids: {}", graph.name(), ids); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - List prefixedColNames = graph.vertexCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - if (ids.isEmpty()) { - if (prefixedColNames.size() > 1) { - queryBuilder.union(prefixedColNames, "v", bindVars); - } else { - queryBuilder.iterateCollection("v", prefixedColNames.get(0), bindVars); - } - } - else { - if (!collections.isEmpty()) { - prefixedColNames = collections.stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - } - queryBuilder.with(prefixedColNames, bindVars) - .documentsById(ids, "v", bindVars); - - } - queryBuilder.ret("v"); - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBVertex.class); - } - - /** - * Get edges of a graph. If no ids are provided, get all edges. - * - * @param ids the ids to match - * @param collections the collections to search within - * @return ArangoDBBaseQuery the query object - */ - - public ArangoCursor getGraphEdges( - List ids, - List collections) { - logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - if (ids.isEmpty()) { - if (prefixedColNames.size() > 1) { - queryBuilder.union(prefixedColNames, "e", bindVars); - } else { - queryBuilder.iterateCollection("e", prefixedColNames.get(0), bindVars); - } - } - else { - if (!collections.isEmpty()) { - prefixedColNames = collections.stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); + public void insertEdge(ArangoDBEdge edge) { + logger.debug("Insert edge {} in {} ", edge, graph.name()); + EdgeEntity insertEntity; + try { + insertEntity = db.graph(graph.name()) + .edgeCollection(edge.collection()) + .insertEdge(edge.data()); + } catch (ArangoDBException e) { + logger.error("Failed to insert edge: {}", e.getErrorMessage()); + ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); + if (arangoDBException.getErrorCode() == 1210) { + throw Graph.Exceptions.edgeWithIdAlreadyExists(edge.collection() + "/" + edge.key()); } - queryBuilder.with(prefixedColNames, bindVars) - .documentsById(ids, "e", bindVars); - - } - queryBuilder.ret("e"); - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBEdge.class); - } - - /** - * Gets the edge vertices. - * - * @param edgeId the edge id - * @param edgeCollection the edge collection - * @param from if true, get incoming vertex - * @param to if true, get outgoing edge - * @return the edge vertices - */ - - public ArangoCursor getEdgeVertices( - String edgeId, - String edgeCollection, - boolean from, boolean to) { - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - List edgeCollections = new ArrayList<>(); - List vertices = new ArrayList<>(); - edgeCollections.add(graph.getPrefixedCollectioName(edgeCollection)); - if (from) { - vertices.add("_from"); - } - if (to) { - vertices.add("_to"); - } - queryBuilder.with(edgeCollections, bindVars) - .documentById(edgeId, "e", bindVars) - .append("FOR v IN ") - .append(vertices.stream().map(v->String.format("e.%s", v)) - .collect(Collectors.joining(",", "[", "]\n"))) - .ret("Document(v)"); - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBVertex.class); - } - - /** - * Delete a graph from the db, and all its collections. - * - * @param name the name of the graph to delete - * @return true, if the graph was deleted - */ - - public boolean deleteGraph(String name) { - return deleteGraph(name, true); - } - - /** - * Delete a graph from the db. If dropCollection is true, then all the graph collections are also - * dropped - * - * @param name the name - * @param dropCollections true to drop the graph collections too - * @return true if the graph was deleted - */ - - public boolean deleteGraph(String name, boolean dropCollections) { - if (db != null) { - ArangoGraph graph = db.graph(name); - if (graph.exists()) { - try { - Collection edgeDefinitions = dropCollections ? graph.getEdgeDefinitions() : Collections.emptyList(); - Collection vertexCollections = dropCollections ? graph.getVertexCollections(): Collections.emptyList(); - // Drop graph first because it will break if the graph collections do not exist - graph.drop(); - for (String definitionName : edgeDefinitions) { - String collectioName = definitionName; - if (db.collection(collectioName).exists()) { - db.collection(collectioName).drop(); - } - } - for (String vc : vertexCollections) { - String collectioName = vc; - if (db.collection(collectioName).exists()) { - db.collection(collectioName).drop(); - } - } - // Delete the graph variables - db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); - } catch (ArangoDBException e) { - System.out.println(e); - } - return true; - } else { - try { - graph.drop(); - } catch (ArangoDBException e) { - //throw ArangoDBExceptions.getArangoDBException(e); - } - } - } - return false; - } + throw arangoDBException; + } + edge.update(insertEntity); + } - /** - * Delete collection. - * - * @param name the name - * @return true, if successful - */ - - public boolean deleteCollection(String name) { - ArangoCollection collection = db.collection(name); - if (collection.exists()) { - collection.drop(); - return collection.exists(); - } - return false; - } - - /** - * Create a new graph. - * - * @param name the name of the new graph - * @param edgeDefinitions the edge definitions for the graph - * @throws ArangoDBGraphException If the graph can not be created - */ - - public void createGraph(String name, List edgeDefinitions) - throws ArangoDBGraphException { - this.createGraph(name, edgeDefinitions, null); - } - - /** - * Create a new graph. - * - * @param name the name of the new graph - * @param edgeDefinitions the edge definitions for the graph - * @param options additional graph options - * @return the arango graph - * @throws ArangoDBGraphException If the graph can not be created - */ - - public ArangoGraph createGraph(String name, - List edgeDefinitions, - GraphCreateOptions options) - throws ArangoDBGraphException { - logger.info("Creating graph {}", name); - try { - logger.info("Creating graph in database."); - db.createGraph(name, edgeDefinitions, options); - } catch (ArangoDBException e) { - logger.info("Error creating graph in database.", e); + public void deleteEdge(ArangoDBEdge edge) { + logger.debug("Delete edge {} in {}", edge, graph.name()); + try { + db.graph(graph.name()) + .edgeCollection(edge.collection()) + .deleteEdge(edge.key()); + } catch (ArangoDBException e) { + if (e.getErrorNum() == 1202) { // document not found + return; + } + logger.error("Failed to delete vertex: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - ArangoGraph g = db.graph(name); - return g; - } - - - /** - * Get the ArangoGraph that is linked to the client's graph - * - * @return the graph or null if the graph was not found - */ - - public ArangoGraph getArangoGraph() { - return db.graph(graph.name()); - } - - /** - * Execute AQL query. - * - * @param the generic type of the returned values - * @param query the query string - * @param bindVars the value of the bind parameters - * @param aqlQueryOptions the aql query options - * @param type the type of the result (POJO class, VPackSlice, String for Json, or Collection/List/Map) - * @return the cursor result - * @throws ArangoDBGraphException if executing the query raised an exception - */ + } - public ArangoCursor executeAqlQuery( - String query, - Map bindVars, - AqlQueryOptions aqlQueryOptions, - final Class type) - throws ArangoDBGraphException { - logger.debug("Executing AQL query ({}) against db, with bind vars: {}", query, bindVars); - try { - return db.query(query, type, bindVars, aqlQueryOptions); - } catch (ArangoDBException e) { - logger.error("Error executing query", e); + public void updateEdge(ArangoDBEdge edge) { + logger.debug("Update edge {} in {}", edge, graph.name()); + EdgeUpdateEntity updateEntity; + try { + updateEntity = db.graph(graph.name()) + .edgeCollection(edge.collection()) + .replaceEdge(edge.key(), edge.data()); + } catch (ArangoDBException e) { + logger.error("Failed to update edge: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); - } - } + } + logger.debug("Edge updated, new rev {}", updateEntity.getRev()); + edge.update(updateEntity); + } - /** - * Insert a document into a specific collection. - * @param document the document to insert - * @param colName the collection - */ + public VertexData readVertex(ArangoDBId id) { + logger.debug("Read vertex {} in {}", id, graph.name()); + try { + return db.graph(graph.name()) + .vertexCollection(id.getCollection()) + .getVertex(id.getKey(), VertexData.class); + } catch (ArangoDBException e) { + logger.error("Failed to read vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } - private void insertDocument(ArangoDBBaseDocument document, String colName) { + public void insertVertex(ArangoDBVertex vertex) { + logger.debug("Insert vertex {} in {}", vertex, graph.name()); VertexEntity vertexEntity; - if (document.isPaired()) { - throw new ArangoDBGraphException("Paired docuemnts can not be inserted, only updated"); - } try { vertexEntity = db.graph(graph.name()) - .vertexCollection(colName) - .insertVertex(document); + .vertexCollection(vertex.collection()) + .insertVertex(vertex.data()); } catch (ArangoDBException e) { logger.error("Failed to insert document: {}", e.getMessage()); ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); if (arangoDBException.getErrorCode() == 1210) { - throw Graph.Exceptions.vertexWithIdAlreadyExists(document._key); + throw Graph.Exceptions.vertexWithIdAlreadyExists(vertex.key()); } throw arangoDBException; } - document._id(vertexEntity.getId()); - document._rev(vertexEntity.getRev()); - if (document._key() == null) { - document._key(vertexEntity.getKey()); + vertex.update(vertexEntity); + } + + public void deleteVertex(ArangoDBVertex vertex) { + logger.debug("Delete vertex {} in {}", vertex, graph.name()); + try { + db.graph(graph.name()) + .vertexCollection(vertex.collection()) + .deleteVertex(vertex.key()); + } catch (ArangoDBException e) { + if (e.getErrorNum() == 1202) { // document not found + return; + } + logger.error("Failed to delete vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); } - document.setPaired(true); } - // TODO Decide what of these methods should be restored. -// -// /** -// * Creates vertices (bulk import). -// * -// * @param graph The graph -// * @param vertices The list of new vertices -// * @param details True, for details -// * @return a ImportResultEntity object -// * @throws ArangoDBException if an error occurs -// */ -// public ImportResultEntity createVertices( -// ArangoDBSimpleGraph graph, -// List vertices, -// boolean details) throws ArangoDBException { -// -// List> values = new ArrayList>(); -// -// for (ArangoDBSimpleVertex v : vertices) { -// values.add(v.getProperties()); -// } -// -// try { -// return driver.importDocuments(graph.getVertexCollection(), true, values); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } -// -// /** -// * Creates edges (bulk import). -// * -// * @param graph The graph -// * @param edges The list of new edges -// * @param details True, for details -// * @return a ImportResultEntity object -// * @throws ArangoDBException if an error occurs -// */ -// public ImportResultEntity createEdges(ArangoDBSimpleGraph graph, List edges, boolean details) -// throws ArangoDBException { -// -// List> values = new ArrayList>(); -// -// for (ArangoDBSimpleEdge e : edges) { -// values.add(e.getProperties()); -// } -// -// try { -// return driver.importDocuments(graph.getEdgeCollection(), true, values); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } + public void updateVertex(ArangoDBVertex vertex) { + logger.debug("Update document {} in {}", vertex, graph.name()); + VertexUpdateEntity vertexEntity; + try { + vertexEntity = db.graph(graph.name()) + .vertexCollection(vertex.collection()) + .replaceVertex(vertex.key(), vertex.data()); + } catch (ArangoDBException e) { + logger.error("Failed to update document: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + logger.debug("Document updated, new rev {}", vertexEntity.getRev()); + } -// /** -// * Create an index on collection keys. -// * -// * @param graph the simple graph -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex createVertexIndex( -// ArangoDBSimpleGraph graph, -// IndexType type, -// boolean unique, -// List fields) throws ArangoDBException { -// return createIndex(graph.getVertexCollection(), type, unique, fields); -// } -// -// /** -// * Create an index on collection keys. -// * -// * @param graph the simple graph -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex createEdgeIndex(ArangoDBSimpleGraph graph, IndexType type, boolean unique, List fields) -// throws ArangoDBException { -// return createIndex(graph.getEdgeCollection(), type, unique, fields); -// } -// -// /** -// * Get an index. -// * -// * @param id id of the index -// * @return ArangoDBIndex the index, or null if the index was not found -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex getIndex(String id) throws ArangoDBException { -// IndexEntity index; -// try { -// index = driver.getIndex(id); -// } catch (ArangoException e) { -// -// if (e.getErrorNumber() == ErrorNums.ERROR_ARANGO_INDEX_NOT_FOUND) { -// return null; -// } -// -// throw new ArangoDBException(e); -// } -// return new ArangoDBIndex(index); -// } -// -// /** -// * Returns the indices of the vertex collection. -// * -// * @param graph The graph -// * @return List of indices -// * @throws ArangoDBException if an error occurs -// */ -// public List getVertexIndices(ArangoDBSimpleGraph graph) throws ArangoDBException { -// return getIndices(graph.getVertexCollection()); -// } -// -// /** -// * Returns the indices of the edge collection. -// * -// * @param graph The graph -// * @return List of indices -// * @throws ArangoDBException if an error occurs -// */ -// public List getEdgeIndices(ArangoDBSimpleGraph graph) throws ArangoDBException { -// return getIndices(graph.getEdgeCollection()); -// } -// -// /** -// * Deletes an index. -// * -// * @param id The identifier of the index -// * @return true, if the index was deleted -// * @throws ArangoDBException if an error occurs -// */ -// public boolean deleteIndex(String id) throws ArangoDBException { -// try { -// driver.deleteIndex(id); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// return true; -// } -// -// /** -// * Create an index on collection keys. -// * -// * @param collectionName the collection name -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// private ArangoDBIndex createIndex(String collectionName, IndexType type, boolean unique, List fields) -// throws ArangoDBException { -// -// IndexEntity indexEntity; -// try { -// indexEntity = driver.createIndex(collectionName, type, unique, fields.toArray(new String[0])); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// return new ArangoDBIndex(indexEntity); -// } -// -// /** -// * Get the List of indices of a collection. -// * -// * @param collectionName the collection name -// * @return Vector List of indices -// * @throws ArangoDBException if creation failed -// */ -// -// private List getIndices(String collectionName) throws ArangoDBException { -// List indices = new ArrayList(); -// -// IndexesEntity indexes; -// try { -// indexes = driver.getIndexes(collectionName); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// for (IndexEntity indexEntity : indexes.getIndexes()) { -// indices.add(new ArangoDBIndex(indexEntity)); -// } -// -// return indices; -// } -// -// /** -// * Returns the current connection configuration. -// * -// * @param collectionName the collection name -// * @return the configuration -// * @throws ArangoDBException the arango DB exception -// */ -//// public ArangoDBConfiguration getConfiguration() { -//// return configuration; -//// } -// -// /** -// * Truncates a collection -// * -// * @param collectionName -// */ -// public void truncateCollection(String collectionName) throws ArangoDBException { -// try { -// driver.truncateCollection(collectionName); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } + public Iterator getVertexNeighbors(ArangoDBId vertexId, List edgeCollections, Direction direction) { + logger.debug("Get vertex {}:{} Neighbors, in {}, from collections {}", vertexId, direction, graph.name(), edgeCollections); + Map bindVars = new HashMap<>(); + bindVars.put("start", vertexId); + bindVars.put("graph", graph.name()); + bindVars.put("edgeCollections", edgeCollections); + String query = "FOR v IN 1..1 " + getArangoDirectionFromGremlinDirection(direction).getAqlName() + + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections, order: 'bfs' } RETURN v"; + return executeAqlQuery(query, bindVars, null, VertexData.class); + } + public Iterator getVertexEdges(ArangoDBId vertexId, List edgeCollections, Direction direction) { + logger.debug("Get vertex {}:{} Edges, in {}, from collections {}", vertexId, direction, graph.name(), edgeCollections); + Map bindVars = new HashMap<>(); + bindVars.put("start", vertexId); + bindVars.put("graph", graph.name()); + bindVars.put("edgeCollections", edgeCollections); + String query = "FOR v, e IN 1..1 " + getArangoDirectionFromGremlinDirection(direction).getAqlName() + + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections } RETURN e"; + return executeAqlQuery(query, bindVars, null, EdgeData.class); + } } \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBIterator.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBIterator.java deleted file mode 100644 index fdfbaf3..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBIterator.java +++ /dev/null @@ -1,59 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.client; - -import java.util.Iterator; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -/** - * The ArangoDBIterator is used to wrap ArangoDB documents from a query iterator into Graph elements: Vertex, Edge, - * and Property. - * - * @param the Graph Element type returned at each iteration - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -public class ArangoDBIterator implements Iterator { - - private final ArangoCursor delegate; - - private final ArangoDBGraph graph; - - /** - * Instantiates a new ArangoDB iterator. - * - * @param graph the graph - * @param delegate the delegate - */ - public ArangoDBIterator(ArangoDBGraph graph, ArangoCursor delegate) { - super(); - this.delegate = delegate; - this.graph = graph; - } - - @Override - public boolean hasNext() { - return delegate.hasNext(); - } - - @SuppressWarnings("unchecked") - @Override - public IType next() { - ArangoDBBaseDocument next = null; - next = (ArangoDBBaseDocument) delegate.next(); - next.graph(graph); - next.collection(graph.getPrefixedCollectioName(next.label)); - next.setPaired(true); - return (IType) next; - } - -} \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBPropertyIterator.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBPropertyIterator.java deleted file mode 100644 index a07ce4d..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBPropertyIterator.java +++ /dev/null @@ -1,65 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.client; - -import java.util.Iterator; - -import org.apache.tinkerpop.gremlin.structure.Property; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBElementProperty; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -/** - * The ArangoDBIterator is used to wrap Arango DB Element Properties from a query iterator into Graph - * elements: Vertex, Edge, Property. A separate iterator is needed since Properties have a generic - * type. - * - * @see ArangoDBIterator - * @param the Property's type - * @param

the Property implementation returned by the iterator - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -public class ArangoDBPropertyIterator> implements Iterator

{ - - private final ArangoCursor> delegate; - - private final ArangoDBGraph graph; - - /** - * Instantiates a new ArangoDB Property iterator. - * - * @param graph the graph - * @param documentNeighbors the delegate cursor - */ - public ArangoDBPropertyIterator(ArangoDBGraph graph, ArangoCursor> documentNeighbors) { - super(); - this.delegate = documentNeighbors; - this.graph = graph; - } - - @Override - public boolean hasNext() { - return delegate.hasNext(); - } - - @SuppressWarnings("unchecked") - @Override - public P next() { - ArangoDBElementProperty next = null; - next = (ArangoDBElementProperty) delegate.next(); - next.graph(graph); - next.collection(graph.getPrefixedCollectioName(next.label)); - next.setPaired(true); - return (P) next; - } - -} \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java index eea7b66..80786aa 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java @@ -8,13 +8,10 @@ package com.arangodb.tinkerpop.gremlin.client; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,83 +71,6 @@ String getAqlName() { } - /** - * Options for vertices in Graph Traversals. - * - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - */ - - enum UniqueVertices { - - /** It is guaranteed that there is no path returned with a duplicate vertex. */ - PATH("path"), - - /** it is guaranteed that each vertex is visited at most once during the traversal, no - * matter how many paths lead from the start vertex to this one. */ - GLOBAL("global"), - - /** No uniqueness check is applied on vertices - (default). */ - NONE("none"); - - /** The aql name. */ - private final String aqlName; - - /** - * Instantiates a new unique vertices. - * - * @param aqlName the aql name - */ - UniqueVertices(String aqlName) { - this.aqlName = aqlName; - } - - /** - * Gets the aql name. - * - * @return the aql name - */ - String getAqlName() { - return aqlName; - } - } - - /** - * Options for edges in Graph Traversals. - * - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - */ - - enum UniqueEdges { - - /** It is guaranteed that there is no path returned with a duplicate edge - (default). */ - PATH("path"), - - /** No uniqueness check is applied on edges. Note: Using this configuration the - * traversal will follow cycles in edges. */ - NONE("none"); - - /** The aql name. */ - private final String aqlName; - - /** - * Instantiates a new unique edges. - * - * @param aqlName the aql name - */ - UniqueEdges(String aqlName) { - this.aqlName = aqlName; - } - - /** - * Gets the aql name. - * - * @return the aql name - */ - String getAqlName() { - return aqlName; - } - } - /** * Create a new QueryBuilder with config of whether Collection Names should be prefixed with Graph name or not. */ @@ -195,34 +115,14 @@ public ArangoDBQueryBuilder with(List collections, Map b */ public ArangoDBQueryBuilder documentsById( - List ids, + List ids, String loopVariable, Map bindVars) { queryBuilder.append("LET docs = FLATTEN(RETURN Document(@ids))\n"); queryBuilder.append(String.format("FOR %s IN docs\n", loopVariable)); queryBuilder.append(String.format(" FILTER NOT IS_NULL(%s)\n", loopVariable)); // Not needed? bindVars.put("ids", ids); - logger.debug("documentsById", queryBuilder.toString()); - return this; - } - - /** - * Append a Document statement to find a single element in the graph. This segment should be - * used in conjunction with the {@link #with(List, Map)} segment. - * - * @param id the id to look for - * @param loopVariable the loop variable name - * @param bindVars the the map of bind parameters - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder documentById( - String id, - String loopVariable, - Map bindVars) { - queryBuilder.append(String.format("LET %s = Document(@id)\n", loopVariable)); - bindVars.put("id", id); - logger.debug("documentById", queryBuilder.toString()); + logger.debug("documentsById: {}", queryBuilder.toString()); return this; } @@ -274,202 +174,6 @@ public ArangoDBQueryBuilder iterateCollection( return this; } - /** - * Add a graph iteration segment. - * @param graphName the graph name - * @param vertexVariable the vertex variable - * @param edgeVariable the edge variable - * @param pathVariable the path variable - * @param min edges and vertices returned by this query will start at the traversal depth of min - * @param max up to max length paths are traversed - * @param direction follow edges pointing in the direction - * @param startVertex the start vertex id - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder iterateGraph( - String graphName, - String vertexVariable, - Optional edgeVariable, - Optional pathVariable, - Optional min, - Optional max, - Direction direction, - String startVertex, - Map bindVars) { - queryBuilder.append(String.format("FOR %s", vertexVariable)); - if (edgeVariable.isPresent()) { - queryBuilder.append(String.format(", %s", edgeVariable.get())); - } - if (pathVariable.isPresent()) { - queryBuilder.append(String.format(", %s", pathVariable.get())); - } - queryBuilder.append("\n IN "); - if (min.isPresent()) { - queryBuilder.append(min.get()); - if (max.isPresent()) { - queryBuilder.append(String.format("..%s", max.get())); - } - queryBuilder.append(" "); - } - queryBuilder.append(direction.getAqlName()).append(" @startVertex\n") - .append(" GRAPH '").append(graphName).append("'\n"); // FIXME Graph could be a parameter - bindVars.put("startVertex", startVertex); - logger.debug("iterateGraph", queryBuilder.toString()); - return this; - } - - /** - * Iterate over a collection of edges. - * @param graphName the graph name - * @param vertexVariable the vertex variable - * @param edgeVariable the edge variable - * @param pathVariable the path variable - * @param min edges and vertices returned by this query will start at the traversal depth of min - * @param max up to max length paths are traversed - * @param direction follow edges pointing in the direction - * @param edgeCollections the edge collections - * @param startVertex the start vertex id - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder iterateEdges( - String graphName, - String vertexVariable, - Optional edgeVariable, - Optional pathVariable, - Optional min, - Optional max, - Direction direction, - List edgeCollections, - String startVertex, Map bindVars) { - queryBuilder.append(String.format("FOR %s", vertexVariable)); - edgeVariable.ifPresent(ev -> queryBuilder.append(String.format(", %s", ev))); - pathVariable.ifPresent(pv -> queryBuilder.append(String.format(", %s", pv))); - queryBuilder.append("\n IN "); - min.ifPresent(queryBuilder::append); - max.ifPresent(m -> queryBuilder.append(String.format("..%s", m))); - queryBuilder.append(direction.getAqlName()).append(" @startVertex\n"); - String separator = ""; - for (String c : edgeCollections) { - queryBuilder.append(separator); - separator = ", "; - queryBuilder.append(String.format("@@col%s", iterateCnt)); - bindVars.put(String.format("@col%s", iterateCnt++), c); - } - bindVars.put("@startVertex", startVertex); - logger.debug("iterateGraph", queryBuilder.toString()); - return this; - } - - /** - * Add a Graph options segment. - * - * @see UniqueVertices - * @see UniqueEdges - * @param onVertices the vertices options - * @param onEdges the edges options - * @param bfs if true, the traversal will be executed breadth-first, else it will - * be executed depth-first. - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder graphOptions( - Optional onVertices, - Optional onEdges, - boolean bfs) { - if (onVertices.isPresent() || onEdges.isPresent() || bfs) { - queryBuilder.append(" OPTIONS {"); - if (onVertices.isPresent()) { - queryBuilder.append(String.format("uniqueVertices: '%s', ", onVertices.get().getAqlName())); - } - if (onEdges.isPresent()) { - queryBuilder.append(String.format("uniqueEdges: '%s', ", onEdges.get().getAqlName())); - } - if (bfs) { - queryBuilder.append("bfs: true"); - } - queryBuilder.append("}\n"); - } - logger.debug("graphOptions", queryBuilder.toString()); - return this; - } - - /** - * Add a filter same collections segment, i.e. element represented by variable must be in any - * of the provided collections. - * @param filterVariable the filter variable - * @param collections the collections to filter by - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder filterSameCollections( - String filterVariable, - List collections, - Map bindVars) { - if (!collections.isEmpty()) { - queryBuilder.append(" FILTER (IS_SAME_COLLECTION("); - String separator = ""; - for (String c : collections) { - queryBuilder.append(separator); - separator = String.format(", %s) OR IS_SAME_COLLECTION(", filterVariable); - queryBuilder.append(String.format("@@col%s", iterateCnt)); - bindVars.put(String.format("@col%s", iterateCnt++), c); - } - queryBuilder.append(String.format(", %s))\n", filterVariable)); - } - filtered = true; - logger.debug("filterSameCollections", queryBuilder.toString()); - return this; - } - - /** - * Add a filter on element properties segment. The filter operations are defined using a - * #link {@link ArangoDBPropertyFilter}. - * - * @param propertyFilter the property filter - * @param filterVariable the filter variable - * @param bindVars the map of bind parameters - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder filterProperties( - ArangoDBPropertyFilter propertyFilter, - String filterVariable, - Map bindVars) { - List filterSegments = new ArrayList(); - propertyFilter.addAqlSegments(String.format("%s.", filterVariable), filterSegments, bindVars); - if (CollectionUtils.isNotEmpty(filterSegments)) { - if (filtered) { - queryBuilder.append(" AND "); - } else { - queryBuilder.append(" FILTER "); - } - queryBuilder.append(StringUtils.join(filterSegments, " AND ")).append("\n"); - } - logger.debug("filterProperties", queryBuilder.toString()); - return this; - } - - /** - * Add a limit segment to limit the number of elements returned. - * - * @param limit the limit number - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder limit(Long limit) { - queryBuilder.append(" LIMIT " + limit.toString()); - logger.debug("limit", queryBuilder.toString()); - return this; - } - /** * Add a RETURN Segment. * TODO provide finer grained return statements diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java index 9b06c45..a5e52d7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -1,13 +1,15 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.jsr223; +import com.arangodb.tinkerpop.gremlin.persistence.*; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; @@ -18,44 +20,60 @@ /** * The Class ArangoDBGremlinPlugin. + * * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { - /** The Constant NAME. */ - private static final String NAME = "tinkerpop.arangodb"; + /** + * The Constant NAME. + */ + private static final String NAME = "tinkerpop.arangodb"; - /** The Constant IMPORTS. */ - private static final ImportCustomizer IMPORTS; + /** + * The Constant IMPORTS. + */ + private static final ImportCustomizer IMPORTS; static { try { IMPORTS = DefaultImportCustomizer.build().addClassImports( - ArangoDBBaseDocument.class, - ArangoDBBaseEdge.class, - ArangoDBGraphClient.class, - ArangoDBGraphException.class, - ArangoDBIterator.class, - ArangoDBPropertyFilter.class, - ArangoDBPropertyIterator.class, - ArangoDBQueryBuilder.class, - ArangoDBEdge.class, - ArangoDBEdgeProperty.class, - ArangoDBElementProperty.class, - ArangoDBGraph.class, - ArangoDBGraphVariables.class, - ArangoDBPropertyProperty.class, - ArangoDBVertex.class, - ArangoDBVertexProperty.class, - ArangoDBUtil.class - ) - .create(); + ArangoDBBaseDocument.class, + ArangoDBGraphClient.class, + ArangoDBGraphException.class, + ArangoDBPropertyFilter.class, + ArangoDBQueryBuilder.class, + ArangoDBUtil.class, + + // structure + ArangoDBEdge.class, + ArangoDBElement.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + ArangoDBPersistentElement.class, + ArangoDBProperty.class, + ArangoDBSimpleElement.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class, + + // persistence + AdbValue.class, + EdgeData.class, + PersistentData.class, + PropertyData.class, + SimplePropertyData.class, + VertexData.class, + VertexPropertyData.class + ) + .create(); } catch (Exception ex) { throw new RuntimeException(ex); } } - /** The Constant INSTANCE. */ + /** + * The Constant INSTANCE. + */ private static final ArangoDBGremlinPlugin INSTANCE = new ArangoDBGremlinPlugin(); /** diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java new file mode 100644 index 0000000..cf4461b --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; + +import java.util.Objects; + +public class AdbValue { + + private final Object value; + private final String valueType; + + @JsonCreator + AdbValue( + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.value = value; + this.valueType = valueType; + } + + public static AdbValue of(Object value) { + return new AdbValue(value, (value != null ? value.getClass() : Void.class).getCanonicalName()); + } + + public Object getValue() { + return ArangoDBUtil.getCorretctPrimitive(value, valueType); + } + + public String getValueType() { + return valueType; + } + + @Override + public String toString() { + return "AdbValue{" + + "value=" + value + + ", valueType='" + valueType + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof AdbValue)) return false; + AdbValue adbValue = (AdbValue) o; + return Objects.equals(value, adbValue.value) && Objects.equals(valueType, adbValue.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(value, valueType); + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java new file mode 100644 index 0000000..8c789f8 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.serde.*; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; + +import java.util.*; + +public class EdgeData extends SimplePropertyData implements PersistentData { + + @InternalId + private ArangoDBId id; + + @JsonProperty + private String label; + + @InternalKey + private String key; + + @InternalFrom + private ArangoDBId from; + + @InternalTo + private ArangoDBId to; + + public static EdgeData of( + ArangoDBId id, + ArangoDBId from, + ArangoDBId to + ) { + EdgeData data = new EdgeData(); + data.id = id; + data.label = id.getLabel(); + data.key = id.getKey(); + data.from = from; + data.to = to; + return data; + } + + public EdgeData() { + } + + @Override + public ArangoDBId getId() { + return id; + } + + @Override + public void setId(ArangoDBId id) { + this.id = id; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + public ArangoDBId getFrom() { + return from; + } + + public void setFrom(ArangoDBId from) { + this.from = from; + } + + public ArangoDBId getTo() { + return to; + } + + public void setTo(ArangoDBId to) { + this.to = to; + } + + @Override + public String toString() { + return "EdgeData{" + + "from=" + from + + ", id=" + id + + ", label='" + label + '\'' + + ", key='" + key + '\'' + + ", to=" + to + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof EdgeData)) return false; + if (!super.equals(o)) return false; + EdgeData edgeData = (EdgeData) o; + return Objects.equals(id, edgeData.id) && Objects.equals(label, edgeData.label) && Objects.equals(key, edgeData.key) && Objects.equals(from, edgeData.from) && Objects.equals(to, edgeData.to); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, label, key, from, to); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java new file mode 100644 index 0000000..91c640d --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.entity.DocumentEntity; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; + +public interface PersistentData { + + ArangoDBId getId(); + + void setId(ArangoDBId id); + + void setKey(String key); + + default String getLabel() { + return getId().getLabel(); + } + + default String getKey() { + return getId().getKey(); + } + + default String getCollection() { + return getId().getCollection(); + } + + default void update(DocumentEntity entity) { + String k = entity.getKey(); + setKey(k); + setId(getId().withKey(k)); + } + +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java new file mode 100644 index 0000000..8f04e7e --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import java.util.Map; +import java.util.stream.Stream; + +public interface PropertyData { + + Stream> entries(); + + void add(String key, V value); +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java new file mode 100644 index 0000000..2a25714 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +public class SimplePropertyData implements PropertyData { + + @JsonProperty + private final Map properties = new HashMap<>(); + + @Override + public Stream> entries() { + return properties.entrySet().stream(); + } + + @Override + public void add(String key, AdbValue value) { + properties.put(key, value); + } + + public void remove(String key) { + properties.remove(key); + } + + @Override + public String toString() { + return "SimplePropertyData{" + + "properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SimplePropertyData)) return false; + SimplePropertyData that = (SimplePropertyData) o; + return Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hashCode(properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java new file mode 100644 index 0000000..e017cc1 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.serde.InternalId; +import com.arangodb.serde.InternalKey; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; + +import java.util.*; +import java.util.stream.Stream; + +public class VertexData implements PropertyData, PersistentData { + + @InternalId + private ArangoDBId id; + + @JsonProperty + private String label; + + @InternalKey + private String key; + + @JsonProperty + private final Map> properties = new HashMap<>(); + + public VertexData() { + } + + public static VertexData of(ArangoDBId id) { + VertexData data = new VertexData(); + data.id = id; + data.label = id.getLabel(); + data.key = id.getKey(); + return data; + } + + @Override + public ArangoDBId getId() { + return id; + } + + @Override + public void setId(ArangoDBId id) { + this.id = id; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + @Override + public Stream> entries() { + return properties.entrySet().stream().flatMap(e -> e.getValue().stream() + .map(v -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v))); + } + + @Override + public void add(String key, VertexPropertyData value) { + properties.computeIfAbsent(key, k -> new HashSet<>()).add(value); + } + + public void remove(String key, VertexPropertyData value) { + Set props = properties.getOrDefault(key, Collections.emptySet()); + props.remove(value); + if (props.isEmpty()) { + properties.remove(key); + } + } + + @Override + public String toString() { + return "VertexData{" + + "id=" + id + + ", label='" + label + '\'' + + ", key='" + key + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexData)) return false; + VertexData that = (VertexData) o; + return Objects.equals(id, that.id) && Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(id, label, key, properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java new file mode 100644 index 0000000..9f5084f --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; + +import java.util.Objects; + +public class VertexPropertyData extends SimplePropertyData { + + private final String id; + private final Object value; + private final String valueType; + + @JsonCreator + VertexPropertyData( + @JsonProperty("id") String id, + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.id = id; + this.value = value; + this.valueType = valueType; + } + + public static VertexPropertyData of(String id, Object value) { + return new VertexPropertyData(id, value, (value != null ? value.getClass() : Void.class).getCanonicalName()); + } + + public String getId() { + return id; + } + + public Object getValue() { + return ArangoDBUtil.getCorretctPrimitive(value, valueType); + } + + public String getValueType() { + return valueType; + } + + @Override + public String toString() { + return "VertexPropertyData{" + + "id='" + id + '\'' + + ", value=" + value + + ", valueType='" + valueType + '\'' + + ", super=" + super.toString() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexPropertyData)) return false; + if (!super.equals(o)) return false; + VertexPropertyData that = (VertexPropertyData) o; + return Objects.equals(id, that.id) && Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, value, valueType); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java index 9cf6088..8f0f47b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -1,168 +1,86 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import com.arangodb.tinkerpop.gremlin.client.*; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; -import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - - -/** - * The ArangoDB Edge class. +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 * - * @author Achim Brandt (http://www.triagens.de) - * @author Johannes Gocke (http://www.triagens.de) - * @author Guido Schwab (http://www.triagens.de) - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) + * 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. */ -public class ArangoDBEdge extends ArangoDBBaseEdge implements Edge { - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBEdge.class); +package com.arangodb.tinkerpop.gremlin.structure; - /** - * Constructor used for ArabgoDB JavaBeans de-/serialisation. - */ +import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; - public ArangoDBEdge() { super(); } +import java.util.*; - /** - * Create a new ArangoDBEdge that connects the given vertices. The edge name can be provided. - * @param key the edge name - * @param label the edge label - * @param from the source vertex - * @param to the target vertex - * @param graph the graph in which the edge is created - */ - public ArangoDBEdge( - String key, - String label, - ArangoDBVertex from, - ArangoDBVertex to, - ArangoDBGraph graph) { - super(key, label, from._id(), to._id(), graph); - } +public class ArangoDBEdge extends ArangoDBSimpleElement implements Edge, ArangoDBPersistentElement { - /** - * Create a new ArangoDBEdge that connects the given vertices. - * - * @param graph the graph in which the edge is created - * @param label the label into with the edge is created - * @param from the source vertex - * @param to the target vertex - */ + public static ArangoDBEdge of(final ArangoDBId id, final ArangoDBId outVertexId, final ArangoDBId inVertexId, ArangoDBGraph graph) { + return new ArangoDBEdge(graph, EdgeData.of(id, outVertexId, inVertexId)); + } - public ArangoDBEdge( - ArangoDBGraph graph, - String label, - ArangoDBVertex from, - ArangoDBVertex to) { - this(null, label, from, to, graph); - } + public ArangoDBEdge(ArangoDBGraph graph, EdgeData data) { + super(graph, data); + } @Override - public Object id() { - return _id(); + protected void doRemove() { + graph.getClient().deleteEdge(this); } @Override - public String label() { - return label; + protected void doUpdate() { + graph.getClient().updateEdge(this); } + public void doInsert() { + graph.getClient().insertEdge(this); + } - @Override - public Property property( - String key, - V value) { - logger.info("set property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - Property p = property(key); - if (!p.isPresent()) { - p = ArangoDBUtil.createArangoDBEdgeProperty(key, value, this); - } - else { - ((ArangoDBEdgeProperty) p).value(value); - } - return p; - } - - @Override - public void remove() { - logger.info("removing {} from graph {}.", this._key(), graph.name()); - if (paired) { - try { - graph.getClient().deleteEdge(this); - } catch (ArangoDBGraphException ex) { - - } - } - } - - @Override - public Iterator vertices(Direction direction) { - boolean from = true; - boolean to = true; - switch(direction) { - case BOTH: - break; - case IN: - from = false; - break; - case OUT: - to = false; - break; - } - String edgeCollection = isPaired() ? label() : graph.getPrefixedCollectioName(label()); - return new ArangoDBIterator<>(graph, graph.getClient().getEdgeVertices(_id(), edgeCollection, from, to)); - } - - @SuppressWarnings("unchecked") - @Override - public Iterator> properties(String... propertyKeys) { - List labels = new ArrayList<>(); - labels.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)); - ArangoDBPropertyFilter filter = new ArangoDBPropertyFilter(); - for (String pk : propertyKeys) { - filter.has("name", pk, ArangoDBPropertyFilter.Compare.EQUAL); - } - ArangoCursor documentNeighbors = graph.getClient().getElementProperties(this, labels, filter, ArangoDBEdgeProperty.class); - return new ArangoDBPropertyIterator<>(graph, (ArangoCursor>) documentNeighbors); - } + @Override + protected String stringify() { + return StringFactory.edgeString(this); + } - @Override - public String toString() { - return StringFactory.edgeString(this); + @Override + public Vertex outVertex() { + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getFrom())); } - + @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); + public Vertex inVertex() { + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getTo())); } @Override - public int hashCode() { - return ElementHelper.hashCode(this); + public Iterator vertices(final Direction direction) { + if (removed()) return Collections.emptyIterator(); + switch (direction) { + case OUT: + return IteratorUtils.of(this.outVertex()); + case IN: + return IteratorUtils.of(this.inVertex()); + default: + return IteratorUtils.of(this.outVertex(), this.inVertex()); + } } + @Override + public Iterator> properties(final String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); + } } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeProperty.java deleted file mode 100644 index 9caa9a0..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeProperty.java +++ /dev/null @@ -1,57 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.Collections; - -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Element; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - -/** - * The Class ArangoDBEdgeProperty. - * - * @param the generic type - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ -public class ArangoDBEdgeProperty extends ArangoDBElementProperty { - - /** - * Constructor used for Arabgo DB JavaBeans serialisation. - */ - - public ArangoDBEdgeProperty() { - super(); - } - - /** - * Instantiates a new Arango DB edge property. - * - * @param key the name of the property - * @param value the value of the property - * @param owner the owner of the property - */ - - public ArangoDBEdgeProperty(String key, V value, ArangoDBBaseDocument owner) { - super(key, value, owner, ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION); - } - - @Override - public Element element() { - ArangoCursor q = graph.getClient().getDocumentNeighbors( this, Collections.emptyList(), Direction.IN, ArangoDBPropertyFilter.empty(), ArangoDBEdge.class); - ArangoDBIterator iterator = new ArangoDBIterator(graph, q); - return iterator.hasNext() ? iterator.next() : null; - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java new file mode 100644 index 0000000..326ecae --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.PropertyData; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.stream.Collectors; + +public abstract class ArangoDBElement> implements Element { + + protected final ArangoDBGraph graph; + protected final D data; + private boolean removed = false; + + public ArangoDBElement(ArangoDBGraph graph, D data) { + this.graph = graph; + this.data = data; + } + + protected abstract Property createProperty(String key, P value); + + //region CRUD ops + protected abstract void doUpdate(); + + protected abstract void doRemove(); + + protected abstract void doInsert(); + //endregion + + protected abstract String stringify(); + + public D data() { + return data; + } + + protected boolean removed() { + return removed; + } + + @Override + public ArangoDBGraph graph() { + return graph; + } + + @Override + public void remove() { + if (removed) return; + doRemove(); + removed = true; + } + + @Override + public Iterator> properties(String... propertyKeys) { + if (removed) return Collections.emptyIterator(); + return data.entries() + .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) + .map((Map.Entry e) -> this.createProperty(e.getKey(), e.getValue())) + .collect(Collectors.toList()) // avoids ConcurrentModificationException on removal from downstream + .iterator(); + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(final Object object) { + return ElementHelper.areEqual(this, object); + } + + @Override + public int hashCode() { + return ElementHelper.hashCode(this); + } + + @Override + public String toString() { + return stringify(); + } + + public static class Exceptions { + private Exceptions() { + } + + public static IllegalStateException elementAlreadyRemoved(final Object id) { + return new IllegalStateException(String.format("Element with id %s was removed.", id)); + } + + public static IllegalStateException unsupportedIdType(final Object id) { + return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); + } + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElementProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElementProperty.java deleted file mode 100644 index 4acea54..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElementProperty.java +++ /dev/null @@ -1,189 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.NoSuchElementException; - -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; -import org.apache.tinkerpop.gremlin.structure.util.StringFactory; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseEdge; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * The Class ArangoDBProperty. - * - * @param the property value type - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -public abstract class ArangoDBElementProperty extends ArangoDBBaseDocument implements Property { - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBElementProperty.class); - - /** - * An Edge to link an ArangoDBBaseDocument to one of its properties. The from parameter is an - * ArangoDBBaseDocument since we allow ArangoDBElementProperty to have properties too. - */ - - public static class ElementHasProperty extends ArangoDBBaseEdge { - - /** - * Instantiates a new element has property. - * - * @param from the from - * @param to the to - * @param graph the graph - */ - public ElementHasProperty(ArangoDBBaseDocument from, ArangoDBElementProperty to, ArangoDBGraph graph) { - super(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION, from._id(), to._id(), graph); - } - } - - /** The property name. */ - - @JsonProperty - protected String name; - - /** The property value. */ - - @JsonProperty - protected V value; - - /** The property type */ - - @JsonProperty - protected String valueType; - - - /** - * Constructor used for Arango DB JavaBeans serialisation. - */ - - public ArangoDBElementProperty() { super(); } - - /** - * Instantiates a new Arango DB element property. - * - * @param key the id - * @param name the name - * @param value the value - * @param owner the owner - * @param label the label - */ - - public ArangoDBElementProperty(String key, String name, V value, ArangoDBBaseDocument owner, String label) { - super(key, label, owner.graph()); - this.name = name; - this.value = value; - this.valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); - } - - /** - * Instantiates a new Arango DB element property. - * - * @param name the name - * @param value the value - * @param owner the owner - * @param label the collection - */ - - public ArangoDBElementProperty(String name, V value, ArangoDBBaseDocument owner, String label) { - this(null, name, value, owner, label); - } - - @JsonIgnore - @Override - public boolean isPresent() { - return value != null; - } - - @Override - public String key() { - return name; - } - - @Override - public void remove() { - logger.info("remove {}", this._id()); - if (paired) { - //Remove vertex - try { - graph.getClient().deleteDocument(this); - } catch (ArangoDBGraphException ex) { - // Pass Removing a property that does not exists should not throw an exception. - } - } - } - - @SuppressWarnings("unchecked") - @Override - public V value() throws NoSuchElementException { - return (V) ArangoDBUtil.getCorretctPrimitive(value, valueType); - } - - /** - * Value. - * - * @param value the value - * @return the v - */ - public V value(V value) { - V oldValue = this.value; - this.value = value; - if (!value.equals(oldValue)) { - save(); - } - return oldValue; - } - - /** - * Save. - */ - - public void save() { - if (paired) { - graph.getClient().updateDocument(this); - } - } - - @Override - public String toString() { - return StringFactory.propertyString(this); - } - - @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); - } - - @Override - public int hashCode() { - return ElementHelper.hashCode(this); - } - - /** - * Assign this property to a Document and create an ElementHasProperty that represents the connection. - * @param doc The document - * @return an ElementHasProperty (edge) that connects the element to this property - */ - - public ElementHasProperty assignToElement(ArangoDBBaseDocument doc) { - return new ElementHasProperty(doc, this, doc.graph()); - } -} \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index 20a2b90..23e5869 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -1,15 +1,14 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.structure; import java.util.*; -import java.util.regex.Matcher; import java.util.stream.Collectors; import com.arangodb.entity.EdgeDefinition; @@ -18,10 +17,7 @@ import org.apache.commons.configuration2.ConfigurationConverter; import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.slf4j.Logger; @@ -31,12 +27,13 @@ import com.arangodb.model.GraphCreateOptions; import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.unsupportedIdType; + /** * The ArangoDB graph class. - * + *

* NOTE: USE OF THIS API REQUIRES A USER WITH ADMINISTRATOR ACCESS IF THE DB USED FOR * THE GRAPH DOES NOT EXIST. As per ArangoDB, creating DB is only allowed for the root user, hence * only the root user can be used if the DB does not exist. @@ -54,7 +51,7 @@ * An ArangoDBGraph is instantiated from an Apache Commons Configuration instance. The configuration * must provide both TinkerPop and ArangoDB configuration options. The ArangoDB options are * described in the ArangoDB Java Driver documentation. - * + *

* For the TinkerPop part, the configuration must provide as a minimum the database name and the * graph name. If no vertex, edge and relation information is provided, the graph will be considered * schema-less. @@ -74,7 +71,7 @@ * simple graphs, only one graph.vertex and graph.edge properties need to be provided. In this case * edges are allowed to connect to any two nodes. For example: *

gremlin.arangodb.conf.graph.vertex = Place
- *gremlin.arangodb.conf.graph.edge = Transition
+ * gremlin.arangodb.conf.graph.edge = Transition
  * 
* would allow the user to create Vertices that represent Places, and Edges that represent * Transitions. A transition can be created between any two Places. If additional vertices and edges @@ -85,13 +82,13 @@ * relations are allowed, e.g.: *
    *
  • One-to-one edges - *
    gremlin.arangodb.conf.graph.vertex = Place
    - *gremlin.arangodb.conf.graph.vertex = Transition
    - *gremlin.arangodb.conf.graph.edge = PTArc
    - *gremlin.arangodb.conf.graph.edge = TPArc
    - *gremlin.arangodb.conf.graph.relation = PTArc:Place->Transition
    - *gremlin.arangodb.conf.graph.relation = TPArc:Transition->Place
    - *
    + *
    gremlin.arangodb.conf.graph.vertex = Place
    + * gremlin.arangodb.conf.graph.vertex = Transition
    + * gremlin.arangodb.conf.graph.edge = PTArc
    + * gremlin.arangodb.conf.graph.edge = TPArc
    + * gremlin.arangodb.conf.graph.relation = PTArc:Place->Transition
    + * gremlin.arangodb.conf.graph.relation = TPArc:Transition->Place
    + * 
    * would allow the user to create nodes to represent Places and Transitions, and edges to represent * Arcs. However, in this case, we have two type of arcs: PTArc and TPArc. The relations specify * that PTArcs can only go from Place to Transitions and TPArcs can only go from Transitions to @@ -99,9 +96,9 @@ * comma separated list of names. *
  • Many-to-many edges *
    gremlin.arangodb.conf.graph.vertex = male
    - *gremlin.arangodb.conf.graph.vertex = female
    - *gremlin.arangodb.conf.graph.edge = relation
    - *gremlin.arangodb.conf.graph.relation = relation:male,female->male,female
    + * gremlin.arangodb.conf.graph.vertex = female
    + * gremlin.arangodb.conf.graph.edge = relation
    + * gremlin.arangodb.conf.graph.relation = relation:male,female->male,female
      *  
    *
*

@@ -134,761 +131,630 @@ * @author Johannes Gocke (http://www.triagens.de) * @author Guido Schwab (http://www.triagens.de) * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - * */ -@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD) -@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_INTEGRATE) -@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) -@Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldRemoveVertices", - reason = "Test creates vertices with random labels, which does not work with our schema-based approach.") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldRemoveEdges", - reason = "Test creates edges with random labels, which does not work with our schema-based approach.") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "Test creates vertex with no labels in schema-based approach") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "Test creates vertex with no labels in schema-based approach") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", - method = "testAttachableCreateMethod", - reason = "test creates id without label prefix") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertexProperty", - reason = "Cannot distinguish between null and not present properties." -) -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertex", - reason = "Cannot distinguish between null and not present properties." -) -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddEdge", - reason = "Cannot distinguish between null and not present properties." -) -// FIXME, OptOut failing tests -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldAddVertexWithUserSuppliedStringId", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldEvaluateConnectivityPatterns", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexPropertyTest$VertexPropertyAddition", - method = "shouldAllowIdAssignment", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV3Test", - method = "shouldSerializeTree", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV1Test", - method = "shouldSerializeTree", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldAttachWithCreateMethod", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldCopyFromGraphAToGraphB", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", - method = "shouldAddEdgeWithUserSuppliedStringId", - reason = "FIXME") public class ArangoDBGraph implements Graph { - /** - * The Class ArangoDBGraphFeatures. - */ - - public class ArangoDBGraphFeatures implements Features { - - /** - * The Class ArangoDBGraphGraphFeatures. - */ - - private class ArangoDBGraphGraphFeatures implements GraphFeatures { + public static class ArangoDBGraphFeatures implements Features { - /** The variable features. */ - private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); + protected static class ArangoDBGraphGraphFeatures implements GraphFeatures { - /** - * Instantiates a new ArangoDB graph graph features. - */ + protected ArangoDBGraphGraphFeatures() { + } - ArangoDBGraphGraphFeatures () { } + @Override + public boolean supportsComputer() { + return false; + } - @Override - public boolean supportsComputer() { - return false; - } + @Override + public boolean supportsThreadedTransactions() { + return false; + } - @Override - public boolean supportsThreadedTransactions() { - return false; - } + @Override + public boolean supportsTransactions() { + return false; + } - @Override - public boolean supportsTransactions() { - return false; - } + @Override + public VariableFeatures variables() { + return new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); + } + } - @Override - public VariableFeatures variables() { - return variableFeatures; - } - } + protected static class ArangoDBGraphElementFeatures implements ElementFeatures { - /** - * The Class ArangoDBGraphElementFeatures. - */ + protected ArangoDBGraphElementFeatures() { + } - private class ArangoDBGraphElementFeatures implements ElementFeatures { + @Override + public boolean supportsAnyIds() { + return false; + } - /** - * Instantiates a new ArangoDB graph element features. - */ + @Override + public boolean supportsCustomIds() { + return false; + } - ArangoDBGraphElementFeatures() { } + @Override + public boolean supportsNumericIds() { + return false; + } @Override - public boolean supportsAnyIds() { - return false; - } - - @Override - public boolean supportsCustomIds() { - return false; - } - - @Override - public boolean supportsNumericIds() { - return false; - } - - @Override - public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer - * the string representation of these is fine for ArangoDB, which makes the test - * complain because it expects the actual class to be deserialized. We can test - * to see if a string is accepted for deserialization. - * TODO As with properties, a way to support this is to store the id value class - */ - return false; - } + public boolean supportsUuidIds() { + return false; + } } - /** - * The Class ArangoDBGraphVertexFeatures. - */ - - private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { - - /** The vertex property features. */ - - private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); - - /** - * Instantiates a new ArangoDB graph vertex features. - */ - - ArangoDBGraphVertexFeatures () { } + protected static class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { + protected ArangoDBGraphVertexFeatures() { + } - @Override + @Override public VertexPropertyFeatures properties() { - return vertexPropertyFeatures; + return new ArangoDBGraphVertexPropertyFeatures(); } } - /** - * The Class ArangoDBGraphEdgeFeatures. - */ - public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { - - /** The edge property features. */ - - private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); - - /** - * Instantiates a new ArangoDB graph edge features. - */ + public static class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { - ArangoDBGraphEdgeFeatures() { } + protected ArangoDBGraphEdgeFeatures() { + } @Override public EdgePropertyFeatures properties() { - return edgePropertyFeatures; + return new ArangoDBGraphEdgePropertyFeatures(); } } - /** - * The Class ArangoDBGraphVertexPropertyFeatures. - */ - - private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - - /** - * Instantiates a new ArangoDB graph vertex property features. - */ - - ArangoDBGraphVertexPropertyFeatures() { } - - @Override - public boolean supportsAnyIds() { - return false; - } - - @Override - public boolean supportsCustomIds() { - return false; - } - - @Override - public boolean supportsNumericIds() { - return false; - } - - @Override - public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer - * the string representation of these is fine for ArangoDB, which makes the test - * complain because it expects the actual class to be deserialized. We can test - * to see if a string is accepted for deserialization. - * TODO As with properties, a way to support this is to store the id value class - */ - return false; - } - } - - /** - * The Class ArangoDBGraphEdgePropertyFeatures. - */ - private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { + protected static class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - /** - * Instantiates a new ArangoDB graph edge property features. - */ + protected ArangoDBGraphVertexPropertyFeatures() { + } - ArangoDBGraphEdgePropertyFeatures() { } - } + @Override + public boolean supportsAnyIds() { + return false; + } - /** The graph features. */ + @Override + public boolean supportsCustomIds() { + return false; + } - protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); + @Override + public boolean supportsNumericIds() { + return false; + } - /** The vertex features. */ + @Override + public boolean supportsUuidIds() { + return false; + } + } - protected VertexFeatures vertexFeatures = new ArangoDBGraphVertexFeatures(); + protected static class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { - /** The edge features. */ + protected ArangoDBGraphEdgePropertyFeatures() { + } - protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); + } @Override - public EdgeFeatures edge() { - return edgeFeatures; - } + public EdgeFeatures edge() { + return new ArangoDBGraphEdgeFeatures(); + } @Override - public GraphFeatures graph() { - return graphFeatures; - } + public GraphFeatures graph() { + return new ArangoDBGraphGraphFeatures(); + } @Override - public String toString() { - return StringFactory.featureString(this); - } + public String toString() { + return StringFactory.featureString(this); + } @Override - public VertexFeatures vertex() { - return vertexFeatures; - } + public VertexFeatures vertex() { + return new ArangoDBGraphVertexFeatures(); + } } - /** The Logger. */ + /** + * The Logger. + */ - private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraph.class); + private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraph.class); - /** The properties name CONFIG_CONF. */ + /** + * The properties name CONFIG_CONF. + */ public static final String PROPERTY_KEY_PREFIX = "gremlin.arangodb.conf"; - /** The properties name CONFIG_DB. */ + /** + * The properties name CONFIG_DB. + */ public static final String PROPERTY_KEY_DB_NAME = "graph.db"; - /** The properties name CONFIG_NAME. */ + /** + * The properties name CONFIG_NAME. + */ public static final String PROPERTY_KEY_GRAPH_NAME = "graph.name"; - /** The properties name CONFIG_VERTICES. */ + /** + * The properties name CONFIG_VERTICES. + */ public static final String PROPERTY_KEY_VERTICES = "graph.vertex"; - /** The properties name CONFIG_EDGES. */ + /** + * The properties name CONFIG_EDGES. + */ public static final String PROPERTY_KEY_EDGES = "graph.edge"; - /** The properties name CONFIG_RELATIONS. */ + /** + * The properties name CONFIG_RELATIONS. + */ public static final String PROPERTY_KEY_RELATIONS = "graph.relation"; - /** The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES **/ - - public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; - - /** The Constant DEFAULT_VERTEX_COLLECTION. */ + /** + * The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES + **/ - public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; + public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; - /** The Constant DEFAULT_VERTEX_COLLECTION. */ + /** + * The Constant DEFAULT_VERTEX_COLLECTION. + */ - public static final String DEFAULT_EDGE_COLLECTION = "edge"; + public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; - /** The Constant GRAPH_VARIABLES_COLLECTION. */ + /** + * The Constant DEFAULT_VERTEX_COLLECTION. + */ - public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; + public static final String DEFAULT_EDGE_COLLECTION = "edge"; - /** The Constant ELEMENT_PROPERTIES_COLLECTION. */ + /** + * The Constant GRAPH_VARIABLES_COLLECTION. + */ - public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; + public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; - /** The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. */ + /** + * The Constant ELEMENT_PROPERTIES_COLLECTION. + */ - public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; + public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; - public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); + /** + * The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. + */ - /** The features. */ + public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; - private final Features FEATURES = new ArangoDBGraphFeatures(); + public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); - /** A ArangoDBGraphClient to handle the connection to the Database. */ + /** + * The features. + */ - private ArangoDBGraphClient client = null; + private final Features FEATURES = new ArangoDBGraphFeatures(); - /** The name. */ + /** + * A ArangoDBGraphClient to handle the connection to the Database. + */ - private String name; + private ArangoDBGraphClient client = null; - /** The vertex collections. */ + /** + * The name. + */ - private final List vertexCollections; + private String name; - /** The edge collections. */ + /** + * The vertex collections. + */ - private final List edgeCollections; + private final List vertexCollections; - /** The relations. */ + /** + * The edge collections. + */ - private final List relations; + private final List edgeCollections; - /** Flat to indicate that the graph has no schema. */ + /** + * The relations. + */ - private boolean schemaless = false; + private final List relations; - /** The configuration. */ + /** + * The configuration. + */ - private Configuration configuration; + private final Configuration configuration; - /** If collection names should be prefixed with graph name */ - private final boolean shouldPrefixCollectionNames; + /** + * If collection names should be prefixed with graph name + */ + private final boolean shouldPrefixCollectionNames; /** * Create a new ArangoDBGraph from the provided configuration. * - * @param configuration the Apache Commons configuration - * @return the Arango DB graph + * @param configuration the Apache Commons configuration + * @return the Arango DB graph */ public static ArangoDBGraph open(Configuration configuration) { - return new ArangoDBGraph(configuration); - } - - /** - * Creates a Graph (simple configuration). - * - * @param configuration the Apache Commons configuration - */ - - public ArangoDBGraph(Configuration configuration) { - - logger.info("Creating new ArangoDB Graph from configuration"); - Configuration arangoConfig = configuration.subset(PROPERTY_KEY_PREFIX); - vertexCollections = arangoConfig.getList(PROPERTY_KEY_VERTICES).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - edgeCollections = arangoConfig.getList(PROPERTY_KEY_EDGES).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - relations = arangoConfig.getList(PROPERTY_KEY_RELATIONS).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - name = arangoConfig.getString(PROPERTY_KEY_GRAPH_NAME); - checkValues(arangoConfig.getString(PROPERTY_KEY_DB_NAME), name, vertexCollections, edgeCollections, relations); - if (CollectionUtils.isEmpty(vertexCollections)) { - schemaless = true; - vertexCollections.add(DEFAULT_VERTEX_COLLECTION); - } - if (CollectionUtils.isEmpty(edgeCollections)) { - edgeCollections.add(DEFAULT_EDGE_COLLECTION); - } - shouldPrefixCollectionNames = arangoConfig.getBoolean(PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, true); - - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - int batchSize = 0; - client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME), - batchSize, shouldPrefixCollectionNames); - - ArangoGraph graph = client.getArangoGraph(); - GraphCreateOptions options = new GraphCreateOptions(); + return new ArangoDBGraph(configuration); + } + + /** + * Creates a Graph (simple configuration). + * + * @param configuration the Apache Commons configuration + */ + + public ArangoDBGraph(Configuration configuration) { + + logger.info("Creating new ArangoDB Graph from configuration"); + Configuration arangoConfig = configuration.subset(PROPERTY_KEY_PREFIX); + vertexCollections = arangoConfig.getList(PROPERTY_KEY_VERTICES).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + edgeCollections = arangoConfig.getList(PROPERTY_KEY_EDGES).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + relations = arangoConfig.getList(PROPERTY_KEY_RELATIONS).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + name = arangoConfig.getString(PROPERTY_KEY_GRAPH_NAME); + checkValues(arangoConfig.getString(PROPERTY_KEY_DB_NAME), name, vertexCollections, edgeCollections, relations); + if (CollectionUtils.isEmpty(vertexCollections)) { + vertexCollections.add(DEFAULT_VERTEX_COLLECTION); + } + if (CollectionUtils.isEmpty(edgeCollections)) { + edgeCollections.add(DEFAULT_EDGE_COLLECTION); + } + shouldPrefixCollectionNames = arangoConfig.getBoolean(PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, true); + + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME)); + + ArangoGraph graph = client.getArangoGraph(); + GraphCreateOptions options = new GraphCreateOptions(); // FIXME Cant be in orphan collections because it will be deleted with graph? // options.orphanCollections(GRAPH_VARIABLES_COLLECTION); - final List prefVCols = vertexCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); - final List prefECols = edgeCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); - final List edgeDefinitions = new ArrayList<>(); - if (relations.isEmpty()) { - logger.info("No relations, creating default ones."); - edgeDefinitions.addAll(ArangoDBUtil.createDefaultEdgeDefinitions(prefVCols, prefECols)); - } else { - for (String value : relations) { - EdgeDefinition ed = ArangoDBUtil.relationPropertyToEdgeDefinition(this, value); - edgeDefinitions.add(ed); - } - } - edgeDefinitions.add(ArangoDBUtil.createPropertyEdgeDefinitions(this, prefVCols, prefECols)); + final List prefVCols = vertexCollections.stream().map(this::getPrefixedCollectionName).collect(Collectors.toList()); + final List prefECols = edgeCollections.stream().map(this::getPrefixedCollectionName).collect(Collectors.toList()); + final List edgeDefinitions = new ArrayList<>(); + if (relations.isEmpty()) { + logger.info("No relations, creating default ones."); + edgeDefinitions.addAll(ArangoDBUtil.createDefaultEdgeDefinitions(prefVCols, prefECols)); + } else { + for (String value : relations) { + EdgeDefinition ed = ArangoDBUtil.relationPropertyToEdgeDefinition(this, value); + edgeDefinitions.add(ed); + } + } + edgeDefinitions.add(ArangoDBUtil.createPropertyEdgeDefinitions(this, prefVCols, prefECols)); if (graph.exists()) { ArangoDBUtil.checkGraphForErrors(prefVCols, prefECols, edgeDefinitions, graph, options); ArangoDBGraphVariables variables = null; try { - variables = client.getGraphVariables(); - } catch (NullPointerException ex) { - logger.warn("Existing graph missing Graph Variables collection ({}), will attempt to create one.", GRAPH_VARIABLES_COLLECTION); - } + variables = client.getGraphVariables(); + } catch (NullPointerException ex) { + logger.warn("Existing graph missing Graph Variables collection ({}), will attempt to create one.", GRAPH_VARIABLES_COLLECTION); + } if (variables == null) { - variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); - try { - client.insertGraphVariables(variables); - } catch (ArangoDBGraphException ex) { - throw new ArangoDBGraphException( - String.format( - "Unable to add graph variables collection (%s) to existing graph. %s", - ex.getMessage(), - GRAPH_VARIABLES_COLLECTION) - , ex); - } - } + variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); + try { + client.insertGraphVariables(variables); + } catch (ArangoDBGraphException ex) { + throw new ArangoDBGraphException( + String.format( + "Unable to add graph variables collection (%s) to existing graph. %s", + ex.getMessage(), + GRAPH_VARIABLES_COLLECTION) + , ex); + } + } + } else { + graph = client.createGraph(name, edgeDefinitions, options); + this.name = graph.name(); + ArangoDBGraphVariables variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); + client.insertGraphVariables(variables); } - else { - graph = client.createGraph(name, edgeDefinitions, options); - this.name = graph.name(); - ArangoDBGraphVariables variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); - client.insertGraphVariables(variables); - } - this.configuration = configuration; - } + this.configuration = configuration; + } @Override - public Vertex addVertex(Object... keyValues) { + public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); - Object id; - String collection; - if (!schemaless) { - collection = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(collection); + String label = ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL); + ArangoDBId id = createId(features().vertex(), label, keyValues); + ArangoDBVertex vertex = ArangoDBVertex.of(id, this); + if (!vertexCollections().contains(vertex.label())) { + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", vertex.label(), name)); } - else { - collection = DEFAULT_VERTEX_COLLECTION; - } - if (!vertexCollections().contains(collection)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", collection, name)); - } - ArangoDBVertex vertex = null; - if (ElementHelper.getIdValue(keyValues).isPresent()) { - id = ElementHelper.getIdValue(keyValues).get(); - if (this.features().vertex().willAllowId(id)) { - if (id.toString().contains("/")) { - String fullId = id.toString(); - String[] parts = fullId.split("/"); - // The collection name is the last part of the full name - String[] collectionParts = parts[0].split("_"); - String collectionName = collectionParts[collectionParts.length-1]; - if (collectionName.contains(collection)) { - id = parts[1]; - - } - } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); - if (m.matches()) { - vertex = new ArangoDBVertex(id.toString(), collection, this); - } - else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - } - else { - vertex = new ArangoDBVertex(this, collection); - } - // The vertex needs to exist before we can attach properties - client.insertDocument(vertex); + // TODO: optmize writing only once + vertex.doInsert(); ElementHelper.attachProperties(vertex, keyValues); return vertex; - } - - /** - * Check that the configuration values are sound. - * - * @param db the db - * @param name the name - * @param vertices the vertices - * @param edges the edges - * @param relations the relations - */ - - private void checkValues( - String db, - String name, - List vertices, - List edges, - List relations) { - - if (StringUtils.isBlank(db)) { + } + + /** + * Check that the configuration values are sound. + * + * @param db the db + * @param name the name + * @param vertices the vertices + * @param edges the edges + * @param relations the relations + */ + + private void checkValues( + String db, + String name, + List vertices, + List edges, + List relations) { + + if (StringUtils.isBlank(db)) { throw new ArangoDBGraphException("The db name can not be empty/null. Check that your configuration file " + - "has a 'graph.db' setting."); - } - if (StringUtils.isBlank(name)) { + "has a 'graph.db' setting."); + } + if (StringUtils.isBlank(name)) { throw new ArangoDBGraphException("The graph name can not be empty/null. Check that your configuration file " + - "has a 'graph.name' name setting."); - } - if (CollectionUtils.isEmpty(edges)) { - logger.warn("Empty edges collection(s), the default 'edge' collection will be used."); - } - if ((vertices.size() > 1) && (edges.size() > 1) && CollectionUtils.isEmpty(relations)) { - throw new ArangoDBGraphException("If more than one vertex/edge collection is provided, relations must be defined"); - } - } - - @Override - public void close() { - client.shutdown(); - } - - - @Override - public GraphComputer compute() throws IllegalArgumentException { + "has a 'graph.name' name setting."); + } + if (CollectionUtils.isEmpty(edges)) { + logger.warn("Empty edges collection(s), the default 'edge' collection will be used."); + } + if ((vertices.size() > 1) && (edges.size() > 1) && CollectionUtils.isEmpty(relations)) { + throw new ArangoDBGraphException("If more than one vertex/edge collection is provided, relations must be defined"); + } + } + + @Override + public void close() { + client.shutdown(); + } + + + @Override + public GraphComputer compute() throws IllegalArgumentException { throw Graph.Exceptions.graphComputerNotSupported(); - } - - @Override - public C compute(Class graphComputerClass) throws IllegalArgumentException { - throw new UnsupportedOperationException(); - } - - @Override - public Configuration configuration() { - return configuration; - } - - /** - * Edge collections. - * - * @return the list - */ - - public List edgeCollections() { - return Collections.unmodifiableList(edgeCollections); - } - - @Override - public Iterator edges(Object... edgeIds) { - List edgeCollections = new ArrayList<>(); - List ids = Arrays.stream(edgeIds) - .map(id -> { - if (id instanceof ArangoDBEdge) { - ArangoDBEdge edge = (ArangoDBEdge) id; - if (edge.isPaired()) { - edgeCollections.add(edge.label()); - } - else { - edgeCollections.add(getPrefixedCollectioName(edge.label())); - } - return edge.id(); - } - else { - // We only support String ids - return id; - } - }) - .map(id -> id == null ? (String)id : id.toString()) - .collect(Collectors.toList()); - return new ArangoDBIterator(this, getClient().getGraphEdges(ids, edgeCollections)); - } - - @Override - public Features features() { - return FEATURES; - } - - /** - * Returns the ArangoDBGraphClient object. - * - * @return the ArangoDBGraphClient object - */ - - public ArangoDBGraphClient getClient() { - return client; - } - - /** - * Returns the identifier of the graph. - * - * @return the identifier of the graph - */ - - public String getId() { - ArangoGraph graph = client.getArangoGraph(); - return graph.getInfo().getName(); - } - - /** - * The graph name - * - * @return the name - */ - - public String name() { - return this.name; - } - - @Override - public Transaction tx() { - throw Graph.Exceptions.transactionsNotSupported(); - } - - @Override - public Variables variables() { - ArangoDBGraphVariables v = client.getGraphVariables(); - if (v != null) { - v.graph(this); - return v; + } + + @Override + public C compute(Class graphComputerClass) throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + @Override + public Configuration configuration() { + return configuration; + } + + /** + * Edge collections. + * + * @return the list + */ + + public List edgeCollections() { + return Collections.unmodifiableList(edgeCollections); + } + + @Override + public Iterator edges(Object... edgeIds) { + return getClient().getGraphEdges(getIdValues(Edge.DEFAULT_LABEL, edgeIds)).stream() + .map(it -> (Edge) new ArangoDBEdge(this, it)) + .iterator(); + } + + @Override + public Iterator vertices(Object... vertexIds) { + return getClient().getGraphVertices(getIdValues(Vertex.DEFAULT_LABEL, vertexIds)).stream() + .map(it -> (Vertex) new ArangoDBVertex(this, it)) + .iterator(); + } + + @Override + public Features features() { + return FEATURES; + } + + /** + * Returns the ArangoDBGraphClient object. + * + * @return the ArangoDBGraphClient object + */ + + public ArangoDBGraphClient getClient() { + return client; + } + + /** + * Returns the identifier of the graph. + * + * @return the identifier of the graph + */ + + public String getId() { + ArangoGraph graph = client.getArangoGraph(); + return graph.getInfo().getName(); + } + + /** + * The graph name + * + * @return the name + */ + + public String name() { + return this.name; + } + + @Override + public Transaction tx() { + throw Graph.Exceptions.transactionsNotSupported(); + } + + @Override + public Variables variables() { + ArangoDBGraphVariables v = client.getGraphVariables(); + if (v != null) { + v.graph(this); + return v; + } else { + throw new ArangoDBGraphException("Existing graph does not have a Variables collection"); } - else { - throw new ArangoDBGraphException("Existing graph does not have a Variables collection"); + } + + /** + * Vertex collections. + * + * @return the list + */ + public List vertexCollections() { + return Collections.unmodifiableList(vertexCollections); + } + + /** + * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag + * + * @param collectionName the collection name + * @return the Collection name prefixed + */ + public String getPrefixedCollectionName(String collectionName) { + if (!shouldPrefixCollectionNames) { + return collectionName; + } + + if (collectionName.startsWith(name + "_")) { + return collectionName; } - } - - /** - * Vertex collections. - * - * @return the list - */ - public List vertexCollections() { - return Collections.unmodifiableList(vertexCollections); - } - - @Override - public Iterator vertices(Object... vertexIds) { - List vertexCollections = new ArrayList<>(); - List ids = Arrays.stream(vertexIds) - .map(id -> { - if (id instanceof Vertex) { - vertexCollections.add(((Vertex)id).label()); - return ((Vertex)id).id(); - } - else { - // We only support String ids - return id; - } - }) - .map(id -> id == null ? (String)id : id.toString()) - .collect(Collectors.toList()); - return new ArangoDBIterator<>(this, getClient().getGraphVertices(ids, vertexCollections)); - } - - /** - * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag - * @param collectionName the collection name - * @return the Collection name prefixed - */ - public String getPrefixedCollectioName(String collectionName) { - if (GRAPH_VARIABLES_COLLECTION.equals(collectionName)) { - return collectionName; - } - if (GRAPH_COLLECTIONS.contains(collectionName)) { - return String.format("%s_%s", name, collectionName); - } - if(shouldPrefixCollectionNames) { - return String.format("%s_%s", name, collectionName); - }else{ - return collectionName; - } - } - - @Override - public String toString() { - String vertices = vertexCollections().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String edges = edgeCollections().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String relations = relations().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String internal = "{" - + "\"name\":\"" + name() + "\"," - + "\"vertices\":" + vertices + "," - + "\"edges\":" + edges+ "," - + "\"relations\":" + relations - +"}"; - return StringFactory.graphString(this, internal); - } - - /** - * The graph relations. - * - * @return the collection of relations - */ - private Collection relations() { - return relations; - } - - // TODO Decide which of these methods we want to keep + return name + "_" + collectionName; + } + + public ArangoDBId getArangoDBId(ArangoDBId id) { + return ArangoDBId.of(name, id.getLabel(), id.getKey()); + } + + @Override + public String toString() { + String vertices = vertexCollections().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String edges = edgeCollections().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String relations = relations().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String internal = "{" + + "\"name\":\"" + name() + "\"," + + "\"vertices\":" + vertices + "," + + "\"edges\":" + edges + "," + + "\"relations\":" + relations + + "}"; + return StringFactory.graphString(this, internal); + } + + /** + * The graph relations. + * + * @return the collection of relations + */ + private Collection relations() { + return relations; + } + + private ArangoDBId getIdValue(String defaultLabel, Object id) { + if (id instanceof String) { + return ArangoDBId.parseWithDefaultLabel(name, defaultLabel, (String) id); + } else if (id instanceof Element) { + return getIdValue(defaultLabel, ((Element) id).id()); + } else { + throw unsupportedIdType(id); + } + } + + private List getIdValues(String defaultLabel, Object[] ids) { + return Arrays.stream(ids) + .map(it -> getIdValue(defaultLabel, it)) + .collect(Collectors.toList()); + } + + private String extractKey(final String id) { + if (id == null) { + return null; + } + int separator = id.indexOf('/'); + if (separator > 0) { + return id.substring(separator + 1); + } else { + return id; + } + } + + private String extractCollection(final String id) { + if (id == null) { + return null; + } + int separator = id.indexOf('/'); + if (separator > 0) { + return id.substring(0, separator); + } else { + return null; + } + } + + private String extractLabel(final String collection, final String label) { + if (collection != null) { + if (label != null && !label.equals(collection)) { + throw new IllegalArgumentException("Mismatching label: [" + label + "] and collection: [" + collection + "]"); + } + return collection; + } else { + return label; + } + } + + public ArangoDBId createId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { + ElementHelper.validateLabel(label); + Optional optionalId = ElementHelper.getIdValue(keyValues); + if (!optionalId.isPresent()) { + return ArangoDBId.of(name, label, null); + } + String id = optionalId + .filter(features::willAllowId) + .map(Object::toString) + .orElseThrow(Vertex.Exceptions::userSuppliedIdsOfThisTypeNotSupported); + return ArangoDBId.of(name, extractLabel(extractCollection(id), label), extractKey(id)); + } + + // TODO Decide which of these methods we want to keep // @Override // public void dropKeyIndex(String name, Class elementClass) { diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBId.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBId.java new file mode 100644 index 0000000..fab38fb --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBId.java @@ -0,0 +1,118 @@ +package com.arangodb.tinkerpop.gremlin.structure; + + +import com.arangodb.shaded.fasterxml.jackson.core.JsonGenerator; +import com.arangodb.shaded.fasterxml.jackson.core.JsonParser; +import com.arangodb.shaded.fasterxml.jackson.databind.DeserializationContext; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonDeserializer; +import com.arangodb.shaded.fasterxml.jackson.databind.JsonSerializer; +import com.arangodb.shaded.fasterxml.jackson.databind.SerializerProvider; +import com.arangodb.shaded.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.arangodb.shaded.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.io.IOException; +import java.util.Objects; + +@JsonSerialize(using = ArangoDBId.Serializer.class) +@JsonDeserialize(using = ArangoDBId.Deserializer.class) +public class ArangoDBId { + private final String prefix; + private final String label; + private final String key; + + public static ArangoDBId of(String prefix, String label, String key) { + Objects.requireNonNull(prefix); + Objects.requireNonNull(label); + validateIdParts(prefix, label, key); + return new ArangoDBId(prefix, label, key); + } + + public static ArangoDBId parse(String prefix, String fullName) { + String[] parts = fullName.replaceFirst("^" + prefix + "_", "").split("/"); + String label = parts[0]; + String key = parts[1]; + return ArangoDBId.of(prefix, label, key); + } + + public static ArangoDBId parseWithDefaultLabel(String prefix, String defaultLabel, String fullName) { + String[] parts = fullName.replaceFirst("^" + prefix + "_", "").split("/"); + String label = parts.length == 2 ? parts[0] : defaultLabel; + String key = parts[parts.length - 1]; + return ArangoDBId.of(prefix, label, key); + } + + public static ArangoDBId parse(String fullName) { + String[] parts = fullName.split("_"); + String prefix = parts[0]; + parts = parts[1].split("/"); + String collection = parts[0]; + String key = parts[1]; + return ArangoDBId.of(prefix, collection, key); + } + + private static void validateIdParts(String... names) { + for (String name : names) { + if (name == null) + continue; + if (name.contains("_")) { + throw new IllegalArgumentException(String.format("key (%s) contains invalid character '_'", name)); + } + if (name.contains("/")) { + throw new IllegalArgumentException(String.format("key (%s) contains invalid character '/'", name)); + } + } + } + + private ArangoDBId(String prefix, String label, String key) { + this.prefix = prefix; + this.label = label.replaceFirst("^" + prefix + "_", ""); + this.key = key; + } + + public ArangoDBId withKey(String newKey) { + return ArangoDBId.of(prefix, label, newKey); + } + + public String getCollection() { + return prefix + "_" + label; + } + + public String getLabel() { + return label; + } + + public String getKey() { + return key; + } + + @Override + public String toString() { + return prefix + "_" + label + "/" + key; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ArangoDBId)) return false; + ArangoDBId arangoDBId = (ArangoDBId) o; + return Objects.equals(prefix, arangoDBId.prefix) && Objects.equals(label, arangoDBId.label) && Objects.equals(key, arangoDBId.key); + } + + @Override + public int hashCode() { + return Objects.hash(prefix, label, key); + } + + static class Serializer extends JsonSerializer { + @Override + public void serialize(ArangoDBId value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(value.toString()); + } + } + + static class Deserializer extends JsonDeserializer { + @Override + public ArangoDBId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + return ArangoDBId.parse(p.getValueAsString()); + } + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java new file mode 100644 index 0000000..6d190f7 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.entity.DocumentEntity; +import com.arangodb.tinkerpop.gremlin.persistence.PersistentData; +import org.apache.tinkerpop.gremlin.structure.Element; + +import java.util.Optional; + +public interface ArangoDBPersistentElement extends Element { + + @Override + ArangoDBGraph graph(); + + PersistentData data(); + + default String key() { + return data().getKey(); + } + + default void update(DocumentEntity entity) { + data().update(entity); + } + + @Override + default String label() { + return data().getLabel(); + } + + default String collection() { + return data().getCollection(); + } + + default ArangoDBId arangoId() { + return data().getId(); + } + + @Override + default String id() { + return Optional.ofNullable(key()) + .map(it -> label() + '/' + it) + .orElse(null); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java new file mode 100644 index 0000000..5ce0cd4 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.structure; + +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; + +public class ArangoDBProperty implements Property { + + private final String key; + private final V value; + private final ArangoDBSimpleElement element; + + public ArangoDBProperty(final ArangoDBSimpleElement element, final String key, final V value) { + this.element = element; + this.key = key; + this.value = value; + } + + @Override + public ArangoDBElement element() { + return element; + } + + @Override + public String key() { + return key; + } + + @Override + public V value() { + return value; + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public void remove() { + element.removeProperty(key); + } + + @Override + public String toString() { + return StringFactory.propertyString(this); + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(final Object object) { + return ElementHelper.areEqual(this, object); + } + + @Override + public int hashCode() { + return ElementHelper.hashCode(this); + } + +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyProperty.java deleted file mode 100644 index ae77d52..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyProperty.java +++ /dev/null @@ -1,70 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// - -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.Collections; - -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Element; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - -/** - * The Class ArangoDBPropertyProperty. - * - * @param the type of the property value - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ -public class ArangoDBPropertyProperty extends ArangoDBElementProperty { - /** - * Constructor used for ArabgoDB JavaBeans serialisation. - */ - - public ArangoDBPropertyProperty() { - super(); - } - - /** - * Instantiates a new ArangoDB property property. - * - * @param name the name of the property - * @param value the value of the property - * @param owner the owner of the property - */ - - public ArangoDBPropertyProperty(String name, U value, ArangoDBBaseDocument owner) { - super(name, value, owner, ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION); - } - - @SuppressWarnings("rawtypes") - @Override - public Element element() { - ArangoCursor q = graph.getClient() - .getDocumentNeighbors(this, Collections.emptyList(), Direction.IN, ArangoDBPropertyFilter.empty(), ArangoDBVertexProperty.class); - ArangoDBIterator iterator = new ArangoDBIterator(graph, q); - return iterator.hasNext() ? iterator.next() : null; - } - - @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); - } - - @Override - public int hashCode() { - return key().hashCode() + value().hashCode(); - } - -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java new file mode 100644 index 0000000..0cd16f1 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java @@ -0,0 +1,35 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.AdbValue; +import com.arangodb.tinkerpop.gremlin.persistence.SimplePropertyData; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +public abstract class ArangoDBSimpleElement extends ArangoDBElement { + public ArangoDBSimpleElement(ArangoDBGraph graph, D data) { + super(graph, data); + } + + @Override + @SuppressWarnings("unchecked") + protected Property createProperty(String key, AdbValue value) { + return new ArangoDBProperty<>(this, key, (V) value.getValue()); + } + + @Override + public Property property(String key, V value) { + if (removed()) throw Exceptions.elementAlreadyRemoved(id()); + ElementHelper.validateProperty(key, value); + AdbValue dataValue = AdbValue.of(value); + data().add(key, dataValue); + doUpdate(); + return createProperty(key, dataValue); + } + + public void removeProperty(String key) { + if (removed()) throw Exceptions.elementAlreadyRemoved(id()); + data.remove(key); + doUpdate(); + } + +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index ad3c589..5cad6c9 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -1,304 +1,186 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.structure; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; +import com.arangodb.tinkerpop.gremlin.persistence.VertexData; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyIterator; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; -/** - * The ArangoDB vertex class. - * - * @author Achim Brandt (http://www.triagens.de) - * @author Johannes Gocke (http://www.triagens.de) - * @author Guido Schwab (http://www.triagens.de) - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ +import java.util.*; +import java.util.stream.Collectors; -public class ArangoDBVertex extends ArangoDBBaseDocument implements Vertex { +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.elementAlreadyRemoved; - private static final Logger logger = LoggerFactory.getLogger(ArangoDBVertex.class); +public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { - /** - * Constructor used for ArabgoDB JavaBeans serialisation. - */ + public static ArangoDBVertex of(final ArangoDBId id, ArangoDBGraph graph) { + return new ArangoDBVertex(graph, VertexData.of(id)); + } - public ArangoDBVertex() { - super(); - } + public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { + super(graph, data); + } - /** - * Instantiates a new ArangoDB vertex with he given key. - * - * @param key the key to assign to the vertex - * @param label the label of the vertex - * @param graph the graph that owns the vertex - */ + @Override + public VertexProperty property( + final VertexProperty.Cardinality cardinality, + final String key, + final V value, + final Object... keyValues + ) { + if (removed()) throw elementAlreadyRemoved(id()); + ElementHelper.legalPropertyKeyValueArray(keyValues); + ElementHelper.validateProperty(key, value); + + Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); + if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); - public ArangoDBVertex(String key, String label, ArangoDBGraph graph) { - super(key, label, graph); - } + String idValue = ElementHelper.getIdValue(keyValues) + .map(it -> { + if (!graph.features().vertex().properties().willAllowId(it)) { + throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + return it.toString(); + }) + .orElseGet(() -> UUID.randomUUID().toString()); + + VertexPropertyData prop = VertexPropertyData.of(idValue, value); + data.add(key, prop); + + ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); + // TODO: optmize writing only once + ElementHelper.attachProperties(vertexProperty, keyValues); + doUpdate(); + return vertexProperty; + } - /** - * Instantiates a new ArangoDB vertex. - * - * @param graph the graph - * @param collection the collection - */ + @Override + public Edge addEdge(String label, Vertex vertex, Object... keyValues) { + if (null == vertex) throw Graph.Exceptions.argumentCanNotBeNull("vertex"); + if (removed() || ((ArangoDBVertex) vertex).removed()) throw elementAlreadyRemoved(id()); + + ElementHelper.legalPropertyKeyValueArray(keyValues); + ElementHelper.validateLabel(label); + ArangoDBId id = graph.createId(graph.features().edge(), label, keyValues); + ArangoDBId outVertexId = ArangoDBId.parse(graph.name(), id()); + ArangoDBId inVertexId = ArangoDBId.parse(graph.name(), (String) vertex.id()); + ArangoDBEdge edge = ArangoDBEdge.of(id, outVertexId, inVertexId, graph); + if (!graph.edgeCollections().contains(edge.label())) { + throw new IllegalArgumentException(String.format("Edge label (%s) not in graph (%s) edge collections.", edge.label(), graph.name())); + } - public ArangoDBVertex(ArangoDBGraph graph, String collection) { - this(null, collection, graph); - } + // TODO: optmize writing only once + edge.doInsert(); + ElementHelper.attachProperties(edge, keyValues); + return edge; + } @Override - public Object id() { - return _id(); + protected void doRemove() { + edges(Direction.BOTH).forEachRemaining(Edge::remove); + graph.getClient().deleteVertex(this); } @Override - public String label() { - return label; + protected String stringify() { + return StringFactory.vertexString(this); } - @Override - public void remove() { - logger.info("remove {}", this._id()); - if (paired) { - Map bindVars = new HashMap<>(); - // Delete properties - properties().forEachRemaining(VertexProperty::remove); - //Remove vertex - try { - graph.getClient().deleteDocument(this); - // Need to delete incoming edges too - } catch (ArangoDBGraphException ex) { - // Pass Removing a property that does not exists should not throw an exception. - } - } - } + @Override + protected Property createProperty(String key, VertexPropertyData value) { + return new ArangoDBVertexProperty<>(key, value, this); + } - @Override - public Edge addEdge(String label, Vertex inVertex, Object... keyValues) { - logger.info("addEdge in collection {} to vertex {}", label, inVertex == null ? "?" :inVertex.id()); - ElementHelper.legalPropertyKeyValueArray(keyValues); - ElementHelper.validateLabel(label); - if (!graph.edgeCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Edge label (%s)not in graph (%s) edge collections.", label, graph.name())); - } - if (inVertex == null) { - throw Graph.Exceptions.argumentCanNotBeNull("vertex"); - } - Object id; - ArangoDBEdge edge = null; - if (ElementHelper.getIdValue(keyValues).isPresent()) { - id = ElementHelper.getIdValue(keyValues).get(); - if (graph.features().edge().willAllowId(id)) { - if (id.toString().contains("/")) { - String fullId = id.toString(); - String[] parts = fullId.split("/"); - // The collection name is the last part of the full name - String[] collectionParts = parts[0].split("_"); - String collectionName = collectionParts[collectionParts.length-1]; - if (collectionName.contains(label)) { - id = parts[1]; - - } - } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); - if (m.matches()) { - edge = new ArangoDBEdge(id.toString(), label, this, ((ArangoDBVertex) inVertex), graph); - } - else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } + @Override + public Iterator edges(Direction direction, String... edgeLabels) { + List edgeCollections = getQueryEdgeCollections(edgeLabels); + // If edgeLabels was not empty but all were discarded, this means that we should + // return an empty iterator, i.e. no edges for that edgeLabels exist. + if (edgeCollections.isEmpty()) { + return Collections.emptyIterator(); } - else { - edge = new ArangoDBEdge(graph, label, this, ((ArangoDBVertex) inVertex)); - } - // The vertex needs to exist before we can attach properties - graph.getClient().insertEdge(edge); - ElementHelper.attachProperties(edge, keyValues); - return edge; - } - - - @Override - public VertexProperty property( - Cardinality cardinality, - String key, - V value, - Object... keyValues) { - logger.debug("setting vertex property {} = {} ({})", key, value, keyValues); - ElementHelper.validateProperty(key, value); - ElementHelper.legalPropertyKeyValueArray(keyValues); - Optional idValue = ElementHelper.getIdValue(keyValues); - String id = null; - if (idValue.isPresent()) { - if (graph.features().vertex().willAllowId(idValue.get())) { - id = idValue.get().toString(); - if (id.toString().contains("/")) { - String fullId = id.toString(); - String[] parts = fullId.split("/"); - // The collection name is the last part of the full name - String[] collectionParts = parts[0].split("_"); - String collectionName = collectionParts[collectionParts.length-1]; - if (collectionName.contains(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)) { - id = parts[1]; - - } - } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); - if (!m.matches()) { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - int idIndex = 0; - for (int i = 0; i < keyValues.length; i+=2) { - if (keyValues[i] == T.id) { - idIndex = i; - break; - } - } - keyValues = ArrayUtils.remove(keyValues, idIndex); - keyValues = ArrayUtils.remove(keyValues, idIndex); - } - final Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); - if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); - - - ArangoDBVertexProperty p = ArangoDBUtil.createArangoDBVertexProperty(id, key, value, this); - ElementHelper.attachProperties(p, keyValues); - return p; - } - - - @Override - public Iterator edges(Direction direction, String... edgeLabels) { - List edgeCollections = getQueryEdgeCollections(edgeLabels); - // If edgeLabels was not empty but all were discarded, this means that we should - // return an empty iterator, i.e. no edges for that edgeLabels exist. - if (edgeCollections.isEmpty()) { - return Collections.emptyIterator(); - } - return new ArangoDBIterator<>(graph, graph.getClient().getVertexEdges(this, edgeCollections, direction)); - } - - - @Override - public Iterator vertices(Direction direction, String... edgeLabels) { - List edgeCollections = getQueryEdgeCollections(edgeLabels); - // If edgeLabels was not empty but all were discarded, this means that we should - // return an empty iterator, i.e. no edges for that edgeLabels exist. - if (edgeCollections.isEmpty()) { - return Collections.emptyIterator(); - } - ArangoCursor documentNeighbors = graph.getClient().getDocumentNeighbors(this, edgeCollections, direction, ArangoDBPropertyFilter.empty(), ArangoDBVertex.class); - return new ArangoDBIterator<>(graph, documentNeighbors); - } - + return IteratorUtils.map(graph.getClient().getVertexEdges(arangoId(), edgeCollections, direction), + it -> new ArangoDBEdge(graph, it)); + } - @SuppressWarnings("unchecked") - @Override - public Iterator> properties(String... propertyKeys) { - logger.debug("Get properties {}", (Object[])propertyKeys); - List labels = new ArrayList<>(); - labels.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)); - ArangoDBPropertyFilter filter = new ArangoDBPropertyFilter(); - for (String pk : propertyKeys) { - filter.has("name", pk, ArangoDBPropertyFilter.Compare.EQUAL); + @Override + public Iterator vertices(Direction direction, String... edgeLabels) { + List edgeCollections = getQueryEdgeCollections(edgeLabels); + // If edgeLabels was not empty but all were discarded, this means that we should + // return an empty iterator, i.e. no edges for that edgeLabels exist. + if (edgeCollections.isEmpty()) { + return Collections.emptyIterator(); } - ArangoCursor query = graph.getClient().getElementProperties(this, labels, filter, ArangoDBVertexProperty.class); - return new ArangoDBPropertyIterator<>(graph, (ArangoCursor>) query); + return IteratorUtils.map(graph.getClient().getVertexNeighbors(arangoId(), edgeCollections, direction), + it -> new ArangoDBVertex(graph, it)); } - - @Override - public String toString() { - return StringFactory.vertexString(this); + @Override + public void doInsert() { + graph.getClient().insertVertex(this); } - /** - * Save. - */ - public void save() { - if (paired) { - graph.getClient().updateDocument(this); - } - } + @Override + public void doUpdate() { + graph.getClient().updateVertex(this); + } - /** - * Query will raise an exception if the edge_collection name is not in the graph, so we need to filter out - * edgeLabels not in the graph. - * - * @param edgeLabels - * @return - */ - private List getQueryEdgeCollections(String... edgeLabels) { - List vertexCollections; - if (edgeLabels.length == 0) { - vertexCollections = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - } - else { - vertexCollections = Arrays.stream(edgeLabels) - .filter(el -> graph.edgeCollections().contains(el)) - .map(graph::getPrefixedCollectioName) - .collect(Collectors.toList()); + @Override + public VertexProperty property(final String key) { + return Vertex.super.property(key); + } - } - return vertexCollections; - } - @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); + public VertexProperty property(final String key, final V value) { + return Vertex.super.property(key, value); } @Override - public int hashCode() { - return ElementHelper.hashCode(this); + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); } -} + public void removeProperty(ArangoDBVertexProperty prop) { + if (removed()) throw ArangoDBElement.Exceptions.elementAlreadyRemoved(id()); + data.remove(prop.key(), prop.data()); + doUpdate(); + } + /** + * Query will raise an exception if the edge_collection name is not in the graph, so we need to filter out + * edgeLabels not in the graph. + */ + private List getQueryEdgeCollections(String... edgeLabels) { + List vertexCollections; + if (edgeLabels.length == 0) { + vertexCollections = graph.edgeCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); + } else { + vertexCollections = Arrays.stream(edgeLabels) + .filter(el -> graph.edgeCollections().contains(el)) + .map(graph::getPrefixedCollectionName) + .collect(Collectors.toList()); + + } + return vertexCollections; + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java index 5befc49..0283831 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -1,147 +1,102 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// Implementation of the TinkerPop OLTP Provider API for ArangoDB -// -// Copyright triAGENS GmbH Cologne and The University of York -// -////////////////////////////////////////////////////////////////////////////////////////// +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.structure; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import com.arangodb.tinkerpop.gremlin.client.*; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; -import com.arangodb.ArangoCursor; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import java.util.*; +public class ArangoDBVertexProperty

extends ArangoDBSimpleElement implements VertexProperty

{ -/** - * The Class ArangoDBVertexProperty. - * - * @param the type of the property value - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ + private final String key; + private final ArangoDBVertex vertex; -public class ArangoDBVertexProperty extends ArangoDBElementProperty implements VertexProperty { - - /** The Logger. */ - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBVertexProperty.class); - - /** - * Constructor used for ArabgoDB JavaBeans serialisation. - */ - - public ArangoDBVertexProperty() { - super(); - } - - /** - * Instantiates a new arango DB vertex property. - * - * @param name the name - * @param value the value - * @param owner the owner - */ - - public ArangoDBVertexProperty(String name, V value, ArangoDBBaseDocument owner) { - super(name, value, owner, ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION); - } - - /** - * Instantiates a new Arango DB vertex property. - * - * @param key the id - * @param name the name - * @param value the value - * @param owner the owner - */ - - public ArangoDBVertexProperty(String key, String name, V value, ArangoDBBaseDocument owner) { - super(key, name, value, owner, ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION); + public ArangoDBVertexProperty(String key, VertexPropertyData data, ArangoDBVertex vertex) { + super(vertex.graph(), data); + this.key = key; + this.vertex = vertex; } - @Override - public String toString() { - return StringFactory.propertyString(this); + @Override + protected boolean removed() { + return super.removed() || vertex.removed(); } - @Override - public Object id() { - return _id(); - } - - @Override - public String label() { - return name; + @Override + public String key() { + return key; } + @SuppressWarnings("unchecked") @Override - public Vertex element() { - ArangoCursor q = graph.getClient() - .getDocumentNeighbors(this, Collections.emptyList(), Direction.IN, ArangoDBPropertyFilter.empty(), ArangoDBVertex.class); - ArangoDBIterator iterator = new ArangoDBIterator(graph, q); - return iterator.hasNext() ? iterator.next() : null; + public P value() throws NoSuchElementException { + return (P) data.getValue(); } - @Override - public Property property(String key, U value) { - logger.info("property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - Property p = property(key); - if (!p.isPresent()) { - p = ArangoDBUtil.createArangoDBPropertyProperty(key, value, this); - } else { - ((ArangoDBElementProperty) p).value(value); - } - return p; - } - - - @SuppressWarnings("unchecked") - @Override - public Iterator> properties(String... propertyKeys) { - List labels = new ArrayList<>(); - labels.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)); - ArangoDBPropertyFilter filter = new ArangoDBPropertyFilter(); - for (String pk : propertyKeys) { - filter.has("name", pk, ArangoDBPropertyFilter.Compare.EQUAL); - } - ArangoCursor query = graph.getClient().getElementProperties(this, labels, filter, ArangoDBPropertyProperty.class); - return new ArangoDBPropertyIterator<>(graph, (ArangoCursor>) query); - } + @Override + public boolean isPresent() { + return true; + } @Override - public void remove() { - logger.info("remove {}", this._id()); - if (paired) { - // Delete properties - properties().forEachRemaining(Property::remove); - } - super.remove(); + public ArangoDBVertex element() { + return vertex; } - + + @Override + public Object id() { + return data.getId(); + } + @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); + protected void doUpdate() { + vertex.doUpdate(); } @Override - public int hashCode() { - return key().hashCode() + value().hashCode(); + protected void doRemove() { + vertex.removeProperty(this); + vertex.doUpdate(); + } + + @Override + protected void doInsert() { + doUpdate(); + } + + @Override + public String stringify() { + return StringFactory.propertyString(this); + } + + @Override + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); + } + + @Override + public String label() { + return VertexProperty.super.label(); } +} -} \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java index 9f54150..63eda3c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -1,36 +1,23 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.utils; -import static org.apache.tinkerpop.gremlin.structure.Graph.Hidden.isHidden; - import com.arangodb.ArangoGraph; import com.arangodb.entity.EdgeDefinition; import com.arangodb.entity.GraphEntity; import com.arangodb.model.GraphCreateOptions; import com.arangodb.shaded.fasterxml.jackson.databind.ObjectMapper; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder; import com.arangodb.tinkerpop.gremlin.structure.*; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBElementProperty.ElementHasProperty; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; + +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -42,575 +29,394 @@ /** * This class provides utility methods for creating properties and for normalising property and * collections names (to satisfy Arango DB naming conventions. - * + * * @author Achim Brandt (http://www.triagens.de) * @author Johannes Gocke (http://www.triagens.de) * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ //FIXME We should add more util methods to validate attribute names, e.g. scape ".". public class ArangoDBUtil { - - /** The Logger. */ - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBUtil.class); - - /** Utiliy mapper for conversions. **/ - - private static final ObjectMapper mapper = new ObjectMapper(); - - /** - * The prefix to denote that a collection is a hidden collection. - */ - - private final static String HIDDEN_PREFIX = "adbt_"; - - /** The Constant HIDDEN_PREFIX_LENGTH. */ - - private static final int HIDDEN_PREFIX_LENGTH = HIDDEN_PREFIX.length(); - - /** The regex to match DOCUMENT_KEY. */ - - public static final Pattern DOCUMENT_KEY = Pattern.compile("^[A-Za-z0-9_:\\.@()\\+,=;\\$!\\*'%-]*"); - - /** - * Instantiates a new ArangoDB Util. - */ - private ArangoDBUtil() { - // this is a helper class - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename name "_XXXX" to "«a»XXXX" for storage. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String normalizeKey(String key) { - if (key.charAt(0) == '_') { - return "«a»" + key.substring(1); - } - return key; - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String denormalizeKey(String key) { - if (key.startsWith("«a»")) { - return "_" + key.substring(3); - } - return key; - } - - /** - * Hidden keys, labels, etc. are prefixed in Tinkerpop with @link Graph.Hidden.HIDDEN_PREFIX). Since in ArangoDB - * collection names must always start with a letter, this method normalises Hidden collections name to valid - * ArangoDB names by replacing the "~" with - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String normalizeCollection(String key) { - String nname = isHidden(key) ? key : HIDDEN_PREFIX.concat(key); - if (!NamingConventions.COLLECTION.hasValidNameSize(nname)) { - throw ArangoDBGraphClient.ArangoDBExceptions.getNamingConventionError(ArangoDBGraphClient.ArangoDBExceptions.NAME_TO_LONG, key); - } - return nname; - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String denormalizeCollection(String key) { - return isHidden(key) ? key.substring(HIDDEN_PREFIX_LENGTH) : key; - } - - /** - * The Enum NamingConventions. - */ - - public enum NamingConventions { - - /** The collection. */ - COLLECTION(64), - - /** The name. */ - KEY(256); - - /** The max length. */ - - private int maxLength; - - /** - * Instantiates a new naming conventions. - * - * @param maxLength the max length - */ - NamingConventions(int maxLength) { - this.maxLength = maxLength; - } - - /** - * Checks for valid name size. - * - * @param name the name - * @return true, if successful - */ - public boolean hasValidNameSize(String name) { - final byte[] utf8Bytes; - try { - utf8Bytes = name.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - return false; - } - return utf8Bytes.length <= maxLength; - } - } - - /** - * Create an EdgeDefinition from a relation in the Configuration. The format of a relation is: - *

-	 * collection:from->to
-	 * 
- * Where collection is the name of the Edge collection, and to and from are comma separated list of - * node collection names. - * - * @param graph the name of the graph - * @param relation the relation - * @return an EdgeDefinition that represents the relation. - * @throws ArangoDBGraphException if the relation is malformed - */ - - public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph graph, String relation) throws ArangoDBGraphException { - logger.info("Creating EdgeRelation from {}", relation); - EdgeDefinition result = new EdgeDefinition(); - String[] info = relation.split(":"); - if (info.length != 2) { - throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation); - } - result.collection(graph.getPrefixedCollectioName(info[0])); - info = info[1].split("->"); - if (info.length != 2) { - throw new ArangoDBGraphException("Error in configuration. Malformed relation> " + relation); - } - List trimmed = Arrays.stream(info[0].split(",")) - .map(String::trim) - .map(c -> graph.getPrefixedCollectioName(c)) - .collect(Collectors.toList()); - String[] from = new String[trimmed.size()]; - from = trimmed.toArray(from); - - trimmed = Arrays.stream(info[1].split(",")) - .map(String::trim) - .map(c -> graph.getPrefixedCollectioName(c)) - .collect(Collectors.toList()); - String[] to = new String[trimmed.size()]; - to = trimmed.toArray(to); - result.from(from).to(to); - return result; - } - - - /** - * Creates the default edge definitions. When no relations are provided, the graph schema is - * assumed to be fully connected, i.e. there is an EdgeDefintion for each possible combination - * of Vertex-Edge-Vertex triplets. - * - * @param verticesCollectionNames the vertex collection names - * @param edgesCollectionNames the edge collection names - * @return the list of edge definitions - */ - - public static List createDefaultEdgeDefinitions( - List verticesCollectionNames, - List edgesCollectionNames) { - List result = new ArrayList<>(); - for (String e : edgesCollectionNames) { - for (String from : verticesCollectionNames) { - for (String to : verticesCollectionNames) { - EdgeDefinition ed = new EdgeDefinition() - .collection(e) - .from(from) - .to(to); - result.add(ed); - } - } - } - return result; - } - - - /** - * Gets a collection that is unique for the given graph. - * - * @param graphName the graph name - * @param collectionName the collection name - * @param shouldPrefixWithGraphName flag to indicate if the name should be prefixed - * @return the unique collection name - */ - @Deprecated - public static String getCollectioName(String graphName, String collectionName, Boolean shouldPrefixWithGraphName) { - if(shouldPrefixWithGraphName) { - return String.format("%s_%s", graphName, collectionName); - }else{ - return collectionName; - } - } - - /** - * Validate if an existing graph is correctly configured to handle the desired vertex, edges - * and relations. - * - * @param verticesCollectionNames The names of collections for nodes - * @param edgesCollectionNames The names of collections for edges - * @param requiredDefinitions The description of edge definitions - * @param graph the graph - * @param options The options used to create the graph - * @throws ArangoDBGraphException If the graph settings do not match the configuration information - */ - - public static void checkGraphForErrors( - List verticesCollectionNames, - List edgesCollectionNames, - List requiredDefinitions, - ArangoGraph graph, - GraphCreateOptions options) throws ArangoDBGraphException { - - checkGraphVertexCollections(verticesCollectionNames, graph, options); - - GraphEntity ge = graph.getInfo(); - Collection graphEdgeDefinitions = ge.getEdgeDefinitions(); - if (CollectionUtils.isEmpty(requiredDefinitions)) { - // If no relations are defined, vertices and edges can only have one value - if ((verticesCollectionNames.size() != 1) || (edgesCollectionNames.size() != 1)) { - throw new ArangoDBGraphException("No relations where specified but more than one vertex/edge where defined."); - } - if (graphEdgeDefinitions.size() != 2) { // There is always a edgeDefinition for ELEMENT_HAS_PROPERTIES - throw new ArangoDBGraphException("No relations where specified but the graph has more than one EdgeDefinition."); - } - } - Map eds = requiredDefinitions.stream().collect(Collectors.toMap(EdgeDefinition::getCollection, ed -> ed)); - Iterator it = graphEdgeDefinitions.iterator(); - while (it.hasNext()) { - EdgeDefinition existing = it.next(); - if (eds.containsKey(existing.getCollection())) { - EdgeDefinition requiredEdgeDefinition = eds.remove(existing.getCollection()); - HashSet existingSet = new HashSet(existing.getFrom()); - HashSet requiredSet = new HashSet(requiredEdgeDefinition.getFrom()); - if (!existingSet.equals(requiredSet)) { - throw new ArangoDBGraphException(String.format("The from collections dont match for edge definition %s", existing.getCollection())); - } - existingSet.clear(); - existingSet.addAll(existing.getTo()); - requiredSet.clear(); - requiredSet.addAll(requiredEdgeDefinition.getTo()); - if (!existingSet.equals(requiredSet)) { - throw new ArangoDBGraphException(String.format("The to collections dont match for edge definition %s", existing.getCollection())); - } - } else { - throw new ArangoDBGraphException(String.format("The graph has a surplus edge definition %s", edgeDefinitionString(existing))); - } + /** + * The Logger. + */ + + private static final Logger logger = LoggerFactory.getLogger(ArangoDBUtil.class); + + /** + * Utiliy mapper for conversions. + **/ + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * Instantiates a new ArangoDB Util. + */ + private ArangoDBUtil() { + // this is a helper class + } + + /** + * Create an EdgeDefinition from a relation in the Configuration. The format of a relation is: + *
+     * collection:from->to
+     * 
+ * Where collection is the name of the Edge collection, and to and from are comma separated list of + * node collection names. + * + * @param graph the name of the graph + * @param relation the relation + * @return an EdgeDefinition that represents the relation. + * @throws ArangoDBGraphException if the relation is malformed + */ + + public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph graph, String relation) throws ArangoDBGraphException { + logger.debug("Creating EdgeRelation from {}", relation); + EdgeDefinition result = new EdgeDefinition(); + String[] info = relation.split(":"); + if (info.length != 2) { + throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation); + } + result.collection(graph.getPrefixedCollectionName(info[0])); + info = info[1].split("->"); + if (info.length != 2) { + throw new ArangoDBGraphException("Error in configuration. Malformed relation> " + relation); } + List trimmed = Arrays.stream(info[0].split(",")) + .map(String::trim) + .map(c -> graph.getPrefixedCollectionName(c)) + .collect(Collectors.toList()); + String[] from = new String[trimmed.size()]; + from = trimmed.toArray(from); + + trimmed = Arrays.stream(info[1].split(",")) + .map(String::trim) + .map(c -> graph.getPrefixedCollectionName(c)) + .collect(Collectors.toList()); + String[] to = new String[trimmed.size()]; + to = trimmed.toArray(to); + result.from(from).to(to); + return result; + } - } - - private static void checkGraphVertexCollections(List verticesCollectionNames, ArangoGraph graph, GraphCreateOptions options) { - List allVertexCollections = new ArrayList<>(verticesCollectionNames); - final Collection orphanCollections = options.getOrphanCollections(); - if (orphanCollections != null) { - allVertexCollections.addAll(orphanCollections); - } - if (!graph.getVertexCollections().containsAll(allVertexCollections)) { - Set avc = new HashSet<>(allVertexCollections); - avc.removeAll(graph.getVertexCollections()); - throw new ArangoDBGraphException("Not all declared vertex names appear in the graph. Missing " + avc); - } - } - - /** - * Get a string representation of the Edge definition that complies with the configuration options. - * - * @param ed the Edge definition - * @return the string that represents the edge definition - */ - - public static String edgeDefinitionString(EdgeDefinition ed) { - return String.format("[%s]: %s->%s", ed.getCollection(), ed.getFrom(), ed.getTo()); - } /** - * Create the EdgeDefinition for the graph properties. + * Creates the default edge definitions. When no relations are provided, the graph schema is + * assumed to be fully connected, i.e. there is an EdgeDefintion for each possible combination + * of Vertex-Edge-Vertex triplets. * - * @param graph The graph - * @param vertexCollections the vertex collections - * @param edgeCollections the edge collections - * @return the edge definition + * @param verticesCollectionNames the vertex collection names + * @param edgesCollectionNames the edge collection names + * @return the list of edge definitions */ - - public static EdgeDefinition createPropertyEdgeDefinitions( - final ArangoDBGraph graph, - final List vertexCollections, - final List edgeCollections) { - final List from = new ArrayList<>(vertexCollections); - from.addAll(edgeCollections); - from.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); - String[] f = from.toArray(new String[from.size()]); - EdgeDefinition ed = new EdgeDefinition() - .collection(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)) - .from(f) - .to(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); - return ed; + + public static List createDefaultEdgeDefinitions( + List verticesCollectionNames, + List edgesCollectionNames) { + List result = new ArrayList<>(); + for (String e : edgesCollectionNames) { + for (String from : verticesCollectionNames) { + for (String to : verticesCollectionNames) { + EdgeDefinition ed = new EdgeDefinition() + .collection(e) + .from(from) + .to(to); + result.add(ed); + } + } + } + return result; } /** - * Creates an Arango DB edge property. + * Gets a collection that is unique for the given graph. * - * @param the generic type - * @param key the name - * @param value the value - * @param edge the edge - * @return the created Arango DB edge property + * @param graphName the graph name + * @param collectionName the collection name + * @param shouldPrefixWithGraphName flag to indicate if the name should be prefixed + * @return the unique collection name */ - - public static ArangoDBEdgeProperty createArangoDBEdgeProperty( - String key, - U value, - ArangoDBEdge edge) { - ArangoDBEdgeProperty p = new ArangoDBEdgeProperty<>(key, value, edge); - insertElementAndProperty(edge, p); - return p; + @Deprecated + public static String getCollectioName(String graphName, String collectionName, Boolean shouldPrefixWithGraphName) { + if (shouldPrefixWithGraphName) { + return String.format("%s_%s", graphName, collectionName); + } else { + return collectionName; + } } /** - * Creates an Arango DB vertex property. + * Validate if an existing graph is correctly configured to handle the desired vertex, edges + * and relations. * - * @param the generic type - * @param propertyName the name - * @param propertyValue the value - * @param vertex the vertex - * @return the created Arango DB vertex property + * @param verticesCollectionNames The names of collections for nodes + * @param edgesCollectionNames The names of collections for edges + * @param requiredDefinitions The description of edge definitions + * @param graph the graph + * @param options The options used to create the graph + * @throws ArangoDBGraphException If the graph settings do not match the configuration information */ - - public static ArangoDBVertexProperty createArangoDBVertexProperty(String propertyName, U propertyValue, ArangoDBVertex vertex) { - ArangoDBVertexProperty p = new ArangoDBVertexProperty<>(propertyName, propertyValue, vertex); - insertElementAndProperty(vertex, p); - return p; + + public static void checkGraphForErrors( + List verticesCollectionNames, + List edgesCollectionNames, + List requiredDefinitions, + ArangoGraph graph, + GraphCreateOptions options) throws ArangoDBGraphException { + + checkGraphVertexCollections(verticesCollectionNames, graph, options); + + GraphEntity ge = graph.getInfo(); + Collection graphEdgeDefinitions = ge.getEdgeDefinitions(); + if (CollectionUtils.isEmpty(requiredDefinitions)) { + // If no relations are defined, vertices and edges can only have one value + if ((verticesCollectionNames.size() != 1) || (edgesCollectionNames.size() != 1)) { + throw new ArangoDBGraphException("No relations where specified but more than one vertex/edge where defined."); + } + if (graphEdgeDefinitions.size() != 2) { // There is always a edgeDefinition for ELEMENT_HAS_PROPERTIES + throw new ArangoDBGraphException("No relations where specified but the graph has more than one EdgeDefinition."); + } + } + Map eds = requiredDefinitions.stream().collect(Collectors.toMap(EdgeDefinition::getCollection, ed -> ed)); + + Iterator it = graphEdgeDefinitions.iterator(); + while (it.hasNext()) { + EdgeDefinition existing = it.next(); + if (eds.containsKey(existing.getCollection())) { + EdgeDefinition requiredEdgeDefinition = eds.remove(existing.getCollection()); + HashSet existingSet = new HashSet(existing.getFrom()); + HashSet requiredSet = new HashSet(requiredEdgeDefinition.getFrom()); + if (!existingSet.equals(requiredSet)) { + throw new ArangoDBGraphException(String.format("The from collections dont match for edge definition %s", existing.getCollection())); + } + existingSet.clear(); + existingSet.addAll(existing.getTo()); + requiredSet.clear(); + requiredSet.addAll(requiredEdgeDefinition.getTo()); + if (!existingSet.equals(requiredSet)) { + throw new ArangoDBGraphException(String.format("The to collections dont match for edge definition %s", existing.getCollection())); + } + } else { + throw new ArangoDBGraphException(String.format("The graph has a surplus edge definition %s", edgeDefinitionString(existing))); + } + } + + } + + private static void checkGraphVertexCollections(List verticesCollectionNames, ArangoGraph graph, GraphCreateOptions options) { + List allVertexCollections = new ArrayList<>(verticesCollectionNames); + final Collection orphanCollections = options.getOrphanCollections(); + if (orphanCollections != null) { + allVertexCollections.addAll(orphanCollections); + } + if (!graph.getVertexCollections().containsAll(allVertexCollections)) { + Set avc = new HashSet<>(allVertexCollections); + avc.removeAll(graph.getVertexCollections()); + throw new ArangoDBGraphException("Not all declared vertex names appear in the graph. Missing " + avc); + } } /** - * Creates an Arango DB vertex property. + * Get a string representation of the Edge definition that complies with the configuration options. * - * @param the generic type - * @param id the id - * @param propertyName the name - * @param propertyValue the value - * @param vertex the vertex - * @return the created Arango DB vertex property + * @param ed the Edge definition + * @return the string that represents the edge definition */ - - public static ArangoDBVertexProperty createArangoDBVertexProperty(String id, String propertyName, U propertyValue, ArangoDBVertex vertex) { - ArangoDBVertexProperty p; - p = new ArangoDBVertexProperty<>(id, propertyName, propertyValue, vertex); - insertElementAndProperty(vertex, p); - return p; + + public static String edgeDefinitionString(EdgeDefinition ed) { + return String.format("[%s]: %s->%s", ed.getCollection(), ed.getFrom(), ed.getTo()); } /** - * Creates an Arango DB property property. + * Create the EdgeDefinition for the graph properties. * - * @param the generic type - * @param key the name - * @param value the value - * @param vertexProperty the vertex property - * @return the created Arango DB property property + * @param graph The graph + * @param vertexCollections the vertex collections + * @param edgeCollections the edge collections + * @return the edge definition */ - - public static ArangoDBPropertyProperty createArangoDBPropertyProperty(String key, U value, ArangoDBVertexProperty vertexProperty) { - ArangoDBPropertyProperty p; - p = new ArangoDBPropertyProperty<>(key, value, vertexProperty); - insertElementAndProperty(vertexProperty, p); - return p; + + public static EdgeDefinition createPropertyEdgeDefinitions( + final ArangoDBGraph graph, + final List vertexCollections, + final List edgeCollections) { + final List from = new ArrayList<>(vertexCollections); + from.addAll(edgeCollections); + from.add(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); + String[] f = from.toArray(new String[from.size()]); + EdgeDefinition ed = new EdgeDefinition() + .collection(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)) + .from(f) + .to(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); + return ed; } /** * Gets the correct primitive. * - * @param value the value + * @param value the value * @param valueClass the exoected class of the value - * @param the value type - * @return the correct Java primitive + * @param the value type + * @return the correct Java primitive */ - + @SuppressWarnings("unchecked") - public static Object getCorretctPrimitive(V value, String valueClass) { - - switch(valueClass) { - case "java.lang.Float": - { - if (value instanceof Double) { - return ((Double) value).floatValue(); - } - else if (value instanceof Long) { - return ((Long) value).floatValue(); - } - else if (value instanceof Integer) { - return ((Integer) value).floatValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Double": - { - if (value instanceof Double) { - return value; - } - else if (value instanceof Long) { - return ((Long) value).doubleValue(); - } - else if (value instanceof Integer) { - return ((Integer) value).doubleValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Long": - { - if (value instanceof Long) { - return value; - } - else if (value instanceof Double) { - return ((Double)value).longValue(); - } - else if (value instanceof Integer) { - return ((Integer)value).longValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Integer": - { - if (value instanceof Long) { - return ((Long) value).intValue(); - } - break; - } - case "java.lang.String": - case "java.lang.Boolean": - case "": - return value; - case "java.util.HashMap": - //logger.debug(((Map)value).keySet().stream().map(Object::getClass).collect(Collectors.toList())); - //logger.debug("Add conversion for map values to " + valueClass); - // Maps are handled by ArangoOK, but we have an extra field, remove it - Map valueMap = (Map)value; - for (String key : valueMap.keySet()) { - if (key.startsWith("_")) { - valueMap.remove(key); - } - // We might need to check individual values... - } - break; - case "java.util.ArrayList": - // Should we save the type per item? - List list = new ArrayList<>(); - ((ArrayList)value).forEach(e -> list.add(getCorretctPrimitive(e, ""))); - return list; - case "boolean[]": - List barray = (List)value; - boolean[] br = new boolean[barray.size()]; - IntStream.range(0, barray.size()) - .forEach(i -> br[i] = (boolean) barray.get(i)); - return br; - case "double[]": - List darray = (List)value; - double[] dr = new double[darray.size()]; - IntStream.range(0, darray.size()) - .forEach(i -> dr[i] = (double) getCorretctPrimitive(darray.get(i), "java.lang.Double")); - return dr; - case "float[]": - List farray = (List)value; - float[] fr = new float[farray.size()]; - IntStream.range(0, farray.size()) - .forEach(i -> fr[i] = (float) getCorretctPrimitive(farray.get(i), "java.lang.Float")); - return fr; - case "int[]": - List iarray = (List)value; - int[] ir = new int[iarray.size()]; - IntStream.range(0, iarray.size()) - .forEach(i -> ir[i] = (int) getCorretctPrimitive(iarray.get(i), "java.lang.Integer")); - return ir; - case "long[]": - List larray = (List)value; - long[] lr = new long[larray.size()]; - IntStream.range(0, larray.size()) - .forEach(i -> lr[i] = (long) getCorretctPrimitive(larray.get(i), "java.lang.Long")); - return lr; - case "java.lang.String[]": - List sarray = (List)value; - String[] sr = new String[sarray.size()]; - IntStream.range(0, sarray.size()) - .forEach(i -> sr[i] = (String) sarray.get(i)); - return sr; - default: - Object result; - try { - result = mapper.convertValue(value, Class.forName(valueClass)); - return result; - } catch (IllegalArgumentException | ClassNotFoundException e1) { - logger.warn("Type not deserializable", e1); - } - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - return value; + public static Object getCorretctPrimitive(V value, String valueClass) { + + switch (valueClass) { + case "java.lang.Float": { + if (value instanceof Double) { + return ((Double) value).floatValue(); + } else if (value instanceof Long) { + return ((Long) value).floatValue(); + } else if (value instanceof Integer) { + return ((Integer) value).floatValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Double": { + if (value instanceof Double) { + return value; + } else if (value instanceof Long) { + return ((Long) value).doubleValue(); + } else if (value instanceof Integer) { + return ((Integer) value).doubleValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Long": { + if (value instanceof Long) { + return value; + } else if (value instanceof Double) { + return ((Double) value).longValue(); + } else if (value instanceof Integer) { + return ((Integer) value).longValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Integer": { + if (value instanceof Long) { + return ((Long) value).intValue(); + } + break; + } + case "java.lang.String": + case "java.lang.Boolean": + case "": + return value; + case "java.util.HashMap": + //logger.debug(((Map)value).keySet().stream().map(Object::getClass).collect(Collectors.toList())); + //logger.debug("Add conversion for map values to " + valueClass); + // Maps are handled by ArangoOK, but we have an extra field, remove it + Map valueMap = (Map) value; + for (String key : valueMap.keySet()) { + if (key.startsWith("_")) { + valueMap.remove(key); + } + // We might need to check individual values... + } + break; + case "java.util.ArrayList": + // Should we save the type per item? + List list = new ArrayList<>(); + ((ArrayList) value).forEach(e -> list.add(getCorretctPrimitive(e, ""))); + return list; + case "boolean[]": + if (value instanceof List) { + List barray = (List) value; + boolean[] br = new boolean[barray.size()]; + IntStream.range(0, barray.size()) + .forEach(i -> br[i] = (boolean) barray.get(i)); + return br; + } else { + return value; + } + case "double[]": + if (value instanceof List) { + List darray = (List) value; + double[] dr = new double[darray.size()]; + IntStream.range(0, darray.size()) + .forEach(i -> dr[i] = (double) getCorretctPrimitive(darray.get(i), "java.lang.Double")); + return dr; + } else { + return value; + } + case "float[]": + if (value instanceof List) { + List farray = (List) value; + float[] fr = new float[farray.size()]; + IntStream.range(0, farray.size()) + .forEach(i -> fr[i] = (float) getCorretctPrimitive(farray.get(i), "java.lang.Float")); + return fr; + } else { + return value; + } + case "int[]": + if (value instanceof List) { + List iarray = (List) value; + int[] ir = new int[iarray.size()]; + IntStream.range(0, iarray.size()) + .forEach(i -> ir[i] = (int) getCorretctPrimitive(iarray.get(i), "java.lang.Integer")); + return ir; + } else { + return value; + } + case "long[]": + if (value instanceof List) { + List larray = (List) value; + long[] lr = new long[larray.size()]; + IntStream.range(0, larray.size()) + .forEach(i -> lr[i] = (long) getCorretctPrimitive(larray.get(i), "java.lang.Long")); + return lr; + } else { + return value; + } + case "java.lang.String[]": + if (value instanceof List) { + List sarray = (List) value; + String[] sr = new String[sarray.size()]; + IntStream.range(0, sarray.size()) + .forEach(i -> sr[i] = (String) sarray.get(i)); + return sr; + } else { + return value; + } + default: + Object result; + try { + result = mapper.convertValue(value, Class.forName(valueClass)); + return result; + } catch (IllegalArgumentException | ClassNotFoundException e1) { + logger.warn("Type not deserializable", e1); + } + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + return value; + } + + /** + * Translate a Gremlin direction to Arango direction + * + * @param direction the direction to translate + * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction + */ + public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirection(final Direction direction) { + switch (direction) { + case BOTH: + return ArangoDBQueryBuilder.Direction.ALL; + case IN: + return ArangoDBQueryBuilder.Direction.IN; + case OUT: + return ArangoDBQueryBuilder.Direction.OUT; + } + throw new IllegalArgumentException("Unsupported direction: " + direction); } - /** - * Translate a Gremlin direction to Arango direction - * @param direction the direction to translate - * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction - */ - public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirection(final Direction direction) { - switch (direction) { - case BOTH: - return ArangoDBQueryBuilder.Direction.ALL; - case IN: - return ArangoDBQueryBuilder.Direction.IN; - case OUT: - return ArangoDBQueryBuilder.Direction.OUT; - } - return null; - } - - private static void insertElementAndProperty(ArangoDBBaseDocument element, ArangoDBElementProperty p) { - ArangoDBGraph g = element.graph(); - ArangoDBGraphClient c = g.getClient(); - c.insertDocument(p); - ElementHasProperty e = p.assignToElement(element); - c.insertEdge(e); - } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java deleted file mode 100644 index fff483c..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ /dev/null @@ -1,313 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import com.arangodb.tinkerpop.gremlin.structure.*; -import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.tinkerpop.gremlin.AbstractGraphProvider; -import org.apache.tinkerpop.gremlin.LoadGraphWith; -import org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData; -import org.apache.tinkerpop.gremlin.structure.Element; -import org.apache.tinkerpop.gremlin.structure.Graph; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; -import org.apache.tinkerpop.gremlin.structure.VertexTest; - -/** - * The Class ArangoDBGraphProvider. This provider assumes that there is a local ArangoDB running (i.e. - * http://127.0.0.1:8529) with a tinkerpop database and a gremlin user that has Administrate permissions - * on the db. - * - */ -public class ArangoDBGraphProvider extends AbstractGraphProvider { - - /** The Constant IMPLEMENTATIONS. */ - private static final Set IMPLEMENTATIONS = new HashSet() {{ - add(ArangoDBEdge.class); - add(ArangoDBGraph.class); - add(ArangoDBGraphVariables.class); - add(ArangoDBEdgeProperty.class); - add(ArangoDBElementProperty.class); - add(ArangoDBPropertyProperty.class); - add(ArangoDBVertex.class); - add(ArangoDBVertexProperty.class); - }}; - - - @Override - public Configuration newGraphConfiguration(final String graphName, final Class test, - final String testMethodName, - final Map configurationOverrides, - final LoadGraphWith.GraphData loadGraphWith) { - Configuration conf = getConfiguration(graphName, test, testMethodName, loadGraphWith); - - // assign overrides but don't allow gremlin.graph setting to be overridden. the test suite should - // not be able to override that. - configurationOverrides.entrySet().stream() - .filter(c -> !c.getKey().equals(Graph.GRAPH)) - .forEach(e -> conf.setProperty(e.getKey(), e.getValue())); - return conf; - } - - private Configuration getConfiguration( - String graphName, - Class test, - String testMethodName, - GraphData loadGraphWith) { - ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() - .arangoHosts("127.0.0.1:8529") - .arangoUser("root") - .arangoPassword("test") - .graph(graphName); - if (loadGraphWith != null) { - switch(loadGraphWith) { - case CLASSIC: - System.out.println("CLASSIC"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "vertex", "vertex"); - builder.configureEdge("created", "vertex", "vertex"); - break; - case CREW: - System.out.println("CREW"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("uses"); - builder.withEdgeCollection("develops"); - builder.withEdgeCollection("traverses"); - builder.configureEdge("uses", "person", "software"); - builder.configureEdge("develops", "person", "software"); - builder.configureEdge("traverses", "software", "software"); - break; - case GRATEFUL: - System.out.println("GRATEFUL"); - break; - case MODERN: - System.out.println("MODERN"); - builder.withVertexCollection("dog"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("created", "person", "software"); - break; - default: - System.out.println("default"); - break; - } - } - else { - if (testMethodName.startsWith("shouldProcessVerticesEdges") - || testMethodName.startsWith("shouldGenerate") - || testMethodName.startsWith("shouldSetValueOnEdge") - || testMethodName.startsWith("shouldAutotype")) { - builder.withEdgeCollection("knows"); - } - else if(testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { - builder.withEdgeCollection("self"); - } - else if(testMethodName.startsWith("shouldSupportUserSuppliedIds")) { - builder.withEdgeCollection("test"); - } - else if(testMethodName.startsWith("shouldSupportUUID")) { - builder.withEdgeCollection("friend"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithINEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldReadWriteEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { - builder.withEdgeCollection("self"); - } - else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { - builder.withEdgeCollection("self"); - } - else { - // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? - switch (testMethodName) { - case "shouldGetPropertyKeysOnEdge": - case "shouldNotGetConcurrentModificationException": - builder.withEdgeCollection("friend"); - builder.withEdgeCollection("knows"); - break; - case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": - case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": - builder.withEdgeCollection("hate"); - builder.withEdgeCollection("friend"); - break; - case "shouldPersistDataOnClose": - builder.withEdgeCollection("collaborator"); - break; - case "shouldTestTreeConnectivity": - builder.withEdgeCollection("test1"); - builder.withEdgeCollection("test2"); - builder.withEdgeCollection("test3"); - break; - case "shouldEvaluateConnectivityPatterns": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("knows"); - break; - case "shouldRemoveEdgesWithoutConcurrentModificationException": - builder.withEdgeCollection("link"); - break; - case "shouldGetValueThatIsNotPresentOnEdge": - case "shouldHaveStandardStringRepresentationForEdgeProperty": - case "shouldHaveTruncatedStringRepresentationForEdgeProperty": - case "shouldValidateIdEquality": - case "shouldValidateEquality": - case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": - case "shouldAddEdgeWithUserSuppliedStringId": - case "shouldAllowNullAddEdge": - builder.withEdgeCollection("self"); - break; - case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": - case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": - case "shouldProcessEdges": - case "shouldReturnOutThenInOnVertexIterator": - case "shouldReturnEmptyIteratorIfNoProperties": - builder.withEdgeCollection("knows"); - break; - case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("pets"); - builder.withEdgeCollection("walks"); - builder.withEdgeCollection("livesWith"); - break; - case "shouldHaveStandardStringRepresentation": - builder.withEdgeCollection("friends"); - break; - case "shouldReadWriteSelfLoopingEdges": - builder.withEdgeCollection("CONTROL"); - builder.withEdgeCollection("SELFLOOP"); - break; - case "shouldReadGraphML": - case "shouldReadGraphMLUnorderedElements": - case "shouldTransformGraphMLV2ToV3ViaXSLT": - case "shouldReadLegacyGraphSON": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - break; - case "shouldAddVertexWithLabel": - case "shouldAllowNullAddVertexProperty": - builder.withVertexCollection("person"); - break; - case "shouldNotAllowSetProperty": - case "shouldHashAndEqualCorrectly": - case "shouldNotAllowRemove": - case "shouldNotConstructNewWithSomethingAlreadyDetached": - case "shouldNotConstructNewWithSomethingAlreadyReferenced": - builder.withEdgeCollection("test"); - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertex": - builder.withEdgeCollection("tonothing"); - break; - case "shouldHandleSelfLoops": - builder.withVertexCollection("person"); - builder.withEdgeCollection("self"); - break; - case "shouldAttachWithCreateMethod": - case "testAttachableCreateMethod": - builder.withVertexCollection("person"); - builder.withVertexCollection("project"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("developedBy"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("developedBy", "project", "person"); - break; - case "shouldConstructReferenceVertex": - builder.withVertexCollection("blah"); - break; - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": - if (VertexTest.class.equals(test.getEnclosingClass())) { - builder.withVertexCollection("foo"); - } - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": - builder.withVertexCollection("foo"); - break; - case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": - builder.withEdgeCollection("created"); - builder.withEdgeCollection("knows"); - break; - default: - System.out.println("case \"" + testMethodName + "\":"); - } - } - } - return builder.build(); - } - - @Override - public void clear(Graph graph, Configuration configuration) throws Exception { - ArangoDBGraphClient client; - if (graph ==null) { - Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 0, true); - client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); - } - else { - ArangoDBGraph agraph = (ArangoDBGraph) graph; - client = agraph.getClient(); - client.clear(agraph); - agraph.close(); - } - - } - - @Override - public Set getImplementations() { - return IMPLEMENTATIONS; - } - - @Override - public Map getBaseConfiguration(String graphName, Class test, String testMethodName, - GraphData loadGraphWith) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object convertId(Object id, Class c) { - return id.toString(); - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java deleted file mode 100644 index 3224571..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import org.apache.tinkerpop.gremlin.GraphProviderClass; -import org.junit.runner.RunWith; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -@RunWith(ArangoDBTestSuite.class) -@GraphProviderClass(provider = ArangoDBGraphProvider.class, graph = ArangoDBGraph.class) -public class ArangoDBGraphTest { - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java deleted file mode 100644 index a60343c..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; -import org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest; -import org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest; -import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; -import org.apache.tinkerpop.gremlin.structure.*; -import org.apache.tinkerpop.gremlin.structure.io.IoCustomTest; -import org.apache.tinkerpop.gremlin.structure.io.IoEdgeTest; -import org.apache.tinkerpop.gremlin.structure.io.IoGraphTest; -import org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest; -import org.apache.tinkerpop.gremlin.structure.io.IoTest; -import org.apache.tinkerpop.gremlin.structure.io.IoVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdgeTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdgeTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceGraphTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - - -/** - * Run with {@code GREMLIN_TESTS} environment variable set to a list of any of these to enable - * particular tests: - * org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest, - * org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest, - * org.apache.tinkerpop.gremlin.structure.EdgeTest, - * org.apache.tinkerpop.gremlin.structure.FeatureSupportTest, - * org.apache.tinkerpop.gremlin.structure.io.IoCustomTest, - * org.apache.tinkerpop.gremlin.structure.io.IoGraphTest, - * org.apache.tinkerpop.gremlin.structure.io.IoVertexTest, - * org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest, - * org.apache.tinkerpop.gremlin.structure.GraphTest, - * org.apache.tinkerpop.gremlin.structure.GraphConstructionTest, - * org.apache.tinkerpop.gremlin.structure.io.IoTest, - * org.apache.tinkerpop.gremlin.structure.VertexPropertyTest - * - */ -public class ArangoDBTestSuite extends AbstractGremlinSuite { - - /** - * This list of tests in the suite that will be executed. Gremlin developers should add to this list - * as needed to enforce tests upon implementations. - */ - private static final Class[] allTests = new Class[]{ - CommunityGeneratorTest.class, - DetachedGraphTest.class, - DetachedEdgeTest.class, - DetachedVertexPropertyTest.class, - DetachedPropertyTest.class, - DetachedVertexTest.class, - DistributionGeneratorTest.class, - EdgeTest.class, - FeatureSupportTest.class, - IoCustomTest.class, - IoEdgeTest.class, - IoGraphTest.class, - IoVertexTest.class, - IoPropertyTest.class, - GraphTest.class, - GraphConstructionTest.class, - IoTest.class, - VertexPropertyTest.class, - VariablesTest.class, - PropertyTest.class, - ReferenceGraphTest.class, - ReferenceEdgeTest.class, - ReferenceVertexPropertyTest.class, - ReferenceVertexTest.class, - SerializationTest.class, - StarGraphTest.class, - TransactionTest.class, - TransactionMultiThreadedTest.class, - VertexTest.class, - //ArangoDBIndexCheck.class, - //ArangoDBCypherCheck.class, - }; - - public ArangoDBTestSuite( - Class klass, - RunnerBuilder builder) - throws InitializationError { - super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); - } - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java new file mode 100644 index 0000000..5753614 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java @@ -0,0 +1,11 @@ +package com.arangodb.tinkerpop.gremlin; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; + +public abstract class BaseGremlinTest extends AbstractGremlinTest { + + protected ArangoDBGraph getGraph() { + return (ArangoDBGraph) this.graph; + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java b/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java deleted file mode 100644 index 752d726..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; -import org.apache.commons.configuration2.Configuration; - -import java.io.File; - - -public class Issue57 { - - public static void main(String... args) { - ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder(); - builder.dataBase("Test02") - .graph("Test02Graph01") - .arangoUser("gremlin") - .arangoPassword("gremlin") - .arangoHosts("127.0.0.1:8529") - .withEdgeCollection("testedge") - .withVertexCollection("testfrom") - .withVertexCollection("testto") - .shouldPrefixCollectionNamesWithGraphName(false) - .configureEdge("testedge", "testfrom", "testto"); - - ArangoDBGraph g = ArangoDBGraph.open(builder.build()); - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java new file mode 100644 index 0000000..eb7c593 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java @@ -0,0 +1,88 @@ +package com.arangodb.tinkerpop.gremlin; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.structure.Graph; + +@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD) +@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", + method = "testAttachableCreateMethod", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.detached.DetachedGraphTest#testAttachableCreateMethod()") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldAddVertexWithUserSuppliedStringId", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveVertices", + reason = "FIXME: DE-998") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveEdges", + reason = "FIXME: DE-998") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldEvaluateConnectivityPatterns", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldAttachWithCreateMethod", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest.shouldAttachWithCreateMethod") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldCopyFromGraphAToGraphB", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest.shouldCopyFromGraphAToGraphB") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", + method = "shouldAddEdgeWithUserSuppliedStringId", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeTest$Traversals", + method = "*", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map.MergeEdgeTest" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexTest$Traversals", + method = "g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", + reason = "FIXME: DE-995" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexTest$Traversals", + method = "g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", + reason = "FIXME: DE-995" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.OrderabilityTest$Traversals", + method = "*", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.OrderabilityTest" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest$Traversals", + method = "*", + reason = "FIXME: DE-996" +) +public class TestGraph extends ArangoDBGraph { + + @SuppressWarnings("unused") + public static TestGraph open(Configuration configuration) { + return new TestGraph(configuration); + } + + public TestGraph(Configuration configuration) { + super(configuration); + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java b/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java deleted file mode 100644 index 6a2139c..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.client.test; - -import java.util.Properties; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.junit.After; -import org.junit.Before; - -public abstract class BaseTestCase { - - protected ArangoDBGraphClient client; - protected final String graphName = "test_graph1"; - protected final String vertices = "test_vertices1"; - protected final String edges = "test_edges1"; - - @Before - public void setUp() throws Exception { - - // host name and port see: arangodb.properties - PropertiesConfiguration configuration = new PropertiesConfiguration(); - configuration.setProperty("arangodb.hosts", "127.0.0.1:8529"); - configuration.setProperty("arangodb.user", "gremlin"); - configuration.setProperty("arangodb.password", "gremlin"); - Properties arangoProperties = ConfigurationConverter.getProperties(configuration); - - client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 30000, true); - - client.deleteGraph(graphName); - client.deleteCollection(vertices); - client.deleteCollection(edges); - - } - - @After - public void tearDown() { - - client.deleteCollection(vertices); - client.deleteCollection(edges); - client.deleteGraph(graphName); - client = null; - } - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java deleted file mode 100644 index b05af42..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.client.test; - -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; - -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabase; -import com.arangodb.ArangoGraph; -import com.arangodb.entity.EdgeDefinition; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; - -import java.util.*; -import java.util.stream.Collectors; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.junit.*; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/** - * This tests require four users: - *
    - *
  • root: To test DB creation, requires root access - *
  • gremlin: To test graph/collection creation+access in the db, requires "Administrate" permission to DB - *
  • reader: To test graph/collection access in the db, requires "Access" permission to DB - *
  • limited: To test no access to the db, requires "No access" permission to DB - *
- * For all users (except root) the password is expected to be the same as the username. For the root, - * the password must be set via environment variable ARANGODB_ROOT_PWD (so no system password is shared - * via code). - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - * - */ -@RunWith(Parameterized.class) -@Ignore -public class ClientTest { - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { true, "knows_", "social_", "routeplanner_" }, - { false, "", "", "" } - }); - } - - @Rule - public ExpectedException exception = ExpectedException.none(); - - private PropertiesConfiguration configuration; - private ArangoDBGraphClient client; - - @Parameterized.Parameter - public boolean shouldPrefixCollectionWithGraphName; - - @Parameterized.Parameter(1) - public String knowsPrefix; - - @Parameterized.Parameter(2) - public String socialPrefix; - - @Parameterized.Parameter(3) - public String routeplannerPrefix; - - @Before - public void setUp() throws Exception { - - configuration = new PropertiesConfiguration(); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_DB_NAME, "tinkerpop"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME, "standard"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.hosts", "127.0.0.1:8529"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.user", "gremlin"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.password", "gremlin"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, shouldPrefixCollectionWithGraphName); - Properties arangoProperties = ConfigurationConverter.getProperties(configuration); - ArangoDBGraph g = new ArangoDBGraph(configuration); - System.out.println(g.features()); - // client = new ArangoDBGraphClient(g, arangoProperties, "tinkerpop", 30000); - } - - @Test - public void dummy() { - assert true; - } -// -// @After -// public void tearDown() { -// // Drop used graphs and collections, if present -// ArangoDatabase db = client.getDB(); -// // know_graph -// deleteGraph(db, "knows", true); -// // social graph -// deleteGraph(db, "social", true); -// // city graph -// deleteGraph(db, "routeplanner", true); -// client.shutdown(); -// client = null; -// } -// -// private boolean deleteGraph( -// ArangoDatabase db, -// String name, -// boolean dropCollections) { -// if (db != null) { -// ArangoGraph graph = db.graph(name); -// if (graph.exists()) { -// Collection edgeDefinitions = dropCollections ? graph.getEdgeDefinitions() : Collections.emptyList(); -// Collection vertexCollections = dropCollections ? graph.getVertexCollections(): Collections.emptyList();; -// // Drop graph first because it will break if the graph collections do not exist -// graph.drop(); -// for (String definitionName : edgeDefinitions) { -// String collectioName = definitionName; -// if (db.collection(collectioName).exists()) { -// db.collection(collectioName).drop(); -// } -// } -// for (String vc : vertexCollections) { -// String collectioName = vc; -// if (db.collection(collectioName).exists()) { -// db.collection(collectioName).drop(); -// } -// } -// return true; -// } else { -// try { -// graph.drop(); -// } catch (ArangoDBException e) { -// //throw ArangoDBExceptions.getArangoDBException(e); -// } -// } -// } -// return false; -// } -// -// -// // ********* The following methods test a local ArangoDBGraphClient ********* -// -// @Test -// public void test_RestrictedUserNewDatabase_should_throw_ArangoDBGraphException() throws Exception { -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); -// ArangoDBGraph g = new ArangoDBGraph(configuration); -// new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); -// } -// -// @Test -// public void test_AuthorizedUserNewDatabase_can_create_new_database() throws Exception { -// org.junit.Assume.assumeTrue(System.getenv("ARANGODB_ROOT_PWD") != null); -// configuration.setProperty("arangodb.user", "root"); -// String pwd = System.getenv("ARANGODB_ROOT_PWD"); -// configuration.setProperty("arangodb.password", pwd); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// ArangoDBGraph g = new ArangoDBGraph(configuration); -// ArangoDBGraphClient localClient = new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); -// assertThat(localClient.dbExists(), is(true)); -// localClient.deleteDb(); -// localClient.shutdown(); -// } -// -// @Test -// public void test_RestrictedUserExistingDb_should_throw_ArangoDBGraphException() throws Exception { -// configuration.setProperty("arangodb.user", "limited"); -// configuration.setProperty("arangodb.password", "limited"); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("DB not found or user has no access:")); -// new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); -// } -// -// @Test -// public void test_ReadAccessUserCreateGraph_should_throw_ArangoDBGraphException() throws Exception { -// configuration.setProperty("arangodb.user", "reader"); -// configuration.setProperty("arangodb.password", "reader"); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// ArangoDBGraphClient localClient = new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); -// assertThat(localClient.dbExists(), is(true)); -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// verticesCollectionNames.add("person"); -// edgesCollectionNames.add("knows"); -// -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); -// localClient.createGraph("knows", verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); -// localClient.shutdown(); -// } -// -// // ********* The following tests use the ArangoDBGraphClient from @Setup ********* -// -// @Test -// public void test_ServerVersion() throws Exception { -// String version = client.getVersion(); -// assertThat(version, is(notNullValue())); -// } -// -// @Test -// public void test_CreateSimpleGraph() throws Exception { -// -// String graph_name = "knows"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// verticesCollectionNames.add("person"); -// edgesCollectionNames.add("knows"); -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); -// ArangoDatabase db = client.getDB(); -// assertThat("Graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection found in db", db.collection(String.format("%sperson", knowsPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sknows", knowsPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat(defs, hasSize(2)); // +1 for ELEMENT_HAS_PROPERTIES -// EdgeDefinition d = defs.iterator().next(); -// assertThat(d.getCollection(), is(String.format("%sknows", knowsPrefix))); -// assertThat(d.getFrom(), contains(String.format("%sperson", knowsPrefix))); -// assertThat(d.getTo(), contains(String.format("%sperson", knowsPrefix))); -// } -// -// @Test -// public void test_CreateMultiVertexGraph() throws Exception { -// String graph_name = "social"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// List relations = new ArrayList<>(); -// verticesCollectionNames.add("male"); -// verticesCollectionNames.add("female"); -// edgesCollectionNames.add("relation"); -// relations.add("relation:male,female->male,female"); -// -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); -// ArangoDatabase db = client.getDB(); -// assertThat("Created graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%smale", socialPrefix)).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sfemale", socialPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%srelation", socialPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat(defs, hasSize(2)); -// EdgeDefinition d = defs.iterator().next(); -// assertThat(d.getCollection(), is(String.format("%srelation", socialPrefix))); -// assertThat(d.getFrom(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); -// assertThat(d.getTo(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); -// } -// -// @Test -// public void test_CreateMultiVertexMultiEdgeGraph() throws Exception { -// String graph_name = "routeplanner"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// List relations = new ArrayList<>(); -// verticesCollectionNames.add("germanCity"); -// verticesCollectionNames.add("frenchCity"); -// edgesCollectionNames.add("frenchHighway"); -// edgesCollectionNames.add("germanHighway"); -// edgesCollectionNames.add("internationalHighway"); -// relations.add("frenchHighway:frenchCity->frenchCity"); -// relations.add("germanHighway:germanCity->germanCity"); -// relations.add("internationalHighway:germanCity,frenchCity->germanCity,frenchCity"); -// -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); -// ArangoDatabase db = client.getDB(); -// assertThat("Craeted graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sgermanCity", routeplannerPrefix)).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sfrenchCity", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sfrenchHighway", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sgermanHighway", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sinternationalHighway", routeplannerPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat("Not all edge definitions created", defs, hasSize(4)); -// List edgeNames = defs.stream().map(EdgeDefinition::getCollection).collect(Collectors.toList()); -// assertThat("Missmatch name in edge collecion names", edgeNames, -// containsInAnyOrder(String.format("%sfrenchHighway", routeplannerPrefix), -// String.format("%sgermanHighway", routeplannerPrefix), -// String.format("%sinternationalHighway", routeplannerPrefix), "routeplanner_ELEMENT-HAS-PROPERTIES")); -// EdgeDefinition fh = defs.stream().filter(ed -> String.format("%sfrenchHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("FrenchHighway connects incorrect collections", fh.getFrom(), contains(String.format("%sfrenchCity", routeplannerPrefix))); -// assertThat("FrenchHighway connects incorrect collections", fh.getTo(), contains(String.format("%sfrenchCity", routeplannerPrefix))); -// EdgeDefinition gh = defs.stream().filter(ed -> String.format("%sgermanHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("GermanHighway connects incorrect collections", gh.getFrom(), contains(String.format("%sgermanCity", routeplannerPrefix))); -// assertThat("GermanHighway connects incorrect collections", gh.getTo(), contains(String.format("%sgermanCity", routeplannerPrefix))); -// EdgeDefinition ih = defs.stream().filter(ed -> String.format("%sinternationalHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("InternationalHighway connects incorrect collections", ih.getFrom(), -// containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); -// assertThat("InternationalHighway connects incorrect collections", ih.getTo(), -// containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); -// } -// -// - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java new file mode 100644 index 0000000..2718340 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -0,0 +1,58 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; + +public class CustomGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, CustomTestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + switch (testMethodName) { + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists": + case "g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadasX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists": + case "g_V_mergeEXlabel_self_weight_05X": + case "g_injectXlabel_knows_out_marko_in_vadasX_mergeE": + case "g_mergeE_with_outV_inV_options": + builder.withVertexCollection("person"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("self"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("self", "person", "person"); + break; + case "testAttachableCreateMethod": + case "shouldAttachWithCreateMethod": + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withVertexCollection("project"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("developedBy"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("developedBy", "project", "person"); + break; + case "shouldCopyFromGraphAToGraphB": + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withVertexCollection("software"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + break; + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java new file mode 100644 index 0000000..60bba07 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -0,0 +1,26 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.OrderabilityTest; +import com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map.MergeEdgeTest; +import com.arangodb.tinkerpop.gremlin.custom.structure.util.detached.DetachedGraphTest; +import com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest; +import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + + +public class CustomStandardSuite extends AbstractGremlinSuite { + + private static final Class[] allTests = new Class[]{ + MergeEdgeTest.Traversals.class, + OrderabilityTest.Traversals.class, + DetachedGraphTest.class, + StarGraphTest.class + }; + + public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { + super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java new file mode 100644 index 0000000..d810b70 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java @@ -0,0 +1,10 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.junit.runner.RunWith; + +@RunWith(CustomStandardSuite.class) +@GraphProviderClass(provider = CustomGraphProvider.class, graph = CustomTestGraph.class) +public class CustomStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java new file mode 100644 index 0000000..56e4df5 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java @@ -0,0 +1,54 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.structure.Graph; + +@Graph.OptIn("com.arangodb.tinkerpop.gremlin.custom.CustomStandardSuite") +public class CustomTestGraph extends ArangoDBGraph { + + @SuppressWarnings("unused") + public static CustomTestGraph open(Configuration configuration) { + return new CustomTestGraph(configuration); + } + + public CustomTestGraph(Configuration configuration) { + super(configuration); + } + + @Override + public Features features() { + return new ArangoDBGraph.ArangoDBGraphFeatures() { + + @Override + public Features.EdgeFeatures edge() { + return new ArangoDBGraphFeatures.ArangoDBGraphEdgeFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + + @Override + public Features.VertexFeatures vertex() { + return new ArangoDBGraphFeatures.ArangoDBGraphVertexFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + + @Override + public Features.VertexPropertyFeatures properties() { + return new ArangoDBGraphFeatures.ArangoDBGraphVertexPropertyFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + }; + } + }; + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java new file mode 100644 index 0000000..384c6c1 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java @@ -0,0 +1,509 @@ +package com.arangodb.tinkerpop.gremlin.custom.process.traversal.step; + + +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; +import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; +import org.apache.tinkerpop.gremlin.process.traversal.Order; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.util.CollectionUtil; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.EdgeFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.GraphFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexPropertyFeatures; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +@RunWith(GremlinProcessRunner.class) +public abstract class OrderabilityTest extends AbstractGremlinProcessTest { + + private interface Constants { + UUID uuid = UUID.randomUUID(); + Date date = new Date(); + List list1 = Arrays.asList(1, 2, 3); + List list2 = Arrays.asList(1, 2, 3, 4); + Set set1 = CollectionUtil.asSet(list1); + Set set2 = CollectionUtil.asSet(list2); + Map map1 = CollectionUtil.asMap(1, 11, 2, 22, 3, false, 4, 44); + Map map2 = CollectionUtil.asMap(1, 11, 2, 22, 33); + + Object[] unordered = { map2, 1, map1, "foo", null, list1, date, set1, list2, true, uuid, "bar", 2.0, false, set2 }; + } + + public abstract Traversal get_g_V_values_order(); + + public abstract Traversal get_g_V_properties_order(); + + public abstract Traversal get_g_E_properties_order_value(); + + public abstract Traversal get_g_E_properties_order_byXdescX_value(); + + public abstract Traversal get_g_inject_order(); + + // order asc by vertex: v[3], v[5] + public abstract Traversal get_g_V_out_out_order_byXascX(); + + // order asc by vertex in path: v[3], v[5] + public abstract Traversal get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX(); + + // order asc by edge: e[10], v[e11] + public abstract Traversal get_g_V_out_outE_order_byXascX(); + + // order asc by edge in path: e[10], e[11] + public abstract Traversal get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX(); + + // order asc by vertex and then vertex property id in path. + public abstract Traversal get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value(); + + // order asc by vertex and then vertex property value in path. + public abstract Traversal get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX(); + + // order desc by vertex: v[3], v[5] + public abstract Traversal get_g_V_out_out_order_byXdescX(); + + // order desc by vertex in path: v[3], v[5] + public abstract Traversal get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX(); + + // order desc by edge: e[10], v[e11] + public abstract Traversal get_g_V_out_outE_order_byXdescX(); + + // order desc by edge in path: e[10], e[11] + public abstract Traversal get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX(); + + // order desc by vertex and then vertex property id in path. + public abstract Traversal get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value(); + + // order desc by vertex and then vertex property value in path. + public abstract Traversal get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX(); + + /** + * Order by property value (mixed types). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_V_values_order() { + final Traversal traversal = get_g_V_values_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + 27, 29, 32, 35, "java", "java", "josh", "lop", "marko", "peter", "ripple", "vadas" + ), traversal); + } + + /** + * Order by vertex property (orders by id). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_properties_order() { + final Traversal traversal = get_g_V_properties_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertexProperty("marko", "name", "marko"), // vpid = 0 + convertToVertexProperty("marko", "age", 29), // vpid = 1 + convertToVertexProperty("peter", "name", "peter"), // vpid = 10 + convertToVertexProperty("peter", "age", 35), // vpid = 11 + convertToVertexProperty("vadas", "name", "vadas"), // vpid = 2 + convertToVertexProperty("vadas", "age", 27), // vpid = 3 + convertToVertexProperty("lop", "name", "lop"), // vpid = 4 + convertToVertexProperty("lop", "lang", "java"), // vpid = 5 + convertToVertexProperty("josh", "name", "josh"), // vpid = 6 + convertToVertexProperty("josh", "age", 32), // vpid = 7 + convertToVertexProperty("ripple", "name", "ripple"), // vpid = 8 + convertToVertexProperty("ripple", "lang", "java") // vpid = 9 + ), traversal); + } + + /** + * Order by edge property (orders by key, then value). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_ADD_PROPERTY) + public void g_E_properties_order_value() { + { // add some more edge properties + final AtomicInteger a = new AtomicInteger(); + g.E().forEachRemaining(e -> e.property("a", a.getAndIncrement())); + } + + final Traversal asc = get_g_E_properties_order_value(); + printTraversalForm(asc); + checkOrderedResults(Arrays.asList( + 0, 1, 2, 3, 4, 5, 0.2, 0.4, 0.4, 0.5, 1.0, 1.0 + ), asc); + + final Traversal desc = get_g_E_properties_order_byXdescX_value(); + printTraversalForm(desc); + checkOrderedResults(Arrays.asList( + 1.0, 1.0, 0.5, 0.4, 0.4, 0.2, 5, 4, 3, 2, 1, 0 + ), desc); + } + + /** + * Mixed type values including list, set, map, uuid, date, boolean, numeric, string, null. + */ + @Test + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_inject_order() { + final Traversal traversal = get_g_inject_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + null, + false, true, + 1, 2.0, + OrderabilityTest.Constants.date, + "bar", "foo", + OrderabilityTest.Constants.uuid, + OrderabilityTest.Constants.set1, OrderabilityTest.Constants.set2, + OrderabilityTest.Constants.list1, OrderabilityTest.Constants.list2, + OrderabilityTest.Constants.map1, OrderabilityTest.Constants.map2 + ), traversal); + } + + /** + * More mixed type values including a Java Object (unknown type). + */ + @Test + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_inject_order_with_unknown_type() { + final Object unknown = new Object(); + final Object[] unordered = new Object[OrderabilityTest.Constants.unordered.length+1]; + unordered[0] = unknown; + System.arraycopy(OrderabilityTest.Constants.unordered, 0, unordered, 1, OrderabilityTest.Constants.unordered.length); + + final Traversal traversal = g.inject(unordered).order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + null, + false, true, + 1, 2.0, + OrderabilityTest.Constants.date, + "bar", "foo", + OrderabilityTest.Constants.uuid, + OrderabilityTest.Constants.set1, OrderabilityTest.Constants.set2, + OrderabilityTest.Constants.list1, OrderabilityTest.Constants.list2, + OrderabilityTest.Constants.map1, OrderabilityTest.Constants.map2, + unknown + ), traversal); + } + + /** + * Order asc by vertex: v[3], v[5] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_order_byXascX() { + final Traversal traversal = get_g_V_out_out_order_byXascX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("lop"), // vid = 3 + convertToVertex("ripple") // vid = 5 + ), traversal); + } + + /** + * Order asc by vertex in path: v[3], v[5] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("lop"), // vid = 3 + convertToVertex("ripple") // vid = 5 + ), traversal); + } + + /** + * Order asc by edge: e[10], v[e11] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_order_byXascX() { + final Traversal traversal = get_g_V_out_outE_order_byXascX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "ripple"), // eid = 10 + convertToEdge("josh", "created", "lop") // eid = 11 + ), traversal); + } + + /** + * Order asc by edge in path: e[10], e[11] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "ripple"), // eid = 10 + convertToEdge("josh", "created", "lop") // eid = 11 + ), traversal); + } + + /** + * Order asc by vertex and then vertex property id in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value() { + final Traversal traversal = get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "lop", // vid = 3, vpid = 4 + "java", // vid = 3, vpid = 5 + "ripple", // vid = 5, vpid = 8 + "java" // vid = 5, vpid = 9 + ), traversal); + } + + /** + * Order asc by vertex and then vertex property value in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "java", // vid = 3, val = "java" + "lop", // vid = 3, val = "lop" + "java", // vid = 5, val = "java" + "ripple" // vid = 5, val = "ripple" + ), traversal); + } + + /** + * Order desc by vertex: v[5], v[3] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_order_byXdescX() { + final Traversal traversal = get_g_V_out_out_order_byXdescX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("ripple"), // vid = 5 + convertToVertex("lop") // vid = 3 + ), traversal); + } + + /** + * Order desc by vertex in path: v[5], v[3] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("ripple"), // vid = 5 + convertToVertex("lop") // vid = 3 + ), traversal); + } + + /** + * Order desc by edge: e[11], v[e10] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_order_byXdescX() { + final Traversal traversal = get_g_V_out_outE_order_byXdescX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "lop"), // eid = 11 + convertToEdge("josh", "created", "ripple") // eid = 10 + ), traversal); + } + + /** + * Order desc by edge in path: e[11], e[10] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "lop"), // eid = 11 + convertToEdge("josh", "created", "ripple") // eid = 10 + ), traversal); + } + + /** + * Order desc by vertex and then vertex property id in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value() { + final Traversal traversal = get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "java", // vid = 5, vpid = 9 + "ripple", // vid = 5, vpid = 8 + "java", // vid = 3, vpid = 5 + "lop" // vid = 3, vpid = 4 + ), traversal); + } + + /** + * Order desc by vertex and then vertex property value in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "ripple", // vid = 5, val = "ripple" + "java", // vid = 5, val = "java" + "lop", // vid = 3, val = "lop" + "java" // vid = 3, val = "java" + ), traversal); + } + + public static class Traversals extends OrderabilityTest implements OrderabilityTest.Constants { + + @Override + public Traversal get_g_V_values_order() { + return g.V().values().order(); + } + + @Override + public Traversal get_g_V_properties_order() { + return g.V().properties().order(); + } + + @Override + public Traversal get_g_E_properties_order_value() { + return g.E().properties().order().value(); + } + + @Override + public Traversal get_g_E_properties_order_byXdescX_value() { + return g.E().properties().order().by(Order.desc).value(); + } + + @Override + public Traversal get_g_inject_order() { + return g.inject(unordered).order(); + } + + // order asc by vertex: v[3], v[5] + @Override + public Traversal get_g_V_out_out_order_byXascX() { + return g.V().out().out().order().by(Order.asc); + } + + // order asc by vertex in path: v[3], v[5] + @Override + public Traversal get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().out().as("head").path().order().by(Order.asc).select("head"); + } + + // order asc by edge: e[10], v[e11] + @Override + public Traversal get_g_V_out_outE_order_byXascX() { + return g.V().out().outE().order().by(Order.asc); + } + + // order asc by edge in path: e[10], e[11] + @Override + public Traversal get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().outE().as("head").path().order().by(Order.asc).select("head"); + } + + // order asc by vertex and then vertex property id in path. + @Override + public Traversal get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value() { + return g.V().out().out().properties().as("head").path().order().by(Order.asc).select("head").value(); + } + + // order asc by vertex and then vertex property value in path. + @Override + public Traversal get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().out().values().as("head").path().order().by(Order.asc).select("head"); + } + + // order desc by vertex: v[5], v[3] + @Override + public Traversal get_g_V_out_out_order_byXdescX() { + return g.V().out().out().order().by(Order.desc); + } + + // order desc by vertex in path: v[5], v[3] + @Override + public Traversal get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().out().as("head").path().order().by(Order.desc).select("head"); + } + + // order desc by edge: e[11], v[e10] + @Override + public Traversal get_g_V_out_outE_order_byXdescX() { + return g.V().out().outE().order().by(Order.desc); + } + + // order desc by edge in path: e[11], e[10] + @Override + public Traversal get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().outE().as("head").path().order().by(Order.desc).select("head"); + } + + // order desc by vertex and then vertex property id in path. + @Override + public Traversal get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value() { + return g.V().out().out().properties().as("head").path().order().by(Order.desc).select("head").value(); + } + + // order desc by vertex and then vertex property value in path. + @Override + public Traversal get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().out().values().as("head").path().order().by(Order.desc).select("head"); + } + + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java new file mode 100644 index 0000000..5e9ee40 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map; + +import java.util.Map; + +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.FeatureRequirementSet; +import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; +import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringEndsWith.endsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +@RunWith(GremlinProcessRunner.class) +public abstract class MergeEdgeTest extends AbstractGremlinTest { + + public abstract Traversal get_g_V_mergeEXlabel_self_weight_05X(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + + public abstract Traversal get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + + public abstract Traversal, Edge> get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE(); + + public abstract Traversal get_g_mergeE_with_outV_inV_options(); + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_V_mergeEXlabel_self_weight_05X() { + g.addV("person").property("name", "stephen").iterate(); + final Traversal traversal = get_g_V_mergeEXlabel_self_weight_05X(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("self", edge.label()); + assertEquals(0.5d, edge.value("weight").doubleValue(), 0.0001d); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX() { + g.addV("person").property(T.id, "100").property("name", "marko"). + addV("person").property(T.id, "101").property("name", "vadas").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("person/100", edge.outVertex().id()); + assertEquals("person/101", edge.inVertex().id()); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists() { + g.addV("person").property(T.id, "100").property("name", "marko").as("a"). + addV("person").property(T.id, "101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("person/100", edge.outVertex().id()); + assertEquals("person/101", edge.inVertex().id()); + assertEquals(0.5d, edge.value("weight").doubleValue(), 0.0001d); + assertFalse(traversal.hasNext()); + assertEquals(2, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX() { + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX(); + printTraversalForm(traversal); + try { + traversal.next(); + fail("Should have failed as vertices are not created"); + } catch (Exception ex) { + assertThat(ex.getMessage(), endsWith("Vertex id could not be resolved from mergeE: person/100")); + } + assertEquals(0, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists() { + g.addV("person").property(T.id, "100").property("name", "marko").as("a"). + addV("person").property(T.id, "101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("person/100", edge.outVertex().id()); + assertEquals("person/101", edge.inVertex().id()); + assertEquals("N", edge.value("created")); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + g.addV("person").property(T.id, "person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "person/101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").property("created","Y").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("person/100", edge.outVertex().id()); + assertEquals("person/101", edge.inVertex().id()); + assertEquals("N", edge.value("created")); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + g.addV("person").property(T.id, "person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "person/101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").property("created","Y"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + printTraversalForm(traversal); + + assertEquals(2, IteratorUtils.count(traversal)); + assertEquals(2, IteratorUtils.count(g.V())); + assertEquals(2, IteratorUtils.count(g.E())); + assertEquals(2, IteratorUtils.count(g.E().hasLabel("knows").has("created", "N"))); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_injectXlabel_knows_out_marko_in_vadasX_mergeE() { + g.addV("person").property(T.id, "person/100").property("name", "marko"). + addV("person").property(T.id, "person/101").property("name", "vadas").iterate(); + final Traversal, Edge> traversal = get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE(); + printTraversalForm(traversal); + + assertEquals(1, IteratorUtils.count(traversal)); + assertEquals(2, IteratorUtils.count(g.V())); + assertEquals(1, IteratorUtils.count(g.E())); + assertEquals(1, IteratorUtils.count(g.V("person/100").out("knows").hasId("person/101"))); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_with_outV_inV_options() { + g.addV("person").property(T.id, "person/1") + .addV("person").property(T.id, "person/2") + .iterate(); + + final Traversal traversal = get_g_mergeE_with_outV_inV_options(); + printTraversalForm(traversal); + + assertEquals(1, IteratorUtils.count(traversal)); + assertEquals(1, IteratorUtils.count(g.V("person/1").out("knows").hasId("person/2"))); + } + + public static class Traversals extends MergeEdgeTest { + + @Override + public Traversal get_g_V_mergeEXlabel_self_weight_05X() { + return g.V().as("v").mergeE(asMap(T.label, "self", "weight", 0.5d, Direction.OUT, Merge.outV, Direction.IN, Merge.inV)).option(Merge.outV, select("v")).option(Merge.inV, select("v")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"))); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"), "weight", 0.5d)); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + return g.V().has("person","name","marko"). + mergeE(asMap(T.label, "knows")). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal, Edge> get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE() { + return g.inject((Map) asMap(T.label, "knows", Direction.IN, new ReferenceVertex("person/101"), Direction.OUT, new ReferenceVertex("person/100"))).mergeE(); + } + + @Override + public Traversal get_g_mergeE_with_outV_inV_options() { + return g.mergeE(asMap(T.label, "knows", Direction.OUT, Merge.outV, Direction.IN, Merge.inV)) + .option(Merge.outV, asMap(T.id, "person/1")) + .option(Merge.inV, asMap(T.id, "person/2")); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java new file mode 100644 index 0000000..2237314 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.custom.structure.util.detached; + +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.TestHelper; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Random; +import java.util.UUID; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class DetachedGraphTest extends AbstractGremlinTest { + + @Ignore("FIXME: numeric vertex properties ids") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + public void testAttachableCreateMethod() { + final Random random = TestHelper.RANDOM; + StarGraph starGraph = StarGraph.open(); + final String label = "person"; + final String id = label + "/" + UUID.randomUUID(); + Vertex starVertex = starGraph.addVertex(T.id, id, T.label, label, "name", "stephen", "name", "spmallete"); + starVertex.property("acl", true, "timestamp", random.nextLong(), "creator", "marko"); + for (int i = 0; i < 100; i++) { + starVertex.addEdge("knows", starGraph.addVertex("person", "name", new UUID(random.nextLong(), random.nextLong()), "since", random.nextLong())); + starGraph.addVertex(T.label, "project").addEdge("developedBy", starVertex, "public", random.nextBoolean()); + } + final DetachedVertex detachedVertex = DetachedFactory.detach(starGraph.getStarVertex(), true); + final Vertex createdVertex = detachedVertex.attach(Attachable.Method.create(graph)); + TestHelper.validateVertexEquality(detachedVertex, createdVertex, false); + TestHelper.validateVertexEquality(detachedVertex, starVertex, false); + + starGraph.getStarVertex().edges(Direction.BOTH).forEachRemaining(starEdge -> { + final DetachedEdge detachedEdge = DetachedFactory.detach(starEdge, true); + final Edge createdEdge = detachedEdge.attach(Attachable.Method.create(random.nextBoolean() ? graph : createdVertex)); + TestHelper.validateEdgeEquality(detachedEdge, starEdge); + TestHelper.validateEdgeEquality(detachedEdge, createdEdge); + }); + + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java new file mode 100644 index 0000000..d9092c5 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.arangodb.tinkerpop.gremlin.custom.structure.util.star; + +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.TestHelper; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class StarGraphTest extends AbstractGremlinTest { + + @Ignore("FIXME") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + public void shouldCopyFromGraphAToGraphB() throws Exception { + final List starGraphs = IteratorUtils.stream(graph.vertices()).map(StarGraph::of).collect(Collectors.toList()); + + // via vertices and then edges + final Configuration g1Configuration = graphProvider.newGraphConfiguration("readGraph", this.getClass(), name.getMethodName(), null); + Graph g1 = graphProvider.openTestGraph(g1Configuration); + starGraphs.stream().map(StarGraph::getStarVertex).forEach(vertex -> vertex.attach(Attachable.Method.getOrCreate(g1))); + starGraphs.stream().forEach(starGraph -> starGraph.edges().forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.getOrCreate(g1)))); + assertEquals(IteratorUtils.count(graph.vertices()), IteratorUtils.count(g1.vertices())); + assertEquals(IteratorUtils.count(graph.edges()), IteratorUtils.count(g1.edges())); + graph.vertices().forEachRemaining(vertex -> TestHelper.validateVertexEquality(vertex, g1.vertices(vertex.id()).next(), true)); + graphProvider.clear(g1, g1Configuration); + + // via edges only + final Configuration g2Configuration = graphProvider.newGraphConfiguration("readGraph", this.getClass(), name.getMethodName(), null); + final Graph g2 = graphProvider.openTestGraph(g2Configuration); + starGraphs.stream().forEach(starGraph -> starGraph.edges().forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.getOrCreate(g2)))); + assertEquals(IteratorUtils.count(graph.vertices()), IteratorUtils.count(g2.vertices())); + assertEquals(IteratorUtils.count(graph.edges()), IteratorUtils.count(g2.edges())); + // TODO: you can't get adjacent labels -- graph.vertices().forEachRemaining(vertex -> TestHelper.validateVertexEquality(vertex, g1.vertices(vertex.id()).next(), true)); + graphProvider.clear(g2, g2Configuration); + } + +// @Ignore("FIXME: DE-996") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + public void shouldAttachWithCreateMethod() { + final Random random = TestHelper.RANDOM; + StarGraph starGraph = StarGraph.open(); + Vertex starVertex = starGraph.addVertex(T.id, "person/" + UUID.randomUUID(), T.label, "person", "name", "stephen", "name", "spmallete"); + starVertex.property("acl", true, "timestamp", random.nextLong(), "creator", "marko"); + for (int i = 0; i < 100; i++) { + starVertex.addEdge( + "knows", + starGraph.addVertex(T.id, "person/" + UUID.randomUUID(), T.label, "person", "name", new UUID(random.nextLong(), random.nextLong()), "since", random.nextLong()), + T.id, "knows/" + UUID.randomUUID() + ); + starGraph + .addVertex(T.id, "project/" + UUID.randomUUID(), T.label, "project") + .addEdge("developedBy", starVertex, T.id, "developedBy/" + UUID.randomUUID(), "public", random.nextBoolean()); + } + final Vertex createdVertex = starGraph.getStarVertex().attach(Attachable.Method.create(graph)); + starGraph.getStarVertex().edges(Direction.BOTH).forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.create(random.nextBoolean() ? graph : createdVertex))); + TestHelper.validateEquality(starVertex, createdVertex); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java new file mode 100644 index 0000000..1562cd1 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java @@ -0,0 +1,112 @@ +package com.arangodb.tinkerpop.gremlin.process; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; + +public class ProcessGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, TestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + switch (testMethodName) { + case "g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX": + builder.withEdgeCollection("next"); + break; + case "shouldGenerateDefaultIdOnAddEWithSpecifiedId": + case "shouldSetIdOnAddEWithNamePropertyKeySpecifiedAndNameSuppliedAsProperty": + case "shouldSetIdOnAddEWithIdPropertyKeySpecifiedAndNameSuppliedAsProperty": + case "shouldGenerateDefaultIdOnAddEWithGeneratedId": + case "shouldTriggerAddEdgePropertyAdded": + case "shouldReferencePropertyOfEdgeWhenRemoved": + case "shouldTriggerAddEdge": + case "shouldTriggerRemoveEdge": + case "shouldTriggerRemoveEdgeProperty": + case "shouldReferenceEdgeWhenRemoved": + case "shouldUseActualEdgeWhenAdded": + case "shouldDetachEdgeWhenAdded": + case "shouldUseActualEdgeWhenRemoved": + case "shouldDetachPropertyOfEdgeWhenNew": + case "shouldDetachPropertyOfEdgeWhenChanged": + case "shouldUseActualPropertyOfEdgeWhenChanged": + case "shouldDetachEdgeWhenRemoved": + case "shouldTriggerUpdateEdgePropertyAddedViaMergeE": + case "shouldDetachPropertyOfEdgeWhenRemoved": + case "shouldUseActualPropertyOfEdgeWhenRemoved": + case "shouldUseActualPropertyOfEdgeWhenNew": + case "shouldTriggerEdgePropertyChanged": + case "shouldTriggerAddEdgeViaMergeE": + case "shouldReferencePropertyOfEdgeWhenNew": + case "shouldReferenceEdgeWhenAdded": + case "shouldReferencePropertyOfEdgeWhenChanged": + case "shouldTriggerAddEdgeByPath": + case "shouldWriteToMultiplePartitions": + case "shouldAppendPartitionToEdge": + case "shouldThrowExceptionOnEInDifferentPartition": + builder.withEdgeCollection("self"); + builder.withEdgeCollection("self-but-different"); + builder.withEdgeCollection("aTOa"); + builder.withEdgeCollection("aTOb"); + builder.withEdgeCollection("aTOc"); + builder.withEdgeCollection("bTOc"); + builder.withEdgeCollection("connectsTo"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("relatesTo"); + break; + case "g_io_read_withXreader_graphsonX": + case "g_io_read_withXreader_gryoX": + case "g_io_read_withXreader_graphmlX": + case "g_io_readXjsonX": + case "g_io_readXkryoX": + case "g_io_readXxmlX": + builder.withVertexCollection("person"); + builder.withVertexCollection("software"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + break; + case "g_addV_propertyXlabel_personX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists": + case "g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadasX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists": + case "g_V_mergeEXlabel_self_weight_05X": + case "g_mergeE_with_outV_inV_options": + case "g_injectXlabel_knows_out_marko_in_vadasX_mergeE": + builder.withVertexCollection("person"); + builder.withEdgeCollection("self"); + break; + case "g_V_hasXname_regexXTinkerXX": + case "g_V_hasXname_regexXTinkerUnicodeXX": + builder.withVertexCollection("software"); + break; + case "shouldDetachVertexWhenAdded": + case "shouldReferenceVertexWhenAdded": + case "shouldUseActualVertexWhenAdded": + builder.withVertexCollection("thing"); + break; + case "shouldAppendPartitionToAllVertexProperties": + builder.withVertexCollection("person"); + builder.withVertexCollection("vertex"); + builder.configureEdge("edge", "person", "person"); + break; + case "shouldPartitionWithAbstractLambdaChildTraversal": + builder.withVertexCollection("testV"); + builder.withEdgeCollection("self"); + break; + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java new file mode 100644 index 0000000..19f8451 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java @@ -0,0 +1,12 @@ +package com.arangodb.tinkerpop.gremlin.process; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.apache.tinkerpop.gremlin.process.ProcessStandardSuite; +import org.junit.runner.RunWith; + +@RunWith(ProcessStandardSuite.class) +@GraphProviderClass(provider = ProcessGraphProvider.class, graph = TestGraph.class) +public class ProcessStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java deleted file mode 100644 index 0bf0dca..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import org.apache.tinkerpop.gremlin.AbstractGremlinTest; - -public class ArangoDBGremlinTest extends AbstractGremlinTest { - - protected ArangoDBGraph getGraph() { - return (ArangoDBGraph) this.graph; - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java new file mode 100644 index 0000000..889c769 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -0,0 +1,177 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; + +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.VertexTest; + +import java.util.Map; + +public class StructureGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, TestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + if (testMethodName.startsWith("shouldProcessVerticesEdges") + || testMethodName.startsWith("shouldGenerate") + || testMethodName.startsWith("shouldSetValueOnEdge") + || testMethodName.startsWith("shouldAutotype")) { + builder.withEdgeCollection("knows"); + } else if (testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldSupportUserSuppliedIds")) { + builder.withEdgeCollection("test"); + } else if (testMethodName.startsWith("shouldSupportUUID")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldReadWriteVerticesNoEdgesToGryoManual") || + testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges") || + testMethodName.startsWith("shouldReadWriteVerticesNoEdgesToGraphSONManual") || + testMethodName.startsWith("shouldReadWriteVerticesNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexWithINEdges") || + testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges") || + testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges") || + testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("friends"); + builder.configureEdge("friends", "person", "person"); + } else { + // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? + switch (testMethodName) { + case "shouldGetPropertyKeysOnEdge": + case "shouldNotGetConcurrentModificationException": + builder.withEdgeCollection("friend"); + builder.withEdgeCollection("knows"); + break; + case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": + case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": + builder.withEdgeCollection("hate"); + builder.withEdgeCollection("friend"); + break; + case "shouldPersistDataOnClose": + builder.withEdgeCollection("collaborator"); + break; + case "shouldTestTreeConnectivity": + builder.withEdgeCollection("test1"); + builder.withEdgeCollection("test2"); + builder.withEdgeCollection("test3"); + break; + case "shouldEvaluateConnectivityPatterns": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("hates"); + break; + case "shouldRemoveEdgesWithoutConcurrentModificationException": + builder.withEdgeCollection("link"); + break; + case "shouldGetValueThatIsNotPresentOnEdge": + case "shouldHaveStandardStringRepresentationForEdgeProperty": + case "shouldHaveTruncatedStringRepresentationForEdgeProperty": + case "shouldValidateIdEquality": + case "shouldValidateEquality": + case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": + case "shouldAddEdgeWithUserSuppliedStringId": + case "shouldAllowNullAddEdge": + builder.withEdgeCollection("self"); + break; + case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": + case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": + case "shouldProcessEdges": + case "shouldReturnOutThenInOnVertexIterator": + case "shouldReturnEmptyIteratorIfNoProperties": + builder.withEdgeCollection("knows"); + break; + case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("pets"); + builder.withEdgeCollection("walks"); + builder.withEdgeCollection("livesWith"); + break; + case "shouldHaveStandardStringRepresentation": + builder.withEdgeCollection("friends"); + break; + case "shouldReadWriteSelfLoopingEdges": + builder.withEdgeCollection("CONTROL"); + builder.withEdgeCollection("SELFLOOP"); + break; + case "shouldReadGraphML": + case "shouldReadGraphMLUnorderedElements": + case "shouldTransformGraphMLV2ToV3ViaXSLT": + case "shouldReadLegacyGraphSON": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + break; + case "shouldAddVertexWithLabel": + case "shouldAllowNullAddVertexProperty": + builder.withVertexCollection("person"); + break; + case "shouldNotAllowSetProperty": + case "shouldHashAndEqualCorrectly": + case "shouldNotAllowRemove": + case "shouldNotConstructNewWithSomethingAlreadyDetached": + case "shouldNotConstructNewWithSomethingAlreadyReferenced": + builder.withEdgeCollection("test"); + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertex": + builder.withEdgeCollection("tonothing"); + break; + case "shouldHandleSelfLoops": + builder.withVertexCollection("person"); + builder.withEdgeCollection("self"); + break; + case "testAttachableCreateMethod": + builder.withVertexCollection("person"); + builder.withVertexCollection("project"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("developedBy"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("developedBy", "project", "person"); + break; + case "shouldConstructReferenceVertex": + builder.withVertexCollection("blah"); + break; + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": + if (VertexTest.class.equals(test.getEnclosingClass())) { + builder.withVertexCollection("foo"); + } + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": + builder.withVertexCollection("foo"); + break; + case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": + builder.withEdgeCollection("created"); + builder.withEdgeCollection("knows"); + break; + } + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java new file mode 100644 index 0000000..5a330be --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java @@ -0,0 +1,12 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite; +import org.junit.runner.RunWith; + +@RunWith(StructureStandardSuite.class) +@GraphProviderClass(provider = StructureGraphProvider.class, graph = TestGraph.class) +public class StructureStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf b/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf deleted file mode 100644 index 30c495d..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf +++ /dev/null @@ -1,11 +0,0 @@ -gremlin.graph = com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph -gremlin.arangodb.conf.graph.db = Test02 -gremlin.arangodb.conf.graph.name = Test02Graph01 -gremlin.arangodb.conf.graph.vertex = testfrom -gremlin.arangodb.conf.graph.vertex = testto -gremlin.arangodb.conf.graph.edge = testedge -gremlin.arangodb.conf.graph.relation = testedge:testfrom->testto -gremlin.arangodb.conf.graph.shouldPrefixCollectionNames = false -gremlin.arangodb.conf.arangodb.hosts = 127.0.0.1:8529 -gremlin.arangodb.conf.arangodb.user = xxxx -gremlin.arangodb.conf.arangodb.password = xxxx \ No newline at end of file diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java new file mode 100644 index 0000000..086a8d7 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -0,0 +1,156 @@ +package com.arangodb.tinkerpop.gremlin.util; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.custom.CustomTestGraph; +import com.arangodb.tinkerpop.gremlin.structure.*; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.ConfigurationConverter; +import org.apache.tinkerpop.gremlin.AbstractGraphProvider; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class BaseGraphProvider extends AbstractGraphProvider { + + private final String dbName = getClass().getSimpleName(); + + protected abstract void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName); + + @Override + public Configuration newGraphConfiguration(final String graphName, final Class test, + final String testMethodName, + final Map configurationOverrides, + final LoadGraphWith.GraphData loadGraphWith) { + return getConfiguration(graphName, test, testMethodName, loadGraphWith); + } + + private Configuration getConfiguration( + String graphName, + Class test, + String testMethodName, + LoadGraphWith.GraphData loadGraphWith) { + System.out.println("case \"" + test.getCanonicalName() + "." + testMethodName + "\":"); + ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() + .arangoHosts("127.0.0.1:8529") + .arangoUser("root") + .arangoPassword("test") + .dataBase(dbName) + .graph(graphName); + if (loadGraphWith != null) { + switch (loadGraphWith) { + case CLASSIC: + System.out.println("CLASSIC"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "vertex", "vertex"); + builder.configureEdge("created", "vertex", "vertex"); + break; + case MODERN: + System.out.println("MODERN"); + builder.withVertexCollection("name"); + builder.withVertexCollection("vertex"); + builder.withVertexCollection("animal"); + builder.withVertexCollection("dog"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.withEdgeCollection("createdBy"); + builder.withEdgeCollection("existsWith"); + builder.withEdgeCollection("codeveloper"); + builder.withEdgeCollection("uses"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + builder.configureEdge("createdBy", "software", "person"); + builder.configureEdge("existsWith", "software", "software"); + builder.configureEdge("codeveloper", "person", "person"); + builder.configureEdge("uses", "person", "software"); + break; + case CREW: + System.out.println("CREW"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("uses"); + builder.withEdgeCollection("develops"); + builder.withEdgeCollection("traverses"); + builder.configureEdge("uses", "person", "software"); + builder.configureEdge("develops", "person", "software"); + builder.configureEdge("traverses", "software", "software"); + break; + case GRATEFUL: + System.out.println("GRATEFUL"); + builder.withVertexCollection("vertex"); + builder.withVertexCollection("song"); + builder.withVertexCollection("artist"); + builder.withEdgeCollection("followedBy"); + builder.withEdgeCollection("sungBy"); + builder.withEdgeCollection("writtenBy"); + builder.configureEdge("followedBy", "vertex", "vertex"); + builder.configureEdge("sungBy", "song", "artist"); + builder.configureEdge("writtenBy", "song", "artist"); + break; + case SINK: + System.out.println("SINK"); + builder.withVertexCollection("loops"); + builder.withVertexCollection("message"); + builder.withEdgeCollection("link"); + builder.withEdgeCollection("self"); + builder.configureEdge("self", "loops", "loops"); + builder.configureEdge("link", "message", "message"); + break; + } + } else { + configure(builder, test, testMethodName); + } + return builder.build(); + } + + @Override + public void clear(Graph graph, Configuration configuration) throws Exception { + Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + TestGraphClient client = new TestGraphClient(arangoProperties, dbName); + client.clear(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); + if (graph != null) { + graph.close(); + } + } + + @Override + @SuppressWarnings("rawtypes") + public Set getImplementations() { + return Stream.of( + ArangoDBEdge.class, + ArangoDBElement.class, + TestGraph.class, + CustomTestGraph.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + ArangoDBPersistentElement.class, + ArangoDBProperty.class, + ArangoDBSimpleElement.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class + ).collect(Collectors.toSet()); + } + + @Override + public Map getBaseConfiguration(String graphName, Class test, String testMethodName, + LoadGraphWith.GraphData loadGraphWith) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object convertId(Object id, Class c) { + return id.toString(); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java new file mode 100644 index 0000000..746b43b --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java @@ -0,0 +1,38 @@ +package com.arangodb.tinkerpop.gremlin.util; + +import com.arangodb.ArangoDBException; +import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; +import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; + +import java.util.Properties; + +public class TestGraphClient extends ArangoDBGraphClient { + + public TestGraphClient(Properties properties, String dbname) throws ArangoDBGraphException { + super(null, properties, dbname); + if (!db.exists()) { + if (!db.create()) { + throw new ArangoDBGraphException("Unable to crate the database " + dbname); + } + } + } + + public void clear(String name) { + try { + db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); + } catch (ArangoDBException e) { + if (e.getErrorNum() != 1202 // document not found + && e.getErrorNum() != 1203 // collection not found + ) throw e; + } + + try { + db.graph(name).drop(true); + } catch (ArangoDBException e) { + if (e.getErrorNum() != 1924) // graph not found + throw e; + } + } + +} diff --git a/src/test/resources/arangodb.properties b/src/test/resources/arangodb.properties deleted file mode 100644 index 56c9a88..0000000 --- a/src/test/resources/arangodb.properties +++ /dev/null @@ -1,3 +0,0 @@ -port=8529 -host=localhost -enableCURLLogger=false diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 9e037b2..717bd79 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -25,7 +25,6 @@ - - + diff --git a/tests/travis/setup_arangodb.sh b/tests/travis/setup_arangodb.sh deleted file mode 100644 index c02f450..0000000 --- a/tests/travis/setup_arangodb.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd $DIR - -VERSION=2.8.11 -NAME=ArangoDB-$VERSION - -if [ ! -d "$DIR/$NAME" ]; then - # download ArangoDB - echo "wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz" - wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz - echo "tar zxf $NAME.tar.gz" - tar zvxf $NAME.tar.gz -fi - -ARCH=$(arch) -PID=$(echo $PPID) -TMP_DIR="/tmp/arangodb.$PID" -PID_FILE="/tmp/arangodb.$PID.pid" -ARANGODB_DIR="$DIR/$NAME" -ARANGOD="${ARANGODB_DIR}/bin/arangod_x86_64" - -# create database directory -mkdir ${TMP_DIR} - -echo "Starting ArangoDB '${ARANGOD}'" - -${ARANGOD} \ - --database.directory ${TMP_DIR} \ - --configuration none \ - --server.endpoint tcp://127.0.0.1:8529 \ - --javascript.app-path ${ARANGODB_DIR}/js/apps \ - --javascript.startup-directory ${ARANGODB_DIR}/js \ - --server.disable-authentication true & - -sleep 2 - -echo "Check for arangod process" -process=$(ps auxww | grep "bin/arangod" | grep -v grep) - -if [ "x$process" == "x" ]; then - echo "no 'arangod' process found" - echo "ARCH = $ARCH" - exit 1 -fi - -echo "Waiting until ArangoDB is ready on port 8529" -while [[ -z `curl -uroot: -s 'http://127.0.0.1:8529/_api/version' ` ]] ; do - echo -n "." - sleep 2s -done - -echo "ArangoDB is up"