From 42a6da22f8d1a36a6722d07e0182905d9004d6bb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 31 Jan 2025 09:40:34 +0100 Subject: [PATCH 01/28] embedded edge properties --- docker/jwtHeader | 1 + docker/jwtSecret | 1 + docker/server.pem | 60 ++++ pom.xml | 4 +- .../gremlin/client/ArangoDBGraphClient.java | 185 +++++------ .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 3 +- .../gremlin/structure/ArangoDBEdge.java | 293 ++++++++++-------- .../structure/ArangoDBEdgeDocument.java | 171 ++++++++++ .../structure/ArangoDBEdgeProperty.java | 57 ---- .../gremlin/structure/ArangoDBGraph.java | 107 +++---- .../gremlin/structure/ArangoDBProperty.java | 83 +++++ .../gremlin/structure/ArangoDBVertex.java | 60 ++-- .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 114 ++++--- .../gremlin/ArangoDBGraphProvider.java | 2 +- src/test/resources/logback-test.xml | 1 - 15 files changed, 706 insertions(+), 436 deletions(-) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeProperty.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java 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/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/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index 60771e9..5173855 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -22,6 +22,8 @@ import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; +import com.arangodb.model.*; +import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Graph; import org.slf4j.Logger; @@ -33,13 +35,7 @@ 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; /** @@ -380,36 +376,6 @@ public void updateDocument(ArangoDBBaseDocument document) { 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()); - 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) @@ -437,46 +403,6 @@ public void insertEdge(ArangoDBBaseEdge edge) { 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 - */ - - 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()); - throw ArangoDBExceptions.getArangoDBException(e); - } - logger.info("Edge updated, new rev {}", edgeEntity.getRev()); - edge._rev(edgeEntity.getRev()); - } public ArangoDBGraphVariables getGraphVariables() { logger.debug("Get graph variables"); @@ -569,7 +495,7 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { } /** - * Create a query to get all the edges of a vertex. + * 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 @@ -578,27 +504,27 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { * @throws ArangoDBException if there is an error executing the query */ - public ArangoCursor getVertexEdges( - ArangoDBVertex vertex, - List edgeLabels, - Direction direction) - throws ArangoDBException { + 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"); - + 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); + return executeAqlQuery(query, bindVars, null, ArangoDBEdgeDocument.class); } - + /** * Get all neighbours of a document. * @@ -708,13 +634,9 @@ public ArangoCursor getGraphVertices( * 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) { + public ArangoCursor getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); @@ -725,21 +647,15 @@ public ArangoCursor getGraphEdges( } else { queryBuilder.iterateCollection("e", prefixedColNames.get(0), bindVars); } - } - else { - if (!collections.isEmpty()) { - prefixedColNames = collections.stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - } - queryBuilder.with(prefixedColNames, bindVars) - .documentsById(ids, "e", bindVars); - + } else { + 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); + return executeAqlQuery(query, bindVars, null, ArangoDBEdgeDocument.class); } - + /** * Gets the edge vertices. * @@ -1182,4 +1098,63 @@ private void insertDocument(ArangoDBBaseDocument document, String colName) { // } // } + public void insertEdge(ArangoDBEdgeDocument edge) { + logger.debug("Insert edge {} in {} ", edge, graph.name()); + EdgeEntity insertEntity; + String collection = graph.getPrefixedCollectioName(edge.getLabel()); + try { + insertEntity = db.graph(graph.name()) + .edgeCollection(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(collection + "/" + edge.getKey()); + } + throw arangoDBException; + } + edge.setKey(insertEntity.getKey()); + edge.setRev(insertEntity.getRev()); + } + +// public TinkerEdgeDocument getEdge(String key, String collection) { +// logger.debug("Get edge {} from {}:{}", key, graph.name(), graph.getPrefixedCollectioName(collection)); +// try { +// return db.graph(graph.name()) +// .edgeCollection(graph.getPrefixedCollectioName(collection)) +// .getEdge(key, TinkerEdgeDocument.class); +// } catch (ArangoDBException e) { +// logger.error("Failed to retrieve edge: {}", e.getErrorMessage()); +// throw ArangoDBExceptions.getArangoDBException(e); +// } +// } + + public void deleteEdge(ArangoDBEdgeDocument edge) { + logger.debug("Delete edge {} in {}", edge, graph.name()); + try { + db.graph(graph.name()) + .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) + .deleteEdge(edge.getKey()); + } catch (ArangoDBException e) { + logger.error("Failed to delete vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + + public void updateEdge(ArangoDBEdgeDocument edge) { + logger.debug("Update edge {} in {}", edge, graph.name()); + EdgeUpdateEntity updateEntity; + try { + updateEntity = db.graph(graph.name()) + .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) + .replaceEdge(edge.getKey(), edge); + } catch (ArangoDBException e) { + logger.error("Failed to update vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + logger.info("Edge updated, new rev {}", updateEntity.getRev()); + edge.setKey(updateEntity.getKey()); + edge.setRev(updateEntity.getRev()); + } } \ No newline at end of file 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..0f23feb 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -40,7 +40,8 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { ArangoDBPropertyIterator.class, ArangoDBQueryBuilder.class, ArangoDBEdge.class, - ArangoDBEdgeProperty.class, + ArangoDBEdgeDocument.class, + ArangoDBProperty.class, ArangoDBElementProperty.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, 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..72b53e9 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -1,160 +1,187 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// 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.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 com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; +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.utils.ArangoDBUtil; +import java.util.*; +import java.util.stream.Collectors; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; -/** - * The ArangoDB Edge 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) - */ -public class ArangoDBEdge extends ArangoDBBaseEdge implements Edge { - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBEdge.class); - - /** - * Constructor used for ArabgoDB JavaBeans de-/serialisation. - */ - - public ArangoDBEdge() { super(); } - - /** - * 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); - } - - /** - * 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 ArangoDBEdge( - ArangoDBGraph graph, - String label, - ArangoDBVertex from, - ArangoDBVertex to) { - this(null, label, from, to, graph); - } +public class ArangoDBEdge implements Edge { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBEdge.class); + + private final ArangoDBGraph graph; + private final ArangoDBEdgeDocument data; + private boolean removed; + + public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeDocument data) { + this.graph = graph; + this.data = data; + this.removed = false; + } + + public ArangoDBEdge(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { + this.graph = graph; + String inferredLabel, key; + if (id != null) { + int separator = id.indexOf('/'); + if (separator > 0) { + inferredLabel = id.substring(0, separator); + key = id.substring(separator + 1); + } else { + inferredLabel = label != null ? label : DEFAULT_LABEL; + key = id; + } + } else { + inferredLabel = label != null ? label : DEFAULT_LABEL; + key = null; + } + + if (inferredLabel.isEmpty()) { + throw new IllegalArgumentException("empty label"); + } + + if (key != null && key.isEmpty()) { + throw new IllegalArgumentException("empty key"); + } + + data = new ArangoDBEdgeDocument(inferredLabel, key, outVertexId, inVertexId); + removed = false; + } @Override - public Object id() { - return _id(); + public String id() { + String key = data.getKey(); + if (key == null) { + return null; + } + return graph.getPrefixedCollectioName(label()) + "/" + key; } @Override public String label() { - return label; + return data.getLabel(); } + @Override + public ArangoDBGraph graph() { + return graph; + } + + public void insert() { + graph.getClient().insertEdge(data); + } + + public void update() { + graph.getClient().updateEdge(data); + } - @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); + public void removeProperty(String key) { + if (removed) throw elementAlreadyRemoved(Edge.class, id()); + if (data.hasProperty(key)) { + data.removeProperty(key); + update(); } - 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); + } + + @Override + @SuppressWarnings("unchecked") + public Iterator> properties(final String... propertyKeys) { + return data.getProperties() + .entrySet() + .stream() + .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) + .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue().getValue())) + .collect(Collectors.toList()).iterator(); + } + + @Override + public Property property(final String key, final V value) { + if (removed) throw elementAlreadyRemoved(Edge.class, id()); + LOGGER.info("set property {} = {}", key, value); + ElementHelper.validateProperty(key, value); + data.setProperty(key, value); + update(); + return new ArangoDBProperty<>(this, key, value); + } + + @SuppressWarnings("unchecked") + @Override + public Property property(final String key) { + if (data.hasProperty(key)) { + Object value = data.getProperty(key); + return new ArangoDBProperty<>(this, key, (V) value); } - ArangoCursor documentNeighbors = graph.getClient().getElementProperties(this, labels, filter, ArangoDBEdgeProperty.class); - return new ArangoDBPropertyIterator<>(graph, (ArangoCursor>) documentNeighbors); - } + return Property.empty(); + } + + @Override + public Set keys() { + return data.getProperties().keySet(); + } - @Override + @Override + public void remove() { + LOGGER.info("removing {} from graph {}.", id(), graph.name()); + graph.getClient().deleteEdge(data); + this.removed = true; + } + + @Override + public Iterator values(String... propertyKeys) { + return Edge.super.values(propertyKeys); + } + + @Override public String toString() { - return StringFactory.edgeString(this); + return StringFactory.edgeString(this); + } + + @Override + public Iterator vertices(Direction direction) { + if (removed) return Collections.emptyIterator(); + List ids = new ArrayList<>(); + switch (direction) { + case BOTH: + ids.add(data.getFrom()); + ids.add(data.getTo()); + break; + case IN: + ids.add(data.getTo()); + break; + case OUT: + ids.add(data.getFrom()); + break; + } + return new ArangoDBIterator<>(graph, graph.getClient().getGraphVertices(ids, Collections.emptyList())); } - + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(final Object object) { return ElementHelper.areEqual(this, object); diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java new file mode 100644 index 0000000..588e501 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java @@ -0,0 +1,171 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.serde.*; +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.HashMap; +import java.util.Map; +import java.util.Objects; + +public class ArangoDBEdgeDocument { + + @InternalKey + private String key; + + @InternalRev + private String rev; + + @InternalFrom + private String from; + + @InternalTo + private String to; + + private String label; + + private final Map properties = new HashMap<>(); + + public ArangoDBEdgeDocument() { + } + + public ArangoDBEdgeDocument(String label, String key, String from, String to) { + this.label = label; + this.key = key; + this.from = from; + this.to = to; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRev() { + return rev; + } + + public void setRev(String rev) { + this.rev = rev; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Map getProperties() { + return properties; + } + + public boolean hasProperty(String key) { + return properties.containsKey(key); + } + + public void removeProperty(String key) { + properties.remove(key); + } + + public void setProperty(String key, Object value) { + properties.put(key, new TinkerValue(value)); + } + + public Object getProperty(String key) { + return properties.get(key).getValue(); + } + + @Override + public String toString() { + return "ArangoDBEdgeDocument{" + + ", key='" + key + '\'' + + ", rev='" + rev + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", label='" + label + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ArangoDBEdgeDocument that = (ArangoDBEdgeDocument) o; + return Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(from, that.from) && Objects.equals(to, that.to) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(key, rev, from, to, label, properties); + } + + public static class TinkerValue { + private final Object value; + private final String valueType; + + @JsonCreator + public TinkerValue( + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.value = value; + this.valueType = valueType; + } + + public TinkerValue(Object value) { + this.value = value; + valueType = (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 "TinkerValue{" + + "value=" + value + + ", valueType='" + valueType + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + TinkerValue that = (TinkerValue) o; + return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(value, valueType); + } + } + +} 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/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index 20a2b90..46a80a1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -34,6 +34,8 @@ import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.unsupportedIdType; + /** * The ArangoDB graph class. * @@ -141,6 +143,18 @@ @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.util.detached.DetachedGraphTest", + method = "testAttachableCreateMethod", + reason = "test creates id without label prefix") +@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.GraphTest", + method = "shouldAddVertexWithUserSuppliedStringId", + reason = "FIXME") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldRemoveVertices", @@ -150,44 +164,26 @@ 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") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldEvaluateConnectivityPatterns", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", - method = "testAttachableCreateMethod", - reason = "test creates id without label prefix") + test = "org.apache.tinkerpop.gremlin.structure.VertexPropertyTest$VertexPropertyAddition", + method = "shouldAllowIdAssignment", + reason = "FIXME") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", method = "shouldAllowNullAddVertexProperty", - reason = "Cannot distinguish between null and not present properties." + reason = "FIXME" ) @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." + reason = "FIXME" ) -// 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", + test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", + method = "shouldNotEvaluateToEqualDifferentId", reason = "FIXME") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV3Test", @@ -197,14 +193,6 @@ 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", @@ -213,6 +201,14 @@ test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldCopyFromGraphAToGraphB", 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.VertexTest$BasicVertexTest", method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", @@ -736,27 +732,21 @@ public List 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)); + List ids = Arrays.stream(edgeIds) + .map(id -> { + if (id instanceof ArangoDBEdge) { + return ((ArangoDBEdge) id).id(); + } else if(id instanceof String) { + // We only support String ids + return (String) id; + } else { + throw unsupportedIdType(id); + } + }) + .collect(Collectors.toList()); + return getClient().getGraphEdges(ids).stream() + .map(it -> (Edge) new ArangoDBEdge(this, it)) + .iterator(); } @Override @@ -853,6 +843,9 @@ public String getPrefixedCollectioName(String collectionName) { return String.format("%s_%s", name, collectionName); } if(shouldPrefixCollectionNames) { + if(collectionName.startsWith(name + "_")) { + return collectionName; + } return String.format("%s_%s", name, collectionName); }else{ return collectionName; 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..33a7a5e --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.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.structure; + +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; + +public final class ArangoDBProperty implements Property { + + private final String key; + private final V value; + private final Element element; + + public ArangoDBProperty(final Element element, final String key, final V value) { + this.element = element; + this.key = key; + this.value = value; + } + + @Override + public Element 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() { + if (element instanceof ArangoDBEdge) { + ((ArangoDBEdge) element).removeProperty(key); + } else { + throw new UnsupportedOperationException("Property " + this.key() + " is not an Edge"); + } + } + + @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/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index ad3c589..f8a9877 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -126,41 +126,40 @@ public Edge addEdge(String label, Vertex inVertex, Object... keyValues) { 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("_"); + 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(); - } - } + id = parts[1]; + + } + } + Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); + if (m.matches()) { + edge = new ArangoDBEdge(id.toString(), label, (String) this.id(), (String) inVertex.id(), graph); + } + else { + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); + } + } + else { + throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + } else { - edge = new ArangoDBEdge(graph, label, this, ((ArangoDBVertex) inVertex)); + edge = new ArangoDBEdge(null, label, (String) this.id(),(String) inVertex.id(), graph); } - // The vertex needs to exist before we can attach properties - graph.getClient().insertEdge(edge); - ElementHelper.attachProperties(edge, keyValues); + // The vertex needs to exist before we can attach properties + edge.insert(); + ElementHelper.attachProperties(edge, keyValues); return edge; } - @Override public VertexProperty property( Cardinality cardinality, @@ -222,7 +221,10 @@ public Iterator edges(Direction direction, String... edgeLabels) { if (edgeCollections.isEmpty()) { return Collections.emptyIterator(); } - return new ArangoDBIterator<>(graph, graph.getClient().getVertexEdges(this, edgeCollections, direction)); + return graph.getClient().getVertexEdges(this, edgeCollections, direction) + .stream() + .map(it -> (Edge) new ArangoDBEdge(graph, it)) + .iterator(); } 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..7193ba0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -36,6 +36,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -381,25 +382,6 @@ public static EdgeDefinition createPropertyEdgeDefinitions( } - /** - * Creates an Arango DB edge property. - * - * @param the generic type - * @param key the name - * @param value the value - * @param edge the edge - * @return the created Arango DB edge property - */ - - public static ArangoDBEdgeProperty createArangoDBEdgeProperty( - String key, - U value, - ArangoDBEdge edge) { - ArangoDBEdgeProperty p = new ArangoDBEdgeProperty<>(key, value, edge); - insertElementAndProperty(edge, p); - return p; - } - /** * Creates an Arango DB vertex property. * @@ -541,41 +523,65 @@ else if (value instanceof Integer) { ((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; + 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[]": - 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; + 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[]": - 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; + 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[]": - 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; + 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[]": - 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; + 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 { @@ -606,6 +612,14 @@ public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirect return null; } + public static IllegalStateException elementAlreadyRemoved(final Class clazz, final Object id) { + return new IllegalStateException(String.format("%s with id %s was removed.", clazz.getSimpleName(), id)); + } + + public static IllegalStateException unsupportedIdType(final Object id) { + return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); + } + private static void insertElementAndProperty(ArangoDBBaseDocument element, ArangoDBElementProperty p) { ArangoDBGraph g = element.graph(); ArangoDBGraphClient c = g.getClient(); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index fff483c..8ad40b5 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -31,7 +31,7 @@ public class ArangoDBGraphProvider extends AbstractGraphProvider { add(ArangoDBEdge.class); add(ArangoDBGraph.class); add(ArangoDBGraphVariables.class); - add(ArangoDBEdgeProperty.class); + add(ArangoDBProperty.class); add(ArangoDBElementProperty.class); add(ArangoDBPropertyProperty.class); add(ArangoDBVertex.class); diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 9e037b2..50b2be0 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -26,6 +26,5 @@ - From 42d86492db2c480e320cb7e7e96891231890fc7e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 19 Feb 2025 12:15:02 +0100 Subject: [PATCH 02/28] embedded vertex properties --- .../gremlin/client/ArangoDBBaseDocument.java | 7 + .../gremlin/client/ArangoDBGraphClient.java | 1446 ++++++++--------- .../gremlin/client/ArangoDBIterator.java | 59 - .../client/ArangoDBPropertyIterator.java | 65 - .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 7 +- .../gremlin/structure/ArangoDBEdge.java | 21 +- ...dgeDocument.java => ArangoDBEdgeData.java} | 80 +- .../structure/ArangoDBElementProperty.java | 189 --- .../gremlin/structure/ArangoDBGraph.java | 52 +- .../gremlin/structure/ArangoDBProperty.java | 2 + .../structure/ArangoDBPropertyProperty.java | 70 - .../gremlin/structure/ArangoDBVertex.java | 481 +++--- .../gremlin/structure/ArangoDBVertexData.java | 88 + .../structure/ArangoDBVertexProperty.java | 194 +-- .../structure/ArangoDBVertexPropertyData.java | 77 + .../gremlin/structure/PropertyValue.java | 56 + .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 60 +- .../gremlin/ArangoDBGraphProvider.java | 3 +- src/test/resources/logback-test.xml | 2 +- 19 files changed, 1283 insertions(+), 1676 deletions(-) delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBIterator.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBPropertyIterator.java rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{ArangoDBEdgeDocument.java => ArangoDBEdgeData.java} (53%) delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElementProperty.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyProperty.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java 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..efb6972 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java @@ -14,6 +14,10 @@ import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertexPropertyData; + +import java.util.List; +import java.util.Map; /** * The ArangoDB BaseBaseDocument provides the internal fields required for the driver to correctly @@ -44,6 +48,9 @@ public abstract class ArangoDBBaseDocument { @JsonProperty protected String label; + @JsonProperty + protected Map> properties; + /** The collection in which the element is placed. */ @JsonIgnore 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 5173855..1be8aee 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -8,7 +8,6 @@ package com.arangodb.tinkerpop.gremlin.client; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -51,24 +50,25 @@ 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]+).+"); - + /** 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 */ - private ArangoDBExceptions() { } - + private ArangoDBExceptions() { + } + /** * Translate ArangoDB Error code into exception (@see Error codes) * @@ -77,29 +77,29 @@ private ArangoDBExceptions() { } */ 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); + 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. */ @@ -130,734 +130,233 @@ public static ArangoDBGraphException errorPersistingElmenentProperty(ArangoDBGra return new ArangoDBGraphException("Error persisting property in element. ", 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 ArangoDB driver; - private final int batchSize; + private final ArangoDatabase db; - private final ArangoDBGraph graph; + private final int batchSize; + + 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 one. * - * @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 + * @param batchSize the size of the batch mode chunks + * @throws ArangoDBGraphException If the db does not exist and cannot be created */ public ArangoDBGraphClient( - ArangoDBGraph graph, - Properties properties, - String dbname, - int batchSize) - throws ArangoDBGraphException { + ArangoDBGraph graph, + Properties properties, + String dbname, + int batchSize) + throws ArangoDBGraphException { this(graph, properties, dbname, batchSize, false); } - - /** - * 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) { + + /** + * 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); } - } - - - /** - * Gets the database. - * - * @return the ArangoDB - */ - - public ArangoDatabase getDB() { - return db; - } - - /** - * 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) { + } + + + /** + * Gets the database. + * + * @return the ArangoDB + */ + + public ArangoDatabase getDB() { + return db; + } + + /** + * 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) + } + } + } + + /** + * 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; - } - - /** - * 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 - */ - - public void insertDocument(ArangoDBBaseDocument document) { - logger.debug("Insert document {} in {}", document, graph.name()); - insertDocument(document, document.collection()); - } - - /** - * 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); - } - - /** - * 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()); - } - - /** - * 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 - */ - - 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); - } - - 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 name - * (if not * present) - * @param document the document - * @throws ArangoDBGraphException If there was an error inserting the document - */ - - 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); - } - - /** - * Delete a document from the graph. - * @param document the document to delete - * @throws ArangoDBGraphException If there was an error deleting the document - */ - - 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); - } - - /** - * Update the document in the graph. - * @param document the document - * - * @throws ArangoDBGraphException If there was an error updating the document - */ - - 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, ArangoDBEdgeDocument.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 - */ - - 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); - } - - /** - * 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 - */ - - 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 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 - * @return ArangoDBBaseQuery the query object - */ - public ArangoCursor getGraphEdges(List ids) { - 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 { - queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "e", bindVars); - } - queryBuilder.ret("e"); - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBEdgeDocument.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; - } - - /** - * 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); - throw ArangoDBExceptions.getArangoDBException(e); + } catch (ArangoDBException e) { + logger.error("Failed to retrieve vertex: {}", e.getErrorMessage()); + throw new ArangoDBGraphException("Failed to retrieve vertex.", 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); - throw ArangoDBExceptions.getArangoDBException(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 document into a specific collection. - * @param document the document to insert - * @param colName the collection + * 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 */ - private void insertDocument(ArangoDBBaseDocument document, String colName) { - VertexEntity vertexEntity; + public void insertGraphVariables(ArangoDBGraphVariables document) { + logger.debug("Insert graph variables {} in {}", document, graph.name()); if (document.isPaired()) { - throw new ArangoDBGraphException("Paired docuemnts can not be inserted, only updated"); + 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 = db.graph(graph.name()) - .vertexCollection(colName) - .insertVertex(document); + vertexEntity = gVars.insertDocument(document); } catch (ArangoDBException e) { logger.error("Failed to insert document: {}", e.getMessage()); ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); @@ -874,6 +373,323 @@ private void insertDocument(ArangoDBBaseDocument document, String colName) { 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 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); + } + + /** + * Update the document in the graph. + * @param document the document + * + * @throws ArangoDBGraphException If there was an error updating the document + */ + + 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 vertexId 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( + String vertexId, + List edgeLabels, + Direction direction) + throws ArangoDBException { + logger.debug("Get Vertex's {}:{} Edges, in {}, from collections {}", vertexId, 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, vertexId, bindVars) + .graphOptions(Optional.of(UniqueVertices.NONE), Optional.empty(), true) + .filterSameCollections("e", edgeLabels, bindVars) + .ret("e"); + + String query = queryBuilder.toString(); + return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.class); + } + + /** + * Get all neighbours of a document. + * + * @param the document type + * @param vertexId 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 + */ + + public ArangoCursor getDocumentNeighbors( + String vertexId, + List edgeLabelsFilter, + Direction direction, + ArangoDBPropertyFilter propertyFilter, + Class resultType) { + logger.debug("Get Document's {}:{} Neighbors, in {}, from collections {}", vertexId, 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, vertexId, 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); + } + + /** + * 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 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, ArangoDBVertexData.class); + } + + /** + * Get edges of a graph. If no ids are provided, get all edges. + * + * @param ids the ids to match + * @return ArangoDBBaseQuery the query object + */ + public ArangoCursor getGraphEdges(List ids) { + 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 { + queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "e", bindVars); + } + queryBuilder.ret("e"); + String query = queryBuilder.toString(); + logger.debug("AQL {}", query); + return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.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; + } + + /** + * 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); + 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); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + // TODO Decide what of these methods should be restored. // // /** @@ -1081,9 +897,10 @@ private void insertDocument(ArangoDBBaseDocument document, String colName) { // * @return the configuration // * @throws ArangoDBException the arango DB exception // */ -//// public ArangoDBConfiguration getConfiguration() { -//// return configuration; -//// } + + //// public ArangoDBConfiguration getConfiguration() { + //// return configuration; + //// } // // /** // * Truncates a collection @@ -1097,26 +914,25 @@ private void insertDocument(ArangoDBBaseDocument document, String colName) { // throw new ArangoDBException(e); // } // } - - public void insertEdge(ArangoDBEdgeDocument edge) { - logger.debug("Insert edge {} in {} ", edge, graph.name()); - EdgeEntity insertEntity; - String collection = graph.getPrefixedCollectioName(edge.getLabel()); - try { - insertEntity = db.graph(graph.name()) - .edgeCollection(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(collection + "/" + edge.getKey()); - } - throw arangoDBException; - } - edge.setKey(insertEntity.getKey()); - edge.setRev(insertEntity.getRev()); - } + public void insertEdge(ArangoDBEdgeData edge) { + logger.debug("Insert edge {} in {} ", edge, graph.name()); + EdgeEntity insertEntity; + String collection = graph.getPrefixedCollectioName(edge.getLabel()); + try { + insertEntity = db.graph(graph.name()) + .edgeCollection(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(collection + "/" + edge.getKey()); + } + throw arangoDBException; + } + edge.setKey(insertEntity.getKey()); + edge.setRev(insertEntity.getRev()); + } // public TinkerEdgeDocument getEdge(String key, String collection) { // logger.debug("Get edge {} from {}:{}", key, graph.name(), graph.getPrefixedCollectioName(collection)); @@ -1130,31 +946,85 @@ public void insertEdge(ArangoDBEdgeDocument edge) { // } // } - public void deleteEdge(ArangoDBEdgeDocument edge) { - logger.debug("Delete edge {} in {}", edge, graph.name()); - try { - db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .deleteEdge(edge.getKey()); - } catch (ArangoDBException e) { - logger.error("Failed to delete vertex: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - } - - public void updateEdge(ArangoDBEdgeDocument edge) { - logger.debug("Update edge {} in {}", edge, graph.name()); - EdgeUpdateEntity updateEntity; - try { - updateEntity = db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .replaceEdge(edge.getKey(), edge); - } catch (ArangoDBException e) { - logger.error("Failed to update vertex: {}", e.getErrorMessage()); - throw ArangoDBExceptions.getArangoDBException(e); - } - logger.info("Edge updated, new rev {}", updateEntity.getRev()); - edge.setKey(updateEntity.getKey()); - edge.setRev(updateEntity.getRev()); - } + public void deleteEdge(ArangoDBEdgeData edge) { + logger.debug("Delete edge {} in {}", edge, graph.name()); + try { + db.graph(graph.name()) + .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) + .deleteEdge(edge.getKey()); + } catch (ArangoDBException e) { + if (e.getErrorNum() == 1202) { // document not found + return; + } + logger.error("Failed to delete vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + + public void updateEdge(ArangoDBEdgeData edge) { + logger.debug("Update edge {} in {}", edge, graph.name()); + EdgeUpdateEntity updateEntity; + try { + updateEntity = db.graph(graph.name()) + .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) + .replaceEdge(edge.getKey(), edge); + } catch (ArangoDBException e) { + logger.error("Failed to update edge: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + logger.info("Edge updated, new rev {}", updateEntity.getRev()); + edge.setKey(updateEntity.getKey()); + edge.setRev(updateEntity.getRev()); + } + + public void insertVertex(ArangoDBVertexData vertex) { + logger.debug("Insert vertex {} in {}", vertex, graph.name()); + VertexEntity vertexEntity; + String colName = graph.getPrefixedCollectioName(vertex.getLabel()); + try { + vertexEntity = db.graph(graph.name()) + .vertexCollection(colName) + .insertVertex(vertex); + } catch (ArangoDBException e) { + logger.error("Failed to insert document: {}", e.getMessage()); + ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); + if (arangoDBException.getErrorCode() == 1210) { + throw Graph.Exceptions.vertexWithIdAlreadyExists(vertex.getKey()); + } + throw arangoDBException; + } + vertex.setKey(vertexEntity.getKey()); + vertex.setRev(vertexEntity.getRev()); + } + + public void deleteVertex(ArangoDBVertexData vertex) { + logger.debug("Delete vertex {} in {}", vertex, graph.name()); + try { + db.graph(graph.name()) + .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) + .deleteVertex(vertex.getKey()); + } catch (ArangoDBException e) { + if(e.getErrorNum() == 1202) { // document not found + return; + } + logger.error("Failed to delete vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + + public void updateVertex(ArangoDBVertexData vertex) { + logger.debug("Update document {} in {}", vertex, graph.name()); + VertexUpdateEntity vertexEntity; + try { + vertexEntity = db.graph(graph.name()) + .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) + .replaceVertex(vertex.getKey(), vertex); + } catch (ArangoDBException e) { + logger.error("Failed to update document: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + logger.info("Document updated, new rev {}", vertexEntity.getRev()); + vertex.setRev(vertexEntity.getRev()); + } + } \ 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/jsr223/ArangoDBGremlinPlugin.java b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java index 0f23feb..54505e5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -35,17 +35,14 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { ArangoDBBaseEdge.class, ArangoDBGraphClient.class, ArangoDBGraphException.class, - ArangoDBIterator.class, ArangoDBPropertyFilter.class, - ArangoDBPropertyIterator.class, ArangoDBQueryBuilder.class, ArangoDBEdge.class, - ArangoDBEdgeDocument.class, + ArangoDBEdgeData.class, ArangoDBProperty.class, - ArangoDBElementProperty.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, - ArangoDBPropertyProperty.class, + ArangoDBVertexPropertyData.class, ArangoDBVertex.class, ArangoDBVertexProperty.class, ArangoDBUtil.class 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 72b53e9..a0039d1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -18,7 +18,6 @@ */ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBIterator; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -36,10 +35,10 @@ public class ArangoDBEdge implements Edge { private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBEdge.class); private final ArangoDBGraph graph; - private final ArangoDBEdgeDocument data; + private final ArangoDBEdgeData data; private boolean removed; - public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeDocument data) { + public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeData data) { this.graph = graph; this.data = data; this.removed = false; @@ -70,7 +69,7 @@ public ArangoDBEdge(final String id, final String label, final String outVertexI throw new IllegalArgumentException("empty key"); } - data = new ArangoDBEdgeDocument(inferredLabel, key, outVertexId, inVertexId); + data = new ArangoDBEdgeData(inferredLabel, key, outVertexId, inVertexId); removed = false; } @@ -94,10 +93,12 @@ public ArangoDBGraph graph() { } public void insert() { + if (removed) throw elementAlreadyRemoved(Edge.class, id()); graph.getClient().insertEdge(data); } public void update() { + if (removed) throw elementAlreadyRemoved(Edge.class, id()); graph.getClient().updateEdge(data); } @@ -112,11 +113,9 @@ public void removeProperty(String key) { @Override @SuppressWarnings("unchecked") public Iterator> properties(final String... propertyKeys) { - return data.getProperties() - .entrySet() - .stream() + return data.properties() .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) - .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue().getValue())) + .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue())) .collect(Collectors.toList()).iterator(); } @@ -142,7 +141,7 @@ public Property property(final String key) { @Override public Set keys() { - return data.getProperties().keySet(); + return data.keys(); } @Override @@ -178,7 +177,9 @@ public Iterator vertices(Direction direction) { ids.add(data.getFrom()); break; } - return new ArangoDBIterator<>(graph, graph.getClient().getGraphVertices(ids, Collections.emptyList())); + return graph.getClient().getGraphVertices(ids, Collections.emptyList()).stream() + .map(it -> (Vertex) new ArangoDBVertex(graph, it)) + .iterator(); } @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java similarity index 53% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java index 588e501..9cae35c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeDocument.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java @@ -1,15 +1,13 @@ package com.arangodb.tinkerpop.gremlin.structure; import com.arangodb.serde.*; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.stream.Stream; -public class ArangoDBEdgeDocument { +public class ArangoDBEdgeData { @InternalKey private String key; @@ -23,14 +21,16 @@ public class ArangoDBEdgeDocument { @InternalTo private String to; + @JsonProperty private String label; - private final Map properties = new HashMap<>(); + @JsonProperty + private final Map properties = new HashMap<>(); - public ArangoDBEdgeDocument() { + public ArangoDBEdgeData() { } - public ArangoDBEdgeDocument(String label, String key, String from, String to) { + public ArangoDBEdgeData(String label, String key, String from, String to) { this.label = label; this.key = key; this.from = from; @@ -77,8 +77,13 @@ public void setLabel(String label) { this.label = label; } - public Map getProperties() { - return properties; + public Set keys() { + return properties.keySet(); + } + + public Stream> properties() { + return properties.entrySet().stream() + .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); } public boolean hasProperty(String key) { @@ -89,10 +94,12 @@ public void removeProperty(String key) { properties.remove(key); } + @JsonIgnore public void setProperty(String key, Object value) { - properties.put(key, new TinkerValue(value)); + properties.put(key, new PropertyValue(value)); } + @JsonIgnore public Object getProperty(String key) { return properties.get(key).getValue(); } @@ -112,7 +119,7 @@ public String toString() { @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - ArangoDBEdgeDocument that = (ArangoDBEdgeDocument) o; + ArangoDBEdgeData that = (ArangoDBEdgeData) o; return Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(from, that.from) && Objects.equals(to, that.to) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); } @@ -121,51 +128,4 @@ public int hashCode() { return Objects.hash(key, rev, from, to, label, properties); } - public static class TinkerValue { - private final Object value; - private final String valueType; - - @JsonCreator - public TinkerValue( - @JsonProperty("value") Object value, - @JsonProperty("valueType") String valueType - ) { - this.value = value; - this.valueType = valueType; - } - - public TinkerValue(Object value) { - this.value = value; - valueType = (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 "TinkerValue{" + - "value=" + value + - ", valueType='" + valueType + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - TinkerValue that = (TinkerValue) o; - return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); - } - - @Override - public int hashCode() { - return Objects.hash(value, valueType); - } - } - } 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 46a80a1..bc937f3 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -31,7 +31,6 @@ 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.utils.ArangoDBUtil.unsupportedIdType; @@ -618,16 +617,16 @@ public ArangoDBGraph(Configuration configuration) { public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); Object id; - String collection; + String label; if (!schemaless) { - collection = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(collection); + label = ElementHelper.getLabelValue(keyValues).orElse(null); + ElementHelper.validateLabel(label); } else { - collection = DEFAULT_VERTEX_COLLECTION; + label = DEFAULT_VERTEX_COLLECTION; } - if (!vertexCollections().contains(collection)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", collection, name)); + if (!vertexCollections().contains(label)) { + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); } ArangoDBVertex vertex = null; if (ElementHelper.getIdValue(keyValues).isPresent()) { @@ -639,14 +638,14 @@ public Vertex addVertex(Object... keyValues) { // 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)) { + if (collectionName.contains(label)) { id = parts[1]; } } Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); if (m.matches()) { - vertex = new ArangoDBVertex(id.toString(), collection, this); + vertex = new ArangoDBVertex(id.toString(), label, this); } else { throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); @@ -658,10 +657,10 @@ public Vertex addVertex(Object... keyValues) { } else { - vertex = new ArangoDBVertex(this, collection); + vertex = new ArangoDBVertex(null, label, this); } // The vertex needs to exist before we can attach properties - client.insertDocument(vertex); + vertex.insert(); ElementHelper.attachProperties(vertex, keyValues); return vertex; } @@ -813,21 +812,22 @@ public List 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)); + 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 getClient().getGraphVertices(ids, vertexCollections).stream() + .map(it -> (Vertex) new ArangoDBVertex(this, it)) + .iterator(); } /** diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java index 33a7a5e..12fb0c0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java @@ -59,6 +59,8 @@ public boolean isPresent() { public void remove() { if (element instanceof ArangoDBEdge) { ((ArangoDBEdge) element).removeProperty(key); + } else if (element instanceof ArangoDBVertexProperty) { + ((ArangoDBVertexProperty) element).removeProperty(key); } else { throw new UnsupportedOperationException("Property " + this.key() + " is not an Edge"); } 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/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index f8a9877..8e66f01 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -1,23 +1,17 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // 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.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.*; import java.util.regex.Matcher; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.ArrayUtils; import org.apache.tinkerpop.gremlin.structure.Direction; @@ -32,14 +26,12 @@ 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 static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; + /** * The ArangoDB vertex class. @@ -50,249 +42,270 @@ * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ -public class ArangoDBVertex extends ArangoDBBaseDocument implements Vertex { +public class ArangoDBVertex implements Vertex { - private static final Logger logger = LoggerFactory.getLogger(ArangoDBVertex.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertex.class); - /** - * Constructor used for ArabgoDB JavaBeans serialisation. - */ + private final ArangoDBGraph graph; + private final ArangoDBVertexData data; + private boolean removed; + + public ArangoDBVertex(ArangoDBGraph graph, ArangoDBVertexData data) { + this.graph = graph; + this.data = data; + this.removed = false; + } - public ArangoDBVertex() { - super(); - } - - /** - * 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 - */ - - public ArangoDBVertex(String key, String label, ArangoDBGraph graph) { - super(key, label, graph); - } - - /** - * Instantiates a new ArangoDB vertex. - * - * @param graph the graph - * @param collection the collection - */ - - public ArangoDBVertex(ArangoDBGraph graph, String collection) { - this(null, collection, graph); - } + public ArangoDBVertex(final String id, final String label, ArangoDBGraph graph) { + this.graph = graph; + String inferredLabel, key; + if (id != null) { + int separator = id.indexOf('/'); + if (separator > 0) { + inferredLabel = id.substring(0, separator); + key = id.substring(separator + 1); + } else { + inferredLabel = label != null ? label : DEFAULT_LABEL; + key = id; + } + } else { + inferredLabel = label != null ? label : DEFAULT_LABEL; + key = null; + } + + if (inferredLabel.isEmpty()) { + throw new IllegalArgumentException("empty label"); + } + + if (key != null && key.isEmpty()) { + throw new IllegalArgumentException("empty key"); + } + + data = new ArangoDBVertexData(inferredLabel, key); + removed = false; + } + + public boolean isRemoved() { + return removed; + } @Override - public Object id() { - return _id(); + public String id() { + String key = data.getKey(); + if (key == null) { + return null; + } + return graph.getPrefixedCollectioName(label()) + "/" + key; } @Override public String label() { - return label; + return data.getLabel(); } - @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 - 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, (String) this.id(), (String) inVertex.id(), graph); - } - else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - } - else { - edge = new ArangoDBEdge(null, label, (String) this.id(),(String) inVertex.id(), graph); - } - // The vertex needs to exist before we can attach properties - edge.insert(); - 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; + @Override + public ArangoDBGraph graph() { + return graph; + } + + @Override + public void remove() { + if (removed) return; + LOGGER.info("removing {} from graph {}.", id(), graph.name()); + edges(Direction.BOTH).forEachRemaining(Edge::remove); + graph.getClient().deleteVertex(data); + this.removed = true; + } + + @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, (String) this.id(), (String) inVertex.id(), graph); + } else { + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); + } + } else { + throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); } - keyValues = ArrayUtils.remove(keyValues, idIndex); - keyValues = ArrayUtils.remove(keyValues, idIndex); - } + } else { + edge = new ArangoDBEdge(null, label, (String) this.id(), (String) inVertex.id(), graph); + } + // The vertex needs to exist before we can attach properties + edge.insert(); + ElementHelper.attachProperties(edge, keyValues); + return edge; + } + + @Override + public VertexProperty property( + final Cardinality cardinality, + final String key, + final V value, + final Object... keyValues + ) { + if (removed) throw elementAlreadyRemoved(Vertex.class, id()); + ElementHelper.legalPropertyKeyValueArray(keyValues); + ElementHelper.validateProperty(key, value); + 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 graph.getClient().getVertexEdges(this, edgeCollections, direction) - .stream() - .map(it -> (Edge) new ArangoDBEdge(graph, it)) - .iterator(); - } - - - @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); - } - - - @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); + + Optional optionalId = ElementHelper.getIdValue(keyValues); + Object[] filteredKeyValues = ArrayUtils.clone(keyValues); + String idValue = null; + if (optionalId.isPresent()) { + if (graph.features().vertex().properties().willAllowId(optionalId.get())) { + idValue = optionalId.get().toString(); + } else { + throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + int idIndex = 0; + for (int i = 0; i < filteredKeyValues.length; i += 2) { + if (filteredKeyValues[i] == T.id) { + idIndex = i; + break; + } + } + filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); + filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); + } + + if (idValue == null) { + idValue = UUID.randomUUID().toString(); } - ArangoCursor query = graph.getClient().getElementProperties(this, labels, filter, ArangoDBVertexProperty.class); - return new ArangoDBPropertyIterator<>(graph, (ArangoCursor>) query); + + ArangoDBVertexPropertyData prop = new ArangoDBVertexPropertyData(idValue, value); + + final List list = data.getProperties().getOrDefault(key, new ArrayList<>()); + list.add(prop); + data.getProperties().put(key, list); + + ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); + ElementHelper.attachProperties(vertexProperty, filteredKeyValues); + update(); + return vertexProperty; } - @Override + @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 graph.getClient().getVertexEdges(id(), edgeCollections, direction) + .stream() + .map(it -> (Edge) new ArangoDBEdge(graph, it)) + .iterator(); + } + + + @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(); + } + return graph.getClient().getDocumentNeighbors(id(), edgeCollections, direction, ArangoDBPropertyFilter.empty(), ArangoDBVertexData.class).stream() + .map(it -> (Vertex) new ArangoDBVertex(graph, it)) + .iterator(); + } + + + @SuppressWarnings("unchecked") + @Override + public Iterator> properties(String... propertyKeys) { + LOGGER.debug("Get properties {}", (Object[]) propertyKeys); + return allProperties() + .filter(it -> ElementHelper.keyExists(it.key(), propertyKeys)) + .map(it -> (VertexProperty) it) + .iterator(); + } + + + @Override public String toString() { - return StringFactory.vertexString(this); + return StringFactory.vertexString(this); + } + + private Stream> allProperties() { + return data.getProperties().entrySet().stream() + .flatMap(x -> x.getValue().stream() + .map(y -> new ArangoDBVertexProperty<>(x.getKey(), y, this)) + ); + } + + public void insert() { + if (removed) throw elementAlreadyRemoved(Vertex.class, id()); + graph.getClient().insertVertex(data); + } + + + public void update() { + if (removed) throw elementAlreadyRemoved(Vertex.class, id()); + graph.getClient().updateVertex(data); + } + + public void removeProperty(ArangoDBVertexPropertyData prop) { + if (removed) throw elementAlreadyRemoved(Vertex.class, id()); + for (List it : data.getProperties().values()) { + if (it.remove(prop)) return; + } + } + + /** + * 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()); + + } + return vertexCollections; } - /** - * Save. - */ - public void save() { - if (paired) { - graph.getClient().updateDocument(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()); - - } - return vertexCollections; - } - @Override + @SuppressWarnings("EqualsDoesntCheckParameterClass") public boolean equals(final Object object) { return ElementHelper.areEqual(this, object); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java new file mode 100644 index 0000000..dc28ca1 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java @@ -0,0 +1,88 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.serde.*; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; + +import java.util.*; + +public class ArangoDBVertexData { + + @InternalKey + private String key; + + @InternalRev + private String rev; + + @JsonProperty + private String label; + + @JsonProperty + private Map> properties; + + public ArangoDBVertexData() { + } + + public ArangoDBVertexData(String label, String key) { + this.label = label; + this.key = key; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRev() { + return rev; + } + + public void setRev(String rev) { + this.rev = rev; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Map> getProperties() { + if (properties == null) { + properties = new HashMap<>(); + } + return properties; + } + + public void setProperties(Map> properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "ArangoDBVertexDocument{" + + ", key='" + key + '\'' + + ", rev='" + rev + '\'' + + ", label='" + label + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ArangoDBVertexData that = (ArangoDBVertexData) o; + return Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(key, rev, label, properties); + } + +} + 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..143b8ee 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -1,139 +1,101 @@ -////////////////////////////////////////////////////////////////////////////////////////// -// -// 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.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.*; 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 Class ArangoDBVertexProperty. - * - * @param the type of the property value - * - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -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); +import java.util.*; +import java.util.stream.Collectors; + +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; + +public class ArangoDBVertexProperty implements Element, VertexProperty { + private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertexProperty.class); + + private final String key; + private final ArangoDBVertex vertex; + private final ArangoDBVertexPropertyData data; + private boolean removed; + + public ArangoDBVertexProperty(String key, ArangoDBVertexPropertyData data, ArangoDBVertex vertex) { + this.key = key; + this.data = data; + this.vertex = vertex; + removed = false; } - @Override - public String toString() { - return StringFactory.propertyString(this); + @Override + public String key() { + return key; } - @Override - public Object id() { - return _id(); - } - - @Override - public String label() { - return name; + @SuppressWarnings("unchecked") + @Override + public V value() throws NoSuchElementException { + return (V) data.getValue(); + } + + @Override + public boolean isPresent() { + return true; } @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; + return vertex; } - @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 Object id() { + return data.getId(); + } + + @Override + public Property property(String key, W value) { + if (removed) throw elementAlreadyRemoved(VertexProperty.class, id()); + LOGGER.info("set property {} = {}", key, value); + ElementHelper.validateProperty(key, value); + data.setProperty(key, value); + vertex.update(); + return new ArangoDBProperty<>(this, key, value); + } @Override public void remove() { - logger.info("remove {}", this._id()); - if (paired) { - // Delete properties - properties().forEachRemaining(Property::remove); + if (removed) return; + vertex.removeProperty(data); + vertex.update(); + removed = true; + } + + @Override + @SuppressWarnings("unchecked") + public Iterator> properties(String... propertyKeys) { + return data.getProperties() + .entrySet() + .stream() + .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) + .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue().getValue())) + .collect(Collectors.toList()).iterator(); + } + + public void removeProperty(String key) { + if (removed) throw elementAlreadyRemoved(Edge.class, id()); + if (data.hasProperty(key)) { + data.removeProperty(key); + vertex.update(); } - super.remove(); } - + + @Override + public String toString() { + return StringFactory.propertyString(this); + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(final Object object) { return ElementHelper.areEqual(this, object); @@ -141,7 +103,7 @@ public boolean equals(final Object object) { @Override public int hashCode() { - return key().hashCode() + value().hashCode(); + return ElementHelper.hashCode((Property) this); } -} \ No newline at end of file +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java new file mode 100644 index 0000000..bebf666 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java @@ -0,0 +1,77 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class ArangoDBVertexPropertyData extends PropertyValue { + private final String id; + private final Map properties; + + @JsonCreator + public ArangoDBVertexPropertyData( + @JsonProperty("id") String id, + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType, + @JsonProperty("properties") Map properties) { + super(value, valueType); + this.id = id; + this.properties = properties; + } + + public ArangoDBVertexPropertyData(String id, Object value) { + super(value); + this.id = id; + this.properties = new HashMap<>(); + } + + public String getId() { + return id; + } + + public Map getProperties() { + return properties; + } + + public void setProperty(String key, Object value) { + properties.put(key, new PropertyValue(value)); + } + + public boolean hasProperty(String key) { + return properties.containsKey(key); + } + + public void removeProperty(String key) { + properties.remove(key); + } + + public Object getProperty(String key) { + return properties.get(key).getValue(); + } + + @Override + public String toString() { + return "TinkerVertexPropertyData{" + + "id='" + id + '\'' + + ", value=" + getValue() + + ", valueType='" + getValueType() + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + ArangoDBVertexPropertyData that = (ArangoDBVertexPropertyData) o; + return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java new file mode 100644 index 0000000..cbc26ed --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java @@ -0,0 +1,56 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +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 PropertyValue { + + private final Object value; + private final String valueType; + + @JsonCreator + public PropertyValue( + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.value = value; + this.valueType = valueType; + } + + public PropertyValue(Object value) { + this.value = value; + valueType = (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 "PropertyValue{" + + "value=" + value + + ", valueType='" + valueType + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + PropertyValue that = (PropertyValue) o; + return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(value, valueType); + } +} + 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 7193ba0..3a32072 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -15,12 +15,11 @@ 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; @@ -381,57 +380,23 @@ public static EdgeDefinition createPropertyEdgeDefinitions( return ed; } - - /** - * Creates an Arango DB vertex property. - * - * @param the generic type - * @param propertyName the name - * @param propertyValue the value - * @param vertex the vertex - * @return the created Arango DB vertex property - */ - - public static ArangoDBVertexProperty createArangoDBVertexProperty(String propertyName, U propertyValue, ArangoDBVertex vertex) { - ArangoDBVertexProperty p = new ArangoDBVertexProperty<>(propertyName, propertyValue, vertex); - insertElementAndProperty(vertex, p); - return p; - } - /** * Creates an Arango DB vertex property. * * @param the generic type * @param id the id - * @param propertyName the name - * @param propertyValue the value + * @param key the name + * @param value the value * @param vertex the vertex * @return the created Arango DB vertex property */ - 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; - } - - /** - * Creates an Arango DB property property. - * - * @param the generic type - * @param key the name - * @param value the value - * @param vertexProperty the vertex property - * @return the created Arango DB property property - */ - - 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 TinkerVertexProperty createArangoDBVertexProperty(String id, String key, U value, ArangoDBVertex vertex) { +// TinkerVertexProperty p; +// p = new TinkerVertexProperty<>(id, key, value, vertex); +// insertElementAndProperty(vertex, p); +// return p; +// } /** * Gets the correct primitive. @@ -620,11 +585,4 @@ public static IllegalStateException unsupportedIdType(final Object id) { return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); } - 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 index 8ad40b5..0a9ff28 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -32,8 +32,7 @@ public class ArangoDBGraphProvider extends AbstractGraphProvider { add(ArangoDBGraph.class); add(ArangoDBGraphVariables.class); add(ArangoDBProperty.class); - add(ArangoDBElementProperty.class); - add(ArangoDBPropertyProperty.class); + add(ArangoDBVertexPropertyData.class); add(ArangoDBVertex.class); add(ArangoDBVertexProperty.class); }}; diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index 50b2be0..717bd79 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -25,6 +25,6 @@ - + From d2622d372f8e05ad8c33db16782cf0d2d3ba7463 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 24 Feb 2025 22:08:57 +0100 Subject: [PATCH 03/28] data classes refactoring --- .../gremlin/client/ArangoDBBaseEdge.java | 105 ------------------ .../gremlin/client/ArangoDBGraphClient.java | 31 ------ .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 1 - .../gremlin/structure/ArangoDBData.java | 87 +++++++++++++++ .../gremlin/structure/ArangoDBEdge.java | 10 +- .../gremlin/structure/ArangoDBEdgeData.java | 95 +++------------- ...tyValue.java => ArangoDBPropertyData.java} | 11 +- .../gremlin/structure/ArangoDBVertex.java | 8 -- .../gremlin/structure/ArangoDBVertexData.java | 77 +------------ .../structure/ArangoDBVertexPropertyData.java | 26 +---- .../structure/PropertiesContainer.java | 30 +++++ 11 files changed, 150 insertions(+), 331 deletions(-) delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseEdge.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{PropertyValue.java => ArangoDBPropertyData.java} (86%) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java 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 1be8aee..4ca2bfc 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -291,37 +291,6 @@ public void deleteDb() throws ArangoDBGraphException { } } - /** - * 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; 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 54505e5..36e6ad3 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -32,7 +32,6 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { try { IMPORTS = DefaultImportCustomizer.build().addClassImports( ArangoDBBaseDocument.class, - ArangoDBBaseEdge.class, ArangoDBGraphClient.class, ArangoDBGraphException.class, ArangoDBPropertyFilter.class, diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java new file mode 100644 index 0000000..e2519f7 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java @@ -0,0 +1,87 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.serde.InternalKey; +import com.arangodb.serde.InternalRev; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +abstract class ArangoDBData { + private String label; + + @InternalKey + private String key; + + @InternalRev + private String rev; + + private Map properties = new HashMap<>(); + + public ArangoDBData() { + } + + public ArangoDBData(String label, String key) { + Objects.requireNonNull(label, "label"); + if (label.isEmpty()) throw new IllegalArgumentException("empty label"); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); + + this.label = label; + this.key = key; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRev() { + return rev; + } + + public void setRev(String rev) { + this.rev = rev; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Map getProperties() { + if (properties == null) { + properties = new HashMap<>(); + } + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + @Override + public String toString() { + return "key='" + key + '\'' + + ", label='" + label + '\'' + + ", rev='" + rev + '\'' + + ", properties=" + properties; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ArangoDBData that = (ArangoDBData) o; + return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(label, key, rev, properties); + } +} 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 a0039d1..034b83f 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -61,14 +61,6 @@ public ArangoDBEdge(final String id, final String label, final String outVertexI key = null; } - if (inferredLabel.isEmpty()) { - throw new IllegalArgumentException("empty label"); - } - - if (key != null && key.isEmpty()) { - throw new IllegalArgumentException("empty key"); - } - data = new ArangoDBEdgeData(inferredLabel, key, outVertexId, inVertexId); removed = false; } @@ -141,7 +133,7 @@ public Property property(final String key) { @Override public Set keys() { - return data.keys(); + return data.getProperties().keySet(); } @Override diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java index 9cae35c..da065f4 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java @@ -1,19 +1,10 @@ package com.arangodb.tinkerpop.gremlin.structure; import com.arangodb.serde.*; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; import java.util.*; -import java.util.stream.Stream; -public class ArangoDBEdgeData { - - @InternalKey - private String key; - - @InternalRev - private String rev; +public class ArangoDBEdgeData extends ArangoDBData implements PropertiesContainer { @InternalFrom private String from; @@ -21,38 +12,23 @@ public class ArangoDBEdgeData { @InternalTo private String to; - @JsonProperty - private String label; - - @JsonProperty - private final Map properties = new HashMap<>(); - public ArangoDBEdgeData() { } - public ArangoDBEdgeData(String label, String key, String from, String to) { - this.label = label; - this.key = key; + public ArangoDBEdgeData( + String label, + String key, + String from, + String to + ) { + super(label, key); + Objects.requireNonNull(from, "from"); + Objects.requireNonNull(to, "to"); + this.from = from; this.to = to; } - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getRev() { - return rev; - } - - public void setRev(String rev) { - this.rev = rev; - } - public String getFrom() { return from; } @@ -69,63 +45,26 @@ public void setTo(String to) { this.to = to; } - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Set keys() { - return properties.keySet(); - } - - public Stream> properties() { - return properties.entrySet().stream() - .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); - } - - public boolean hasProperty(String key) { - return properties.containsKey(key); - } - - public void removeProperty(String key) { - properties.remove(key); - } - - @JsonIgnore - public void setProperty(String key, Object value) { - properties.put(key, new PropertyValue(value)); - } - - @JsonIgnore - public Object getProperty(String key) { - return properties.get(key).getValue(); - } - @Override public String toString() { - return "ArangoDBEdgeDocument{" + - ", key='" + key + '\'' + - ", rev='" + rev + '\'' + + return "ArangoDBEdgeData{" + + super.toString() + ", from='" + from + '\'' + ", to='" + to + '\'' + - ", label='" + label + '\'' + - ", properties=" + properties + + ", properties=" + getProperties() + '}'; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; ArangoDBEdgeData that = (ArangoDBEdgeData) o; - return Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(from, that.from) && Objects.equals(to, that.to) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); + return Objects.equals(from, that.from) && Objects.equals(to, that.to); } @Override public int hashCode() { - return Objects.hash(key, rev, from, to, label, properties); + return Objects.hash(super.hashCode(), from, to); } - } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java similarity index 86% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java index cbc26ed..eaca24b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertyValue.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java @@ -6,13 +6,12 @@ import java.util.Objects; -public class PropertyValue { - +public class ArangoDBPropertyData { private final Object value; private final String valueType; @JsonCreator - public PropertyValue( + ArangoDBPropertyData( @JsonProperty("value") Object value, @JsonProperty("valueType") String valueType ) { @@ -20,7 +19,7 @@ public PropertyValue( this.valueType = valueType; } - public PropertyValue(Object value) { + public ArangoDBPropertyData(Object value) { this.value = value; valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); } @@ -35,7 +34,7 @@ public String getValueType() { @Override public String toString() { - return "PropertyValue{" + + return "ArangoDBPropertyValue{" + "value=" + value + ", valueType='" + valueType + '\'' + '}'; @@ -44,7 +43,7 @@ public String toString() { @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - PropertyValue that = (PropertyValue) o; + ArangoDBPropertyData that = (ArangoDBPropertyData) o; return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); } 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 8e66f01..4057613 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -73,14 +73,6 @@ public ArangoDBVertex(final String id, final String label, ArangoDBGraph graph) key = null; } - if (inferredLabel.isEmpty()) { - throw new IllegalArgumentException("empty label"); - } - - if (key != null && key.isEmpty()) { - throw new IllegalArgumentException("empty key"); - } - data = new ArangoDBVertexData(inferredLabel, key); removed = false; } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java index dc28ca1..d7c2829 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java @@ -1,88 +1,21 @@ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.serde.*; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; - import java.util.*; -public class ArangoDBVertexData { - - @InternalKey - private String key; - - @InternalRev - private String rev; - - @JsonProperty - private String label; - - @JsonProperty - private Map> properties; +public class ArangoDBVertexData extends ArangoDBData> { public ArangoDBVertexData() { } public ArangoDBVertexData(String label, String key) { - this.label = label; - this.key = key; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getRev() { - return rev; - } - - public void setRev(String rev) { - this.rev = rev; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Map> getProperties() { - if (properties == null) { - properties = new HashMap<>(); - } - return properties; - } - - public void setProperties(Map> properties) { - this.properties = properties; + super(label, key); } @Override public String toString() { - return "ArangoDBVertexDocument{" + - ", key='" + key + '\'' + - ", rev='" + rev + '\'' + - ", label='" + label + '\'' + - ", properties=" + properties + - '}'; + return "ArangoDBVertexData{" + + super.toString() + + "}"; } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - ArangoDBVertexData that = (ArangoDBVertexData) o; - return Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); - } - - @Override - public int hashCode() { - return Objects.hash(key, rev, label, properties); - } - } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java index bebf666..05a22d7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java @@ -7,16 +7,16 @@ import java.util.Map; import java.util.Objects; -public class ArangoDBVertexPropertyData extends PropertyValue { +public class ArangoDBVertexPropertyData extends ArangoDBPropertyData implements PropertiesContainer { private final String id; - private final Map properties; + private final Map properties; @JsonCreator - public ArangoDBVertexPropertyData( + ArangoDBVertexPropertyData( @JsonProperty("id") String id, @JsonProperty("value") Object value, @JsonProperty("valueType") String valueType, - @JsonProperty("properties") Map properties) { + @JsonProperty("properties") Map properties) { super(value, valueType); this.id = id; this.properties = properties; @@ -32,26 +32,10 @@ public String getId() { return id; } - public Map getProperties() { + public Map getProperties() { return properties; } - public void setProperty(String key, Object value) { - properties.put(key, new PropertyValue(value)); - } - - public boolean hasProperty(String key) { - return properties.containsKey(key); - } - - public void removeProperty(String key) { - properties.remove(key); - } - - public Object getProperty(String key) { - return properties.get(key).getValue(); - } - @Override public String toString() { return "TinkerVertexPropertyData{" + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java new file mode 100644 index 0000000..4f23904 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java @@ -0,0 +1,30 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.stream.Stream; + +interface PropertiesContainer { + Map getProperties(); + + default Stream> properties() { + return getProperties().entrySet().stream() + .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); + } + + default boolean hasProperty(String key) { + return getProperties().containsKey(key); + } + + default void removeProperty(String key) { + getProperties().remove(key); + } + + default void setProperty(String key, Object value) { + getProperties().put(key, new ArangoDBPropertyData(value)); + } + + default Object getProperty(String key) { + return getProperties().get(key).getValue(); + } +} From 85dfe91045d6b9e40cf436004cf62642de5b07aa Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 25 Feb 2025 09:14:19 +0100 Subject: [PATCH 04/28] db startup option --query.require-with --- docker/start_db.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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() { From 72c0b7812042ba977d34dc18a4bd2d62dfc614d0 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 25 Feb 2025 12:15:04 +0100 Subject: [PATCH 05/28] structure classes refactoring review --- .../gremlin/client/ArangoDBBaseDocument.java | 7 - .../gremlin/client/ArangoDBGraphClient.java | 263 ++-- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 65 +- .../persistence/AbstractElementData.java | 85 ++ .../AdbValue.java} | 32 +- .../EdgeData.java} | 36 +- .../gremlin/persistence/ElementData.java | 28 + .../gremlin/persistence/VertexData.java | 39 + .../persistence/VertexPropertyData.java | 81 ++ .../tinkerpop/gremlin/structure/AdbEdge.java | 106 ++ .../gremlin/structure/AdbElement.java | 141 +++ .../gremlin/structure/AdbEntityElement.java | 55 + ...ArangoDBProperty.java => AdbProperty.java} | 19 +- .../gremlin/structure/AdbVertex.java | 199 ++++ .../gremlin/structure/AdbVertexProperty.java | 116 ++ .../gremlin/structure/ArangoDBData.java | 87 -- .../gremlin/structure/ArangoDBEdge.java | 188 --- .../gremlin/structure/ArangoDBGraph.java | 953 +++++++-------- .../gremlin/structure/ArangoDBVertex.java | 311 ----- .../gremlin/structure/ArangoDBVertexData.java | 21 - .../structure/ArangoDBVertexProperty.java | 109 -- .../structure/ArangoDBVertexPropertyData.java | 61 - .../structure/PropertiesContainer.java | 30 - .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 1057 +++++++++-------- .../gremlin/ArangoDBGraphProvider.java | 508 ++++---- 25 files changed, 2291 insertions(+), 2306 deletions(-) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java rename src/main/java/com/arangodb/tinkerpop/gremlin/{structure/ArangoDBPropertyData.java => persistence/AdbValue.java} (56%) rename src/main/java/com/arangodb/tinkerpop/gremlin/{structure/ArangoDBEdgeData.java => persistence/EdgeData.java} (50%) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{ArangoDBProperty.java => AdbProperty.java} (74%) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java 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 efb6972..6f5d391 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java @@ -14,10 +14,6 @@ import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertexPropertyData; - -import java.util.List; -import java.util.Map; /** * The ArangoDB BaseBaseDocument provides the internal fields required for the driver to correctly @@ -48,9 +44,6 @@ public abstract class ArangoDBBaseDocument { @JsonProperty protected String label; - @JsonProperty - protected Map> properties; - /** The collection in which the element is placed. */ @JsonIgnore 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 4ca2bfc..8aa704b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -1,20 +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.client; -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; @@ -22,6 +16,10 @@ import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; import com.arangodb.model.*; +import com.arangodb.tinkerpop.gremlin.structure.AdbEdge; +import com.arangodb.tinkerpop.gremlin.structure.AdbVertex; +import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; +import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -34,9 +32,10 @@ import com.arangodb.ArangoDBException; import com.arangodb.ArangoDatabase; import com.arangodb.ArangoGraph; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder.UniqueVertices; 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 * operations on the ArangoDatabase. @@ -119,16 +118,6 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri String.format(cause, details)); } - /** - * Error persisting element property. - * - * @param ex the ex - * @return the arango DB graph exception - */ - - public static ArangoDBGraphException errorPersistingElmenentProperty(ArangoDBGraphException ex) { - return new ArangoDBGraphException("Error persisting property in element. ", ex); - } } private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); @@ -180,7 +169,7 @@ public ArangoDBGraphClient( int batchSize, boolean createDatabase) throws ArangoDBGraphException { - logger.info("Initiating the ArangoDb Client"); + logger.debug("Initiating the ArangoDb Client"); this.graph = graph; driver = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromProperties(properties)) @@ -188,7 +177,7 @@ public ArangoDBGraphClient( db = driver.db(dbname); if (createDatabase) { if (!db.exists()) { - logger.info("DB not found, attemtping to create it."); + logger.debug("DB not found, attemtping to create it."); try { if (!driver.createDatabase(dbname)) { throw new ArangoDBGraphException("Unable to crate the database " + dbname); @@ -234,7 +223,7 @@ public void shutdown() { */ public void clear(ArangoDBGraph graph) throws ArangoDBGraphException { - logger.info("Clear {}", graph.name()); + logger.debug("Clear {}", graph.name()); deleteGraph(graph.name()); } @@ -281,7 +270,7 @@ public boolean dbExists() { */ public void deleteDb() throws ArangoDBGraphException { - logger.info("Delete current db"); + logger.debug("Delete current db"); if (db != null) { try { db.drop(); @@ -377,109 +366,21 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { logger.error("Failed to update document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Document updated, new rev {}", updateEntity.getRev()); + logger.debug("Document updated, new rev {}", updateEntity.getRev()); document._rev(updateEntity.getRev()); } - /** - * Create a query to get all the edges of a vertex. - * - * @param vertexId 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( - String vertexId, - List edgeLabels, - Direction direction) - throws ArangoDBException { - logger.debug("Get Vertex's {}:{} Edges, in {}, from collections {}", vertexId, 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, vertexId, bindVars) - .graphOptions(Optional.of(UniqueVertices.NONE), Optional.empty(), true) - .filterSameCollections("e", edgeLabels, bindVars) - .ret("e"); - - String query = queryBuilder.toString(); - return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.class); - } - - /** - * Get all neighbours of a document. - * - * @param the document type - * @param vertexId 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 - */ - - public ArangoCursor getDocumentNeighbors( - String vertexId, - List edgeLabelsFilter, - Direction direction, - ArangoDBPropertyFilter propertyFilter, - Class resultType) { - logger.debug("Get Document's {}:{} Neighbors, in {}, from collections {}", vertexId, 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, vertexId, 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); - } - /** * 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 getGraphVertices( - final List ids, - final List collections) { + public ArangoCursor getGraphVertices(final List ids) { 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, ArangoDBVertexData.class); + return getGraphDocuments(ids, prefixedColNames, VertexData.class); } /** @@ -488,24 +389,28 @@ public ArangoCursor getGraphVertices( * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphEdges(List ids) { + public ArangoCursor getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); + List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); + return getGraphDocuments(ids, prefixedColNames, EdgeData.class); + } + + private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { 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); + queryBuilder.union(prefixedColNames, "d", bindVars); } else { - queryBuilder.iterateCollection("e", prefixedColNames.get(0), bindVars); + queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); } } else { - queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "e", bindVars); + queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "d", bindVars); } - queryBuilder.ret("e"); + queryBuilder.ret("d"); String query = queryBuilder.toString(); logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.class); + return executeAqlQuery(query, bindVars, null, clazz); } /** @@ -609,16 +514,15 @@ public ArangoGraph createGraph(String name, List edgeDefinitions, GraphCreateOptions options) throws ArangoDBGraphException { - logger.info("Creating graph {}", name); + logger.debug("Creating graph {}", name); try { - logger.info("Creating graph in database."); + logger.debug("Creating graph in database."); db.createGraph(name, edgeDefinitions, options); } catch (ArangoDBException e) { - logger.info("Error creating graph in database.", e); + logger.debug("Error creating graph in database.", e); throw ArangoDBExceptions.getArangoDBException(e); } - ArangoGraph g = db.graph(name); - return g; + return db.graph(name); } @@ -883,44 +787,30 @@ public ArangoCursor executeAqlQuery( // throw new ArangoDBException(e); // } // } - public void insertEdge(ArangoDBEdgeData edge) { + public void insertEdge(AdbEdge edge) { logger.debug("Insert edge {} in {} ", edge, graph.name()); EdgeEntity insertEntity; - String collection = graph.getPrefixedCollectioName(edge.getLabel()); try { insertEntity = db.graph(graph.name()) - .edgeCollection(collection) - .insertEdge(edge); + .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(collection + "/" + edge.getKey()); + throw Graph.Exceptions.edgeWithIdAlreadyExists(edge.collection() + "/" + edge.key()); } throw arangoDBException; } - edge.setKey(insertEntity.getKey()); - edge.setRev(insertEntity.getRev()); + edge.key(insertEntity.getKey()); } -// public TinkerEdgeDocument getEdge(String key, String collection) { -// logger.debug("Get edge {} from {}:{}", key, graph.name(), graph.getPrefixedCollectioName(collection)); -// try { -// return db.graph(graph.name()) -// .edgeCollection(graph.getPrefixedCollectioName(collection)) -// .getEdge(key, TinkerEdgeDocument.class); -// } catch (ArangoDBException e) { -// logger.error("Failed to retrieve edge: {}", e.getErrorMessage()); -// throw ArangoDBExceptions.getArangoDBException(e); -// } -// } - - public void deleteEdge(ArangoDBEdgeData edge) { + public void deleteEdge(AdbEdge edge) { logger.debug("Delete edge {} in {}", edge, graph.name()); try { db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .deleteEdge(edge.getKey()); + .edgeCollection(edge.collection()) + .deleteEdge(edge.key()); } catch (ArangoDBException e) { if (e.getErrorNum() == 1202) { // document not found return; @@ -930,50 +820,61 @@ public void deleteEdge(ArangoDBEdgeData edge) { } } - public void updateEdge(ArangoDBEdgeData edge) { + public void updateEdge(AdbEdge edge) { logger.debug("Update edge {} in {}", edge, graph.name()); EdgeUpdateEntity updateEntity; try { updateEntity = db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .replaceEdge(edge.getKey(), edge); + .edgeCollection(edge.collection()) + .replaceEdge(edge.key(), edge.data()); } catch (ArangoDBException e) { logger.error("Failed to update edge: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Edge updated, new rev {}", updateEntity.getRev()); - edge.setKey(updateEntity.getKey()); - edge.setRev(updateEntity.getRev()); + logger.debug("Edge updated, new rev {}", updateEntity.getRev()); + edge.key(updateEntity.getKey()); } - public void insertVertex(ArangoDBVertexData vertex) { + public VertexData readVertex(String id) { + logger.debug("Read vertex {} in {}", id, graph.name()); + String col = ArangoDBUtil.extractCollection(id); + String key = ArangoDBUtil.extractKey(id); + try { + return db.graph(graph.name()) + .vertexCollection(graph.getPrefixedCollectioName(col)) + .getVertex(key, VertexData.class); + } catch (ArangoDBException e) { + logger.error("Failed to read vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + + public void insertVertex(AdbVertex vertex) { logger.debug("Insert vertex {} in {}", vertex, graph.name()); VertexEntity vertexEntity; - String colName = graph.getPrefixedCollectioName(vertex.getLabel()); try { vertexEntity = db.graph(graph.name()) - .vertexCollection(colName) - .insertVertex(vertex); + .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(vertex.getKey()); + throw Graph.Exceptions.vertexWithIdAlreadyExists(vertex.key()); } throw arangoDBException; } - vertex.setKey(vertexEntity.getKey()); - vertex.setRev(vertexEntity.getRev()); + vertex.key(vertexEntity.getKey()); } - public void deleteVertex(ArangoDBVertexData vertex) { + public void deleteVertex(AdbVertex vertex) { logger.debug("Delete vertex {} in {}", vertex, graph.name()); try { db.graph(graph.name()) - .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) - .deleteVertex(vertex.getKey()); + .vertexCollection(vertex.collection()) + .deleteVertex(vertex.key()); } catch (ArangoDBException e) { - if(e.getErrorNum() == 1202) { // document not found + if (e.getErrorNum() == 1202) { // document not found return; } logger.error("Failed to delete vertex: {}", e.getErrorMessage()); @@ -981,19 +882,39 @@ public void deleteVertex(ArangoDBVertexData vertex) { } } - public void updateVertex(ArangoDBVertexData vertex) { + public void updateVertex(AdbVertex vertex) { logger.debug("Update document {} in {}", vertex, graph.name()); VertexUpdateEntity vertexEntity; try { vertexEntity = db.graph(graph.name()) - .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) - .replaceVertex(vertex.getKey(), vertex); + .vertexCollection(vertex.collection()) + .replaceVertex(vertex.key(), vertex.data()); } catch (ArangoDBException e) { logger.error("Failed to update document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Document updated, new rev {}", vertexEntity.getRev()); - vertex.setRev(vertexEntity.getRev()); + logger.debug("Document updated, new rev {}", vertexEntity.getRev()); } + public Iterator getVertexNeighbors(String 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, uniqueVertices: 'global', order: 'bfs' } RETURN v"; + return executeAqlQuery(query, bindVars, null, VertexData.class); + } + + public Iterator getVertexEdges(String 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/jsr223/ArangoDBGremlinPlugin.java b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java index 36e6ad3..2d143b1 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.AdbEdge; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; @@ -18,41 +20,58 @@ /** * 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, - ArangoDBGraphClient.class, - ArangoDBGraphException.class, - ArangoDBPropertyFilter.class, - ArangoDBQueryBuilder.class, - ArangoDBEdge.class, - ArangoDBEdgeData.class, - ArangoDBProperty.class, - ArangoDBGraph.class, - ArangoDBGraphVariables.class, - ArangoDBVertexPropertyData.class, - ArangoDBVertex.class, - ArangoDBVertexProperty.class, - ArangoDBUtil.class - ) - .create(); + ArangoDBBaseDocument.class, + ArangoDBGraphClient.class, + ArangoDBGraphException.class, + ArangoDBPropertyFilter.class, + ArangoDBQueryBuilder.class, + ArangoDBUtil.class, + + // structure + AdbEdge.class, + AdbElement.class, + AdbEntityElement.class, + AdbProperty.class, + AdbVertex.class, + AdbVertexProperty.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + + // persistence + AbstractElementData.class, + AdbValue.class, + EdgeData.class, + ElementData.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/AbstractElementData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java new file mode 100644 index 0000000..cd8388c --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java @@ -0,0 +1,85 @@ +/* + * 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.InternalKey; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public abstract class AbstractElementData implements ElementData { + + private String label; + @InternalKey + private String key; + private final Map properties = new HashMap<>(); + + public AbstractElementData() { + } + + public AbstractElementData(String label, String key) { + ElementHelper.validateLabel(label); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); + + this.label = label; + this.key = key; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getLabel() { + return label; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public String toString() { + return "AbstractElementData{" + + "key='" + key + '\'' + + ", label='" + label + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + AbstractElementData that = (AbstractElementData) o; + return Objects.equals(key, that.key) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(key, label, properties); + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java similarity index 56% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java index eaca24b..ccf1192 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java @@ -1,4 +1,23 @@ -package com.arangodb.tinkerpop.gremlin.structure; +/* + * 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; @@ -6,12 +25,13 @@ import java.util.Objects; -public class ArangoDBPropertyData { +public class AdbValue { + private final Object value; private final String valueType; @JsonCreator - ArangoDBPropertyData( + AdbValue( @JsonProperty("value") Object value, @JsonProperty("valueType") String valueType ) { @@ -19,7 +39,7 @@ public class ArangoDBPropertyData { this.valueType = valueType; } - public ArangoDBPropertyData(Object value) { + public AdbValue(Object value) { this.value = value; valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); } @@ -34,7 +54,7 @@ public String getValueType() { @Override public String toString() { - return "ArangoDBPropertyValue{" + + return "AdbValue{" + "value=" + value + ", valueType='" + valueType + '\'' + '}'; @@ -43,7 +63,7 @@ public String toString() { @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - ArangoDBPropertyData that = (ArangoDBPropertyData) o; + AdbValue that = (AdbValue) o; return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java similarity index 50% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java index da065f4..ec0b05c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -1,10 +1,29 @@ -package com.arangodb.tinkerpop.gremlin.structure; +/* + * 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 java.util.*; -public class ArangoDBEdgeData extends ArangoDBData implements PropertiesContainer { +public class EdgeData extends AbstractElementData { @InternalFrom private String from; @@ -12,10 +31,10 @@ public class ArangoDBEdgeData extends ArangoDBData impleme @InternalTo private String to; - public ArangoDBEdgeData() { + public EdgeData() { } - public ArangoDBEdgeData( + public EdgeData( String label, String key, String from, @@ -47,11 +66,10 @@ public void setTo(String to) { @Override public String toString() { - return "ArangoDBEdgeData{" + - super.toString() + - ", from='" + from + '\'' + + return "EdgeData{" + + "from='" + from + '\'' + ", to='" + to + '\'' + - ", properties=" + getProperties() + + ", super=" + super.toString() + '}'; } @@ -59,7 +77,7 @@ public String toString() { public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; - ArangoDBEdgeData that = (ArangoDBEdgeData) o; + EdgeData that = (EdgeData) o; return Objects.equals(from, that.from) && Objects.equals(to, that.to); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java new file mode 100644 index 0000000..998df87 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java @@ -0,0 +1,28 @@ +/* + * 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; + +public interface ElementData { + + Map getProperties(); + +} 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..b8f7001 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -0,0 +1,39 @@ +/* + * 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.List; + +public class VertexData extends AbstractElementData> { + + public VertexData() { + } + + public VertexData(String label, String key) { + super(label, key); + } + + @Override + public String toString() { + return "VertexData{" + + "super=" + super.toString() + + "}"; + } +} 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..4c06869 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -0,0 +1,81 @@ +/* + * 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 java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class VertexPropertyData extends AdbValue implements ElementData { + + private final String id; + private final Map properties; + + @JsonCreator + VertexPropertyData( + @JsonProperty("id") String id, + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType, + @JsonProperty("properties") Map properties) { + super(value, valueType); + this.id = id; + this.properties = properties; + } + + public VertexPropertyData(String id, Object value) { + super(value); + this.id = id; + this.properties = new HashMap<>(); + } + + public String getId() { + return id; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public String toString() { + return "VertexPropertyData{" + + "id='" + id + '\'' + + ", properties=" + properties + + ", super=" + super.toString() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + VertexPropertyData that = (VertexPropertyData) o; + return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java new file mode 100644 index 0000000..e026b93 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java @@ -0,0 +1,106 @@ +/* + * 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.AdbValue; +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; + +import java.util.*; +import java.util.stream.Stream; + +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; + + +public class AdbEdge extends AdbEntityElement implements Edge { + + public static AdbEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { + return new AdbEdge(graph, new EdgeData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); + } + + public AdbEdge(ArangoDBGraph graph, EdgeData data) { + super(graph, data); + } + + @Override + @SuppressWarnings("unchecked") + protected Property createProperty(String key, Object value) { + return new AdbProperty<>(this, key, (V) value); + } + + @Override + protected Stream> toProperties(String key, AdbValue value) { + return Stream.of(createProperty(key, value.getValue())); + } + + @Override + protected AdbValue toData(Object value) { + return new AdbValue(value); + } + + @Override + protected void doRemove() { + graph.getClient().deleteEdge(this); + } + + @Override + protected void update() { + graph.getClient().updateEdge(this); + } + + public void insert() { + graph.getClient().insertEdge(this); + } + + @Override + protected String stringify() { + return StringFactory.edgeString(this); + } + + @Override + public Vertex outVertex() { + return new AdbVertex(graph, graph.getClient().readVertex(data.getFrom())); + } + + @Override + public Vertex inVertex() { + return new AdbVertex(graph, graph.getClient().readVertex(data.getTo())); + } + + @Override + 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/AdbElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java new file mode 100644 index 0000000..19c9b83 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java @@ -0,0 +1,141 @@ +/* + * 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.ElementData; +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; +import java.util.stream.Stream; + +public abstract class AdbElement> implements Element { + + protected final ArangoDBGraph graph; + protected final D data; + private boolean removed = false; + + public AdbElement(ArangoDBGraph graph, D data) { + this.graph = graph; + this.data = data; + } + + protected abstract String stringify(); + + // properties transformers + protected abstract Stream> toProperties(String key, P value); + + protected abstract Property createProperty(String key, Object value); + + protected abstract P toData(Object value); + + // CRUD ops + protected abstract void update(); + + protected abstract void doRemove(); + + protected abstract void insert(); + + public D data() { + return data; + } + + protected boolean removed() { + return removed; + } + + @Override + public ArangoDBGraph graph() { + return graph; + } + + @Override + public Property property(String key, V value) { + if (removed) throw Exceptions.elementAlreadyRemoved(id()); + ElementHelper.validateProperty(key, value); + P data = toData(value); + setProperty(key, data); + update(); + return createProperty(key, value); + } + + @Override + public void remove() { + if (removed) return; + doRemove(); + removed = true; + } + + @Override + public Iterator> properties(String... propertyKeys) { + if (removed) return Collections.emptyIterator(); + return data.getProperties() + .entrySet() + .stream() + .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) + .flatMap((Map.Entry e) -> this.toProperties(e.getKey(), e.getValue())) + .collect(Collectors.toList()) // avoids ConcurrentModificationException on removal from downstream + .iterator(); + } + + public void removeProperty(String key) { + if (removed) throw Exceptions.elementAlreadyRemoved(id()); + data.getProperties().remove(key); + update(); + } + + private void setProperty(String key, P value) { + data.getProperties().put(key, value); + } + + @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/AdbEntityElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java new file mode 100644 index 0000000..8227567 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java @@ -0,0 +1,55 @@ +/* + * 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.AbstractElementData; + +import java.util.*; + +public abstract class AdbEntityElement> extends AdbElement { + + public AdbEntityElement(ArangoDBGraph graph, D data) { + super(graph, data); + } + + @Override + public String id() { + return Optional.ofNullable(key()) + .map(it -> collection() + '/' + it) + .orElse(label()); + } + + public String key() { + return data.getKey(); + } + + public void key(String key) { + data.setKey(key); + } + + @Override + public String label() { + return data.getLabel(); + } + + public String collection() { + return graph.getPrefixedCollectioName(label()); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java similarity index 74% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java index 12fb0c0..eea6d2c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java @@ -16,27 +16,27 @@ * specific language governing permissions and limitations * under the License. */ + package com.arangodb.tinkerpop.gremlin.structure; -import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -public final class ArangoDBProperty implements Property { +public class AdbProperty implements Property { private final String key; private final V value; - private final Element element; + private final AdbElement element; - public ArangoDBProperty(final Element element, final String key, final V value) { + public AdbProperty(final AdbElement element, final String key, final V value) { this.element = element; this.key = key; this.value = value; } @Override - public Element element() { + public AdbElement element() { return element; } @@ -57,13 +57,7 @@ public boolean isPresent() { @Override public void remove() { - if (element instanceof ArangoDBEdge) { - ((ArangoDBEdge) element).removeProperty(key); - } else if (element instanceof ArangoDBVertexProperty) { - ((ArangoDBVertexProperty) element).removeProperty(key); - } else { - throw new UnsupportedOperationException("Property " + this.key() + " is not an Edge"); - } + element.removeProperty(key); } @Override @@ -83,3 +77,4 @@ public int hashCode() { } } + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java new file mode 100644 index 0000000..21b787c --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java @@ -0,0 +1,199 @@ +/* + * 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.VertexData; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.arangodb.tinkerpop.gremlin.structure.AdbElement.Exceptions.elementAlreadyRemoved; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; + +public class AdbVertex extends AdbEntityElement, VertexData> implements Vertex { + + public static AdbVertex of(final String id, final String label, ArangoDBGraph graph) { + return new AdbVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); + } + + public AdbVertex(ArangoDBGraph graph, VertexData data) { + super(graph, data); + } + + @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(); + + 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 = new VertexPropertyData(idValue, value); + List list = data.getProperties().getOrDefault(key, new ArrayList<>()); + list.add(prop); + data.getProperties().put(key, list); + + AdbVertexProperty vertexProperty = new AdbVertexProperty<>(key, prop, this); + ElementHelper.attachProperties(vertexProperty, keyValues); + update(); + return vertexProperty; + } + + @Override + public Edge addEdge(String label, Vertex vertex, Object... keyValues) { + if (null == vertex) throw Graph.Exceptions.argumentCanNotBeNull("vertex"); + if (removed() || ((AdbVertex) vertex).removed()) throw elementAlreadyRemoved(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())); + } + + String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); + AdbEdge edge = AdbEdge.of(id, label, id(), (String) vertex.id(), graph); + edge.insert(); + ElementHelper.attachProperties(edge, keyValues); + return edge; + } + + @Override + protected void doRemove() { + edges(Direction.BOTH).forEachRemaining(Edge::remove); + graph.getClient().deleteVertex(this); + } + + @Override + protected String stringify() { + return StringFactory.vertexString(this); + } + + @Override + protected Stream> toProperties(String key, List value) { + return value.stream().map(it -> createProperty(key, it)); + } + + @Override + protected Property createProperty(String key, Object value) { + return new AdbVertexProperty<>(key, (VertexPropertyData) value, this); + } + + @Override + protected List toData(Object value) { + throw new UnsupportedOperationException(); + } + + @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 IteratorUtils.map(graph.getClient().getVertexEdges(id(), edgeCollections, direction), + it -> new AdbEdge(graph, it)); + } + + @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(); + } + return IteratorUtils.map(graph.getClient().getVertexNeighbors(id(), edgeCollections, direction), + it -> new AdbVertex(graph, it)); + } + + @Override + public void insert() { + graph.getClient().insertVertex(this); + } + + @Override + public void update() { + graph.getClient().updateVertex(this); + } + + @Override + public VertexProperty property(String key, V value) { + return Vertex.super.property(key, value); + } + + @Override + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); + } + + public void removeVertexProperty(AdbVertexProperty prop) { + Map> props = data.getProperties(); + List pVal = props.get(prop.key()); + if (pVal != null) { + if (pVal.remove(prop.data())) { + if (pVal.isEmpty()) { + props.remove(prop.key()); + } + } + } + } + + /** + * 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::getPrefixedCollectioName).collect(Collectors.toList()); + } else { + vertexCollections = Arrays.stream(edgeLabels) + .filter(el -> graph.edgeCollections().contains(el)) + .map(graph::getPrefixedCollectioName) + .collect(Collectors.toList()); + + } + return vertexCollections; + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java new file mode 100644 index 0000000..1e8f7eb --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.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.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.AdbValue; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.util.StringFactory; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.stream.Stream; + +public class AdbVertexProperty

extends AdbElement implements VertexProperty

{ + + private final String key; + private final AdbVertex vertex; + + public AdbVertexProperty(String key, VertexPropertyData data, AdbVertex vertex) { + super(vertex.graph(), data); + this.key = key; + this.vertex = vertex; + } + + @Override + protected boolean removed() { + return super.removed() || vertex.removed(); + } + + @Override + @SuppressWarnings("unchecked") + protected Property createProperty(String key, Object value) { + return new AdbProperty<>(this, key, (V) value); + } + + @Override + protected Stream> toProperties(String key, AdbValue value) { + return Stream.of(createProperty(key, value.getValue())); + } + + @Override + protected AdbValue toData(Object value) { + return new AdbValue(value); + } + + @Override + public String key() { + return key; + } + + @SuppressWarnings("unchecked") + @Override + public P value() throws NoSuchElementException { + return (P) data.getValue(); + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public AdbVertex element() { + return vertex; + } + + @Override + public Object id() { + return data.getId(); + } + + @Override + protected void update() { + vertex.update(); + } + + @Override + protected void doRemove() { + vertex.removeVertexProperty(this); + vertex.update(); + } + + @Override + protected void insert() { + throw new UnsupportedOperationException("TODO"); + } + + @Override + public String stringify() { + return StringFactory.propertyString(this); + } + + @Override + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java deleted file mode 100644 index e2519f7..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.serde.InternalKey; -import com.arangodb.serde.InternalRev; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -abstract class ArangoDBData { - private String label; - - @InternalKey - private String key; - - @InternalRev - private String rev; - - private Map properties = new HashMap<>(); - - public ArangoDBData() { - } - - public ArangoDBData(String label, String key) { - Objects.requireNonNull(label, "label"); - if (label.isEmpty()) throw new IllegalArgumentException("empty label"); - if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - - this.label = label; - this.key = key; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getRev() { - return rev; - } - - public void setRev(String rev) { - this.rev = rev; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Map getProperties() { - if (properties == null) { - properties = new HashMap<>(); - } - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - @Override - public String toString() { - return "key='" + key + '\'' + - ", label='" + label + '\'' + - ", rev='" + rev + '\'' + - ", properties=" + properties; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - ArangoDBData that = (ArangoDBData) o; - return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(properties, that.properties); - } - - @Override - public int hashCode() { - return Objects.hash(label, key, rev, properties); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java deleted file mode 100644 index 034b83f..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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.*; -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 java.util.*; -import java.util.stream.Collectors; - -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; - - -public class ArangoDBEdge implements Edge { - - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBEdge.class); - - private final ArangoDBGraph graph; - private final ArangoDBEdgeData data; - private boolean removed; - - public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeData data) { - this.graph = graph; - this.data = data; - this.removed = false; - } - - public ArangoDBEdge(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { - this.graph = graph; - String inferredLabel, key; - if (id != null) { - int separator = id.indexOf('/'); - if (separator > 0) { - inferredLabel = id.substring(0, separator); - key = id.substring(separator + 1); - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = id; - } - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = null; - } - - data = new ArangoDBEdgeData(inferredLabel, key, outVertexId, inVertexId); - removed = false; - } - - @Override - public String id() { - String key = data.getKey(); - if (key == null) { - return null; - } - return graph.getPrefixedCollectioName(label()) + "/" + key; - } - - @Override - public String label() { - return data.getLabel(); - } - - @Override - public ArangoDBGraph graph() { - return graph; - } - - public void insert() { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - graph.getClient().insertEdge(data); - } - - public void update() { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - graph.getClient().updateEdge(data); - } - - public void removeProperty(String key) { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - if (data.hasProperty(key)) { - data.removeProperty(key); - update(); - } - } - - @Override - @SuppressWarnings("unchecked") - public Iterator> properties(final String... propertyKeys) { - return data.properties() - .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) - .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue())) - .collect(Collectors.toList()).iterator(); - } - - @Override - public Property property(final String key, final V value) { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - LOGGER.info("set property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - data.setProperty(key, value); - update(); - return new ArangoDBProperty<>(this, key, value); - } - - @SuppressWarnings("unchecked") - @Override - public Property property(final String key) { - if (data.hasProperty(key)) { - Object value = data.getProperty(key); - return new ArangoDBProperty<>(this, key, (V) value); - } - return Property.empty(); - } - - @Override - public Set keys() { - return data.getProperties().keySet(); - } - - @Override - public void remove() { - LOGGER.info("removing {} from graph {}.", id(), graph.name()); - graph.getClient().deleteEdge(data); - this.removed = true; - } - - @Override - public Iterator values(String... propertyKeys) { - return Edge.super.values(propertyKeys); - } - - @Override - public String toString() { - return StringFactory.edgeString(this); - } - - @Override - public Iterator vertices(Direction direction) { - if (removed) return Collections.emptyIterator(); - List ids = new ArrayList<>(); - switch (direction) { - case BOTH: - ids.add(data.getFrom()); - ids.add(data.getTo()); - break; - case IN: - ids.add(data.getTo()); - break; - case OUT: - ids.add(data.getFrom()); - break; - } - return graph.getClient().getGraphVertices(ids, Collections.emptyList()).stream() - .map(it -> (Vertex) new ArangoDBVertex(graph, it)) - .iterator(); - } - - @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/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index bc937f3..1fa4130 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; @@ -33,7 +29,7 @@ import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.unsupportedIdType; +import static com.arangodb.tinkerpop.gremlin.structure.AdbElement.Exceptions.unsupportedIdType; /** * The ArangoDB graph class. @@ -143,124 +139,124 @@ @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) @Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", - method = "testAttachableCreateMethod", - reason = "test creates id without label prefix") + 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.util.detached.DetachedVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "Test creates vertex with no labels in schema-based approach") + 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.GraphTest", - method = "shouldAddVertexWithUserSuppliedStringId", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldAddVertexWithUserSuppliedStringId", + reason = "FIXME") @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.") + 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.") + 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.GraphTest", - method = "shouldEvaluateConnectivityPatterns", - reason = "FIXME") + 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") + test = "org.apache.tinkerpop.gremlin.structure.VertexPropertyTest$VertexPropertyAddition", + method = "shouldAllowIdAssignment", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertexProperty", - reason = "FIXME" + test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", + method = "shouldAllowNullAddVertexProperty", + reason = "FIXME" ) @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertex", - reason = "FIXME" + test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", + method = "shouldAllowNullAddVertex", + reason = "FIXME" ) @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", + method = "shouldNotEvaluateToEqualDifferentId", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV3Test", - method = "shouldSerializeTree", - reason = "FIXME") + 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") + test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV1Test", + method = "shouldSerializeTree", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldAttachWithCreateMethod", - reason = "FIXME") + 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") + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldCopyFromGraphAToGraphB", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", - reason = "FIXME") + 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") + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") + 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") + 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 { + public class ArangoDBGraphFeatures implements Features { - /** + /** * The Class ArangoDBGraphGraphFeatures. */ - private class ArangoDBGraphGraphFeatures implements GraphFeatures { + private class ArangoDBGraphGraphFeatures implements GraphFeatures { /** The variable features. */ - private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); + private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); - /** - * Instantiates a new ArangoDB graph graph features. - */ + /** + * Instantiates a new ArangoDB graph graph features. + */ 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 variableFeatures; - } - } + @Override + public VariableFeatures variables() { + return variableFeatures; + } + } - /** + /** * The Class ArangoDBGraphElementFeatures. */ @@ -273,33 +269,33 @@ private class ArangoDBGraphElementFeatures implements ElementFeatures { ArangoDBGraphElementFeatures() { } @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 + 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; - } + return false; + } } - /** + /** * The Class ArangoDBGraphVertexFeatures. */ @@ -307,7 +303,7 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i /** The vertex property features. */ - private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); + private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); /** * Instantiates a new ArangoDB graph vertex features. @@ -316,20 +312,20 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i ArangoDBGraphVertexFeatures () { } - @Override + @Override public VertexPropertyFeatures properties() { return vertexPropertyFeatures; } } - /** + /** * The Class ArangoDBGraphEdgeFeatures. */ public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { /** The edge property features. */ - private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); + private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); /** * Instantiates a new ArangoDB graph edge features. @@ -349,37 +345,37 @@ public EdgePropertyFeatures properties() { private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - /** - * Instantiates a new ArangoDB graph vertex property features. - */ + /** + * Instantiates a new ArangoDB graph vertex property features. + */ ArangoDBGraphVertexPropertyFeatures() { } - @Override - public boolean supportsAnyIds() { - return false; - } + @Override + public boolean supportsAnyIds() { + return false; + } - @Override - public boolean supportsCustomIds() { - return false; - } + @Override + public boolean supportsCustomIds() { + return false; + } - @Override - public boolean supportsNumericIds() { - 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 + @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; - } + return false; + } } /** @@ -387,16 +383,16 @@ public boolean supportsUuidIds() { */ private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { - /** - * Instantiates a new ArangoDB graph edge property features. - */ + /** + * Instantiates a new ArangoDB graph edge property features. + */ ArangoDBGraphEdgePropertyFeatures() { } } /** The graph features. */ - protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); + protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); /** The vertex features. */ @@ -407,29 +403,29 @@ private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); @Override - public EdgeFeatures edge() { - return edgeFeatures; - } + public EdgeFeatures edge() { + return edgeFeatures; + } @Override - public GraphFeatures graph() { - return graphFeatures; - } + public GraphFeatures graph() { + return graphFeatures; + } @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 vertexFeatures; + } } /** 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. */ @@ -457,431 +453,392 @@ public VertexFeatures vertex() { /** The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES **/ - public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; + public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; /** The Constant DEFAULT_VERTEX_COLLECTION. */ - public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; + public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; /** The Constant DEFAULT_VERTEX_COLLECTION. */ - public static final String DEFAULT_EDGE_COLLECTION = "edge"; + public static final String DEFAULT_EDGE_COLLECTION = "edge"; /** The Constant GRAPH_VARIABLES_COLLECTION. */ - public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; + public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; /** The Constant ELEMENT_PROPERTIES_COLLECTION. */ - public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; + public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; /** The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. */ - public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; + public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; - public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); + public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); /** The features. */ - private final Features FEATURES = new ArangoDBGraphFeatures(); + private final Features FEATURES = new ArangoDBGraphFeatures(); /** A ArangoDBGraphClient to handle the connection to the Database. */ - private ArangoDBGraphClient client = null; + private ArangoDBGraphClient client = null; /** The name. */ - private String name; + private String name; /** The vertex collections. */ - private final List vertexCollections; + private final List vertexCollections; /** The edge collections. */ - private final List edgeCollections; + private final List edgeCollections; /** The relations. */ - private final List relations; + private final List relations; /** Flat to indicate that the graph has no schema. */ - private boolean schemaless = false; + private boolean schemaless = false; /** The configuration. */ - private Configuration configuration; + private Configuration configuration; /** If collection names should be prefixed with graph name */ - private final boolean shouldPrefixCollectionNames; + 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)) { + 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(); // 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::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)); 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 label; if (!schemaless) { - label = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(label); - } - else { - label = DEFAULT_VERTEX_COLLECTION; + label = ElementHelper.getLabelValue(keyValues).orElse(null); + ElementHelper.validateLabel(label); + } else { + label = DEFAULT_VERTEX_COLLECTION; } if (!vertexCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, 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(label)) { - id = parts[1]; - - } - } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); - if (m.matches()) { - vertex = new ArangoDBVertex(id.toString(), label, this); - } - else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - - } - else { - vertex = new ArangoDBVertex(null, label, this); + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); } + + String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); + AdbVertex vertex = AdbVertex.of(id, label, this); + // The vertex needs to exist before we can attach properties - vertex.insert(); + vertex.insert(); 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 ids = Arrays.stream(edgeIds) - .map(id -> { - if (id instanceof ArangoDBEdge) { - return ((ArangoDBEdge) id).id(); - } else if(id instanceof String) { - // We only support String ids - return (String) id; - } else { - throw unsupportedIdType(id); - } - }) - .collect(Collectors.toList()); - return getClient().getGraphEdges(ids).stream() - .map(it -> (Edge) new ArangoDBEdge(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; + } + + @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(edgeIds)).stream() + .map(it -> (Edge) new AdbEdge(this, it)) + .iterator(); + } + + @Override + public Iterator vertices(Object... vertexIds) { + return getClient().getGraphVertices(getIdValues(vertexIds)).stream() + .map(it -> (Vertex) new AdbVertex(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"); + } + } + + /** + * 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 getPrefixedCollectioName(String collectionName) { + if (GRAPH_VARIABLES_COLLECTION.equals(collectionName)) { + return collectionName; + } + if (GRAPH_COLLECTIONS.contains(collectionName)) { + return String.format("%s_%s", name, collectionName); } - else { - throw new ArangoDBGraphException("Existing graph does not have a Variables collection"); + if (shouldPrefixCollectionNames) { + if (collectionName.startsWith(name + "_")) { + return collectionName; + } + return String.format("%s_%s", name, collectionName); + } else { + 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 getClient().getGraphVertices(ids, vertexCollections).stream() - .map(it -> (Vertex) new ArangoDBVertex(this, it)) - .iterator(); - } - - /** - * 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) { - if(collectionName.startsWith(name + "_")) { - return collectionName; - } - 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 + } + + @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 String getIdValue(Object id) { + if (id instanceof String) { + return (String) id; + } else if (id instanceof Element) { + return getIdValue(((Element) id).id()); + } else { + throw unsupportedIdType(id); + } + } + + private List getIdValues(Object[] edgeIds) { + return Arrays.stream(edgeIds) + .map(this::getIdValue) + .collect(Collectors.toList()); + } + + + // 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/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java deleted file mode 100644 index 4057613..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ /dev/null @@ -1,311 +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.*; -import java.util.regex.Matcher; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -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 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.tinkerpop.gremlin.client.ArangoDBGraphException; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; - - -/** - * 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) - */ - -public class ArangoDBVertex implements Vertex { - - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertex.class); - - private final ArangoDBGraph graph; - private final ArangoDBVertexData data; - private boolean removed; - - public ArangoDBVertex(ArangoDBGraph graph, ArangoDBVertexData data) { - this.graph = graph; - this.data = data; - this.removed = false; - } - - public ArangoDBVertex(final String id, final String label, ArangoDBGraph graph) { - this.graph = graph; - String inferredLabel, key; - if (id != null) { - int separator = id.indexOf('/'); - if (separator > 0) { - inferredLabel = id.substring(0, separator); - key = id.substring(separator + 1); - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = id; - } - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = null; - } - - data = new ArangoDBVertexData(inferredLabel, key); - removed = false; - } - - public boolean isRemoved() { - return removed; - } - - @Override - public String id() { - String key = data.getKey(); - if (key == null) { - return null; - } - return graph.getPrefixedCollectioName(label()) + "/" + key; - } - - @Override - public String label() { - return data.getLabel(); - } - - @Override - public ArangoDBGraph graph() { - return graph; - } - - @Override - public void remove() { - if (removed) return; - LOGGER.info("removing {} from graph {}.", id(), graph.name()); - edges(Direction.BOTH).forEachRemaining(Edge::remove); - graph.getClient().deleteVertex(data); - this.removed = true; - } - - @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, (String) this.id(), (String) inVertex.id(), graph); - } else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - } else { - edge = new ArangoDBEdge(null, label, (String) this.id(), (String) inVertex.id(), graph); - } - // The vertex needs to exist before we can attach properties - edge.insert(); - ElementHelper.attachProperties(edge, keyValues); - return edge; - } - - @Override - public VertexProperty property( - final Cardinality cardinality, - final String key, - final V value, - final Object... keyValues - ) { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - ElementHelper.legalPropertyKeyValueArray(keyValues); - ElementHelper.validateProperty(key, value); - - final Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); - if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); - - Optional optionalId = ElementHelper.getIdValue(keyValues); - Object[] filteredKeyValues = ArrayUtils.clone(keyValues); - String idValue = null; - if (optionalId.isPresent()) { - if (graph.features().vertex().properties().willAllowId(optionalId.get())) { - idValue = optionalId.get().toString(); - } else { - throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - int idIndex = 0; - for (int i = 0; i < filteredKeyValues.length; i += 2) { - if (filteredKeyValues[i] == T.id) { - idIndex = i; - break; - } - } - filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); - filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); - } - - if (idValue == null) { - idValue = UUID.randomUUID().toString(); - } - - ArangoDBVertexPropertyData prop = new ArangoDBVertexPropertyData(idValue, value); - - final List list = data.getProperties().getOrDefault(key, new ArrayList<>()); - list.add(prop); - data.getProperties().put(key, list); - - ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); - ElementHelper.attachProperties(vertexProperty, filteredKeyValues); - update(); - return vertexProperty; - } - - - @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 graph.getClient().getVertexEdges(id(), edgeCollections, direction) - .stream() - .map(it -> (Edge) new ArangoDBEdge(graph, it)) - .iterator(); - } - - - @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(); - } - return graph.getClient().getDocumentNeighbors(id(), edgeCollections, direction, ArangoDBPropertyFilter.empty(), ArangoDBVertexData.class).stream() - .map(it -> (Vertex) new ArangoDBVertex(graph, it)) - .iterator(); - } - - - @SuppressWarnings("unchecked") - @Override - public Iterator> properties(String... propertyKeys) { - LOGGER.debug("Get properties {}", (Object[]) propertyKeys); - return allProperties() - .filter(it -> ElementHelper.keyExists(it.key(), propertyKeys)) - .map(it -> (VertexProperty) it) - .iterator(); - } - - - @Override - public String toString() { - return StringFactory.vertexString(this); - } - - private Stream> allProperties() { - return data.getProperties().entrySet().stream() - .flatMap(x -> x.getValue().stream() - .map(y -> new ArangoDBVertexProperty<>(x.getKey(), y, this)) - ); - } - - public void insert() { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - graph.getClient().insertVertex(data); - } - - - public void update() { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - graph.getClient().updateVertex(data); - } - - public void removeProperty(ArangoDBVertexPropertyData prop) { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - for (List it : data.getProperties().values()) { - if (it.remove(prop)) return; - } - } - - /** - * 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()); - - } - return vertexCollections; - } - - @Override - @SuppressWarnings("EqualsDoesntCheckParameterClass") - 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/ArangoDBVertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java deleted file mode 100644 index d7c2829..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.*; - -public class ArangoDBVertexData extends ArangoDBData> { - - public ArangoDBVertexData() { - } - - public ArangoDBVertexData(String label, String key) { - super(label, key); - } - - @Override - public String toString() { - return "ArangoDBVertexData{" + - super.toString() + - "}"; - } -} - diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java deleted file mode 100644 index 143b8ee..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -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 java.util.*; -import java.util.stream.Collectors; - -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; - -public class ArangoDBVertexProperty implements Element, VertexProperty { - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertexProperty.class); - - private final String key; - private final ArangoDBVertex vertex; - private final ArangoDBVertexPropertyData data; - private boolean removed; - - public ArangoDBVertexProperty(String key, ArangoDBVertexPropertyData data, ArangoDBVertex vertex) { - this.key = key; - this.data = data; - this.vertex = vertex; - removed = false; - } - - @Override - public String key() { - return key; - } - - @SuppressWarnings("unchecked") - @Override - public V value() throws NoSuchElementException { - return (V) data.getValue(); - } - - @Override - public boolean isPresent() { - return true; - } - - @Override - public Vertex element() { - return vertex; - } - - - @Override - public Object id() { - return data.getId(); - } - - @Override - public Property property(String key, W value) { - if (removed) throw elementAlreadyRemoved(VertexProperty.class, id()); - LOGGER.info("set property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - data.setProperty(key, value); - vertex.update(); - return new ArangoDBProperty<>(this, key, value); - } - - @Override - public void remove() { - if (removed) return; - vertex.removeProperty(data); - vertex.update(); - removed = true; - } - - @Override - @SuppressWarnings("unchecked") - public Iterator> properties(String... propertyKeys) { - return data.getProperties() - .entrySet() - .stream() - .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) - .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue().getValue())) - .collect(Collectors.toList()).iterator(); - } - - public void removeProperty(String key) { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - if (data.hasProperty(key)) { - data.removeProperty(key); - vertex.update(); - } - } - - @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((Property) this); - } - -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java deleted file mode 100644 index 05a22d7..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class ArangoDBVertexPropertyData extends ArangoDBPropertyData implements PropertiesContainer { - private final String id; - private final Map properties; - - @JsonCreator - ArangoDBVertexPropertyData( - @JsonProperty("id") String id, - @JsonProperty("value") Object value, - @JsonProperty("valueType") String valueType, - @JsonProperty("properties") Map properties) { - super(value, valueType); - this.id = id; - this.properties = properties; - } - - public ArangoDBVertexPropertyData(String id, Object value) { - super(value); - this.id = id; - this.properties = new HashMap<>(); - } - - public String getId() { - return id; - } - - public Map getProperties() { - return properties; - } - - @Override - public String toString() { - return "TinkerVertexPropertyData{" + - "id='" + id + '\'' + - ", value=" + getValue() + - ", valueType='" + getValueType() + '\'' + - ", properties=" + properties + - '}'; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - ArangoDBVertexPropertyData that = (ArangoDBVertexPropertyData) o; - return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), id, properties); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java deleted file mode 100644 index 4f23904..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.AbstractMap; -import java.util.Map; -import java.util.stream.Stream; - -interface PropertiesContainer { - Map getProperties(); - - default Stream> properties() { - return getProperties().entrySet().stream() - .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); - } - - default boolean hasProperty(String key) { - return getProperties().containsKey(key); - } - - default void removeProperty(String key) { - getProperties().remove(key); - } - - default void setProperty(String key, Object value) { - getProperties().put(key, new ArangoDBPropertyData(value)); - } - - default Object getProperty(String key) { - return getProperties().get(key).getValue(); - } -} 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 3a32072..f5bdcda 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -1,10 +1,10 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.utils; @@ -21,354 +21,364 @@ import com.arangodb.tinkerpop.gremlin.structure.*; 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.*; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.collections4.CollectionUtils; import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 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(); + + /** + * 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.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.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."); - } + // 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)); + 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))); - } + 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); + } + } + + /** + * 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 + */ - } - - 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()); - } + 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. * - * @param graph The graph + * @param graph The graph * @param vertexCollections the vertex collections - * @param edgeCollections the edge collections + * @param edgeCollections the edge collections * @return the edge definition */ - + public static EdgeDefinition createPropertyEdgeDefinitions( - final ArangoDBGraph graph, - final List vertexCollections, - final List edgeCollections) { + 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)); @@ -381,208 +391,233 @@ public static EdgeDefinition createPropertyEdgeDefinitions( } /** - * Creates an Arango DB vertex property. + * Gets the correct primitive. * - * @param the generic type - * @param id the id - * @param key the name - * @param value the value - * @param vertex the vertex - * @return the created Arango DB vertex property + * @param value the value + * @param valueClass the exoected class of the value + * @param the value type + * @return the correct Java primitive */ - -// public static TinkerVertexProperty createArangoDBVertexProperty(String id, String key, U value, ArangoDBVertex vertex) { -// TinkerVertexProperty p; -// p = new TinkerVertexProperty<>(id, key, value, vertex); -// insertElementAndProperty(vertex, p); -// return p; -// } + + @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[]": + 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; + } /** - * Gets the correct primitive. + * Translate a Gremlin direction to Arango direction * - * @param value the value - * @param valueClass the exoected class of the value - * @param the value type - * @return the correct Java primitive + * @param direction the direction to translate + * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction */ - - @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[]": - 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; + 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); + } + + public static 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; + } + } + + public static 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; + } + } + + public static Optional extractLabel(final String id, final String label) { + String col = extractCollection(id); + if (col != null) { + if (label != null && !label.equals(col)) { + throw new IllegalArgumentException("Invalid label: [" + label + "] for id: [" + id + "]"); + } + return Optional.of(col); + } + return Optional.ofNullable(label); } - /** - * 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; - } - - public static IllegalStateException elementAlreadyRemoved(final Class clazz, final Object id) { - return new IllegalStateException(String.format("%s with id %s was removed.", clazz.getSimpleName(), id)); - } - - public static IllegalStateException unsupportedIdType(final Object id) { - return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); - } + public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { + Optional optionalId = ElementHelper.getIdValue(keyValues); + if (!optionalId.isPresent()) { + return null; + } + Object id = optionalId.get(); + if (features.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()) { + return id.toString(); + } else { + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); + } + } else { + throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index 0a9ff28..1b4973d 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -5,6 +5,8 @@ import java.util.Properties; import java.util.Set; +import com.arangodb.tinkerpop.gremlin.persistence.*; +import com.arangodb.tinkerpop.gremlin.structure.AdbEdge; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.ConfigurationConverter; @@ -22,22 +24,22 @@ * 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(ArangoDBProperty.class); - add(ArangoDBVertexPropertyData.class); - add(ArangoDBVertex.class); - add(ArangoDBVertexProperty.class); + + /** + * The Constant IMPLEMENTATIONS. + */ + private static final Set IMPLEMENTATIONS = new HashSet() {{ + add(AbstractElementData.class); + add(AdbValue.class); + add(EdgeData.class); + add(ElementData.class); + add(VertexData.class); + add(VertexPropertyData.class); }}; - - + + @Override public Configuration newGraphConfiguration(final String graphName, final Class test, final String testMethodName, @@ -52,258 +54,240 @@ public Configuration newGraphConfiguration(final String graphName, final Class 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(); - } - - } + 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 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 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) { From 61068375bc00d21b21a6ebb93d2d267eb90f2ed2 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Sat, 1 Mar 2025 20:07:29 +0100 Subject: [PATCH 06/28] renamed structure classes --- .../gremlin/client/ArangoDBGraphClient.java | 18 +++++++------- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 14 +++++------ .../{AdbEdge.java => ArangoDBEdge.java} | 14 +++++------ .../{AdbElement.java => ArangoDBElement.java} | 4 ++-- ...lement.java => ArangoDBEntityElement.java} | 4 ++-- .../gremlin/structure/ArangoDBGraph.java | 8 +++---- ...AdbProperty.java => ArangoDBProperty.java} | 8 +++---- .../{AdbVertex.java => ArangoDBVertex.java} | 24 +++++++++---------- ...perty.java => ArangoDBVertexProperty.java} | 10 ++++---- .../gremlin/ArangoDBGraphProvider.java | 1 - 10 files changed, 52 insertions(+), 53 deletions(-) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbEdge.java => ArangoDBEdge.java} (80%) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbElement.java => ArangoDBElement.java} (96%) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbEntityElement.java => ArangoDBEntityElement.java} (89%) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbProperty.java => ArangoDBProperty.java} (88%) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbVertex.java => ArangoDBVertex.java} (86%) rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{AdbVertexProperty.java => ArangoDBVertexProperty.java} (88%) 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 8aa704b..4d4af9a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -16,8 +16,8 @@ import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; import com.arangodb.model.*; -import com.arangodb.tinkerpop.gremlin.structure.AdbEdge; -import com.arangodb.tinkerpop.gremlin.structure.AdbVertex; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertex; import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.structure.*; @@ -787,7 +787,7 @@ public ArangoCursor executeAqlQuery( // throw new ArangoDBException(e); // } // } - public void insertEdge(AdbEdge edge) { + public void insertEdge(ArangoDBEdge edge) { logger.debug("Insert edge {} in {} ", edge, graph.name()); EdgeEntity insertEntity; try { @@ -805,7 +805,7 @@ public void insertEdge(AdbEdge edge) { edge.key(insertEntity.getKey()); } - public void deleteEdge(AdbEdge edge) { + public void deleteEdge(ArangoDBEdge edge) { logger.debug("Delete edge {} in {}", edge, graph.name()); try { db.graph(graph.name()) @@ -820,7 +820,7 @@ public void deleteEdge(AdbEdge edge) { } } - public void updateEdge(AdbEdge edge) { + public void updateEdge(ArangoDBEdge edge) { logger.debug("Update edge {} in {}", edge, graph.name()); EdgeUpdateEntity updateEntity; try { @@ -841,7 +841,7 @@ public VertexData readVertex(String id) { String key = ArangoDBUtil.extractKey(id); try { return db.graph(graph.name()) - .vertexCollection(graph.getPrefixedCollectioName(col)) + .vertexCollection(col) .getVertex(key, VertexData.class); } catch (ArangoDBException e) { logger.error("Failed to read vertex: {}", e.getErrorMessage()); @@ -849,7 +849,7 @@ public VertexData readVertex(String id) { } } - public void insertVertex(AdbVertex vertex) { + public void insertVertex(ArangoDBVertex vertex) { logger.debug("Insert vertex {} in {}", vertex, graph.name()); VertexEntity vertexEntity; try { @@ -867,7 +867,7 @@ public void insertVertex(AdbVertex vertex) { vertex.key(vertexEntity.getKey()); } - public void deleteVertex(AdbVertex vertex) { + public void deleteVertex(ArangoDBVertex vertex) { logger.debug("Delete vertex {} in {}", vertex, graph.name()); try { db.graph(graph.name()) @@ -882,7 +882,7 @@ public void deleteVertex(AdbVertex vertex) { } } - public void updateVertex(AdbVertex vertex) { + public void updateVertex(ArangoDBVertex vertex) { logger.debug("Update document {} in {}", vertex, graph.name()); VertexUpdateEntity vertexEntity; try { 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 2d143b1..e10ace4 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -9,7 +9,7 @@ package com.arangodb.tinkerpop.gremlin.jsr223; import com.arangodb.tinkerpop.gremlin.persistence.*; -import com.arangodb.tinkerpop.gremlin.structure.AdbEdge; +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; @@ -46,12 +46,12 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { ArangoDBUtil.class, // structure - AdbEdge.class, - AdbElement.class, - AdbEntityElement.class, - AdbProperty.class, - AdbVertex.class, - AdbVertexProperty.class, + ArangoDBEdge.class, + ArangoDBElement.class, + ArangoDBEntityElement.class, + ArangoDBProperty.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java similarity index 80% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java index e026b93..c35c52f 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -31,20 +31,20 @@ import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; -public class AdbEdge extends AdbEntityElement implements Edge { +public class ArangoDBEdge extends ArangoDBEntityElement implements Edge { - public static AdbEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { - return new AdbEdge(graph, new EdgeData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); + public static ArangoDBEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { + return new ArangoDBEdge(graph, new EdgeData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); } - public AdbEdge(ArangoDBGraph graph, EdgeData data) { + public ArangoDBEdge(ArangoDBGraph graph, EdgeData data) { super(graph, data); } @Override @SuppressWarnings("unchecked") protected Property createProperty(String key, Object value) { - return new AdbProperty<>(this, key, (V) value); + return new ArangoDBProperty<>(this, key, (V) value); } @Override @@ -78,12 +78,12 @@ protected String stringify() { @Override public Vertex outVertex() { - return new AdbVertex(graph, graph.getClient().readVertex(data.getFrom())); + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getFrom())); } @Override public Vertex inVertex() { - return new AdbVertex(graph, graph.getClient().readVertex(data.getTo())); + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getTo())); } @Override diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java similarity index 96% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java index 19c9b83..1b547b7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java @@ -30,13 +30,13 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public abstract class AdbElement> implements Element { +public abstract class ArangoDBElement> implements Element { protected final ArangoDBGraph graph; protected final D data; private boolean removed = false; - public AdbElement(ArangoDBGraph graph, D data) { + public ArangoDBElement(ArangoDBGraph graph, D data) { this.graph = graph; this.data = data; } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java similarity index 89% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java index 8227567..6aceaee 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbEntityElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java @@ -23,9 +23,9 @@ import java.util.*; -public abstract class AdbEntityElement> extends AdbElement { +public abstract class ArangoDBEntityElement> extends ArangoDBElement { - public AdbEntityElement(ArangoDBGraph graph, D data) { + public ArangoDBEntityElement(ArangoDBGraph graph, D data) { super(graph, data); } 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 1fa4130..3549e9a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -29,7 +29,7 @@ import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; -import static com.arangodb.tinkerpop.gremlin.structure.AdbElement.Exceptions.unsupportedIdType; +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.unsupportedIdType; /** * The ArangoDB graph class. @@ -623,7 +623,7 @@ public Vertex addVertex(Object... keyValues) { } String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); - AdbVertex vertex = AdbVertex.of(id, label, this); + ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); // The vertex needs to exist before we can attach properties vertex.insert(); @@ -698,14 +698,14 @@ public List edgeCollections() { @Override public Iterator edges(Object... edgeIds) { return getClient().getGraphEdges(getIdValues(edgeIds)).stream() - .map(it -> (Edge) new AdbEdge(this, it)) + .map(it -> (Edge) new ArangoDBEdge(this, it)) .iterator(); } @Override public Iterator vertices(Object... vertexIds) { return getClient().getGraphVertices(getIdValues(vertexIds)).stream() - .map(it -> (Vertex) new AdbVertex(this, it)) + .map(it -> (Vertex) new ArangoDBVertex(this, it)) .iterator(); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java similarity index 88% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java index eea6d2c..34d3d7a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java @@ -23,20 +23,20 @@ import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -public class AdbProperty implements Property { +public class ArangoDBProperty implements Property { private final String key; private final V value; - private final AdbElement element; + private final ArangoDBElement element; - public AdbProperty(final AdbElement element, final String key, final V value) { + public ArangoDBProperty(final ArangoDBElement element, final String key, final V value) { this.element = element; this.key = key; this.value = value; } @Override - public AdbElement element() { + public ArangoDBElement element() { return element; } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java similarity index 86% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index 21b787c..3ad7ad7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -31,16 +31,16 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.arangodb.tinkerpop.gremlin.structure.AdbElement.Exceptions.elementAlreadyRemoved; +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.elementAlreadyRemoved; import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; -public class AdbVertex extends AdbEntityElement, VertexData> implements Vertex { +public class ArangoDBVertex extends ArangoDBEntityElement, VertexData> implements Vertex { - public static AdbVertex of(final String id, final String label, ArangoDBGraph graph) { - return new AdbVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); + public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { + return new ArangoDBVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); } - public AdbVertex(ArangoDBGraph graph, VertexData data) { + public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { super(graph, data); } @@ -72,7 +72,7 @@ public VertexProperty property( list.add(prop); data.getProperties().put(key, list); - AdbVertexProperty vertexProperty = new AdbVertexProperty<>(key, prop, this); + ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); ElementHelper.attachProperties(vertexProperty, keyValues); update(); return vertexProperty; @@ -81,7 +81,7 @@ public VertexProperty property( @Override public Edge addEdge(String label, Vertex vertex, Object... keyValues) { if (null == vertex) throw Graph.Exceptions.argumentCanNotBeNull("vertex"); - if (removed() || ((AdbVertex) vertex).removed()) throw elementAlreadyRemoved(id()); + if (removed() || ((ArangoDBVertex) vertex).removed()) throw elementAlreadyRemoved(id()); ElementHelper.legalPropertyKeyValueArray(keyValues); ElementHelper.validateLabel(label); @@ -91,7 +91,7 @@ public Edge addEdge(String label, Vertex vertex, Object... keyValues) { } String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); - AdbEdge edge = AdbEdge.of(id, label, id(), (String) vertex.id(), graph); + ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), graph); edge.insert(); ElementHelper.attachProperties(edge, keyValues); return edge; @@ -115,7 +115,7 @@ protected Stream> toProperties(String key, List Property createProperty(String key, Object value) { - return new AdbVertexProperty<>(key, (VertexPropertyData) value, this); + return new ArangoDBVertexProperty<>(key, (VertexPropertyData) value, this); } @Override @@ -132,7 +132,7 @@ public Iterator edges(Direction direction, String... edgeLabels) { return Collections.emptyIterator(); } return IteratorUtils.map(graph.getClient().getVertexEdges(id(), edgeCollections, direction), - it -> new AdbEdge(graph, it)); + it -> new ArangoDBEdge(graph, it)); } @Override @@ -144,7 +144,7 @@ public Iterator vertices(Direction direction, String... edgeLabels) { return Collections.emptyIterator(); } return IteratorUtils.map(graph.getClient().getVertexNeighbors(id(), edgeCollections, direction), - it -> new AdbVertex(graph, it)); + it -> new ArangoDBVertex(graph, it)); } @Override @@ -167,7 +167,7 @@ public Iterator> properties(String... propertyKeys) { return IteratorUtils.cast(super.properties(propertyKeys)); } - public void removeVertexProperty(AdbVertexProperty prop) { + public void removeVertexProperty(ArangoDBVertexProperty prop) { Map> props = data.getProperties(); List pVal = props.get(prop.key()); if (pVal != null) { diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java similarity index 88% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java index 1e8f7eb..a74d84c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/AdbVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -29,12 +29,12 @@ import java.util.NoSuchElementException; import java.util.stream.Stream; -public class AdbVertexProperty

