From 42a6da22f8d1a36a6722d07e0182905d9004d6bb Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Fri, 31 Jan 2025 09:40:34 +0100 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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() {