extends AdbElement implements VertexProperty

{ +public class ArangoDBVertexProperty

extends ArangoDBElement implements VertexProperty

{ private final String key; - private final AdbVertex vertex; + private final ArangoDBVertex vertex; - public AdbVertexProperty(String key, VertexPropertyData data, AdbVertex vertex) { + public ArangoDBVertexProperty(String key, VertexPropertyData data, ArangoDBVertex vertex) { super(vertex.graph(), data); this.key = key; this.vertex = vertex; @@ -48,7 +48,7 @@ protected boolean removed() { @Override @SuppressWarnings("unchecked") protected Property createProperty(String key, Object value) { - return new AdbProperty<>(this, key, (V) value); + return new ArangoDBProperty<>(this, key, (V) value); } @Override @@ -78,7 +78,7 @@ public boolean isPresent() { } @Override - public AdbVertex element() { + public ArangoDBVertex element() { return vertex; } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index 1b4973d..3469234 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -6,7 +6,6 @@ import java.util.Set; import com.arangodb.tinkerpop.gremlin.persistence.*; -import com.arangodb.tinkerpop.gremlin.structure.AdbEdge; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.ConfigurationConverter; From 179db7c2d6836000e1229a8905fbcb4a7b9bae4d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Sat, 1 Mar 2025 20:38:58 +0100 Subject: [PATCH 07/28] explicit diamond inheritance resolution --- .../tinkerpop/gremlin/structure/ArangoDBVertex.java | 7 ++++++- .../gremlin/structure/ArangoDBVertexProperty.java | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) 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 3ad7ad7..f2b4307 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -158,7 +158,12 @@ public void update() { } @Override - public VertexProperty property(String key, V value) { + public VertexProperty property(final String key) { + return Vertex.super.property(key); + } + + @Override + public VertexProperty property(final String key, final V value) { return Vertex.super.property(key, value); } 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 a74d84c..49859db 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -112,5 +112,10 @@ public String stringify() { public Iterator> properties(String... propertyKeys) { return IteratorUtils.cast(super.properties(propertyKeys)); } + + @Override + public String label() { + return VertexProperty.super.label(); + } } From 25db55018f95e2e854d35e10b2debdf7217a9db1 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 3 Mar 2025 11:34:41 +0100 Subject: [PATCH 08/28] persistence refactoring --- .../gremlin/client/ArangoDBGraphClient.java | 8 ++--- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 4 +-- .../gremlin/persistence/AdbValue.java | 6 ++-- .../gremlin/persistence/EdgeData.java | 18 +++++++---- ...ctElementData.java => PersistentData.java} | 25 +++++----------- .../{ElementData.java => PropertyData.java} | 6 ++-- .../gremlin/persistence/VertexData.java | 30 +++++++++++++++++-- .../persistence/VertexPropertyData.java | 4 +-- .../gremlin/structure/ArangoDBElement.java | 4 +-- .../structure/ArangoDBEntityElement.java | 4 +-- .../gremlin/ArangoDBGraphProvider.java | 4 +-- 11 files changed, 67 insertions(+), 46 deletions(-) rename src/main/java/com/arangodb/tinkerpop/gremlin/persistence/{AbstractElementData.java => PersistentData.java} (68%) rename src/main/java/com/arangodb/tinkerpop/gremlin/persistence/{ElementData.java => PropertyData.java} (92%) 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 4d4af9a..e3c208b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -395,7 +395,7 @@ public ArangoCursor getGraphEdges(List ids) { return getGraphDocuments(ids, prefixedColNames, EdgeData.class); } - private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { + private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); if (ids.isEmpty()) { @@ -539,7 +539,7 @@ public ArangoGraph getArangoGraph() { /** * Execute AQL query. * - * @param the generic type of the returned values + * @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 @@ -548,11 +548,11 @@ public ArangoGraph getArangoGraph() { * @throws ArangoDBGraphException if executing the query raised an exception */ - public ArangoCursor executeAqlQuery( + public ArangoCursor executeAqlQuery( String query, Map bindVars, AqlQueryOptions aqlQueryOptions, - final Class type) + final Class type) throws ArangoDBGraphException { logger.debug("Executing AQL query ({}) against db, with bind vars: {}", query, bindVars); try { 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 e10ace4..9e25f36 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -56,10 +56,10 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { ArangoDBGraphVariables.class, // persistence - AbstractElementData.class, + PersistentData.class, AdbValue.class, EdgeData.class, - ElementData.class, + PropertyData.class, VertexData.class, VertexPropertyData.class ) diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java index ccf1192..d8f4f11 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java @@ -62,9 +62,9 @@ public String toString() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - AdbValue that = (AdbValue) o; - return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); + if (!(o instanceof AdbValue)) return false; + AdbValue adbValue = (AdbValue) o; + return Objects.equals(value, adbValue.value) && Objects.equals(valueType, adbValue.valueType); } @Override diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java index ec0b05c..5fef249 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -23,7 +23,7 @@ import java.util.*; -public class EdgeData extends AbstractElementData { +public class EdgeData extends PersistentData { @InternalFrom private String from; @@ -31,6 +31,8 @@ public class EdgeData extends AbstractElementData { @InternalTo private String to; + private final Map properties = new HashMap<>(); + public EdgeData() { } @@ -64,25 +66,31 @@ public void setTo(String to) { this.to = to; } + @Override + public Map getProperties() { + return properties; + } + @Override public String toString() { return "EdgeData{" + "from='" + from + '\'' + ", to='" + to + '\'' + + ", properties=" + properties + ", super=" + super.toString() + '}'; } @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof EdgeData)) return false; if (!super.equals(o)) return false; - EdgeData that = (EdgeData) o; - return Objects.equals(from, that.from) && Objects.equals(to, that.to); + EdgeData edgeData = (EdgeData) o; + return Objects.equals(from, edgeData.from) && Objects.equals(to, edgeData.to) && Objects.equals(properties, edgeData.properties); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), from, to); + return Objects.hash(super.hashCode(), from, to, properties); } } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java similarity index 68% rename from src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java index cd8388c..914a87a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AbstractElementData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java @@ -22,21 +22,18 @@ import com.arangodb.serde.InternalKey; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; -public abstract class AbstractElementData implements ElementData { +public abstract class PersistentData implements PropertyData { private String label; @InternalKey private String key; - private final Map properties = new HashMap<>(); - public AbstractElementData() { + public PersistentData() { } - public AbstractElementData(String label, String key) { + public PersistentData(String label, String key) { ElementHelper.validateLabel(label); if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); @@ -56,30 +53,24 @@ public String getLabel() { return label; } - @Override - public Map getProperties() { - return properties; - } - @Override public String toString() { - return "AbstractElementData{" + + return "PersistentData{" + "key='" + key + '\'' + ", label='" + label + '\'' + - ", properties=" + properties + '}'; } @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - AbstractElementData that = (AbstractElementData) o; - return Objects.equals(key, that.key) && Objects.equals(label, that.label) && Objects.equals(properties, that.properties); + if (!(o instanceof PersistentData)) return false; + PersistentData that = (PersistentData) o; + return Objects.equals(label, that.label) && Objects.equals(key, that.key); } @Override public int hashCode() { - return Objects.hash(key, label, properties); + return Objects.hash(label, key); } } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java similarity index 92% rename from src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java index 998df87..516dde4 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/ElementData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java @@ -21,8 +21,6 @@ import java.util.Map; -public interface ElementData { - - Map getProperties(); - +public interface PropertyData

{ + Map getProperties(); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java index b8f7001..84fb3cf 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -19,9 +19,14 @@ package com.arangodb.tinkerpop.gremlin.persistence; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; -public class VertexData extends AbstractElementData> { +public class VertexData extends PersistentData> { + + private final Map> properties = new HashMap<>(); public VertexData() { } @@ -30,10 +35,29 @@ public VertexData(String label, String key) { super(label, key); } + @Override + public Map> getProperties() { + return properties; + } + @Override public String toString() { return "VertexData{" + - "super=" + super.toString() + - "}"; + "properties=" + properties + + ", super=" + super.toString() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexData)) return false; + if (!super.equals(o)) return false; + VertexData that = (VertexData) o; + return Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), 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 index 4c06869..decb3ab 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.Objects; -public class VertexPropertyData extends AdbValue implements ElementData { +public class VertexPropertyData extends AdbValue implements PropertyData { private final String id; private final Map properties; @@ -68,7 +68,7 @@ public String toString() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (!(o instanceof VertexPropertyData)) return false; if (!super.equals(o)) return false; VertexPropertyData that = (VertexPropertyData) o; return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java index 1b547b7..d0dda10 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java @@ -19,7 +19,7 @@ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.tinkerpop.gremlin.persistence.ElementData; +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; @@ -30,7 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public abstract class ArangoDBElement> implements Element { +public abstract class ArangoDBElement> implements Element { protected final ArangoDBGraph graph; protected final D data; diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java index 6aceaee..6a1f71d 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java @@ -19,11 +19,11 @@ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.tinkerpop.gremlin.persistence.AbstractElementData; +import com.arangodb.tinkerpop.gremlin.persistence.PersistentData; import java.util.*; -public abstract class ArangoDBEntityElement> extends ArangoDBElement { +public abstract class ArangoDBEntityElement> extends ArangoDBElement { public ArangoDBEntityElement(ArangoDBGraph graph, D data) { super(graph, data); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index 3469234..2533967 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -30,10 +30,10 @@ public class ArangoDBGraphProvider extends AbstractGraphProvider { * The Constant IMPLEMENTATIONS. */ private static final Set IMPLEMENTATIONS = new HashSet() {{ - add(AbstractElementData.class); + add(PersistentData.class); add(AdbValue.class); add(EdgeData.class); - add(ElementData.class); + add(PropertyData.class); add(VertexData.class); add(VertexPropertyData.class); }}; From 2961db8a136ea677a84f4e9fe6b34161e46819d6 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 3 Mar 2025 23:53:55 +0100 Subject: [PATCH 09/28] structure refactoring --- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 6 +- .../gremlin/persistence/AdbValue.java | 5 +- .../gremlin/persistence/EdgeData.java | 46 +++++++++---- .../gremlin/persistence/PersistentData.java | 56 ++------------- .../gremlin/persistence/PropertyData.java | 8 ++- .../persistence/SimplePropertyData.java | 67 ++++++++++++++++++ .../gremlin/persistence/VertexData.java | 69 +++++++++++++++---- .../persistence/VertexPropertyData.java | 39 ++++++----- .../gremlin/structure/ArangoDBEdge.java | 23 +------ .../gremlin/structure/ArangoDBElement.java | 45 +++--------- .../gremlin/structure/ArangoDBGraph.java | 4 +- ...nt.java => ArangoDBPersistentElement.java} | 41 ++++++----- .../gremlin/structure/ArangoDBProperty.java | 4 +- .../structure/ArangoDBSimpleElement.java | 35 ++++++++++ .../gremlin/structure/ArangoDBVertex.java | 45 ++++-------- .../structure/ArangoDBVertexProperty.java | 35 +++------- 16 files changed, 289 insertions(+), 239 deletions(-) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java rename src/main/java/com/arangodb/tinkerpop/gremlin/structure/{ArangoDBEntityElement.java => ArangoDBPersistentElement.java} (67%) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java 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 9e25f36..7ba6b0c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -48,18 +48,20 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { // structure ArangoDBEdge.class, ArangoDBElement.class, - ArangoDBEntityElement.class, + ArangoDBPersistentElement.class, ArangoDBProperty.class, + ArangoDBSimpleElement.class, ArangoDBVertex.class, ArangoDBVertexProperty.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, // persistence - PersistentData.class, AdbValue.class, EdgeData.class, PropertyData.class, + PersistentData.class, + SimplePropertyData.class, VertexData.class, VertexPropertyData.class ) diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java index d8f4f11..cf4461b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java @@ -39,9 +39,8 @@ public class AdbValue { this.valueType = valueType; } - public AdbValue(Object value) { - this.value = value; - valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); + public static AdbValue of(Object value) { + return new AdbValue(value, (value != null ? value.getClass() : Void.class).getCanonicalName()); } public Object getValue() { diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java index 5fef249..35a5e09 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -20,10 +20,18 @@ package com.arangodb.tinkerpop.gremlin.persistence; import com.arangodb.serde.*; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import java.util.*; -public class EdgeData extends PersistentData { +public class EdgeData extends SimplePropertyData implements PersistentData { + + @JsonProperty + private String label; + + @InternalKey + private String key; @InternalFrom private String from; @@ -31,25 +39,42 @@ public class EdgeData extends PersistentData { @InternalTo private String to; - private final Map properties = new HashMap<>(); - public EdgeData() { } + // FIXME: static factory method public EdgeData( String label, String key, String from, String to ) { - super(label, key); + ElementHelper.validateLabel(label); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); Objects.requireNonNull(from, "from"); Objects.requireNonNull(to, "to"); + this.label = label; + this.key = key; this.from = from; this.to = to; } + @Override + public String getLabel() { + return label; + } + + @Override + public String getKey() { + return key; + } + + @Override + public void setKey(String key) { + this.key = key; + } + public String getFrom() { return from; } @@ -66,17 +91,13 @@ public void setTo(String to) { this.to = to; } - @Override - public Map getProperties() { - return properties; - } - @Override public String toString() { return "EdgeData{" + "from='" + from + '\'' + + ", label='" + label + '\'' + + ", key='" + key + '\'' + ", to='" + to + '\'' + - ", properties=" + properties + ", super=" + super.toString() + '}'; } @@ -84,13 +105,12 @@ public String toString() { @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(from, edgeData.from) && Objects.equals(to, edgeData.to) && Objects.equals(properties, edgeData.properties); + return 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(), from, to, properties); + return Objects.hash(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 index 914a87a..6121fd3 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java @@ -19,58 +19,10 @@ package com.arangodb.tinkerpop.gremlin.persistence; -import com.arangodb.serde.InternalKey; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +public interface PersistentData { + String getLabel(); -import java.util.Objects; + String getKey(); -public abstract class PersistentData implements PropertyData { - - private String label; - @InternalKey - private String key; - - public PersistentData() { - } - - public PersistentData(String label, String key) { - ElementHelper.validateLabel(label); - if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - - this.label = label; - this.key = key; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getLabel() { - return label; - } - - @Override - public String toString() { - return "PersistentData{" + - "key='" + key + '\'' + - ", label='" + label + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof PersistentData)) return false; - PersistentData that = (PersistentData) o; - return Objects.equals(label, that.label) && Objects.equals(key, that.key); - } - - @Override - public int hashCode() { - return Objects.hash(label, key); - } + void setKey(String key); } - diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java index 516dde4..8f04e7e 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java @@ -20,7 +20,11 @@ package com.arangodb.tinkerpop.gremlin.persistence; import java.util.Map; +import java.util.stream.Stream; -public interface PropertyData

{ - Map getProperties(); +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 index 84fb3cf..4aa0c24 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -19,45 +19,86 @@ package com.arangodb.tinkerpop.gremlin.persistence; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import com.arangodb.serde.InternalKey; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; -public class VertexData extends PersistentData> { +import java.util.*; +import java.util.stream.Stream; - private final Map> properties = new HashMap<>(); +public class VertexData implements PropertyData, PersistentData { + + @JsonProperty + private String label; + + @InternalKey + private String key; + + @JsonProperty + private final Map> properties = new HashMap<>(); public VertexData() { } public VertexData(String label, String key) { - super(label, key); + ElementHelper.validateLabel(label); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); + this.label = label; + this.key = key; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getKey() { + return key; } @Override - public Map> getProperties() { - return properties; + 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{" + - "properties=" + properties + - ", super=" + super.toString() + + "key='" + key + '\'' + + ", label='" + label + '\'' + + ", properties=" + properties + '}'; } @Override public boolean equals(Object o) { if (!(o instanceof VertexData)) return false; - if (!super.equals(o)) return false; VertexData that = (VertexData) o; - return Objects.equals(properties, that.properties); + return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(properties, that.properties); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), properties); + return Objects.hash(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 index decb3ab..9317f5a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -21,47 +21,54 @@ 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.HashMap; -import java.util.Map; import java.util.Objects; -public class VertexPropertyData extends AdbValue implements PropertyData { +public class VertexPropertyData extends SimplePropertyData { private final String id; - private final Map properties; + private final Object value; + private final String valueType; @JsonCreator VertexPropertyData( @JsonProperty("id") String id, @JsonProperty("value") Object value, - @JsonProperty("valueType") String valueType, - @JsonProperty("properties") Map properties) { - super(value, valueType); + @JsonProperty("valueType") String valueType +// @JsonProperty("properties") Map properties + ) { +// super(properties); this.id = id; - this.properties = properties; + this.value = value; + this.valueType = valueType; } + // FIXME: static factory method public VertexPropertyData(String id, Object value) { - super(value); this.id = id; - this.properties = new HashMap<>(); + this.value = value; + valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); } public String getId() { return id; } - @Override - public Map getProperties() { - return properties; + public Object getValue() { + return ArangoDBUtil.getCorretctPrimitive(value, valueType); + } + + public String getValueType() { + return valueType; } @Override public String toString() { return "VertexPropertyData{" + "id='" + id + '\'' + - ", properties=" + properties + + ", value=" + value + + ", valueType='" + valueType + '\'' + ", super=" + super.toString() + '}'; } @@ -71,11 +78,11 @@ 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(properties, that.properties); + 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, properties); + 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 c35c52f..b07a089 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -26,12 +26,11 @@ import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import java.util.*; -import java.util.stream.Stream; import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; -public class ArangoDBEdge extends ArangoDBEntityElement implements Edge { +public class ArangoDBEdge extends ArangoDBSimpleElement implements Edge, ArangoDBPersistentElement { public static ArangoDBEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { return new ArangoDBEdge(graph, new EdgeData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); @@ -41,33 +40,17 @@ public ArangoDBEdge(ArangoDBGraph graph, EdgeData data) { super(graph, data); } - @Override - @SuppressWarnings("unchecked") - protected Property createProperty(String key, Object value) { - return new ArangoDBProperty<>(this, key, (V) value); - } - - @Override - protected Stream> toProperties(String key, AdbValue value) { - return Stream.of(createProperty(key, value.getValue())); - } - - @Override - protected AdbValue toData(Object value) { - return new AdbValue(value); - } - @Override protected void doRemove() { graph.getClient().deleteEdge(this); } @Override - protected void update() { + protected void doUpdate() { graph.getClient().updateEdge(this); } - public void insert() { + public void doInsert() { graph.getClient().insertEdge(this); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java index d0dda10..326ecae 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java @@ -28,7 +28,6 @@ import java.util.Iterator; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; public abstract class ArangoDBElement> implements Element { @@ -41,21 +40,17 @@ public ArangoDBElement(ArangoDBGraph graph, D data) { this.data = data; } - protected abstract String stringify(); - - // properties transformers - protected abstract Stream> toProperties(String key, P value); - - protected abstract Property createProperty(String key, Object value); - - protected abstract P toData(Object value); + protected abstract Property createProperty(String key, P value); - // CRUD ops - protected abstract void update(); + //region CRUD ops + protected abstract void doUpdate(); protected abstract void doRemove(); - protected abstract void insert(); + protected abstract void doInsert(); + //endregion + + protected abstract String stringify(); public D data() { return data; @@ -70,16 +65,6 @@ public ArangoDBGraph graph() { return graph; } - @Override - public Property property(String key, V value) { - if (removed) throw Exceptions.elementAlreadyRemoved(id()); - ElementHelper.validateProperty(key, value); - P data = toData(value); - setProperty(key, data); - update(); - return createProperty(key, value); - } - @Override public void remove() { if (removed) return; @@ -90,25 +75,13 @@ public void remove() { @Override public Iterator> properties(String... propertyKeys) { if (removed) return Collections.emptyIterator(); - return data.getProperties() - .entrySet() - .stream() + return data.entries() .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) - .flatMap((Map.Entry e) -> this.toProperties(e.getKey(), e.getValue())) + .map((Map.Entry e) -> this.createProperty(e.getKey(), e.getValue())) .collect(Collectors.toList()) // avoids ConcurrentModificationException on removal from downstream .iterator(); } - public void removeProperty(String key) { - if (removed) throw Exceptions.elementAlreadyRemoved(id()); - data.getProperties().remove(key); - update(); - } - - private void setProperty(String key, P value) { - data.getProperties().put(key, value); - } - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override public boolean equals(final Object object) { 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 3549e9a..64cdc53 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -625,8 +625,8 @@ public Vertex addVertex(Object... keyValues) { String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); - // The vertex needs to exist before we can attach properties - vertex.insert(); + // TODO: optmize writing only once + vertex.doInsert(); ElementHelper.attachProperties(vertex, keyValues); return vertex; } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java similarity index 67% rename from src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java rename to src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java index 6a1f71d..dc04b76 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEntityElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -20,36 +20,39 @@ package com.arangodb.tinkerpop.gremlin.structure; import com.arangodb.tinkerpop.gremlin.persistence.PersistentData; +import org.apache.tinkerpop.gremlin.structure.Element; -import java.util.*; +import java.util.Optional; -public abstract class ArangoDBEntityElement> extends ArangoDBElement { - - public ArangoDBEntityElement(ArangoDBGraph graph, D data) { - super(graph, data); - } +public interface ArangoDBPersistentElement extends Element { @Override - public String id() { - return Optional.ofNullable(key()) - .map(it -> collection() + '/' + it) - .orElse(label()); - } + ArangoDBGraph graph(); + + PersistentData data(); - public String key() { - return data.getKey(); + default String key() { + return data().getKey(); } - public void key(String key) { - data.setKey(key); + default void key(String key) { + data().setKey(key); } @Override - public String label() { - return data.getLabel(); + default String label() { + return data().getLabel(); } - public String collection() { - return graph.getPrefixedCollectioName(label()); + @SuppressWarnings("resource") + default String collection() { + return graph().getPrefixedCollectioName(label()); + } + + @Override + default String id() { + return Optional.ofNullable(key()) + .map(it -> collection() + '/' + it) + .orElse(label()); } } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java index 34d3d7a..5ce0cd4 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java @@ -27,9 +27,9 @@ public class ArangoDBProperty implements Property { private final String key; private final V value; - private final ArangoDBElement element; + private final ArangoDBSimpleElement element; - public ArangoDBProperty(final ArangoDBElement element, final String key, final V value) { + public ArangoDBProperty(final ArangoDBSimpleElement element, final String key, final V value) { this.element = element; this.key = key; this.value = value; 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 f2b4307..41d37a1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -29,12 +29,11 @@ import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.elementAlreadyRemoved; import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; -public class ArangoDBVertex extends ArangoDBEntityElement, VertexData> implements Vertex { +public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { return new ArangoDBVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); @@ -68,13 +67,12 @@ public VertexProperty property( .orElseGet(() -> UUID.randomUUID().toString()); VertexPropertyData prop = new VertexPropertyData(idValue, value); - List list = data.getProperties().getOrDefault(key, new ArrayList<>()); - list.add(prop); - data.getProperties().put(key, list); + data.add(key, prop); ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); + // TODO: optmize writing only once ElementHelper.attachProperties(vertexProperty, keyValues); - update(); + doUpdate(); return vertexProperty; } @@ -92,7 +90,8 @@ public Edge addEdge(String label, Vertex vertex, Object... keyValues) { String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), graph); - edge.insert(); + // TODO: optmize writing only once + edge.doInsert(); ElementHelper.attachProperties(edge, keyValues); return edge; } @@ -109,18 +108,8 @@ protected String stringify() { } @Override - protected Stream> toProperties(String key, List value) { - return value.stream().map(it -> createProperty(key, it)); - } - - @Override - protected Property createProperty(String key, Object value) { - return new ArangoDBVertexProperty<>(key, (VertexPropertyData) value, this); - } - - @Override - protected List toData(Object value) { - throw new UnsupportedOperationException(); + protected Property createProperty(String key, VertexPropertyData value) { + return new ArangoDBVertexProperty<>(key, value, this); } @Override @@ -148,12 +137,12 @@ public Iterator vertices(Direction direction, String... edgeLabels) { } @Override - public void insert() { + public void doInsert() { graph.getClient().insertVertex(this); } @Override - public void update() { + public void doUpdate() { graph.getClient().updateVertex(this); } @@ -172,16 +161,10 @@ public Iterator> properties(String... propertyKeys) { return IteratorUtils.cast(super.properties(propertyKeys)); } - public void removeVertexProperty(ArangoDBVertexProperty prop) { - Map> props = data.getProperties(); - List pVal = props.get(prop.key()); - if (pVal != null) { - if (pVal.remove(prop.data())) { - if (pVal.isEmpty()) { - props.remove(prop.key()); - } - } - } + public void removeProperty(ArangoDBVertexProperty prop) { + if (removed()) throw ArangoDBElement.Exceptions.elementAlreadyRemoved(id()); + data.remove(prop.key(), prop.data()); + doUpdate(); } /** 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 49859db..0283831 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -19,17 +19,14 @@ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.tinkerpop.gremlin.persistence.AdbValue; import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.stream.Stream; +import java.util.*; -public class ArangoDBVertexProperty

extends ArangoDBElement implements VertexProperty

{ +public class ArangoDBVertexProperty

extends ArangoDBSimpleElement implements VertexProperty

{ private final String key; private final ArangoDBVertex vertex; @@ -45,22 +42,6 @@ protected boolean removed() { return super.removed() || vertex.removed(); } - @Override - @SuppressWarnings("unchecked") - protected Property createProperty(String key, Object value) { - return new ArangoDBProperty<>(this, key, (V) value); - } - - @Override - protected Stream> toProperties(String key, AdbValue value) { - return Stream.of(createProperty(key, value.getValue())); - } - - @Override - protected AdbValue toData(Object value) { - return new AdbValue(value); - } - @Override public String key() { return key; @@ -88,19 +69,19 @@ public Object id() { } @Override - protected void update() { - vertex.update(); + protected void doUpdate() { + vertex.doUpdate(); } @Override protected void doRemove() { - vertex.removeVertexProperty(this); - vertex.update(); + vertex.removeProperty(this); + vertex.doUpdate(); } @Override - protected void insert() { - throw new UnsupportedOperationException("TODO"); + protected void doInsert() { + doUpdate(); } @Override From 2491801329fd5ecfae63627b3b9957ca872e5d9d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 4 Mar 2025 12:13:57 +0100 Subject: [PATCH 10/28] static factory methods --- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 6 +++--- .../gremlin/persistence/EdgeData.java | 19 ++++++++++--------- .../persistence/VertexPropertyData.java | 9 ++------- .../gremlin/structure/ArangoDBEdge.java | 3 +-- .../gremlin/structure/ArangoDBVertex.java | 2 +- 5 files changed, 17 insertions(+), 22 deletions(-) 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 7ba6b0c..a5e52d7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -48,19 +48,19 @@ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { // structure ArangoDBEdge.class, ArangoDBElement.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, ArangoDBPersistentElement.class, ArangoDBProperty.class, ArangoDBSimpleElement.class, ArangoDBVertex.class, ArangoDBVertexProperty.class, - ArangoDBGraph.class, - ArangoDBGraphVariables.class, // persistence AdbValue.class, EdgeData.class, - PropertyData.class, PersistentData.class, + PropertyData.class, SimplePropertyData.class, VertexData.class, VertexPropertyData.class diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java index 35a5e09..913f34b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -39,11 +39,7 @@ public class EdgeData extends SimplePropertyData implements PersistentData { @InternalTo private String to; - public EdgeData() { - } - - // FIXME: static factory method - public EdgeData( + public static EdgeData of( String label, String key, String from, @@ -54,10 +50,15 @@ public EdgeData( Objects.requireNonNull(from, "from"); Objects.requireNonNull(to, "to"); - this.label = label; - this.key = key; - this.from = from; - this.to = to; + EdgeData data = new EdgeData(); + data.label = label; + data.key = key; + data.from = from; + data.to = to; + return data; + } + + public EdgeData() { } @Override diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java index 9317f5a..9f5084f 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -36,19 +36,14 @@ public class VertexPropertyData extends SimplePropertyData { @JsonProperty("id") String id, @JsonProperty("value") Object value, @JsonProperty("valueType") String valueType -// @JsonProperty("properties") Map properties ) { -// super(properties); this.id = id; this.value = value; this.valueType = valueType; } - // FIXME: static factory method - public VertexPropertyData(String id, Object value) { - this.id = id; - this.value = value; - valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); + public static VertexPropertyData of(String id, Object value) { + return new VertexPropertyData(id, value, (value != null ? value.getClass() : Void.class).getCanonicalName()); } public String getId() { 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 b07a089..6508b0d 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -19,7 +19,6 @@ package com.arangodb.tinkerpop.gremlin.structure; -import com.arangodb.tinkerpop.gremlin.persistence.AdbValue; import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -33,7 +32,7 @@ public class ArangoDBEdge extends ArangoDBSimpleElement implements Edge, ArangoDBPersistentElement { public static ArangoDBEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { - return new ArangoDBEdge(graph, new EdgeData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); + return new ArangoDBEdge(graph, EdgeData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); } public ArangoDBEdge(ArangoDBGraph graph, EdgeData data) { 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 41d37a1..8a94bf5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -66,7 +66,7 @@ public VertexProperty property( }) .orElseGet(() -> UUID.randomUUID().toString()); - VertexPropertyData prop = new VertexPropertyData(idValue, value); + VertexPropertyData prop = VertexPropertyData.of(idValue, value); data.add(key, prop); ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); From 29cbf4801a10edc2e80cf53a6d205ef60fce8758 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 4 Mar 2025 12:27:58 +0100 Subject: [PATCH 11/28] re-enabled tests --- .../gremlin/structure/ArangoDBGraph.java | 22 ------------------- 1 file changed, 22 deletions(-) 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 64cdc53..9f8e535 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -162,32 +162,10 @@ 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.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertexProperty", - reason = "FIXME" -) -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertex", - reason = "FIXME" -) @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", method = "shouldNotEvaluateToEqualDifferentId", 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.util.star.StarGraphTest", method = "shouldAttachWithCreateMethod", From 013b9cb1e03bb9dbecb238afccabad8242a2c24b Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Mar 2025 10:49:31 +0100 Subject: [PATCH 12/28] cleanup unused client methods --- README.md | 11 +- .../gremlin/client/ArangoDBGraphClient.java | 460 ++---------------- .../gremlin/structure/ArangoDBGraph.java | 6 +- .../gremlin/ArangoDBGraphProvider.java | 64 +-- .../tinkerpop/gremlin/ArangoDBGraphTest.java | 3 +- .../tinkerpop/gremlin/ArangoDBTestSuite.java | 92 ---- .../tinkerpop/gremlin/TestGraphClient.java | 26 + 7 files changed, 105 insertions(+), 557 deletions(-) delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/TestGraphClient.java 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/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index e3c208b..4699bfd 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -57,7 +57,9 @@ public class ArangoDBGraphClient { public static class ArangoDBExceptions { - /** The error code regex. Matches response messages from the ArangoDB client */ + /** + * 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]+).+"); @@ -81,7 +83,7 @@ public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) if (m.matches()) { int code = Integer.parseInt(m.group(1)); String msg = m.group(2); - switch ((int) code / 100) { + 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: @@ -101,14 +103,16 @@ public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) return new ArangoDBGraphException("General ArangoDB error (unkown error code)", ex); } - /** "name to long" Message. */ + /** + * "name to long" Message. + */ public static String NAME_TO_LONG = "Name is too long: {} bytes (max 64 bytes for labels, 256 for keys)"; /** * Gets the naming convention error. * - * @param cause the cause + * @param cause the cause * @param details the details * @return the naming convention error */ @@ -122,11 +126,7 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); - private final ArangoDB driver; - - private final ArangoDatabase db; - - private final int batchSize; + protected final ArangoDatabase db; private final ArangoDBGraph graph; @@ -134,52 +134,44 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri * Create a simple graph client and connect to the provided db. If the DB does not exist, the driver will try to * create one. * - * @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); + public ArangoDBGraphClient(ArangoDBGraph graph, Properties properties, String dbname) { + this(graph, properties, dbname, false); } /** * 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 + * @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 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.debug("Initiating the ArangoDb Client"); this.graph = graph; - driver = new ArangoDB.Builder() + db = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromProperties(properties)) - .build(); - db = driver.db(dbname); + .build() + .db(dbname); if (createDatabase) { if (!db.exists()) { logger.debug("DB not found, attemtping to create it."); try { - if (!driver.createDatabase(dbname)) { + if (!db.create()) { throw new ArangoDBGraphException("Unable to crate the database " + dbname); } } catch (ArangoDBException ex) { @@ -195,11 +187,10 @@ public ArangoDBGraphClient( } 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: {}@{}", + throw new ArangoDBGraphException(String.format("DB not found or user has no access: %s@%s", properties.getProperty("arangodb.user"), dbname)); } } - this.batchSize = batchSize; } /** @@ -215,18 +206,6 @@ public void shutdown() { } } - /** - * 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.debug("Clear {}", graph.name()); - deleteGraph(graph.name()); - } - /** * Request the version of ArangoDB. * @@ -242,44 +221,6 @@ public String getVersion() throws ArangoDBGraphException { } } - - /** - * Gets the database. - * - * @return the ArangoDB - */ - - public ArangoDatabase getDB() { - return db; - } - - /** - * 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.debug("Delete current db"); - if (db != null) { - try { - db.drop(); - } catch (ArangoDBException e) { - throw ArangoDBExceptions.getArangoDBException(e); - } - } - } - public ArangoDBGraphVariables getGraphVariables() { logger.debug("Get graph variables"); ArangoDBGraphVariables result; @@ -298,8 +239,9 @@ public ArangoDBGraphVariables getGraphVariables() { /** * 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 + * + * @param document the document + * @throws ArangoDBGraphException If there was an error inserting the document */ public void insertGraphVariables(ArangoDBGraphVariables document) { @@ -333,8 +275,9 @@ public void insertGraphVariables(ArangoDBGraphVariables document) { /** * Delete a document from the graph. - * @param document the document to delete - * @throws ArangoDBGraphException If there was an error deleting the document + * + * @param document the document to delete + * @throws ArangoDBGraphException If there was an error deleting the document */ public void deleteGraphVariables(ArangoDBGraphVariables document) { @@ -351,14 +294,14 @@ public void deleteGraphVariables(ArangoDBGraphVariables document) { /** * Update the document in the graph. - * @param document the document * - * @throws ArangoDBGraphException If there was an error updating the document + * @param document the document + * @throws ArangoDBGraphException If there was an error updating the document */ public void updateGraphVariables(ArangoDBGraphVariables document) { logger.debug("Update variables {} in {}", document, graph.name()); - DocumentUpdateEntity updateEntity; + DocumentUpdateEntity updateEntity; try { updateEntity = db.collection(document.collection()) .updateDocument(document._key(), document); @@ -373,7 +316,7 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { /** * Get vertices of a graph. If no ids are provided, get all vertices. * - * @param ids the ids to match + * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ @@ -386,7 +329,7 @@ public ArangoCursor getGraphVertices(final List ids) { /** * Get edges of a graph. If no ids are provided, get all edges. * - * @param ids the ids to match + * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ public ArangoCursor getGraphEdges(List ids) { @@ -413,101 +356,14 @@ private ArangoCursor getGraphDocuments(List ids, List pre return executeAqlQuery(query, bindVars, null, clazz); } - /** - * 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; - } - - /** - * 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 + * @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 + * @throws ArangoDBGraphException If the graph can not be created */ public ArangoGraph createGraph(String name, @@ -539,13 +395,13 @@ public ArangoGraph getArangoGraph() { /** * 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) + * @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 + * @throws ArangoDBGraphException if executing the query raised an exception */ public ArangoCursor executeAqlQuery( @@ -563,230 +419,6 @@ public ArangoCursor executeAqlQuery( } } - // 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); -// } -// } - -// /** -// * 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 void insertEdge(ArangoDBEdge edge) { logger.debug("Insert edge {} in {} ", edge, graph.name()); EdgeEntity insertEntity; 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 9f8e535..fe1b151 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -135,8 +135,8 @@ */ @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.StructureIntegrateSuite") @Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", @@ -534,9 +534,7 @@ public ArangoDBGraph(Configuration configuration) { 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); + client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME), shouldPrefixCollectionNames); ArangoGraph graph = client.getArangoGraph(); GraphCreateOptions options = new GraphCreateOptions(); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index 2533967..e6578d6 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -1,11 +1,11 @@ package com.arangodb.tinkerpop.gremlin; -import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import com.arangodb.tinkerpop.gremlin.persistence.*; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.ConfigurationConverter; @@ -15,43 +15,17 @@ 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(PersistentData.class); - add(AdbValue.class); - add(EdgeData.class); - add(PropertyData.class); - add(VertexData.class); - add(VertexPropertyData.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; + return getConfiguration(graphName, test, testMethodName, loadGraphWith); } private Configuration getConfiguration( @@ -261,24 +235,29 @@ private Configuration getConfiguration( @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(); + Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + TestGraphClient client = new TestGraphClient(arangoProperties, "tinkerpop"); + client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); + if (graph != null) { + graph.close(); } - } @Override + @SuppressWarnings("rawtypes") public Set getImplementations() { - return IMPLEMENTATIONS; + return Stream.of( + ArangoDBEdge.class, + ArangoDBElement.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + ArangoDBPersistentElement.class, + ArangoDBProperty.class, + ArangoDBSimpleElement.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class + ).collect(Collectors.toSet()); } @Override @@ -292,4 +271,5 @@ public Map getBaseConfiguration(String graphName, Class test, 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 index 3224571..45c787a 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java @@ -1,11 +1,12 @@ package com.arangodb.tinkerpop.gremlin; import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite; import org.junit.runner.RunWith; import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -@RunWith(ArangoDBTestSuite.class) +@RunWith(StructureStandardSuite.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/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraphClient.java new file mode 100644 index 0000000..6edbd21 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraphClient.java @@ -0,0 +1,26 @@ +package com.arangodb.tinkerpop.gremlin; + +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, false); + } + + public void deleteGraph(String name) { + try { + db.graph(name).drop(true); + db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); + } catch (ArangoDBException e) { + if (e.getErrorNum() == 1924) return; // graph not found + throw e; + } + } + +} From 36840145545e048b988ab91e35b120c6261fcb20 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Mar 2025 11:59:49 +0100 Subject: [PATCH 13/28] tests restructuring --- .../gremlin/ArangoDBGraphProvider.java | 275 ---------------- .../tinkerpop/gremlin/ArangoDBGraphTest.java | 13 - .../tinkerpop/gremlin/BaseGremlinTest.java | 11 + .../arangodb/tinkerpop/gremlin/Issue57.java | 27 -- .../gremlin/client/test/BaseTestCase.java | 45 --- .../gremlin/client/test/ClientTest.java | 302 ------------------ .../structure/ArangoDBGremlinTest.java | 10 - .../structure/StructureGraphProvider.java | 166 ++++++++++ .../structure/StructureStandardSuiteTest.java | 11 + .../gremlin/util/BaseGraphProvider.java | 122 +++++++ .../gremlin/{ => util}/TestGraphClient.java | 2 +- 11 files changed, 311 insertions(+), 673 deletions(-) delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java rename src/test/java/com/arangodb/tinkerpop/gremlin/{ => util}/TestGraphClient.java (94%) 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 e6578d6..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ /dev/null @@ -1,275 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -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.utils.ArangoDBConfigurationBuilder; -import org.apache.tinkerpop.gremlin.structure.VertexTest; - -public class ArangoDBGraphProvider extends AbstractGraphProvider { - - @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, - 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 { - Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - TestGraphClient client = new TestGraphClient(arangoProperties, "tinkerpop"); - client.deleteGraph(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, - 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, - 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 45c787a..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import org.apache.tinkerpop.gremlin.GraphProviderClass; -import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite; -import org.junit.runner.RunWith; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -@RunWith(StructureStandardSuite.class) -@GraphProviderClass(provider = ArangoDBGraphProvider.class, graph = ArangoDBGraph.class) -public class ArangoDBGraphTest { - -} 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/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/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..0d9af64 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -0,0 +1,166 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; + +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.tinkerpop.gremlin.structure.VertexTest; + +public class StructureGraphProvider extends BaseGraphProvider { + + @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.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 \"" + test.getCanonicalName() + "." + testMethodName + "\":"); + } + } + } +} 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..907482e --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java @@ -0,0 +1,11 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +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 = ArangoDBGraph.class) +public class StructureStandardSuiteTest { + +} 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..f886d5e --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -0,0 +1,122 @@ +package com.arangodb.tinkerpop.gremlin.util; + +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 { + + 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) { + 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 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; + 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 SINK: + System.out.println("SINK"); + 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, "tinkerpop"); + client.deleteGraph(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, + 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/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java similarity index 94% rename from src/test/java/com/arangodb/tinkerpop/gremlin/TestGraphClient.java rename to src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java index 6edbd21..bfa9d12 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraphClient.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java @@ -1,4 +1,4 @@ -package com.arangodb.tinkerpop.gremlin; +package com.arangodb.tinkerpop.gremlin.util; import com.arangodb.ArangoDBException; import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; From 15f079c66daa9505863334882ec231cdf1e780f2 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Mar 2025 20:31:00 +0100 Subject: [PATCH 14/28] ProcessStandardSuite --- .../gremlin/process/ProcessGraphProvider.java | 67 +++++++++++++++++++ .../process/ProcessStandardSuiteTest.java | 12 ++++ .../structure/StructureGraphProvider.java | 2 - .../gremlin/util/BaseGraphProvider.java | 28 +++++++- 4 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java 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..100e266 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java @@ -0,0 +1,67 @@ +package com.arangodb.tinkerpop.gremlin.process; + +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; + +public class ProcessGraphProvider extends BaseGraphProvider { + + @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 "g_V_mergeEXlabel_self_weight_05X": + 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 "shouldPartitionWithAbstractLambdaChildTraversal": + 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.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + 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..309424c --- /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.structure.ArangoDBGraph; +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 = ArangoDBGraph.class) +public class ProcessStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java index 0d9af64..2f4b2d2 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -158,8 +158,6 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St builder.withEdgeCollection("created"); builder.withEdgeCollection("knows"); break; - default: - System.out.println("case \"" + test.getCanonicalName() + "." + testMethodName + "\":"); } } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index f886d5e..566ef86 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -17,6 +17,8 @@ public abstract class BaseGraphProvider extends AbstractGraphProvider { + private final String dbName = getClass().getSimpleName(); + protected abstract void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName); @Override @@ -32,10 +34,12 @@ private Configuration getConfiguration( 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) { @@ -48,13 +52,24 @@ private Configuration getConfiguration( 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"); @@ -69,9 +84,20 @@ private Configuration getConfiguration( 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.withEdgeCollection("link"); + builder.withEdgeCollection("self"); break; } } else { @@ -84,7 +110,7 @@ private Configuration getConfiguration( 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, "tinkerpop"); + TestGraphClient client = new TestGraphClient(arangoProperties, dbName); client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); if (graph != null) { graph.close(); From 719486a7b16b1c28d1ac6401e46f4332846f3ecd Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Mar 2025 20:42:03 +0100 Subject: [PATCH 15/28] tests restructuring --- jautodoc_templates.xml | 237 ------------------ .../com/arangodb/tinkerpop/gremlin/test.conf | 11 - src/test/resources/arangodb.properties | 3 - tests/travis/setup_arangodb.sh | 54 ---- 4 files changed, 305 deletions(-) delete mode 100644 jautodoc_templates.xml delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/test.conf delete mode 100644 src/test/resources/arangodb.properties delete mode 100644 tests/travis/setup_arangodb.sh 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/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/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/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" From 4fa901a603972babb3ff4870901183100003b6ba Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 6 Mar 2025 08:46:38 +0100 Subject: [PATCH 16/28] fix db creation in tests --- .../gremlin/client/ArangoDBGraphClient.java | 46 +------------------ .../gremlin/structure/ArangoDBGraph.java | 2 +- .../gremlin/util/TestGraphClient.java | 7 ++- 3 files changed, 9 insertions(+), 46 deletions(-) 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 4699bfd..6481e0a 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -131,35 +131,17 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri 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 * @throws ArangoDBGraphException If the db does not exist and cannot be created */ - - public ArangoDBGraphClient(ArangoDBGraph graph, Properties properties, String dbname) { - this(graph, properties, dbname, false); - } - - /** - * 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 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, - boolean createDatabase) + String dbname) throws ArangoDBGraphException { logger.debug("Initiating the ArangoDb Client"); this.graph = graph; @@ -167,30 +149,6 @@ public ArangoDBGraphClient( .loadProperties(ArangoConfigProperties.fromProperties(properties)) .build() .db(dbname); - if (createDatabase) { - if (!db.exists()) { - logger.debug("DB not found, attemtping to create it."); - try { - if (!db.create()) { - 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: %s@%s", - properties.getProperty("arangodb.user"), dbname)); - } - } } /** 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 fe1b151..b4ccc3e 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -534,7 +534,7 @@ public ArangoDBGraph(Configuration configuration) { 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), shouldPrefixCollectionNames); + client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME)); ArangoGraph graph = client.getArangoGraph(); GraphCreateOptions options = new GraphCreateOptions(); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java index bfa9d12..3c5ff1d 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java @@ -10,7 +10,12 @@ public class TestGraphClient extends ArangoDBGraphClient { public TestGraphClient(Properties properties, String dbname) throws ArangoDBGraphException { - super(null, properties, dbname, false); + super(null, properties, dbname); + if (!db.exists()) { + if (!db.create()) { + throw new ArangoDBGraphException("Unable to crate the database " + dbname); + } + } } public void deleteGraph(String name) { From f9420b6d749f7bc4b3fbb8cb601c26d111f696bb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 6 Mar 2025 13:30:24 +0100 Subject: [PATCH 17/28] tests fixes --- .../gremlin/client/ArangoDBGraphClient.java | 6 +-- .../gremlin/persistence/VertexData.java | 9 +++-- .../gremlin/structure/ArangoDBGraph.java | 40 ++++++++----------- .../gremlin/structure/ArangoDBVertex.java | 11 +++-- .../gremlin/process/ProcessGraphProvider.java | 36 ++++++++++++++++- .../structure/StructureGraphProvider.java | 29 +++++++------- .../gremlin/util/BaseGraphProvider.java | 6 ++- .../gremlin/util/TestGraphClient.java | 17 ++++++-- 8 files changed, 95 insertions(+), 59 deletions(-) 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 6481e0a..9ab6b4b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -157,11 +157,7 @@ public ArangoDBGraphClient( public void shutdown() { logger.debug("Shutdown"); - if (db != null) { - if (db.exists()) { - db.clearQueryCache(); - } - } + db.arango().shutdown(); } /** diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java index 4aa0c24..34e09f0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -40,11 +40,14 @@ public class VertexData implements PropertyData, PersistentD public VertexData() { } - public VertexData(String label, String key) { + public static VertexData of(String label, String key) { ElementHelper.validateLabel(label); if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - this.label = label; - this.key = key; + + VertexData data = new VertexData(); + data.label = label; + data.key = key; + return data; } @Override 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 b4ccc3e..d9f65e5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -142,10 +142,6 @@ 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.util.detached.DetachedVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "Test creates vertex with no labels in schema-based approach") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldAddVertexWithUserSuppliedStringId", @@ -162,10 +158,6 @@ test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldEvaluateConnectivityPatterns", reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "FIXME") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldAttachWithCreateMethod", @@ -190,6 +182,18 @@ test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", method = "shouldAddEdgeWithUserSuppliedStringId", reason = "FIXME") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", + method = "g_V_both_both_count", + reason = "FIXME") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", + method = "g_V_repeatXoutX_timesX3X_count", + reason = "FIXME") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", + method = "g_V_repeatXoutX_timesX8X_count", + reason = "FIXME") public class ArangoDBGraph implements Graph { /** @@ -479,10 +483,6 @@ public VertexFeatures vertex() { private final List relations; - /** Flat to indicate that the graph has no schema. */ - - private boolean schemaless = false; - /** The configuration. */ private Configuration configuration; @@ -525,7 +525,6 @@ public ArangoDBGraph(Configuration configuration) { 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)) { @@ -587,19 +586,12 @@ public ArangoDBGraph(Configuration configuration) { @Override public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); - String label; - if (!schemaless) { - label = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(label); - } else { - label = DEFAULT_VERTEX_COLLECTION; - } - if (!vertexCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); - } - + String label = ElementHelper.getLabelValue(keyValues).orElse(null); String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); + if (!vertexCollections().contains(vertex.label())) { + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", vertex.label(), name)); + } // TODO: optmize writing only once vertex.doInsert(); 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 8a94bf5..2960db0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -36,7 +36,7 @@ public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { - return new ArangoDBVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); + return new ArangoDBVertex(graph, VertexData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); } public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { @@ -83,13 +83,12 @@ public Edge addEdge(String label, Vertex vertex, Object... keyValues) { 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())); - } - String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), 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())); + } + // TODO: optmize writing only once edge.doInsert(); ElementHelper.attachProperties(edge, keyValues); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java index 100e266..31df28b 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java @@ -11,7 +11,6 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St case "g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX": builder.withEdgeCollection("next"); break; - case "g_V_mergeEXlabel_self_weight_05X": case "shouldGenerateDefaultIdOnAddEWithSpecifiedId": case "shouldSetIdOnAddEWithNamePropertyKeySpecifiedAndNameSuppliedAsProperty": case "shouldSetIdOnAddEWithIdPropertyKeySpecifiedAndNameSuppliedAsProperty": @@ -41,7 +40,6 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St case "shouldTriggerAddEdgeByPath": case "shouldWriteToMultiplePartitions": case "shouldAppendPartitionToEdge": - case "shouldPartitionWithAbstractLambdaChildTraversal": case "shouldThrowExceptionOnEInDifferentPartition": builder.withEdgeCollection("self"); builder.withEdgeCollection("self-but-different"); @@ -59,8 +57,42 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St 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/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java index 2f4b2d2..5bd2491 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -19,21 +19,8 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St } else if (testMethodName.startsWith("shouldSupportUserSuppliedIds")) { builder.withEdgeCollection("test"); } else if (testMethodName.startsWith("shouldSupportUUID")) { + builder.withVertexCollection("person"); 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"); @@ -47,6 +34,20 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St 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) { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index 566ef86..ca7cab5 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -96,8 +96,12 @@ private Configuration getConfiguration( 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 { @@ -111,7 +115,7 @@ 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.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); + client.clear(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); if (graph != null) { graph.close(); } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java index 3c5ff1d..d6a537a 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java @@ -18,14 +18,23 @@ public TestGraphClient(Properties properties, String dbname) throws ArangoDBGrap } } - public void deleteGraph(String name) { + public void clear(String name) { try { - db.graph(name).drop(true); db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); } catch (ArangoDBException e) { - if (e.getErrorNum() == 1924) return; // graph not found - throw 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; + } + + db.clearQueryCache(); } } From dfe6b8c5e061c67bc6d048055df37611ff75b45a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 7 Mar 2025 14:10:27 +0100 Subject: [PATCH 18/28] tests fixes --- .../gremlin/client/ArangoDBGraphClient.java | 2 +- .../gremlin/client/ArangoDBQueryBuilder.java | 297 ------------------ .../gremlin/structure/ArangoDBGraph.java | 13 +- .../gremlin/custom/CustomGraphProvider.java | 164 ++++++++++ .../gremlin/custom/CustomStandardSuite.java | 21 ++ .../custom/CustomStandardSuiteTest.java | 11 + 6 files changed, 198 insertions(+), 310 deletions(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java 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 9ab6b4b..7f52040 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -489,7 +489,7 @@ public Iterator getVertexNeighbors(String vertexId, List edg 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, uniqueVertices: 'global', order: 'bfs' } RETURN v"; + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections, order: 'bfs' } RETURN v"; return executeAqlQuery(query, bindVars, null, VertexData.class); } 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..f0f4fb2 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,9 @@ 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,83 +70,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. */ @@ -206,26 +125,6 @@ public ArangoDBQueryBuilder documentsById( 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()); - return this; - } - /** * Append an union segment. * @param collections the collections that participate in the union @@ -274,202 +173,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/structure/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index d9f65e5..cacc8a5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -138,6 +138,7 @@ @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) @Graph.OptIn("com.arangodb.tinkerpop.gremlin.StructureIntegrateSuite") @Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") +@Graph.OptIn("com.arangodb.tinkerpop.gremlin.custom.CustomStandardSuite") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", method = "testAttachableCreateMethod", @@ -182,18 +183,6 @@ test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", method = "shouldAddEdgeWithUserSuppliedStringId", reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", - method = "g_V_both_both_count", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", - method = "g_V_repeatXoutX_timesX3X_count", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.CountTest$Traversals", - method = "g_V_repeatXoutX_timesX8X_count", - reason = "FIXME") public class ArangoDBGraph implements Graph { /** 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..56f3a02 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -0,0 +1,164 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.tinkerpop.gremlin.structure.VertexTest; + +public class CustomGraphProvider extends BaseGraphProvider { + + @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("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; + } + } + } +} 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..7d33ae3 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -0,0 +1,21 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +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[]{ + }; + + private static final Class[] testsToEnforce = new Class[]{ + }; + + public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { + super(klass, builder, allTests, testsToEnforce, 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..e91b299 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java @@ -0,0 +1,11 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.junit.runner.RunWith; + +@RunWith(CustomStandardSuite.class) +@GraphProviderClass(provider = CustomGraphProvider.class, graph = ArangoDBGraph.class) +public class CustomStandardSuiteTest { + +} From f6bb8287d0d935e1c1f7d5e2d8db26cef9f0bd8e Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Mon, 10 Mar 2025 13:20:27 +0100 Subject: [PATCH 19/28] tests fixes --- .../gremlin/structure/ArangoDBGraph.java | 189 ++++++++---- .../gremlin/custom/CustomGraphProvider.java | 166 +---------- .../gremlin/custom/CustomStandardSuite.java | 7 +- .../traversal/step/map/MergeEdgeTest.java | 271 ++++++++++++++++++ 4 files changed, 421 insertions(+), 212 deletions(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java 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 cacc8a5..72cf736 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -33,7 +33,7 @@ /** * 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. @@ -51,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. @@ -71,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 @@ -82,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 @@ -96,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
      *  
    *
*

@@ -131,7 +131,6 @@ * @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) @@ -183,6 +182,21 @@ test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", method = "shouldAddEdgeWithUserSuppliedStringId", reason = "FIXME") +@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" +) public class ArangoDBGraph implements Graph { /** @@ -197,14 +211,17 @@ public class ArangoDBGraphFeatures implements Features { private class ArangoDBGraphGraphFeatures implements GraphFeatures { - /** The variable features. */ + /** + * The variable features. + */ private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); /** * Instantiates a new ArangoDB graph graph features. */ - ArangoDBGraphGraphFeatures () { } + ArangoDBGraphGraphFeatures() { + } @Override public boolean supportsComputer() { @@ -237,7 +254,8 @@ private class ArangoDBGraphElementFeatures implements ElementFeatures { * Instantiates a new ArangoDB graph element features. */ - ArangoDBGraphElementFeatures() { } + ArangoDBGraphElementFeatures() { + } @Override public boolean supportsAnyIds() { @@ -257,11 +275,11 @@ public boolean supportsNumericIds() { @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 - */ + * 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; } } @@ -272,7 +290,9 @@ public boolean supportsUuidIds() { private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { - /** The vertex property features. */ + /** + * The vertex property features. + */ private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); @@ -280,7 +300,8 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i * Instantiates a new ArangoDB graph vertex features. */ - ArangoDBGraphVertexFeatures () { } + ArangoDBGraphVertexFeatures() { + } @Override @@ -294,7 +315,9 @@ public VertexPropertyFeatures properties() { */ public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { - /** The edge property features. */ + /** + * The edge property features. + */ private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); @@ -302,7 +325,8 @@ public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures impl * Instantiates a new ArangoDB graph edge features. */ - ArangoDBGraphEdgeFeatures() { } + ArangoDBGraphEdgeFeatures() { + } @Override public EdgePropertyFeatures properties() { @@ -320,7 +344,8 @@ private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatu * Instantiates a new ArangoDB graph vertex property features. */ - ArangoDBGraphVertexPropertyFeatures() { } + ArangoDBGraphVertexPropertyFeatures() { + } @Override public boolean supportsAnyIds() { @@ -340,11 +365,11 @@ public boolean supportsNumericIds() { @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 - */ + * 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; } } @@ -358,18 +383,25 @@ private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures * Instantiates a new ArangoDB graph edge property features. */ - ArangoDBGraphEdgePropertyFeatures() { } + ArangoDBGraphEdgePropertyFeatures() { + } } - /** The graph features. */ + /** + * The graph features. + */ protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); - /** The vertex features. */ + /** + * The vertex features. + */ protected VertexFeatures vertexFeatures = new ArangoDBGraphVertexFeatures(); - /** The edge features. */ + /** + * The edge features. + */ protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); @@ -394,90 +426,132 @@ public VertexFeatures vertex() { } } - /** The Logger. */ + /** + * The Logger. + */ 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 **/ + /** + * 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 Constant DEFAULT_VERTEX_COLLECTION. + */ public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; - /** The Constant DEFAULT_VERTEX_COLLECTION. */ + /** + * The Constant DEFAULT_VERTEX_COLLECTION. + */ public static final String DEFAULT_EDGE_COLLECTION = "edge"; - /** The Constant GRAPH_VARIABLES_COLLECTION. */ + /** + * The Constant GRAPH_VARIABLES_COLLECTION. + */ public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; - /** The Constant ELEMENT_PROPERTIES_COLLECTION. */ + /** + * The Constant ELEMENT_PROPERTIES_COLLECTION. + */ public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; - /** The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. */ + /** + * The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. + */ public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); - /** The features. */ + /** + * The features. + */ private final Features FEATURES = new ArangoDBGraphFeatures(); - /** A ArangoDBGraphClient to handle the connection to the Database. */ + /** + * A ArangoDBGraphClient to handle the connection to the Database. + */ private ArangoDBGraphClient client = null; - /** The name. */ + /** + * The name. + */ private String name; - /** The vertex collections. */ + /** + * The vertex collections. + */ private final List vertexCollections; - /** The edge collections. */ + /** + * The edge collections. + */ private final List edgeCollections; - /** The relations. */ + /** + * The relations. + */ private final List relations; - /** The configuration. */ + /** + * The configuration. + */ private Configuration configuration; - /** If collection names should be prefixed with graph name */ + /** + * If collection names should be prefixed with graph name + */ private final boolean shouldPrefixCollectionNames; @@ -729,6 +803,7 @@ public List vertexCollections() { /** * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag + * * @param collectionName the collection name * @return the Collection name prefixed */ diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java index 56f3a02..0e375d4 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -8,157 +8,21 @@ public class CustomGraphProvider extends BaseGraphProvider { @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("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; - } + 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; } } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java index 7d33ae3..0d58456 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -1,5 +1,6 @@ package com.arangodb.tinkerpop.gremlin.custom; +import com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map.MergeEdgeTest; import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; import org.junit.runners.model.InitializationError; @@ -9,13 +10,11 @@ public class CustomStandardSuite extends AbstractGremlinSuite { private static final Class[] allTests = new Class[]{ - }; - - private static final Class[] testsToEnforce = new Class[]{ + MergeEdgeTest.Traversals.class }; public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { - super(klass, builder, allTests, testsToEnforce, false, TraversalEngine.Type.STANDARD); + super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); } } 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..6616379 --- /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("standard_person/100", edge.outVertex().id()); + assertEquals("standard_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("standard_person/100", edge.outVertex().id()); + assertEquals("standard_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: standard_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("standard_person/100", edge.outVertex().id()); + assertEquals("standard_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, "standard_person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "standard_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("standard_person/100", edge.outVertex().id()); + assertEquals("standard_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, "standard_person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "standard_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, "standard_person/100").property("name", "marko"). + addV("person").property(T.id, "standard_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("standard_person/100").out("knows").hasId("standard_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, "standard_person/1") + .addV("person").property(T.id, "standard_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("standard_person/1").out("knows").hasId("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_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, "standard_person/1")) + .option(Merge.inV, asMap(T.id, "standard_person/2")); + } + } +} \ No newline at end of file From 00ec7af59582ffe8bf3bfbb8a80e026ec1dc0f5c Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 11 Mar 2025 11:56:33 +0100 Subject: [PATCH 20/28] tests fixes --- .../tinkerpop/gremlin/client/ArangoDBGraphClient.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 7f52040..83538af 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -21,6 +21,7 @@ import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; import com.arangodb.tinkerpop.gremlin.persistence.VertexData; 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; @@ -76,8 +77,14 @@ private ArangoDBExceptions() { * @param ex the ex * @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; + } + String errorMessage = ex.getMessage(); Matcher m = ERROR_CODE.matcher(errorMessage); if (m.matches()) { From 0dec39653768fca1ae2aaca0c6802f02b02fbb2a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 11 Mar 2025 15:07:12 +0100 Subject: [PATCH 21/28] tests fixes --- .../gremlin/structure/ArangoDBGraph.java | 27 +- .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 2 +- .../tinkerpop/gremlin/custom/CustomGraph.java | 54 ++ .../gremlin/custom/CustomGraphProvider.java | 13 +- .../gremlin/custom/CustomStandardSuite.java | 4 +- .../custom/CustomStandardSuiteTest.java | 3 +- .../traversal/step/OrderabilityTest.java | 509 ++++++++++++++++++ .../gremlin/util/BaseGraphProvider.java | 2 + 8 files changed, 598 insertions(+), 16 deletions(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java 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 72cf736..fe3255f 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -197,6 +197,11 @@ 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" +) public class ArangoDBGraph implements Graph { /** @@ -209,7 +214,7 @@ public class ArangoDBGraphFeatures implements Features { * The Class ArangoDBGraphGraphFeatures. */ - private class ArangoDBGraphGraphFeatures implements GraphFeatures { + protected class ArangoDBGraphGraphFeatures implements GraphFeatures { /** * The variable features. @@ -220,7 +225,7 @@ private class ArangoDBGraphGraphFeatures implements GraphFeatures { * Instantiates a new ArangoDB graph graph features. */ - ArangoDBGraphGraphFeatures() { + protected ArangoDBGraphGraphFeatures() { } @Override @@ -248,13 +253,13 @@ public VariableFeatures variables() { * The Class ArangoDBGraphElementFeatures. */ - private class ArangoDBGraphElementFeatures implements ElementFeatures { + protected class ArangoDBGraphElementFeatures implements ElementFeatures { /** * Instantiates a new ArangoDB graph element features. */ - ArangoDBGraphElementFeatures() { + protected ArangoDBGraphElementFeatures() { } @Override @@ -288,7 +293,7 @@ public boolean supportsUuidIds() { * The Class ArangoDBGraphVertexFeatures. */ - private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { + protected class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { /** * The vertex property features. @@ -300,7 +305,7 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i * Instantiates a new ArangoDB graph vertex features. */ - ArangoDBGraphVertexFeatures() { + protected ArangoDBGraphVertexFeatures() { } @@ -325,7 +330,7 @@ public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures impl * Instantiates a new ArangoDB graph edge features. */ - ArangoDBGraphEdgeFeatures() { + protected ArangoDBGraphEdgeFeatures() { } @Override @@ -338,13 +343,13 @@ public EdgePropertyFeatures properties() { * The Class ArangoDBGraphVertexPropertyFeatures. */ - private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { + protected class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { /** * Instantiates a new ArangoDB graph vertex property features. */ - ArangoDBGraphVertexPropertyFeatures() { + protected ArangoDBGraphVertexPropertyFeatures() { } @Override @@ -377,13 +382,13 @@ public boolean supportsUuidIds() { /** * The Class ArangoDBGraphEdgePropertyFeatures. */ - private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { + protected class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { /** * Instantiates a new ArangoDB graph edge property features. */ - ArangoDBGraphEdgePropertyFeatures() { + protected ArangoDBGraphEdgePropertyFeatures() { } } 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 f5bdcda..d1cb64d 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -609,7 +609,7 @@ public static String getId(Graph.Features.ElementFeatures features, String label id = parts[1]; } } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String) id); + Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher(id.toString()); if (m.matches()) { return id.toString(); } else { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java new file mode 100644 index 0000000..9d25236 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java @@ -0,0 +1,54 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.commons.configuration2.Configuration; + + +public class CustomGraph extends ArangoDBGraph { + + public static CustomGraph open(Configuration configuration) { + return new CustomGraph(configuration); + } + + class CustomFeatures extends ArangoDBGraph.ArangoDBGraphFeatures { + + @Override + public EdgeFeatures edge() { + return new ArangoDBGraphEdgeFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + + @Override + public VertexFeatures vertex() { + return new ArangoDBGraphVertexFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + + @Override + public VertexPropertyFeatures properties() { + return new ArangoDBGraphVertexPropertyFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + }; + } + } + + public CustomGraph(Configuration configuration) { + super(configuration); + } + + @Override + public Features features() { + return new CustomFeatures(); + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java index 0e375d4..257ab75 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -2,10 +2,21 @@ import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; -import org.apache.tinkerpop.gremlin.structure.VertexTest; +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, CustomGraph.class.getName()); + return conf; + } + @Override protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { switch (testMethodName) { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java index 0d58456..320633d 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -1,5 +1,6 @@ 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 org.apache.tinkerpop.gremlin.AbstractGremlinSuite; import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; @@ -10,7 +11,8 @@ public class CustomStandardSuite extends AbstractGremlinSuite { private static final Class[] allTests = new Class[]{ - MergeEdgeTest.Traversals.class + MergeEdgeTest.Traversals.class, + OrderabilityTest.Traversals.class, }; public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java index e91b299..2d99be0 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java @@ -1,11 +1,10 @@ package com.arangodb.tinkerpop.gremlin.custom; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; import org.apache.tinkerpop.gremlin.GraphProviderClass; import org.junit.runner.RunWith; @RunWith(CustomStandardSuite.class) -@GraphProviderClass(provider = CustomGraphProvider.class, graph = ArangoDBGraph.class) +@GraphProviderClass(provider = CustomGraphProvider.class, graph = CustomGraph.class) public class CustomStandardSuiteTest { } 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/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index ca7cab5..b4ff2d4 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -1,5 +1,6 @@ package com.arangodb.tinkerpop.gremlin.util; +import com.arangodb.tinkerpop.gremlin.custom.CustomGraph; import com.arangodb.tinkerpop.gremlin.structure.*; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; import org.apache.commons.configuration2.Configuration; @@ -127,6 +128,7 @@ public Set getImplementations() { return Stream.of( ArangoDBEdge.class, ArangoDBElement.class, + CustomGraph.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, ArangoDBPersistentElement.class, From 62b6c0af28271c7944a3af245f1e7089b7870e08 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 11 Mar 2025 22:02:11 +0100 Subject: [PATCH 22/28] tests fixes --- .../gremlin/client/ArangoDBGraphClient.java | 18 ++- .../gremlin/structure/ArangoDBGraph.java | 120 +++--------------- .../tinkerpop/gremlin/custom/CustomGraph.java | 68 +++++----- 3 files changed, 61 insertions(+), 145 deletions(-) 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 83538af..f3c6d18 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -13,6 +13,7 @@ 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.*; @@ -27,12 +28,6 @@ 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.tinkerpop.gremlin.utils.ArangoDBUtil; import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.getArangoDirectionFromGremlinDirection; @@ -281,7 +276,7 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphVertices(final List ids) { + 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::getPrefixedCollectioName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, VertexData.class); @@ -293,13 +288,13 @@ public ArangoCursor getGraphVertices(final List ids) { * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphEdges(List ids) { + public ArangoIterable getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, EdgeData.class); } - private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { + private ArangoIterable getGraphDocuments(List ids, List prefixedColNames, Class clazz) { Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); if (ids.isEmpty()) { @@ -309,7 +304,10 @@ private ArangoCursor getGraphDocuments(List ids, List pre queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); } } else { - queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "d", bindVars); + List prunedIds = ids.stream() + .filter(it -> prefixedColNames.contains(ArangoDBUtil.extractCollection(it))) + .collect(Collectors.toList()); + queryBuilder.with(prefixedColNames, bindVars).documentsById(prunedIds, "d", bindVars); } queryBuilder.ret("d"); String query = queryBuilder.toString(); 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 fe3255f..a0e4363 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -202,28 +202,16 @@ 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 ArangoDBGraph implements Graph { - /** - * The Class ArangoDBGraphFeatures. - */ - - public class ArangoDBGraphFeatures implements Features { - - /** - * The Class ArangoDBGraphGraphFeatures. - */ - - protected class ArangoDBGraphGraphFeatures implements GraphFeatures { + public static class ArangoDBGraphFeatures implements Features { - /** - * The variable features. - */ - private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); - - /** - * Instantiates a new ArangoDB graph graph features. - */ + protected static class ArangoDBGraphGraphFeatures implements GraphFeatures { protected ArangoDBGraphGraphFeatures() { } @@ -245,19 +233,11 @@ public boolean supportsTransactions() { @Override public VariableFeatures variables() { - return variableFeatures; + return new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); } } - /** - * The Class ArangoDBGraphElementFeatures. - */ - - protected class ArangoDBGraphElementFeatures implements ElementFeatures { - - /** - * Instantiates a new ArangoDB graph element features. - */ + protected static class ArangoDBGraphElementFeatures implements ElementFeatures { protected ArangoDBGraphElementFeatures() { } @@ -289,65 +269,29 @@ public boolean supportsUuidIds() { } } - /** - * The Class ArangoDBGraphVertexFeatures. - */ - - protected class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { - - /** - * The vertex property features. - */ - - private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); - - /** - * Instantiates a new ArangoDB graph vertex features. - */ + protected static class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { protected ArangoDBGraphVertexFeatures() { } - @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 { protected ArangoDBGraphEdgeFeatures() { } @Override public EdgePropertyFeatures properties() { - return edgePropertyFeatures; + return new ArangoDBGraphEdgePropertyFeatures(); } } - /** - * The Class ArangoDBGraphVertexPropertyFeatures. - */ - - protected class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - - /** - * Instantiates a new ArangoDB graph vertex property features. - */ + protected static class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { protected ArangoDBGraphVertexPropertyFeatures() { } @@ -379,45 +323,21 @@ public boolean supportsUuidIds() { } } - /** - * The Class ArangoDBGraphEdgePropertyFeatures. - */ - protected class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { - - /** - * Instantiates a new ArangoDB graph edge property features. - */ + protected static class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { protected ArangoDBGraphEdgePropertyFeatures() { } - } - - /** - * The graph features. - */ - - protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); - - /** - * The vertex features. - */ - protected VertexFeatures vertexFeatures = new ArangoDBGraphVertexFeatures(); - - /** - * The edge features. - */ - - protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); + } @Override public EdgeFeatures edge() { - return edgeFeatures; + return new ArangoDBGraphEdgeFeatures(); } @Override public GraphFeatures graph() { - return graphFeatures; + return new ArangoDBGraphGraphFeatures(); } @Override @@ -427,7 +347,7 @@ public String toString() { @Override public VertexFeatures vertex() { - return vertexFeatures; + return new ArangoDBGraphVertexFeatures(); } } @@ -551,7 +471,7 @@ public VertexFeatures vertex() { * The configuration. */ - private Configuration configuration; + private final Configuration configuration; /** diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java index 9d25236..9f06315 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java @@ -3,52 +3,50 @@ import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; import org.apache.commons.configuration2.Configuration; - public class CustomGraph extends ArangoDBGraph { + @SuppressWarnings("unused") public static CustomGraph open(Configuration configuration) { return new CustomGraph(configuration); } - class CustomFeatures extends ArangoDBGraph.ArangoDBGraphFeatures { - - @Override - public EdgeFeatures edge() { - return new ArangoDBGraphEdgeFeatures() { - @Override - public boolean supportsNumericIds() { - return true; - } - }; - } - - @Override - public VertexFeatures vertex() { - return new ArangoDBGraphVertexFeatures() { - @Override - public boolean supportsNumericIds() { - return true; - } - - @Override - public VertexPropertyFeatures properties() { - return new ArangoDBGraphVertexPropertyFeatures() { - @Override - public boolean supportsNumericIds() { - return true; - } - }; - } - }; - } - } - public CustomGraph(Configuration configuration) { super(configuration); } @Override public Features features() { - return new CustomFeatures(); + 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; + } + }; + } + }; + } + }; } } From ad5adb246aa11365e5c0baebc757b707a9cb26c7 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 12 Mar 2025 12:38:58 +0100 Subject: [PATCH 23/28] tests fixes --- .../gremlin/structure/ArangoDBGraph.java | 2 +- .../structure/ArangoDBPersistentElement.java | 1 + .../gremlin/custom/CustomGraphProvider.java | 8 ++ .../gremlin/custom/CustomStandardSuite.java | 2 + .../util/detached/DetachedGraphTest.java | 83 +++++++++++++++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java 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 a0e4363..6be4c6b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -141,7 +141,7 @@ @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", method = "testAttachableCreateMethod", - reason = "test creates id without label prefix") + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.detached.DetachedGraphTest#testAttachableCreateMethod()") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldAddVertexWithUserSuppliedStringId", diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java index dc04b76..a63b93b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -53,6 +53,7 @@ default String collection() { default String id() { return Optional.ofNullable(key()) .map(it -> collection() + '/' + it) + // TODO: review .orElse(label()); } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java index 257ab75..aac4a97 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -34,6 +34,14 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St builder.configureEdge("knows", "person", "person"); builder.configureEdge("self", "person", "person"); 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; } } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java index 320633d..3c93b56 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -2,6 +2,7 @@ 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 org.apache.tinkerpop.gremlin.AbstractGremlinSuite; import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; import org.junit.runners.model.InitializationError; @@ -13,6 +14,7 @@ public class CustomStandardSuite extends AbstractGremlinSuite { private static final Class[] allTests = new Class[]{ MergeEdgeTest.Traversals.class, OrderabilityTest.Traversals.class, + DetachedGraphTest.class }; public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { 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..c15d0f4 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.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.custom.structure.util.detached; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +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.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 { + + // FIXME: review after DE-996 and DE-997 + @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) + 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); + }); + + } +} From 8a075125f0640104c18a667c841d6cafa88f9154 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 12 Mar 2025 14:12:45 +0100 Subject: [PATCH 24/28] tests fixes --- .../gremlin/structure/ArangoDBGraph.java | 74 ---------------- .../arangodb/tinkerpop/gremlin/TestGraph.java | 88 +++++++++++++++++++ .../tinkerpop/gremlin/custom/CustomGraph.java | 2 + .../gremlin/process/ProcessGraphProvider.java | 13 +++ .../process/ProcessStandardSuiteTest.java | 4 +- .../structure/StructureGraphProvider.java | 13 +++ .../structure/StructureStandardSuiteTest.java | 3 +- .../gremlin/util/BaseGraphProvider.java | 2 + 8 files changed, 122 insertions(+), 77 deletions(-) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java 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 6be4c6b..92b1cb8 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -133,80 +133,6 @@ * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ -@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD) -@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) -@Graph.OptIn("com.arangodb.tinkerpop.gremlin.StructureIntegrateSuite") -@Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") -@Graph.OptIn("com.arangodb.tinkerpop.gremlin.custom.CustomStandardSuite") -@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") -@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.GraphTest", - method = "shouldEvaluateConnectivityPatterns", - 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 = "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.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", - method = "shouldAddEdgeWithUserSuppliedStringId", - reason = "FIXME") -@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 ArangoDBGraph implements Graph { public static class ArangoDBGraphFeatures implements Features { 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..39f4373 --- /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") +@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.GraphTest", + method = "shouldEvaluateConnectivityPatterns", + 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 = "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.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", + reason = "FIXME") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", + method = "shouldAddEdgeWithUserSuppliedStringId", + reason = "FIXME") +@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/custom/CustomGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java index 9f06315..acc9ffe 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java @@ -2,7 +2,9 @@ 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 CustomGraph extends ArangoDBGraph { @SuppressWarnings("unused") diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java index 31df28b..1562cd1 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java @@ -1,10 +1,23 @@ 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) { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java index 309424c..19f8451 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java @@ -1,12 +1,12 @@ package com.arangodb.tinkerpop.gremlin.process; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +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 = ArangoDBGraph.class) +@GraphProviderClass(provider = ProcessGraphProvider.class, graph = TestGraph.class) public class ProcessStandardSuiteTest { } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java index 5bd2491..90f4b0f 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -1,12 +1,25 @@ 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") diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java index 907482e..5a330be 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java @@ -1,11 +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 = ArangoDBGraph.class) +@GraphProviderClass(provider = StructureGraphProvider.class, graph = TestGraph.class) public class StructureStandardSuiteTest { } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index b4ff2d4..267c0d9 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -1,5 +1,6 @@ package com.arangodb.tinkerpop.gremlin.util; +import com.arangodb.tinkerpop.gremlin.TestGraph; import com.arangodb.tinkerpop.gremlin.custom.CustomGraph; import com.arangodb.tinkerpop.gremlin.structure.*; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; @@ -128,6 +129,7 @@ public Set getImplementations() { return Stream.of( ArangoDBEdge.class, ArangoDBElement.class, + TestGraph.class, CustomGraph.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, From cf51f4833feddc22adb42952f60107e98f75a6f7 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 13 Mar 2025 10:45:51 +0100 Subject: [PATCH 25/28] tests fixes --- .../arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java | 1 + .../java/com/arangodb/tinkerpop/gremlin/TestGraph.java | 8 ++++---- .../gremlin/structure/StructureGraphProvider.java | 4 ++-- .../tinkerpop/gremlin/util/BaseGraphProvider.java | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) 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 d1cb64d..a200ef2 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -592,6 +592,7 @@ public static Optional extractLabel(final String id, final String label) return Optional.ofNullable(label); } + // FIXME: DE-996 public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { Optional optionalId = ElementHelper.getIdValue(keyValues); if (!optionalId.isPresent()) { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java index 39f4373..cfb9788 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java @@ -13,19 +13,19 @@ @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldAddVertexWithUserSuppliedStringId", - reason = "FIXME") + reason = "FIXME: DE-996") @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.") + reason = "FIXME: DE-998") @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.") + reason = "FIXME: DE-998") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.GraphTest", method = "shouldEvaluateConnectivityPatterns", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldAttachWithCreateMethod", diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java index 90f4b0f..c26e119 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -19,7 +19,7 @@ public Configuration newGraphConfiguration(String graphName, Class test, Stri conf.setProperty(Graph.GRAPH, TestGraph.class.getName()); return conf; } - + @Override protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { if (testMethodName.startsWith("shouldProcessVerticesEdges") @@ -84,7 +84,7 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St break; case "shouldEvaluateConnectivityPatterns": builder.withEdgeCollection("knows"); - builder.withEdgeCollection("knows"); + builder.withEdgeCollection("hates"); break; case "shouldRemoveEdgesWithoutConcurrentModificationException": builder.withEdgeCollection("link"); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index 267c0d9..51325df 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -148,6 +148,7 @@ public Map getBaseConfiguration(String graphName, Class test, return null; } + // FIXME: DE-997 @Override public Object convertId(Object id, Class c) { return id.toString(); From ad93347a899126a531315b8ebdf2f5855e3c6069 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Thu, 13 Mar 2025 12:01:56 +0100 Subject: [PATCH 26/28] tests fixes --- .../gremlin/client/ArangoDBGraphClient.java | 3 +++ .../com/arangodb/tinkerpop/gremlin/TestGraph.java | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) 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 f3c6d18..5d0dbc5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -276,6 +276,7 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { * @return ArangoDBBaseQuery the query object */ + // 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::getPrefixedCollectioName).collect(Collectors.toList()); @@ -288,6 +289,7 @@ public ArangoIterable getGraphVertices(final List ids) { * @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::getPrefixedCollectioName).collect(Collectors.toList()); @@ -305,6 +307,7 @@ private ArangoIterable getGraphDocuments(List ids, List p } } else { List prunedIds = ids.stream() + .map(graph::getPrefixedCollectioName) .filter(it -> prefixedColNames.contains(ArangoDBUtil.extractCollection(it))) .collect(Collectors.toList()); queryBuilder.with(prefixedColNames, bindVars).documentsById(prunedIds, "d", bindVars); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java index cfb9788..124c900 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java @@ -29,27 +29,27 @@ @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldAttachWithCreateMethod", - reason = "FIXME") + reason = "FIXME: DE-997") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldCopyFromGraphAToGraphB", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", method = "shouldAddEdgeWithUserSuppliedStringId", - reason = "FIXME") + reason = "FIXME: DE-996") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeTest$Traversals", method = "*", From 721cfaf1ab6963f05695a804ef7cd268be55b57d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 14 Mar 2025 11:42:09 +0100 Subject: [PATCH 27/28] tests fixes --- .../gremlin/structure/ArangoDBGraph.java | 12 -- .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 45 ++++--- .../arangodb/tinkerpop/gremlin/TestGraph.java | 4 +- .../gremlin/custom/CustomGraphProvider.java | 13 +- .../gremlin/custom/CustomStandardSuite.java | 4 +- .../custom/CustomStandardSuiteTest.java | 2 +- ...{CustomGraph.java => CustomTestGraph.java} | 8 +- .../util/detached/DetachedGraphTest.java | 4 +- .../structure/util/star/StarGraphTest.java | 115 ++++++++++++++++++ .../structure/StructureGraphProvider.java | 1 - .../gremlin/util/BaseGraphProvider.java | 5 +- 11 files changed, 166 insertions(+), 47 deletions(-) rename src/test/java/com/arangodb/tinkerpop/gremlin/custom/{CustomGraph.java => CustomTestGraph.java} (87%) create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java 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 92b1cb8..13aff31 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -185,12 +185,6 @@ public boolean supportsNumericIds() { @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; } } @@ -239,12 +233,6 @@ public boolean supportsNumericIds() { @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; } } 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 a200ef2..9d08e16 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -581,43 +581,50 @@ public static String extractCollection(final String id) { } } + // FIXME: review public static Optional extractLabel(final String id, final String label) { String col = extractCollection(id); if (col != null) { - if (label != null && !label.equals(col)) { + String labelFromId = col.replaceFirst("^.*_", ""); + if (label != null && !label.equals(labelFromId)) { throw new IllegalArgumentException("Invalid label: [" + label + "] for id: [" + id + "]"); } - return Optional.of(col); + return Optional.of(labelFromId); } return Optional.ofNullable(label); } - // FIXME: DE-996 + // FIXME: use com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractKey() and com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractLabel() public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { Optional optionalId = ElementHelper.getIdValue(keyValues); if (!optionalId.isPresent()) { return null; } - Object id = optionalId.get(); - if (features.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)) { + String id = optionalId + .filter(features::willAllowId) + .map(Object::toString) + .orElseThrow(Vertex.Exceptions::userSuppliedIdsOfThisTypeNotSupported); + + if (id.contains("/")) { + String fullId = id; + 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]; + Optional inferredLabel = extractLabel(id, label); + if(inferredLabel.isPresent()) { + if (collectionName.contains(inferredLabel.get())) { id = parts[1]; } } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher(id.toString()); - if (m.matches()) { - return id.toString(); - } else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } + } + + // FIXME: review + Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher(id); + if (m.matches()) { + return id; } else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java index 124c900..eb7c593 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java @@ -29,11 +29,11 @@ @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", method = "shouldAttachWithCreateMethod", - reason = "FIXME: DE-997") + 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 = "FIXME: DE-996") + 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", diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java index aac4a97..2718340 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -13,7 +13,7 @@ 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, CustomGraph.class.getName()); + conf.setProperty(Graph.GRAPH, CustomTestGraph.class.getName()); return conf; } @@ -35,6 +35,8 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St builder.configureEdge("self", "person", "person"); break; case "testAttachableCreateMethod": + case "shouldAttachWithCreateMethod": + builder.withVertexCollection("vertex"); builder.withVertexCollection("person"); builder.withVertexCollection("project"); builder.withEdgeCollection("knows"); @@ -42,6 +44,15 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St 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 index 3c93b56..60bba07 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -3,6 +3,7 @@ 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; @@ -14,7 +15,8 @@ public class CustomStandardSuite extends AbstractGremlinSuite { private static final Class[] allTests = new Class[]{ MergeEdgeTest.Traversals.class, OrderabilityTest.Traversals.class, - DetachedGraphTest.class + DetachedGraphTest.class, + StarGraphTest.class }; public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java index 2d99be0..d810b70 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java @@ -4,7 +4,7 @@ import org.junit.runner.RunWith; @RunWith(CustomStandardSuite.class) -@GraphProviderClass(provider = CustomGraphProvider.class, graph = CustomGraph.class) +@GraphProviderClass(provider = CustomGraphProvider.class, graph = CustomTestGraph.class) public class CustomStandardSuiteTest { } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java similarity index 87% rename from src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java rename to src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java index acc9ffe..56e4df5 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraph.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java @@ -5,14 +5,14 @@ import org.apache.tinkerpop.gremlin.structure.Graph; @Graph.OptIn("com.arangodb.tinkerpop.gremlin.custom.CustomStandardSuite") -public class CustomGraph extends ArangoDBGraph { +public class CustomTestGraph extends ArangoDBGraph { @SuppressWarnings("unused") - public static CustomGraph open(Configuration configuration) { - return new CustomGraph(configuration); + public static CustomTestGraph open(Configuration configuration) { + return new CustomTestGraph(configuration); } - public CustomGraph(Configuration configuration) { + public CustomTestGraph(Configuration configuration) { super(configuration); } 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 index c15d0f4..d2e50b8 100644 --- 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 @@ -18,10 +18,8 @@ */ package com.arangodb.tinkerpop.gremlin.custom.structure.util.detached; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 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; @@ -44,7 +42,7 @@ */ public class DetachedGraphTest extends AbstractGremlinTest { - // FIXME: review after DE-996 and DE-997 + // FIXME: review after DE-996 @Ignore("FIXME") @Test @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) 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..0474b7a --- /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: 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) + @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/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java index c26e119..889c769 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -144,7 +144,6 @@ protected void configure(ArangoDBConfigurationBuilder builder, Class test, St builder.withVertexCollection("person"); builder.withEdgeCollection("self"); break; - case "shouldAttachWithCreateMethod": case "testAttachableCreateMethod": builder.withVertexCollection("person"); builder.withVertexCollection("project"); diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java index 51325df..086a8d7 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -1,7 +1,7 @@ package com.arangodb.tinkerpop.gremlin.util; import com.arangodb.tinkerpop.gremlin.TestGraph; -import com.arangodb.tinkerpop.gremlin.custom.CustomGraph; +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; @@ -130,7 +130,7 @@ public Set getImplementations() { ArangoDBEdge.class, ArangoDBElement.class, TestGraph.class, - CustomGraph.class, + CustomTestGraph.class, ArangoDBGraph.class, ArangoDBGraphVariables.class, ArangoDBPersistentElement.class, @@ -148,7 +148,6 @@ public Map getBaseConfiguration(String graphName, Class test, return null; } - // FIXME: DE-997 @Override public Object convertId(Object id, Class c) { return id.toString(); From 29818fb2d643662fe0fa52073f166fef78902f9a Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 18 Mar 2025 16:20:59 +0100 Subject: [PATCH 28/28] collection prefix refactoring --- .../gremlin/client/ArangoDBBaseDocument.java | 2 +- .../gremlin/client/ArangoDBGraphClient.java | 77 ++---- .../gremlin/client/ArangoDBQueryBuilder.java | 5 +- .../gremlin/persistence/EdgeData.java | 53 +++-- .../gremlin/persistence/PersistentData.java | 27 ++- .../gremlin/persistence/VertexData.java | 31 +-- .../gremlin/structure/ArangoDBEdge.java | 6 +- .../gremlin/structure/ArangoDBGraph.java | 93 ++++++-- .../gremlin/structure/ArangoDBId.java | 118 ++++++++++ .../structure/ArangoDBPersistentElement.java | 17 +- .../gremlin/structure/ArangoDBVertex.java | 20 +- .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 221 +----------------- .../traversal/step/map/MergeEdgeTest.java | 62 ++--- .../util/detached/DetachedGraphTest.java | 3 +- .../structure/util/star/StarGraphTest.java | 4 +- .../gremlin/util/TestGraphClient.java | 2 - 16 files changed, 340 insertions(+), 401 deletions(-) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBId.java 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/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index 5d0dbc5..40848b1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -17,10 +17,9 @@ 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.persistence.EdgeData; -import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; import org.apache.tinkerpop.gremlin.structure.Direction; @@ -28,8 +27,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.getArangoDirectionFromGremlinDirection; /** @@ -105,25 +102,6 @@ public static ArangoDBGraphException getArangoDBException(ArangoDBException 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)"; - - /** - * Gets the naming convention error. - * - * @param cause the cause - * @param details the details - * @return the naming convention error - */ - - 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 static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); @@ -162,21 +140,6 @@ public void shutdown() { db.arango().shutdown(); } - /** - * 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); - } - } - public ArangoDBGraphVariables getGraphVariables() { logger.debug("Get graph variables"); ArangoDBGraphVariables result; @@ -205,9 +168,9 @@ public void insertGraphVariables(ArangoDBGraphVariables document) { if (document.isPaired()) { throw new ArangoDBGraphException("Paired docuements can not be inserted, only updated"); } - ArangoCollection gVars = db.collection(document.collection()); + ArangoCollection gVars = db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION); if (!gVars.exists()) { - CollectionEntity ce = db.createCollection(document.collection()); + CollectionEntity ce = gVars.create(); System.out.println(ce.getStatus()); } DocumentCreateEntity vertexEntity; @@ -277,9 +240,9 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { */ // FIXME: use multi-docs API - public ArangoIterable getGraphVertices(final List ids) { + 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::getPrefixedCollectioName).collect(Collectors.toList()); + List prefixedColNames = graph.vertexCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, VertexData.class); } @@ -290,13 +253,13 @@ public ArangoIterable getGraphVertices(final List ids) { * @return ArangoDBBaseQuery the query object */ // FIXME: use multi-docs API - public ArangoIterable getGraphEdges(List ids) { + public ArangoIterable getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); - List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); + List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, EdgeData.class); } - private ArangoIterable getGraphDocuments(List ids, List prefixedColNames, Class clazz) { + private ArangoIterable getGraphDocuments(List ids, List prefixedColNames, Class clazz) { Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); if (ids.isEmpty()) { @@ -306,9 +269,9 @@ private ArangoIterable getGraphDocuments(List ids, List p queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); } } else { - List prunedIds = ids.stream() - .map(graph::getPrefixedCollectioName) - .filter(it -> prefixedColNames.contains(ArangoDBUtil.extractCollection(it))) + List prunedIds = ids.stream() + .map(graph::getArangoDBId) + .filter(it -> prefixedColNames.contains(it.getCollection())) .collect(Collectors.toList()); queryBuilder.with(prefixedColNames, bindVars).documentsById(prunedIds, "d", bindVars); } @@ -396,7 +359,7 @@ public void insertEdge(ArangoDBEdge edge) { } throw arangoDBException; } - edge.key(insertEntity.getKey()); + edge.update(insertEntity); } public void deleteEdge(ArangoDBEdge edge) { @@ -426,17 +389,15 @@ public void updateEdge(ArangoDBEdge edge) { throw ArangoDBExceptions.getArangoDBException(e); } logger.debug("Edge updated, new rev {}", updateEntity.getRev()); - edge.key(updateEntity.getKey()); + edge.update(updateEntity); } - public VertexData readVertex(String id) { + public VertexData readVertex(ArangoDBId id) { logger.debug("Read vertex {} in {}", id, graph.name()); - String col = ArangoDBUtil.extractCollection(id); - String key = ArangoDBUtil.extractKey(id); try { return db.graph(graph.name()) - .vertexCollection(col) - .getVertex(key, VertexData.class); + .vertexCollection(id.getCollection()) + .getVertex(id.getKey(), VertexData.class); } catch (ArangoDBException e) { logger.error("Failed to read vertex: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); @@ -458,7 +419,7 @@ public void insertVertex(ArangoDBVertex vertex) { } throw arangoDBException; } - vertex.key(vertexEntity.getKey()); + vertex.update(vertexEntity); } public void deleteVertex(ArangoDBVertex vertex) { @@ -490,7 +451,7 @@ public void updateVertex(ArangoDBVertex vertex) { logger.debug("Document updated, new rev {}", vertexEntity.getRev()); } - public Iterator getVertexNeighbors(String vertexId, List edgeCollections, Direction direction) { + 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); @@ -501,7 +462,7 @@ public Iterator getVertexNeighbors(String vertexId, List edg return executeAqlQuery(query, bindVars, null, VertexData.class); } - public Iterator getVertexEdges(String vertexId, List edgeCollections, Direction direction) { + 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); 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 f0f4fb2..80786aa 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,14 +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()); + logger.debug("documentsById: {}", queryBuilder.toString()); return this; } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java index 913f34b..8c789f8 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -21,12 +21,15 @@ import com.arangodb.serde.*; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +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; @@ -34,25 +37,20 @@ public class EdgeData extends SimplePropertyData implements PersistentData { private String key; @InternalFrom - private String from; + private ArangoDBId from; @InternalTo - private String to; + private ArangoDBId to; public static EdgeData of( - String label, - String key, - String from, - String to + ArangoDBId id, + ArangoDBId from, + ArangoDBId to ) { - ElementHelper.validateLabel(label); - if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - Objects.requireNonNull(from, "from"); - Objects.requireNonNull(to, "to"); - EdgeData data = new EdgeData(); - data.label = label; - data.key = key; + data.id = id; + data.label = id.getLabel(); + data.key = id.getKey(); data.from = from; data.to = to; return data; @@ -62,13 +60,13 @@ public EdgeData() { } @Override - public String getLabel() { - return label; + public ArangoDBId getId() { + return id; } @Override - public String getKey() { - return key; + public void setId(ArangoDBId id) { + this.id = id; } @Override @@ -76,42 +74,43 @@ public void setKey(String key) { this.key = key; } - public String getFrom() { + public ArangoDBId getFrom() { return from; } - public void setFrom(String from) { + public void setFrom(ArangoDBId from) { this.from = from; } - public String getTo() { + public ArangoDBId getTo() { return to; } - public void setTo(String to) { + public void setTo(ArangoDBId to) { this.to = to; } @Override public String toString() { return "EdgeData{" + - "from='" + from + '\'' + + "from=" + from + + ", id=" + id + ", label='" + label + '\'' + ", key='" + key + '\'' + - ", to='" + to + '\'' + - ", super=" + super.toString() + + ", 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(label, edgeData.label) && Objects.equals(key, edgeData.key) && Objects.equals(from, edgeData.from) && Objects.equals(to, edgeData.to); + 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(label, key, from, to); + 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 index 6121fd3..91c640d 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java @@ -19,10 +19,33 @@ package com.arangodb.tinkerpop.gremlin.persistence; +import com.arangodb.entity.DocumentEntity; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBId; + public interface PersistentData { - String getLabel(); - String getKey(); + 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/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java index 34e09f0..e017cc1 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -19,15 +19,19 @@ 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 org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +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; @@ -40,24 +44,22 @@ public class VertexData implements PropertyData, PersistentD public VertexData() { } - public static VertexData of(String label, String key) { - ElementHelper.validateLabel(label); - if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - + public static VertexData of(ArangoDBId id) { VertexData data = new VertexData(); - data.label = label; - data.key = key; + data.id = id; + data.label = id.getLabel(); + data.key = id.getKey(); return data; } @Override - public String getLabel() { - return label; + public ArangoDBId getId() { + return id; } @Override - public String getKey() { - return key; + public void setId(ArangoDBId id) { + this.id = id; } @Override @@ -87,8 +89,9 @@ public void remove(String key, VertexPropertyData value) { @Override public String toString() { return "VertexData{" + - "key='" + key + '\'' + + "id=" + id + ", label='" + label + '\'' + + ", key='" + key + '\'' + ", properties=" + properties + '}'; } @@ -97,11 +100,11 @@ public String toString() { public boolean equals(Object o) { if (!(o instanceof VertexData)) return false; VertexData that = (VertexData) o; - return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(properties, that.properties); + 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(label, key, properties); + return Objects.hash(id, label, key, properties); } } 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 6508b0d..8f0f47b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -26,13 +26,11 @@ import java.util.*; -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; - public class ArangoDBEdge extends ArangoDBSimpleElement implements Edge, ArangoDBPersistentElement { - public static ArangoDBEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { - return new ArangoDBEdge(graph, EdgeData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); + 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, EdgeData data) { 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 13aff31..23e5869 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -441,8 +441,8 @@ public ArangoDBGraph(Configuration configuration) { 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 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."); @@ -488,9 +488,9 @@ public ArangoDBGraph(Configuration configuration) { @Override public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); - String label = ElementHelper.getLabelValue(keyValues).orElse(null); - String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); - ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); + 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)); } @@ -567,14 +567,14 @@ public List edgeCollections() { @Override public Iterator edges(Object... edgeIds) { - return getClient().getGraphEdges(getIdValues(edgeIds)).stream() + 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(vertexIds)).stream() + return getClient().getGraphVertices(getIdValues(Vertex.DEFAULT_LABEL, vertexIds)).stream() .map(it -> (Vertex) new ArangoDBVertex(this, it)) .iterator(); } @@ -646,21 +646,19 @@ public List vertexCollections() { * @param collectionName the collection name * @return the Collection name prefixed */ - public String getPrefixedCollectioName(String collectionName) { - if (GRAPH_VARIABLES_COLLECTION.equals(collectionName)) { + public String getPrefixedCollectionName(String collectionName) { + if (!shouldPrefixCollectionNames) { return collectionName; } - if (GRAPH_COLLECTIONS.contains(collectionName)) { - return String.format("%s_%s", name, collectionName); - } - if (shouldPrefixCollectionNames) { - if (collectionName.startsWith(name + "_")) { - return collectionName; - } - return String.format("%s_%s", name, collectionName); - } else { + + if (collectionName.startsWith(name + "_")) { return collectionName; } + return name + "_" + collectionName; + } + + public ArangoDBId getArangoDBId(ArangoDBId id) { + return ArangoDBId.of(name, id.getLabel(), id.getKey()); } @Override @@ -692,22 +690,69 @@ private Collection relations() { return relations; } - private String getIdValue(Object id) { + private ArangoDBId getIdValue(String defaultLabel, Object id) { if (id instanceof String) { - return (String) id; + return ArangoDBId.parseWithDefaultLabel(name, defaultLabel, (String) id); } else if (id instanceof Element) { - return getIdValue(((Element) id).id()); + return getIdValue(defaultLabel, ((Element) id).id()); } else { throw unsupportedIdType(id); } } - private List getIdValues(Object[] edgeIds) { - return Arrays.stream(edgeIds) - .map(this::getIdValue) + 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 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 index a63b93b..6d190f7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -19,6 +19,7 @@ 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; @@ -35,8 +36,8 @@ default String key() { return data().getKey(); } - default void key(String key) { - data().setKey(key); + default void update(DocumentEntity entity) { + data().update(entity); } @Override @@ -44,16 +45,18 @@ default String label() { return data().getLabel(); } - @SuppressWarnings("resource") default String collection() { - return graph().getPrefixedCollectioName(label()); + return data().getCollection(); + } + + default ArangoDBId arangoId() { + return data().getId(); } @Override default String id() { return Optional.ofNullable(key()) - .map(it -> collection() + '/' + it) - // TODO: review - .orElse(label()); + .map(it -> label() + '/' + it) + .orElse(null); } } 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 2960db0..5cad6c9 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -21,7 +21,6 @@ import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; @@ -31,12 +30,11 @@ import java.util.stream.Collectors; import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.elementAlreadyRemoved; -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { - public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { - return new ArangoDBVertex(graph, VertexData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); + public static ArangoDBVertex of(final ArangoDBId id, ArangoDBGraph graph) { + return new ArangoDBVertex(graph, VertexData.of(id)); } public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { @@ -83,8 +81,10 @@ public Edge addEdge(String label, Vertex vertex, Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); ElementHelper.validateLabel(label); - String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); - ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), graph); + 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())); } @@ -119,7 +119,7 @@ public Iterator edges(Direction direction, String... edgeLabels) { if (edgeCollections.isEmpty()) { return Collections.emptyIterator(); } - return IteratorUtils.map(graph.getClient().getVertexEdges(id(), edgeCollections, direction), + return IteratorUtils.map(graph.getClient().getVertexEdges(arangoId(), edgeCollections, direction), it -> new ArangoDBEdge(graph, it)); } @@ -131,7 +131,7 @@ public Iterator vertices(Direction direction, String... edgeLabels) { if (edgeCollections.isEmpty()) { return Collections.emptyIterator(); } - return IteratorUtils.map(graph.getClient().getVertexNeighbors(id(), edgeCollections, direction), + return IteratorUtils.map(graph.getClient().getVertexNeighbors(arangoId(), edgeCollections, direction), it -> new ArangoDBVertex(graph, it)); } @@ -173,11 +173,11 @@ public void removeProperty(ArangoDBVertexProperty prop) { private List getQueryEdgeCollections(String... edgeLabels) { List vertexCollections; if (edgeLabels.length == 0) { - vertexCollections = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); + vertexCollections = graph.edgeCollections().stream().map(graph::getPrefixedCollectionName).collect(Collectors.toList()); } else { vertexCollections = Arrays.stream(edgeLabels) .filter(el -> graph.edgeCollections().contains(el)) - .map(graph::getPrefixedCollectioName) + .map(graph::getPrefixedCollectionName) .collect(Collectors.toList()); } 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 9d08e16..63eda3c 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -8,30 +8,21 @@ 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.ArangoDBGraphClient; import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder; import com.arangodb.tinkerpop.gremlin.structure.*; -import java.io.UnsupportedEncodingException; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.collections4.CollectionUtils; import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,24 +49,6 @@ public class ArangoDBUtil { 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. */ @@ -83,117 +56,6 @@ 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: *
@@ -215,21 +77,21 @@ public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph grap
         if (info.length != 2) {
             throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation);
         }
-        result.collection(graph.getPrefixedCollectioName(info[0]));
+        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.getPrefixedCollectioName(c))
+                .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.getPrefixedCollectioName(c))
+                .map(c -> graph.getPrefixedCollectionName(c))
                 .collect(Collectors.toList());
         String[] to = new String[trimmed.size()];
         to = trimmed.toArray(to);
@@ -381,12 +243,12 @@ public static EdgeDefinition createPropertyEdgeDefinitions(
             final List edgeCollections) {
         final List from = new ArrayList<>(vertexCollections);
         from.addAll(edgeCollections);
-        from.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION));
+        from.add(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION));
         String[] f = from.toArray(new String[from.size()]);
         EdgeDefinition ed = new EdgeDefinition()
-                .collection(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION))
+                .collection(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION))
                 .from(f)
-                .to(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION));
+                .to(graph.getPrefixedCollectionName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION));
         return ed;
     }
 
@@ -557,75 +419,4 @@ public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirect
         throw new IllegalArgumentException("Unsupported direction: " + direction);
     }
 
-    public static 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;
-        }
-    }
-
-    public static 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;
-        }
-    }
-
-    // FIXME: review
-    public static Optional extractLabel(final String id, final String label) {
-        String col = extractCollection(id);
-        if (col != null) {
-            String labelFromId = col.replaceFirst("^.*_", "");
-            if (label != null && !label.equals(labelFromId)) {
-                throw new IllegalArgumentException("Invalid label: [" + label + "] for id: [" + id + "]");
-            }
-            return Optional.of(labelFromId);
-        }
-        return Optional.ofNullable(label);
-    }
-
-    // FIXME: use com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractKey() and com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractLabel()
-    public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) {
-        Optional optionalId = ElementHelper.getIdValue(keyValues);
-        if (!optionalId.isPresent()) {
-            return null;
-        }
-        String id = optionalId
-                .filter(features::willAllowId)
-                .map(Object::toString)
-                .orElseThrow(Vertex.Exceptions::userSuppliedIdsOfThisTypeNotSupported);
-
-        if (id.contains("/")) {
-            String fullId = id;
-            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];
-            Optional inferredLabel = extractLabel(id, label);
-            if(inferredLabel.isPresent()) {
-                if (collectionName.contains(inferredLabel.get())) {
-                    id = parts[1];
-                }
-            }
-        }
-
-        // FIXME: review
-        Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher(id);
-        if (m.matches()) {
-            return id;
-        } else {
-            throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id));
-        }
-    }
-
 }
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
index 6616379..5e9ee40 100644
--- 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
@@ -88,8 +88,8 @@ public void g_mergeEXlabel_knows_out_marko_in_vadasX() {
         printTraversalForm(traversal);
         final Edge edge = traversal.next();
         assertEquals("knows", edge.label());
-        assertEquals("standard_person/100", edge.outVertex().id());
-        assertEquals("standard_person/101", edge.inVertex().id());
+        assertEquals("person/100", edge.outVertex().id());
+        assertEquals("person/101", edge.inVertex().id());
         assertFalse(traversal.hasNext());
         assertEquals(1, IteratorUtils.count(g.E()));
     }
@@ -105,8 +105,8 @@ public void g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists() {
         printTraversalForm(traversal);
         final Edge edge = traversal.next();
         assertEquals("knows", edge.label());
-        assertEquals("standard_person/100", edge.outVertex().id());
-        assertEquals("standard_person/101", edge.inVertex().id());
+        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()));
@@ -122,7 +122,7 @@ public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_
             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: standard_person/100"));
+            assertThat(ex.getMessage(), endsWith("Vertex id could not be resolved from mergeE: person/100"));
         }
         assertEquals(0, IteratorUtils.count(g.E()));
     }
@@ -138,8 +138,8 @@ public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_
         printTraversalForm(traversal);
         final Edge edge = traversal.next();
         assertEquals("knows", edge.label());
-        assertEquals("standard_person/100", edge.outVertex().id());
-        assertEquals("standard_person/101", edge.inVertex().id());
+        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()));
@@ -149,15 +149,15 @@ public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_
     @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, "standard_person/100").property("name", "marko").as("a").
-                addV("person").property(T.id, "standard_person/101").property("name", "vadas").as("b").
+        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("standard_person/100", edge.outVertex().id());
-        assertEquals("standard_person/101", edge.inVertex().id());
+        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()));
@@ -167,8 +167,8 @@ public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_
     @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, "standard_person/100").property("name", "marko").as("a").
-                addV("person").property(T.id, "standard_person/101").property("name", "vadas").as("b").
+        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();
@@ -184,30 +184,30 @@ public void g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_crea
     @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, "standard_person/100").property("name", "marko").
-                addV("person").property(T.id, "standard_person/101").property("name", "vadas").iterate();
+        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("standard_person/100").out("knows").hasId("standard_person/101")));
+        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, "standard_person/1")
-                .addV("person").property(T.id, "standard_person/2")
+        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("standard_person/1").out("knows").hasId("standard_person/2")));
+        assertEquals(1, IteratorUtils.count(g.V("person/1").out("knows").hasId("person/2")));
     }
 
     public static class Traversals extends MergeEdgeTest {
@@ -219,32 +219,32 @@ public Traversal get_g_V_mergeEXlabel_self_weight_05X() {
 
         @Override
         public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX() {
-            return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100")));
+            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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "weight", 0.5d));
+            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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))).
-                    option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")).
+            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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))).
-                    option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")).
+            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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))).
-                    option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")).
+            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"));
         }
 
@@ -252,20 +252,20 @@ public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_option
         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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")).
+                    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("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))).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, "standard_person/1"))
-                    .option(Merge.inV, asMap(T.id, "standard_person/2"));
+                    .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
index d2e50b8..2237314 100644
--- 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
@@ -42,8 +42,7 @@
  */
 public class DetachedGraphTest extends AbstractGremlinTest {
 
-    // FIXME: review after DE-996
-    @Ignore("FIXME")
+    @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)
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
index 0474b7a..d9092c5 100644
--- 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
@@ -46,7 +46,7 @@
  */
 public class StarGraphTest extends AbstractGremlinTest {
 
-    @Ignore("FIXME: DE-996")
+    @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)
@@ -81,7 +81,7 @@ public void shouldCopyFromGraphAToGraphB() throws Exception {
         graphProvider.clear(g2, g2Configuration);
     }
 
-    @Ignore("FIXME: DE-996")
+//    @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)
diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java
index d6a537a..746b43b 100644
--- a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java
+++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java
@@ -33,8 +33,6 @@ public void clear(String name) {
             if (e.getErrorNum() != 1924) // graph not found
                 throw e;
         }
-
-        db.clearQueryCache();
     }
 
 }