From e7772ddb1d8d9e9df8270d5683dc2b1e022774b2 Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Tue, 25 Feb 2025 12:15:04 +0100 Subject: [PATCH 1/2] structure refactoring --- .../gremlin/client/ArangoDBBaseDocument.java | 7 - .../gremlin/client/ArangoDBGraphClient.java | 269 ++--- .../gremlin/jsr223/ArangoDBGremlinPlugin.java | 67 +- .../gremlin/persistence/AdbValue.java | 74 ++ .../gremlin/persistence/EdgeData.java | 117 ++ .../gremlin/persistence/PersistentData.java | 28 + .../gremlin/persistence/PropertyData.java | 30 + .../persistence/SimplePropertyData.java | 67 ++ .../gremlin/persistence/VertexData.java | 104 ++ .../persistence/VertexPropertyData.java | 83 ++ .../gremlin/structure/ArangoDBData.java | 87 -- .../gremlin/structure/ArangoDBEdge.java | 160 +-- .../gremlin/structure/ArangoDBEdgeData.java | 70 -- .../gremlin/structure/ArangoDBElement.java | 114 ++ .../gremlin/structure/ArangoDBGraph.java | 947 +++++++-------- .../structure/ArangoDBPersistentElement.java | 58 + .../gremlin/structure/ArangoDBProperty.java | 19 +- .../structure/ArangoDBPropertyData.java | 55 - .../structure/ArangoDBSimpleElement.java | 35 + .../gremlin/structure/ArangoDBVertex.java | 324 ++--- .../gremlin/structure/ArangoDBVertexData.java | 21 - .../structure/ArangoDBVertexProperty.java | 97 +- .../structure/ArangoDBVertexPropertyData.java | 61 - .../structure/PropertiesContainer.java | 30 - .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 1057 +++++++++-------- .../gremlin/ArangoDBGraphProvider.java | 507 ++++---- 26 files changed, 2263 insertions(+), 2225 deletions(-) create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java create mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java delete mode 100644 src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java index efb6972..6f5d391 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java @@ -14,10 +14,6 @@ import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertexPropertyData; - -import java.util.List; -import java.util.Map; /** * The ArangoDB BaseBaseDocument provides the internal fields required for the driver to correctly @@ -48,9 +44,6 @@ public abstract class ArangoDBBaseDocument { @JsonProperty protected String label; - @JsonProperty - protected Map> properties; - /** The collection in which the element is placed. */ @JsonIgnore diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index 4ca2bfc..e3c208b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -1,20 +1,14 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.client; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -22,6 +16,10 @@ import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; import com.arangodb.model.*; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertex; +import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; +import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -34,9 +32,10 @@ import com.arangodb.ArangoDBException; import com.arangodb.ArangoDatabase; import com.arangodb.ArangoGraph; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder.UniqueVertices; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.getArangoDirectionFromGremlinDirection; + /** * The arangodb graph client class handles the HTTP connection to arangodb and performs database * operations on the ArangoDatabase. @@ -119,16 +118,6 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri String.format(cause, details)); } - /** - * Error persisting element property. - * - * @param ex the ex - * @return the arango DB graph exception - */ - - public static ArangoDBGraphException errorPersistingElmenentProperty(ArangoDBGraphException ex) { - return new ArangoDBGraphException("Error persisting property in element. ", ex); - } } private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); @@ -180,7 +169,7 @@ public ArangoDBGraphClient( int batchSize, boolean createDatabase) throws ArangoDBGraphException { - logger.info("Initiating the ArangoDb Client"); + logger.debug("Initiating the ArangoDb Client"); this.graph = graph; driver = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromProperties(properties)) @@ -188,7 +177,7 @@ public ArangoDBGraphClient( db = driver.db(dbname); if (createDatabase) { if (!db.exists()) { - logger.info("DB not found, attemtping to create it."); + logger.debug("DB not found, attemtping to create it."); try { if (!driver.createDatabase(dbname)) { throw new ArangoDBGraphException("Unable to crate the database " + dbname); @@ -234,7 +223,7 @@ public void shutdown() { */ public void clear(ArangoDBGraph graph) throws ArangoDBGraphException { - logger.info("Clear {}", graph.name()); + logger.debug("Clear {}", graph.name()); deleteGraph(graph.name()); } @@ -281,7 +270,7 @@ public boolean dbExists() { */ public void deleteDb() throws ArangoDBGraphException { - logger.info("Delete current db"); + logger.debug("Delete current db"); if (db != null) { try { db.drop(); @@ -377,109 +366,21 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { logger.error("Failed to update document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Document updated, new rev {}", updateEntity.getRev()); + logger.debug("Document updated, new rev {}", updateEntity.getRev()); document._rev(updateEntity.getRev()); } - /** - * Create a query to get all the edges of a vertex. - * - * @param vertexId the vertex - * @param edgeLabels a list of edge labels to follow, empty if all type of edges - * @param direction the direction of the edges - * @return ArangoDBBaseQuery the query object - * @throws ArangoDBException if there is an error executing the query - */ - - public ArangoCursor getVertexEdges( - String vertexId, - List edgeLabels, - Direction direction) - throws ArangoDBException { - logger.debug("Get Vertex's {}:{} Edges, in {}, from collections {}", vertexId, direction, graph.name(), edgeLabels); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - ArangoDBQueryBuilder.Direction arangoDirection = ArangoDBUtil.getArangoDirectionFromGremlinDirection(direction); - logger.debug("Creating query"); - queryBuilder.iterateGraph(graph.name(), "v", Optional.of("e"), - Optional.empty(), Optional.empty(), Optional.empty(), - arangoDirection, vertexId, bindVars) - .graphOptions(Optional.of(UniqueVertices.NONE), Optional.empty(), true) - .filterSameCollections("e", edgeLabels, bindVars) - .ret("e"); - - String query = queryBuilder.toString(); - return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.class); - } - - /** - * Get all neighbours of a document. - * - * @param the document type - * @param vertexId the document - * @param edgeLabelsFilter a list of edge types to follow - * @param direction a direction - * @param propertyFilter filter the neighbours on the given property:value values - * @param resultType the result type - * @return ArangoDBBaseQuery the query object - */ - - public ArangoCursor getDocumentNeighbors( - String vertexId, - List edgeLabelsFilter, - Direction direction, - ArangoDBPropertyFilter propertyFilter, - Class resultType) { - logger.debug("Get Document's {}:{} Neighbors, in {}, from collections {}", vertexId, direction, graph.name(), edgeLabelsFilter); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - ArangoDBQueryBuilder.Direction arangoDirection = ArangoDBUtil.getArangoDirectionFromGremlinDirection(direction); - logger.debug("Creating query"); - queryBuilder.iterateGraph(graph.name(), "v", Optional.of("e"), - Optional.empty(), Optional.empty(), Optional.empty(), - arangoDirection, vertexId, bindVars) - .graphOptions(Optional.of(UniqueVertices.GLOBAL), Optional.empty(), true) - .filterSameCollections("e", edgeLabelsFilter, bindVars) - .filterProperties(propertyFilter, "v", bindVars) - .ret("v"); - - String query = queryBuilder.toString(); - return executeAqlQuery(query, bindVars, null, resultType); - } - /** * Get vertices of a graph. If no ids are provided, get all vertices. * * @param ids the ids to match - * @param collections the collections to search within * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphVertices( - final List ids, - final List collections) { + public ArangoCursor getGraphVertices(final List ids) { logger.debug("Get all {} graph vertices, filtered by ids: {}", graph.name(), ids); - Map bindVars = new HashMap<>(); - ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); List prefixedColNames = graph.vertexCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - if (ids.isEmpty()) { - if (prefixedColNames.size() > 1) { - queryBuilder.union(prefixedColNames, "v", bindVars); - } else { - queryBuilder.iterateCollection("v", prefixedColNames.get(0), bindVars); - } - } else { - if (!collections.isEmpty()) { - prefixedColNames = collections.stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); - } - queryBuilder.with(prefixedColNames, bindVars) - .documentsById(ids, "v", bindVars); - - } - queryBuilder.ret("v"); - String query = queryBuilder.toString(); - logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBVertexData.class); + return getGraphDocuments(ids, prefixedColNames, VertexData.class); } /** @@ -488,24 +389,28 @@ public ArangoCursor getGraphVertices( * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphEdges(List ids) { + public ArangoCursor getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); + List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); + return getGraphDocuments(ids, prefixedColNames, EdgeData.class); + } + + private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); - List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); if (ids.isEmpty()) { if (prefixedColNames.size() > 1) { - queryBuilder.union(prefixedColNames, "e", bindVars); + queryBuilder.union(prefixedColNames, "d", bindVars); } else { - queryBuilder.iterateCollection("e", prefixedColNames.get(0), bindVars); + queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); } } else { - queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "e", bindVars); + queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "d", bindVars); } - queryBuilder.ret("e"); + queryBuilder.ret("d"); String query = queryBuilder.toString(); logger.debug("AQL {}", query); - return executeAqlQuery(query, bindVars, null, ArangoDBEdgeData.class); + return executeAqlQuery(query, bindVars, null, clazz); } /** @@ -609,16 +514,15 @@ public ArangoGraph createGraph(String name, List edgeDefinitions, GraphCreateOptions options) throws ArangoDBGraphException { - logger.info("Creating graph {}", name); + logger.debug("Creating graph {}", name); try { - logger.info("Creating graph in database."); + logger.debug("Creating graph in database."); db.createGraph(name, edgeDefinitions, options); } catch (ArangoDBException e) { - logger.info("Error creating graph in database.", e); + logger.debug("Error creating graph in database.", e); throw ArangoDBExceptions.getArangoDBException(e); } - ArangoGraph g = db.graph(name); - return g; + return db.graph(name); } @@ -635,7 +539,7 @@ public ArangoGraph getArangoGraph() { /** * Execute AQL query. * - * @param the generic type of the returned values + * @param the generic type of the returned values * @param query the query string * @param bindVars the value of the bind parameters * @param aqlQueryOptions the aql query options @@ -644,11 +548,11 @@ public ArangoGraph getArangoGraph() { * @throws ArangoDBGraphException if executing the query raised an exception */ - public ArangoCursor executeAqlQuery( + public ArangoCursor executeAqlQuery( String query, Map bindVars, AqlQueryOptions aqlQueryOptions, - final Class type) + final Class type) throws ArangoDBGraphException { logger.debug("Executing AQL query ({}) against db, with bind vars: {}", query, bindVars); try { @@ -883,44 +787,30 @@ public ArangoCursor executeAqlQuery( // throw new ArangoDBException(e); // } // } - public void insertEdge(ArangoDBEdgeData edge) { + public void insertEdge(ArangoDBEdge edge) { logger.debug("Insert edge {} in {} ", edge, graph.name()); EdgeEntity insertEntity; - String collection = graph.getPrefixedCollectioName(edge.getLabel()); try { insertEntity = db.graph(graph.name()) - .edgeCollection(collection) - .insertEdge(edge); + .edgeCollection(edge.collection()) + .insertEdge(edge.data()); } catch (ArangoDBException e) { logger.error("Failed to insert edge: {}", e.getErrorMessage()); ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); if (arangoDBException.getErrorCode() == 1210) { - throw Graph.Exceptions.edgeWithIdAlreadyExists(collection + "/" + edge.getKey()); + throw Graph.Exceptions.edgeWithIdAlreadyExists(edge.collection() + "/" + edge.key()); } throw arangoDBException; } - edge.setKey(insertEntity.getKey()); - edge.setRev(insertEntity.getRev()); + edge.key(insertEntity.getKey()); } -// public TinkerEdgeDocument getEdge(String key, String collection) { -// logger.debug("Get edge {} from {}:{}", key, graph.name(), graph.getPrefixedCollectioName(collection)); -// try { -// return db.graph(graph.name()) -// .edgeCollection(graph.getPrefixedCollectioName(collection)) -// .getEdge(key, TinkerEdgeDocument.class); -// } catch (ArangoDBException e) { -// logger.error("Failed to retrieve edge: {}", e.getErrorMessage()); -// throw ArangoDBExceptions.getArangoDBException(e); -// } -// } - - public void deleteEdge(ArangoDBEdgeData edge) { + public void deleteEdge(ArangoDBEdge edge) { logger.debug("Delete edge {} in {}", edge, graph.name()); try { db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .deleteEdge(edge.getKey()); + .edgeCollection(edge.collection()) + .deleteEdge(edge.key()); } catch (ArangoDBException e) { if (e.getErrorNum() == 1202) { // document not found return; @@ -930,50 +820,61 @@ public void deleteEdge(ArangoDBEdgeData edge) { } } - public void updateEdge(ArangoDBEdgeData edge) { + public void updateEdge(ArangoDBEdge edge) { logger.debug("Update edge {} in {}", edge, graph.name()); EdgeUpdateEntity updateEntity; try { updateEntity = db.graph(graph.name()) - .edgeCollection(graph.getPrefixedCollectioName(edge.getLabel())) - .replaceEdge(edge.getKey(), edge); + .edgeCollection(edge.collection()) + .replaceEdge(edge.key(), edge.data()); } catch (ArangoDBException e) { logger.error("Failed to update edge: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Edge updated, new rev {}", updateEntity.getRev()); - edge.setKey(updateEntity.getKey()); - edge.setRev(updateEntity.getRev()); + logger.debug("Edge updated, new rev {}", updateEntity.getRev()); + edge.key(updateEntity.getKey()); } - public void insertVertex(ArangoDBVertexData vertex) { + public VertexData readVertex(String id) { + logger.debug("Read vertex {} in {}", id, graph.name()); + String col = ArangoDBUtil.extractCollection(id); + String key = ArangoDBUtil.extractKey(id); + try { + return db.graph(graph.name()) + .vertexCollection(col) + .getVertex(key, VertexData.class); + } catch (ArangoDBException e) { + logger.error("Failed to read vertex: {}", e.getErrorMessage()); + throw ArangoDBExceptions.getArangoDBException(e); + } + } + + public void insertVertex(ArangoDBVertex vertex) { logger.debug("Insert vertex {} in {}", vertex, graph.name()); VertexEntity vertexEntity; - String colName = graph.getPrefixedCollectioName(vertex.getLabel()); try { vertexEntity = db.graph(graph.name()) - .vertexCollection(colName) - .insertVertex(vertex); + .vertexCollection(vertex.collection()) + .insertVertex(vertex.data()); } catch (ArangoDBException e) { logger.error("Failed to insert document: {}", e.getMessage()); ArangoDBGraphException arangoDBException = ArangoDBExceptions.getArangoDBException(e); if (arangoDBException.getErrorCode() == 1210) { - throw Graph.Exceptions.vertexWithIdAlreadyExists(vertex.getKey()); + throw Graph.Exceptions.vertexWithIdAlreadyExists(vertex.key()); } throw arangoDBException; } - vertex.setKey(vertexEntity.getKey()); - vertex.setRev(vertexEntity.getRev()); + vertex.key(vertexEntity.getKey()); } - public void deleteVertex(ArangoDBVertexData vertex) { + public void deleteVertex(ArangoDBVertex vertex) { logger.debug("Delete vertex {} in {}", vertex, graph.name()); try { db.graph(graph.name()) - .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) - .deleteVertex(vertex.getKey()); + .vertexCollection(vertex.collection()) + .deleteVertex(vertex.key()); } catch (ArangoDBException e) { - if(e.getErrorNum() == 1202) { // document not found + if (e.getErrorNum() == 1202) { // document not found return; } logger.error("Failed to delete vertex: {}", e.getErrorMessage()); @@ -981,19 +882,39 @@ public void deleteVertex(ArangoDBVertexData vertex) { } } - public void updateVertex(ArangoDBVertexData vertex) { + public void updateVertex(ArangoDBVertex vertex) { logger.debug("Update document {} in {}", vertex, graph.name()); VertexUpdateEntity vertexEntity; try { vertexEntity = db.graph(graph.name()) - .vertexCollection(graph.getPrefixedCollectioName(vertex.getLabel())) - .replaceVertex(vertex.getKey(), vertex); + .vertexCollection(vertex.collection()) + .replaceVertex(vertex.key(), vertex.data()); } catch (ArangoDBException e) { logger.error("Failed to update document: {}", e.getErrorMessage()); throw ArangoDBExceptions.getArangoDBException(e); } - logger.info("Document updated, new rev {}", vertexEntity.getRev()); - vertex.setRev(vertexEntity.getRev()); + logger.debug("Document updated, new rev {}", vertexEntity.getRev()); } + public Iterator getVertexNeighbors(String vertexId, List edgeCollections, Direction direction) { + logger.debug("Get vertex {}:{} Neighbors, in {}, from collections {}", vertexId, direction, graph.name(), edgeCollections); + Map bindVars = new HashMap<>(); + bindVars.put("start", vertexId); + bindVars.put("graph", graph.name()); + bindVars.put("edgeCollections", edgeCollections); + String query = "FOR v IN 1..1 " + getArangoDirectionFromGremlinDirection(direction).getAqlName() + + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections, uniqueVertices: 'global', order: 'bfs' } RETURN v"; + return executeAqlQuery(query, bindVars, null, VertexData.class); + } + + public Iterator getVertexEdges(String vertexId, List edgeCollections, Direction direction) { + logger.debug("Get vertex {}:{} Edges, in {}, from collections {}", vertexId, direction, graph.name(), edgeCollections); + Map bindVars = new HashMap<>(); + bindVars.put("start", vertexId); + bindVars.put("graph", graph.name()); + bindVars.put("edgeCollections", edgeCollections); + String query = "FOR v, e IN 1..1 " + getArangoDirectionFromGremlinDirection(direction).getAqlName() + + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections } RETURN e"; + return executeAqlQuery(query, bindVars, null, EdgeData.class); + } } \ No newline at end of file diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java index 36e6ad3..a5e52d7 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java @@ -1,13 +1,15 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.jsr223; +import com.arangodb.tinkerpop.gremlin.persistence.*; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBEdge; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; @@ -18,41 +20,60 @@ /** * The Class ArangoDBGremlinPlugin. + * * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { - /** The Constant NAME. */ - private static final String NAME = "tinkerpop.arangodb"; + /** + * The Constant NAME. + */ + private static final String NAME = "tinkerpop.arangodb"; - /** The Constant IMPORTS. */ - private static final ImportCustomizer IMPORTS; + /** + * The Constant IMPORTS. + */ + private static final ImportCustomizer IMPORTS; static { try { IMPORTS = DefaultImportCustomizer.build().addClassImports( - ArangoDBBaseDocument.class, - ArangoDBGraphClient.class, - ArangoDBGraphException.class, - ArangoDBPropertyFilter.class, - ArangoDBQueryBuilder.class, - ArangoDBEdge.class, - ArangoDBEdgeData.class, - ArangoDBProperty.class, - ArangoDBGraph.class, - ArangoDBGraphVariables.class, - ArangoDBVertexPropertyData.class, - ArangoDBVertex.class, - ArangoDBVertexProperty.class, - ArangoDBUtil.class - ) - .create(); + ArangoDBBaseDocument.class, + ArangoDBGraphClient.class, + ArangoDBGraphException.class, + ArangoDBPropertyFilter.class, + ArangoDBQueryBuilder.class, + ArangoDBUtil.class, + + // structure + ArangoDBEdge.class, + ArangoDBElement.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + ArangoDBPersistentElement.class, + ArangoDBProperty.class, + ArangoDBSimpleElement.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class, + + // persistence + AdbValue.class, + EdgeData.class, + PersistentData.class, + PropertyData.class, + SimplePropertyData.class, + VertexData.class, + VertexPropertyData.class + ) + .create(); } catch (Exception ex) { throw new RuntimeException(ex); } } - /** The Constant INSTANCE. */ + /** + * The Constant INSTANCE. + */ private static final ArangoDBGremlinPlugin INSTANCE = new ArangoDBGremlinPlugin(); /** diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java new file mode 100644 index 0000000..cf4461b --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/AdbValue.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; + +import java.util.Objects; + +public class AdbValue { + + private final Object value; + private final String valueType; + + @JsonCreator + AdbValue( + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.value = value; + this.valueType = valueType; + } + + public static AdbValue of(Object value) { + return new AdbValue(value, (value != null ? value.getClass() : Void.class).getCanonicalName()); + } + + public Object getValue() { + return ArangoDBUtil.getCorretctPrimitive(value, valueType); + } + + public String getValueType() { + return valueType; + } + + @Override + public String toString() { + return "AdbValue{" + + "value=" + value + + ", valueType='" + valueType + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof AdbValue)) return false; + AdbValue adbValue = (AdbValue) o; + return Objects.equals(value, adbValue.value) && Objects.equals(valueType, adbValue.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(value, valueType); + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java new file mode 100644 index 0000000..913f34b --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/EdgeData.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.serde.*; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +import java.util.*; + +public class EdgeData extends SimplePropertyData implements PersistentData { + + @JsonProperty + private String label; + + @InternalKey + private String key; + + @InternalFrom + private String from; + + @InternalTo + private String to; + + public static EdgeData of( + String label, + String key, + String from, + String to + ) { + ElementHelper.validateLabel(label); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); + Objects.requireNonNull(from, "from"); + Objects.requireNonNull(to, "to"); + + EdgeData data = new EdgeData(); + data.label = label; + data.key = key; + data.from = from; + data.to = to; + return data; + } + + public EdgeData() { + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getKey() { + return key; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public String toString() { + return "EdgeData{" + + "from='" + from + '\'' + + ", label='" + label + '\'' + + ", key='" + key + '\'' + + ", to='" + to + '\'' + + ", super=" + super.toString() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof EdgeData)) return false; + EdgeData edgeData = (EdgeData) o; + return Objects.equals(label, edgeData.label) && Objects.equals(key, edgeData.key) && Objects.equals(from, edgeData.from) && Objects.equals(to, edgeData.to); + } + + @Override + public int hashCode() { + return Objects.hash(label, key, from, to); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java new file mode 100644 index 0000000..6121fd3 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PersistentData.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +public interface PersistentData { + String getLabel(); + + String getKey(); + + void setKey(String key); +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java new file mode 100644 index 0000000..8f04e7e --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/PropertyData.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +import java.util.Map; +import java.util.stream.Stream; + +public interface PropertyData { + + Stream> entries(); + + void add(String key, V value); +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java new file mode 100644 index 0000000..2a25714 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/SimplePropertyData.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +public class SimplePropertyData implements PropertyData { + + @JsonProperty + private final Map properties = new HashMap<>(); + + @Override + public Stream> entries() { + return properties.entrySet().stream(); + } + + @Override + public void add(String key, AdbValue value) { + properties.put(key, value); + } + + public void remove(String key) { + properties.remove(key); + } + + @Override + public String toString() { + return "SimplePropertyData{" + + "properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SimplePropertyData)) return false; + SimplePropertyData that = (SimplePropertyData) o; + return Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hashCode(properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java new file mode 100644 index 0000000..4aa0c24 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.serde.InternalKey; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +import java.util.*; +import java.util.stream.Stream; + +public class VertexData implements PropertyData, PersistentData { + + @JsonProperty + private String label; + + @InternalKey + private String key; + + @JsonProperty + private final Map> properties = new HashMap<>(); + + public VertexData() { + } + + public VertexData(String label, String key) { + ElementHelper.validateLabel(label); + if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); + this.label = label; + this.key = key; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public String getKey() { + return key; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + @Override + public Stream> entries() { + return properties.entrySet().stream().flatMap(e -> e.getValue().stream() + .map(v -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v))); + } + + @Override + public void add(String key, VertexPropertyData value) { + properties.computeIfAbsent(key, k -> new HashSet<>()).add(value); + } + + public void remove(String key, VertexPropertyData value) { + Set props = properties.getOrDefault(key, Collections.emptySet()); + props.remove(value); + if (props.isEmpty()) { + properties.remove(key); + } + } + + @Override + public String toString() { + return "VertexData{" + + "key='" + key + '\'' + + ", label='" + label + '\'' + + ", properties=" + properties + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexData)) return false; + VertexData that = (VertexData) o; + return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(properties, that.properties); + } + + @Override + public int hashCode() { + return Objects.hash(label, key, properties); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java new file mode 100644 index 0000000..9f5084f --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexPropertyData.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.persistence; + +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; +import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; + +import java.util.Objects; + +public class VertexPropertyData extends SimplePropertyData { + + private final String id; + private final Object value; + private final String valueType; + + @JsonCreator + VertexPropertyData( + @JsonProperty("id") String id, + @JsonProperty("value") Object value, + @JsonProperty("valueType") String valueType + ) { + this.id = id; + this.value = value; + this.valueType = valueType; + } + + public static VertexPropertyData of(String id, Object value) { + return new VertexPropertyData(id, value, (value != null ? value.getClass() : Void.class).getCanonicalName()); + } + + public String getId() { + return id; + } + + public Object getValue() { + return ArangoDBUtil.getCorretctPrimitive(value, valueType); + } + + public String getValueType() { + return valueType; + } + + @Override + public String toString() { + return "VertexPropertyData{" + + "id='" + id + '\'' + + ", value=" + value + + ", valueType='" + valueType + '\'' + + ", super=" + super.toString() + + '}'; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof VertexPropertyData)) return false; + if (!super.equals(o)) return false; + VertexPropertyData that = (VertexPropertyData) o; + return Objects.equals(id, that.id) && Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, value, valueType); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java deleted file mode 100644 index e2519f7..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.serde.InternalKey; -import com.arangodb.serde.InternalRev; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -abstract class ArangoDBData { - private String label; - - @InternalKey - private String key; - - @InternalRev - private String rev; - - private Map properties = new HashMap<>(); - - public ArangoDBData() { - } - - public ArangoDBData(String label, String key) { - Objects.requireNonNull(label, "label"); - if (label.isEmpty()) throw new IllegalArgumentException("empty label"); - if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - - this.label = label; - this.key = key; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getRev() { - return rev; - } - - public void setRev(String rev) { - this.rev = rev; - } - - public String getLabel() { - return label; - } - - public void setLabel(String label) { - this.label = label; - } - - public Map getProperties() { - if (properties == null) { - properties = new HashMap<>(); - } - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - - @Override - public String toString() { - return "key='" + key + '\'' + - ", label='" + label + '\'' + - ", rev='" + rev + '\'' + - ", properties=" + properties; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - ArangoDBData that = (ArangoDBData) o; - return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(properties, that.properties); - } - - @Override - public int hashCode() { - return Objects.hash(label, key, rev, properties); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java index 034b83f..6508b0d 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java @@ -16,173 +16,73 @@ * specific language governing permissions and limitations * under the License. */ + package com.arangodb.tinkerpop.gremlin.structure; +import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; 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 org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import java.util.*; -import java.util.stream.Collectors; - -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; - - -public class ArangoDBEdge implements Edge { - - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBEdge.class); - private final ArangoDBGraph graph; - private final ArangoDBEdgeData data; - private boolean removed; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; - public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeData data) { - this.graph = graph; - this.data = data; - this.removed = false; - } - public ArangoDBEdge(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { - this.graph = graph; - String inferredLabel, key; - if (id != null) { - int separator = id.indexOf('/'); - if (separator > 0) { - inferredLabel = id.substring(0, separator); - key = id.substring(separator + 1); - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = id; - } - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = null; - } +public class ArangoDBEdge extends ArangoDBSimpleElement implements Edge, ArangoDBPersistentElement { - data = new ArangoDBEdgeData(inferredLabel, key, outVertexId, inVertexId); - removed = false; + public static ArangoDBEdge of(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { + return new ArangoDBEdge(graph, EdgeData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id), outVertexId, inVertexId)); } - @Override - public String id() { - String key = data.getKey(); - if (key == null) { - return null; - } - return graph.getPrefixedCollectioName(label()) + "/" + key; + public ArangoDBEdge(ArangoDBGraph graph, EdgeData data) { + super(graph, data); } @Override - public String label() { - return data.getLabel(); + protected void doRemove() { + graph.getClient().deleteEdge(this); } @Override - public ArangoDBGraph graph() { - return graph; + protected void doUpdate() { + graph.getClient().updateEdge(this); } - public void insert() { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - graph.getClient().insertEdge(data); - } - - public void update() { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - graph.getClient().updateEdge(data); - } - - public void removeProperty(String key) { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - if (data.hasProperty(key)) { - data.removeProperty(key); - update(); - } + public void doInsert() { + graph.getClient().insertEdge(this); } @Override - @SuppressWarnings("unchecked") - public Iterator> properties(final String... propertyKeys) { - return data.properties() - .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) - .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue())) - .collect(Collectors.toList()).iterator(); - } - - @Override - public Property property(final String key, final V value) { - if (removed) throw elementAlreadyRemoved(Edge.class, id()); - LOGGER.info("set property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - data.setProperty(key, value); - update(); - return new ArangoDBProperty<>(this, key, value); - } - - @SuppressWarnings("unchecked") - @Override - public Property property(final String key) { - if (data.hasProperty(key)) { - Object value = data.getProperty(key); - return new ArangoDBProperty<>(this, key, (V) value); - } - return Property.empty(); - } - - @Override - public Set keys() { - return data.getProperties().keySet(); - } - - @Override - public void remove() { - LOGGER.info("removing {} from graph {}.", id(), graph.name()); - graph.getClient().deleteEdge(data); - this.removed = true; + protected String stringify() { + return StringFactory.edgeString(this); } @Override - public Iterator values(String... propertyKeys) { - return Edge.super.values(propertyKeys); + public Vertex outVertex() { + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getFrom())); } @Override - public String toString() { - return StringFactory.edgeString(this); + public Vertex inVertex() { + return new ArangoDBVertex(graph, graph.getClient().readVertex(data.getTo())); } @Override - public Iterator vertices(Direction direction) { - if (removed) return Collections.emptyIterator(); - List ids = new ArrayList<>(); + public Iterator vertices(final Direction direction) { + if (removed()) return Collections.emptyIterator(); 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 IteratorUtils.of(this.outVertex()); + case IN: + return IteratorUtils.of(this.inVertex()); + default: + return IteratorUtils.of(this.outVertex(), this.inVertex()); } - return graph.getClient().getGraphVertices(ids, Collections.emptyList()).stream() - .map(it -> (Vertex) new ArangoDBVertex(graph, it)) - .iterator(); } - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); - } - - @Override - public int hashCode() { - return ElementHelper.hashCode(this); + public Iterator> properties(final String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); } - } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java deleted file mode 100644 index da065f4..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.serde.*; - -import java.util.*; - -public class ArangoDBEdgeData extends ArangoDBData implements PropertiesContainer { - - @InternalFrom - private String from; - - @InternalTo - private String to; - - public ArangoDBEdgeData() { - } - - 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 getFrom() { - return from; - } - - public void setFrom(String from) { - this.from = from; - } - - public String getTo() { - return to; - } - - public void setTo(String to) { - this.to = to; - } - - @Override - public String toString() { - return "ArangoDBEdgeData{" + - super.toString() + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", 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(from, that.from) && Objects.equals(to, that.to); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), from, to); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java new file mode 100644 index 0000000..326ecae --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBElement.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.PropertyData; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.stream.Collectors; + +public abstract class ArangoDBElement> implements Element { + + protected final ArangoDBGraph graph; + protected final D data; + private boolean removed = false; + + public ArangoDBElement(ArangoDBGraph graph, D data) { + this.graph = graph; + this.data = data; + } + + protected abstract Property createProperty(String key, P value); + + //region CRUD ops + protected abstract void doUpdate(); + + protected abstract void doRemove(); + + protected abstract void doInsert(); + //endregion + + protected abstract String stringify(); + + public D data() { + return data; + } + + protected boolean removed() { + return removed; + } + + @Override + public ArangoDBGraph graph() { + return graph; + } + + @Override + public void remove() { + if (removed) return; + doRemove(); + removed = true; + } + + @Override + public Iterator> properties(String... propertyKeys) { + if (removed) return Collections.emptyIterator(); + return data.entries() + .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) + .map((Map.Entry e) -> this.createProperty(e.getKey(), e.getValue())) + .collect(Collectors.toList()) // avoids ConcurrentModificationException on removal from downstream + .iterator(); + } + + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(final Object object) { + return ElementHelper.areEqual(this, object); + } + + @Override + public int hashCode() { + return ElementHelper.hashCode(this); + } + + @Override + public String toString() { + return stringify(); + } + + public static class Exceptions { + private Exceptions() { + } + + public static IllegalStateException elementAlreadyRemoved(final Object id) { + return new IllegalStateException(String.format("Element with id %s was removed.", id)); + } + + public static IllegalStateException unsupportedIdType(final Object id) { + return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); + } + } +} + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index bc937f3..9f8e535 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -1,15 +1,14 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.structure; import java.util.*; -import java.util.regex.Matcher; import java.util.stream.Collectors; import com.arangodb.entity.EdgeDefinition; @@ -18,10 +17,7 @@ import org.apache.commons.configuration2.ConfigurationConverter; import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.Transaction; -import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; import org.slf4j.Logger; @@ -33,7 +29,7 @@ import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.unsupportedIdType; +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.unsupportedIdType; /** * The ArangoDB graph class. @@ -143,124 +139,102 @@ @Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) @Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", - method = "testAttachableCreateMethod", - reason = "test creates id without label prefix") + test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", + method = "testAttachableCreateMethod", + reason = "test creates id without label prefix") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "Test creates vertex with no labels in schema-based approach") + test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest", + method = "shouldNotEvaluateToEqualDifferentId", + reason = "Test creates vertex with no labels in schema-based approach") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldAddVertexWithUserSuppliedStringId", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldAddVertexWithUserSuppliedStringId", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldRemoveVertices", - reason = "Test creates vertices with random labels, which does not work with our schema-based approach.") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveVertices", + reason = "Test creates vertices with random labels, which does not work with our schema-based approach.") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldRemoveEdges", - reason = "Test creates edges with random labels, which does not work with our schema-based approach.") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveEdges", + reason = "Test creates edges with random labels, which does not work with our schema-based approach.") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldEvaluateConnectivityPatterns", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldEvaluateConnectivityPatterns", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexPropertyTest$VertexPropertyAddition", - method = "shouldAllowIdAssignment", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", + method = "shouldNotEvaluateToEqualDifferentId", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertexProperty", - reason = "FIXME" -) + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldAttachWithCreateMethod", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.PropertyTest$BasicPropertyTest", - method = "shouldAllowNullAddVertex", - reason = "FIXME" -) + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldCopyFromGraphAToGraphB", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV3Test", - method = "shouldSerializeTree", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.SerializationTest$GryoV1Test", - method = "shouldSerializeTree", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", + reason = "FIXME") @Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldAttachWithCreateMethod", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldCopyFromGraphAToGraphB", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", - method = "shouldAddEdgeWithUserSuppliedStringId", - reason = "FIXME") + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", + method = "shouldAddEdgeWithUserSuppliedStringId", + reason = "FIXME") public class ArangoDBGraph implements Graph { - /** + /** * The Class ArangoDBGraphFeatures. */ - public class ArangoDBGraphFeatures implements Features { + public class ArangoDBGraphFeatures implements Features { - /** + /** * The Class ArangoDBGraphGraphFeatures. */ - private class ArangoDBGraphGraphFeatures implements GraphFeatures { + private class ArangoDBGraphGraphFeatures implements GraphFeatures { /** The variable features. */ - private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); + private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); - /** - * Instantiates a new ArangoDB graph graph features. - */ + /** + * Instantiates a new ArangoDB graph graph features. + */ ArangoDBGraphGraphFeatures () { } - @Override - public boolean supportsComputer() { - return false; - } + @Override + public boolean supportsComputer() { + return false; + } - @Override - public boolean supportsThreadedTransactions() { - return false; - } + @Override + public boolean supportsThreadedTransactions() { + return false; + } - @Override - public boolean supportsTransactions() { - return false; - } + @Override + public boolean supportsTransactions() { + return false; + } - @Override - public VariableFeatures variables() { - return variableFeatures; - } - } + @Override + public VariableFeatures variables() { + return variableFeatures; + } + } - /** + /** * The Class ArangoDBGraphElementFeatures. */ @@ -273,33 +247,33 @@ private class ArangoDBGraphElementFeatures implements ElementFeatures { ArangoDBGraphElementFeatures() { } @Override - public boolean supportsAnyIds() { - return false; - } - - @Override - public boolean supportsCustomIds() { - return false; - } - - @Override - public boolean supportsNumericIds() { - return false; - } - - @Override - public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer + public boolean supportsAnyIds() { + return false; + } + + @Override + public boolean supportsCustomIds() { + return false; + } + + @Override + public boolean supportsNumericIds() { + return false; + } + + @Override + public boolean supportsUuidIds() { + /* We can not use Java Objects as keys, ergo we can not support UUID and Integer * the string representation of these is fine for ArangoDB, which makes the test * complain because it expects the actual class to be deserialized. We can test * to see if a string is accepted for deserialization. * TODO As with properties, a way to support this is to store the id value class */ - return false; - } + return false; + } } - /** + /** * The Class ArangoDBGraphVertexFeatures. */ @@ -307,7 +281,7 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i /** The vertex property features. */ - private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); + private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); /** * Instantiates a new ArangoDB graph vertex features. @@ -316,20 +290,20 @@ private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures i ArangoDBGraphVertexFeatures () { } - @Override + @Override public VertexPropertyFeatures properties() { return vertexPropertyFeatures; } } - /** + /** * The Class ArangoDBGraphEdgeFeatures. */ public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { /** The edge property features. */ - private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); + private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); /** * Instantiates a new ArangoDB graph edge features. @@ -349,37 +323,37 @@ public EdgePropertyFeatures properties() { private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - /** - * Instantiates a new ArangoDB graph vertex property features. - */ + /** + * Instantiates a new ArangoDB graph vertex property features. + */ ArangoDBGraphVertexPropertyFeatures() { } - @Override - public boolean supportsAnyIds() { - return false; - } + @Override + public boolean supportsAnyIds() { + return false; + } - @Override - public boolean supportsCustomIds() { - return false; - } + @Override + public boolean supportsCustomIds() { + return false; + } - @Override - public boolean supportsNumericIds() { - return false; - } + @Override + public boolean supportsNumericIds() { + return false; + } - @Override - public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer + @Override + public boolean supportsUuidIds() { + /* We can not use Java Objects as keys, ergo we can not support UUID and Integer * the string representation of these is fine for ArangoDB, which makes the test * complain because it expects the actual class to be deserialized. We can test * to see if a string is accepted for deserialization. * TODO As with properties, a way to support this is to store the id value class */ - return false; - } + return false; + } } /** @@ -387,16 +361,16 @@ public boolean supportsUuidIds() { */ private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { - /** - * Instantiates a new ArangoDB graph edge property features. - */ + /** + * Instantiates a new ArangoDB graph edge property features. + */ ArangoDBGraphEdgePropertyFeatures() { } } /** The graph features. */ - protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); + protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); /** The vertex features. */ @@ -407,29 +381,29 @@ private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); @Override - public EdgeFeatures edge() { - return edgeFeatures; - } + public EdgeFeatures edge() { + return edgeFeatures; + } @Override - public GraphFeatures graph() { - return graphFeatures; - } + public GraphFeatures graph() { + return graphFeatures; + } @Override - public String toString() { - return StringFactory.featureString(this); - } + public String toString() { + return StringFactory.featureString(this); + } @Override - public VertexFeatures vertex() { - return vertexFeatures; - } + public VertexFeatures vertex() { + return vertexFeatures; + } } /** The Logger. */ - private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraph.class); + private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraph.class); /** The properties name CONFIG_CONF. */ @@ -457,431 +431,392 @@ public VertexFeatures vertex() { /** The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES **/ - public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; + public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; /** The Constant DEFAULT_VERTEX_COLLECTION. */ - public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; + public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; /** The Constant DEFAULT_VERTEX_COLLECTION. */ - public static final String DEFAULT_EDGE_COLLECTION = "edge"; + public static final String DEFAULT_EDGE_COLLECTION = "edge"; /** The Constant GRAPH_VARIABLES_COLLECTION. */ - public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; + public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; /** The Constant ELEMENT_PROPERTIES_COLLECTION. */ - public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; + public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; /** The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. */ - public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; + public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; - public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); + public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); /** The features. */ - private final Features FEATURES = new ArangoDBGraphFeatures(); + private final Features FEATURES = new ArangoDBGraphFeatures(); /** A ArangoDBGraphClient to handle the connection to the Database. */ - private ArangoDBGraphClient client = null; + private ArangoDBGraphClient client = null; /** The name. */ - private String name; + private String name; /** The vertex collections. */ - private final List vertexCollections; + private final List vertexCollections; /** The edge collections. */ - private final List edgeCollections; + private final List edgeCollections; /** The relations. */ - private final List relations; + private final List relations; /** Flat to indicate that the graph has no schema. */ - private boolean schemaless = false; + private boolean schemaless = false; /** The configuration. */ - private Configuration configuration; + private Configuration configuration; /** If collection names should be prefixed with graph name */ - private final boolean shouldPrefixCollectionNames; + private final boolean shouldPrefixCollectionNames; /** * Create a new ArangoDBGraph from the provided configuration. * - * @param configuration the Apache Commons configuration - * @return the Arango DB graph + * @param configuration the Apache Commons configuration + * @return the Arango DB graph */ public static ArangoDBGraph open(Configuration configuration) { - return new ArangoDBGraph(configuration); - } - - /** - * Creates a Graph (simple configuration). - * - * @param configuration the Apache Commons configuration - */ - - public ArangoDBGraph(Configuration configuration) { - - logger.info("Creating new ArangoDB Graph from configuration"); - Configuration arangoConfig = configuration.subset(PROPERTY_KEY_PREFIX); - vertexCollections = arangoConfig.getList(PROPERTY_KEY_VERTICES).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - edgeCollections = arangoConfig.getList(PROPERTY_KEY_EDGES).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - relations = arangoConfig.getList(PROPERTY_KEY_RELATIONS).stream() - .map(String.class::cast) - .collect(Collectors.toList()); - name = arangoConfig.getString(PROPERTY_KEY_GRAPH_NAME); - checkValues(arangoConfig.getString(PROPERTY_KEY_DB_NAME), name, vertexCollections, edgeCollections, relations); - if (CollectionUtils.isEmpty(vertexCollections)) { - schemaless = true; - vertexCollections.add(DEFAULT_VERTEX_COLLECTION); - } - if (CollectionUtils.isEmpty(edgeCollections)) { - edgeCollections.add(DEFAULT_EDGE_COLLECTION); - } - shouldPrefixCollectionNames = arangoConfig.getBoolean(PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, true); - - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - int batchSize = 0; - client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME), - batchSize, shouldPrefixCollectionNames); - - ArangoGraph graph = client.getArangoGraph(); - GraphCreateOptions options = new GraphCreateOptions(); + return new ArangoDBGraph(configuration); + } + + /** + * Creates a Graph (simple configuration). + * + * @param configuration the Apache Commons configuration + */ + + public ArangoDBGraph(Configuration configuration) { + + logger.info("Creating new ArangoDB Graph from configuration"); + Configuration arangoConfig = configuration.subset(PROPERTY_KEY_PREFIX); + vertexCollections = arangoConfig.getList(PROPERTY_KEY_VERTICES).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + edgeCollections = arangoConfig.getList(PROPERTY_KEY_EDGES).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + relations = arangoConfig.getList(PROPERTY_KEY_RELATIONS).stream() + .map(String.class::cast) + .collect(Collectors.toList()); + name = arangoConfig.getString(PROPERTY_KEY_GRAPH_NAME); + checkValues(arangoConfig.getString(PROPERTY_KEY_DB_NAME), name, vertexCollections, edgeCollections, relations); + if (CollectionUtils.isEmpty(vertexCollections)) { + schemaless = true; + vertexCollections.add(DEFAULT_VERTEX_COLLECTION); + } + if (CollectionUtils.isEmpty(edgeCollections)) { + edgeCollections.add(DEFAULT_EDGE_COLLECTION); + } + shouldPrefixCollectionNames = arangoConfig.getBoolean(PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, true); + + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + int batchSize = 0; + client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME), + batchSize, shouldPrefixCollectionNames); + + ArangoGraph graph = client.getArangoGraph(); + GraphCreateOptions options = new GraphCreateOptions(); // FIXME Cant be in orphan collections because it will be deleted with graph? // options.orphanCollections(GRAPH_VARIABLES_COLLECTION); - final List prefVCols = vertexCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); - final List prefECols = edgeCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); - final List edgeDefinitions = new ArrayList<>(); - if (relations.isEmpty()) { - logger.info("No relations, creating default ones."); - edgeDefinitions.addAll(ArangoDBUtil.createDefaultEdgeDefinitions(prefVCols, prefECols)); - } else { - for (String value : relations) { - EdgeDefinition ed = ArangoDBUtil.relationPropertyToEdgeDefinition(this, value); - edgeDefinitions.add(ed); - } - } - edgeDefinitions.add(ArangoDBUtil.createPropertyEdgeDefinitions(this, prefVCols, prefECols)); + final List prefVCols = vertexCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); + final List prefECols = edgeCollections.stream().map(this::getPrefixedCollectioName).collect(Collectors.toList()); + final List edgeDefinitions = new ArrayList<>(); + if (relations.isEmpty()) { + logger.info("No relations, creating default ones."); + edgeDefinitions.addAll(ArangoDBUtil.createDefaultEdgeDefinitions(prefVCols, prefECols)); + } else { + for (String value : relations) { + EdgeDefinition ed = ArangoDBUtil.relationPropertyToEdgeDefinition(this, value); + edgeDefinitions.add(ed); + } + } + edgeDefinitions.add(ArangoDBUtil.createPropertyEdgeDefinitions(this, prefVCols, prefECols)); if (graph.exists()) { ArangoDBUtil.checkGraphForErrors(prefVCols, prefECols, edgeDefinitions, graph, options); ArangoDBGraphVariables variables = null; try { - variables = client.getGraphVariables(); - } catch (NullPointerException ex) { - logger.warn("Existing graph missing Graph Variables collection ({}), will attempt to create one.", GRAPH_VARIABLES_COLLECTION); - } + variables = client.getGraphVariables(); + } catch (NullPointerException ex) { + logger.warn("Existing graph missing Graph Variables collection ({}), will attempt to create one.", GRAPH_VARIABLES_COLLECTION); + } if (variables == null) { - variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); - try { - client.insertGraphVariables(variables); - } catch (ArangoDBGraphException ex) { - throw new ArangoDBGraphException( - String.format( - "Unable to add graph variables collection (%s) to existing graph. %s", - ex.getMessage(), - GRAPH_VARIABLES_COLLECTION) - , ex); - } - } + variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); + try { + client.insertGraphVariables(variables); + } catch (ArangoDBGraphException ex) { + throw new ArangoDBGraphException( + String.format( + "Unable to add graph variables collection (%s) to existing graph. %s", + ex.getMessage(), + GRAPH_VARIABLES_COLLECTION) + , ex); + } + } + } else { + graph = client.createGraph(name, edgeDefinitions, options); + this.name = graph.name(); + ArangoDBGraphVariables variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); + client.insertGraphVariables(variables); } - else { - graph = client.createGraph(name, edgeDefinitions, options); - this.name = graph.name(); - ArangoDBGraphVariables variables = new ArangoDBGraphVariables(name, GRAPH_VARIABLES_COLLECTION, this); - client.insertGraphVariables(variables); - } - this.configuration = configuration; - } + this.configuration = configuration; + } @Override - public Vertex addVertex(Object... keyValues) { + public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); - Object id; String label; if (!schemaless) { - label = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(label); - } - else { - label = DEFAULT_VERTEX_COLLECTION; + label = ElementHelper.getLabelValue(keyValues).orElse(null); + ElementHelper.validateLabel(label); + } else { + label = DEFAULT_VERTEX_COLLECTION; } if (!vertexCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); - } - ArangoDBVertex vertex = null; - if (ElementHelper.getIdValue(keyValues).isPresent()) { - id = ElementHelper.getIdValue(keyValues).get(); - if (this.features().vertex().willAllowId(id)) { - if (id.toString().contains("/")) { - String fullId = id.toString(); - String[] parts = fullId.split("/"); - // The collection name is the last part of the full name - String[] collectionParts = parts[0].split("_"); - String collectionName = collectionParts[collectionParts.length-1]; - if (collectionName.contains(label)) { - id = parts[1]; - - } - } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String)id); - if (m.matches()) { - vertex = new ArangoDBVertex(id.toString(), label, this); - } - else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } - } - else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); } - else { - vertex = new ArangoDBVertex(null, label, this); - } - // The vertex needs to exist before we can attach properties - vertex.insert(); + + String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); + ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); + + // TODO: optmize writing only once + vertex.doInsert(); ElementHelper.attachProperties(vertex, keyValues); return vertex; - } - - /** - * Check that the configuration values are sound. - * - * @param db the db - * @param name the name - * @param vertices the vertices - * @param edges the edges - * @param relations the relations - */ - - private void checkValues( - String db, - String name, - List vertices, - List edges, - List relations) { - - if (StringUtils.isBlank(db)) { + } + + /** + * Check that the configuration values are sound. + * + * @param db the db + * @param name the name + * @param vertices the vertices + * @param edges the edges + * @param relations the relations + */ + + private void checkValues( + String db, + String name, + List vertices, + List edges, + List relations) { + + if (StringUtils.isBlank(db)) { throw new ArangoDBGraphException("The db name can not be empty/null. Check that your configuration file " + - "has a 'graph.db' setting."); - } - if (StringUtils.isBlank(name)) { + "has a 'graph.db' setting."); + } + if (StringUtils.isBlank(name)) { throw new ArangoDBGraphException("The graph name can not be empty/null. Check that your configuration file " + - "has a 'graph.name' name setting."); - } - if (CollectionUtils.isEmpty(edges)) { - logger.warn("Empty edges collection(s), the default 'edge' collection will be used."); - } - if ((vertices.size() > 1) && (edges.size() > 1) && CollectionUtils.isEmpty(relations)) { - throw new ArangoDBGraphException("If more than one vertex/edge collection is provided, relations must be defined"); - } - } - - @Override - public void close() { - client.shutdown(); - } - - - @Override - public GraphComputer compute() throws IllegalArgumentException { + "has a 'graph.name' name setting."); + } + if (CollectionUtils.isEmpty(edges)) { + logger.warn("Empty edges collection(s), the default 'edge' collection will be used."); + } + if ((vertices.size() > 1) && (edges.size() > 1) && CollectionUtils.isEmpty(relations)) { + throw new ArangoDBGraphException("If more than one vertex/edge collection is provided, relations must be defined"); + } + } + + @Override + public void close() { + client.shutdown(); + } + + + @Override + public GraphComputer compute() throws IllegalArgumentException { throw Graph.Exceptions.graphComputerNotSupported(); - } - - @Override - public C compute(Class graphComputerClass) throws IllegalArgumentException { - throw new UnsupportedOperationException(); - } - - @Override - public Configuration configuration() { - return configuration; - } - - /** - * Edge collections. - * - * @return the list - */ - - public List edgeCollections() { - return Collections.unmodifiableList(edgeCollections); - } - - @Override - public Iterator edges(Object... edgeIds) { - List ids = Arrays.stream(edgeIds) - .map(id -> { - if (id instanceof ArangoDBEdge) { - return ((ArangoDBEdge) id).id(); - } else if(id instanceof String) { - // We only support String ids - return (String) id; - } else { - throw unsupportedIdType(id); - } - }) - .collect(Collectors.toList()); - return getClient().getGraphEdges(ids).stream() - .map(it -> (Edge) new ArangoDBEdge(this, it)) - .iterator(); - } - - @Override - public Features features() { - return FEATURES; - } - - /** - * Returns the ArangoDBGraphClient object. - * - * @return the ArangoDBGraphClient object - */ - - public ArangoDBGraphClient getClient() { - return client; - } - - /** - * Returns the identifier of the graph. - * - * @return the identifier of the graph - */ - - public String getId() { - ArangoGraph graph = client.getArangoGraph(); - return graph.getInfo().getName(); - } - - /** - * The graph name - * - * @return the name - */ - - public String name() { - return this.name; - } - - @Override - public Transaction tx() { - throw Graph.Exceptions.transactionsNotSupported(); - } - - @Override - public Variables variables() { - ArangoDBGraphVariables v = client.getGraphVariables(); - if (v != null) { - v.graph(this); - return v; + } + + @Override + public C compute(Class graphComputerClass) throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + @Override + public Configuration configuration() { + return configuration; + } + + /** + * Edge collections. + * + * @return the list + */ + + public List edgeCollections() { + return Collections.unmodifiableList(edgeCollections); + } + + @Override + public Iterator edges(Object... edgeIds) { + return getClient().getGraphEdges(getIdValues(edgeIds)).stream() + .map(it -> (Edge) new ArangoDBEdge(this, it)) + .iterator(); + } + + @Override + public Iterator vertices(Object... vertexIds) { + return getClient().getGraphVertices(getIdValues(vertexIds)).stream() + .map(it -> (Vertex) new ArangoDBVertex(this, it)) + .iterator(); + } + + @Override + public Features features() { + return FEATURES; + } + + /** + * Returns the ArangoDBGraphClient object. + * + * @return the ArangoDBGraphClient object + */ + + public ArangoDBGraphClient getClient() { + return client; + } + + /** + * Returns the identifier of the graph. + * + * @return the identifier of the graph + */ + + public String getId() { + ArangoGraph graph = client.getArangoGraph(); + return graph.getInfo().getName(); + } + + /** + * The graph name + * + * @return the name + */ + + public String name() { + return this.name; + } + + @Override + public Transaction tx() { + throw Graph.Exceptions.transactionsNotSupported(); + } + + @Override + public Variables variables() { + ArangoDBGraphVariables v = client.getGraphVariables(); + if (v != null) { + v.graph(this); + return v; + } else { + throw new ArangoDBGraphException("Existing graph does not have a Variables collection"); } - else { - throw new ArangoDBGraphException("Existing graph does not have a Variables collection"); + } + + /** + * Vertex collections. + * + * @return the list + */ + public List vertexCollections() { + return Collections.unmodifiableList(vertexCollections); + } + + /** + * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag + * @param collectionName the collection name + * @return the Collection name prefixed + */ + public String getPrefixedCollectioName(String collectionName) { + if (GRAPH_VARIABLES_COLLECTION.equals(collectionName)) { + return collectionName; } - } - - /** - * Vertex collections. - * - * @return the list - */ - public List vertexCollections() { - return Collections.unmodifiableList(vertexCollections); - } - - @Override - public Iterator vertices(Object... vertexIds) { - List vertexCollections = new ArrayList<>(); - List ids = Arrays.stream(vertexIds) - .map(id -> { - if (id instanceof Vertex) { - vertexCollections.add(((Vertex) id).label()); - return ((Vertex) id).id(); - } else { - // We only support String ids - return id; - } - }) - .map(id -> id == null ? (String) id : id.toString()) - .collect(Collectors.toList()); - return getClient().getGraphVertices(ids, vertexCollections).stream() - .map(it -> (Vertex) new ArangoDBVertex(this, it)) - .iterator(); - } - - /** - * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag - * @param collectionName the collection name - * @return the Collection name prefixed - */ - public String getPrefixedCollectioName(String collectionName) { - if (GRAPH_VARIABLES_COLLECTION.equals(collectionName)) { - return collectionName; - } - if (GRAPH_COLLECTIONS.contains(collectionName)) { - return String.format("%s_%s", name, collectionName); - } - if(shouldPrefixCollectionNames) { - if(collectionName.startsWith(name + "_")) { - return collectionName; - } - return String.format("%s_%s", name, collectionName); - }else{ - return collectionName; - } - } - - @Override - public String toString() { - String vertices = vertexCollections().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String edges = edgeCollections().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String relations = relations().stream() - .map(vc -> String.format("\"%s\"", vc)) - .collect(Collectors.joining(", ", "{", "}")); - String internal = "{" - + "\"name\":\"" + name() + "\"," - + "\"vertices\":" + vertices + "," - + "\"edges\":" + edges+ "," - + "\"relations\":" + relations - +"}"; - return StringFactory.graphString(this, internal); - } - - /** - * The graph relations. - * - * @return the collection of relations - */ - private Collection relations() { - return relations; - } - - // TODO Decide which of these methods we want to keep + if (GRAPH_COLLECTIONS.contains(collectionName)) { + return String.format("%s_%s", name, collectionName); + } + if (shouldPrefixCollectionNames) { + if (collectionName.startsWith(name + "_")) { + return collectionName; + } + return String.format("%s_%s", name, collectionName); + } else { + return collectionName; + } + } + + @Override + public String toString() { + String vertices = vertexCollections().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String edges = edgeCollections().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String relations = relations().stream() + .map(vc -> String.format("\"%s\"", vc)) + .collect(Collectors.joining(", ", "{", "}")); + String internal = "{" + + "\"name\":\"" + name() + "\"," + + "\"vertices\":" + vertices + "," + + "\"edges\":" + edges + "," + + "\"relations\":" + relations + + "}"; + return StringFactory.graphString(this, internal); + } + + /** + * The graph relations. + * + * @return the collection of relations + */ + private Collection relations() { + return relations; + } + + private String getIdValue(Object id) { + if (id instanceof String) { + return (String) id; + } else if (id instanceof Element) { + return getIdValue(((Element) id).id()); + } else { + throw unsupportedIdType(id); + } + } + + private List getIdValues(Object[] edgeIds) { + return Arrays.stream(edgeIds) + .map(this::getIdValue) + .collect(Collectors.toList()); + } + + + // TODO Decide which of these methods we want to keep // @Override // public void dropKeyIndex(String name, Class elementClass) { diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java new file mode 100644 index 0000000..dc04b76 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.PersistentData; +import org.apache.tinkerpop.gremlin.structure.Element; + +import java.util.Optional; + +public interface ArangoDBPersistentElement extends Element { + + @Override + ArangoDBGraph graph(); + + PersistentData data(); + + default String key() { + return data().getKey(); + } + + default void key(String key) { + data().setKey(key); + } + + @Override + default String label() { + return data().getLabel(); + } + + @SuppressWarnings("resource") + default String collection() { + return graph().getPrefixedCollectioName(label()); + } + + @Override + default String id() { + return Optional.ofNullable(key()) + .map(it -> collection() + '/' + it) + .orElse(label()); + } +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java index 12fb0c0..5ce0cd4 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java @@ -16,27 +16,27 @@ * specific language governing permissions and limitations * under the License. */ + package com.arangodb.tinkerpop.gremlin.structure; -import org.apache.tinkerpop.gremlin.structure.Element; import org.apache.tinkerpop.gremlin.structure.Property; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -public final class ArangoDBProperty implements Property { +public class ArangoDBProperty implements Property { private final String key; private final V value; - private final Element element; + private final ArangoDBSimpleElement element; - public ArangoDBProperty(final Element element, final String key, final V value) { + public ArangoDBProperty(final ArangoDBSimpleElement element, final String key, final V value) { this.element = element; this.key = key; this.value = value; } @Override - public Element element() { + public ArangoDBElement element() { return element; } @@ -57,13 +57,7 @@ public boolean isPresent() { @Override public void remove() { - if (element instanceof ArangoDBEdge) { - ((ArangoDBEdge) element).removeProperty(key); - } else if (element instanceof ArangoDBVertexProperty) { - ((ArangoDBVertexProperty) element).removeProperty(key); - } else { - throw new UnsupportedOperationException("Property " + this.key() + " is not an Edge"); - } + element.removeProperty(key); } @Override @@ -83,3 +77,4 @@ public int hashCode() { } } + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java deleted file mode 100644 index eaca24b..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; - -import java.util.Objects; - -public class ArangoDBPropertyData { - private final Object value; - private final String valueType; - - @JsonCreator - ArangoDBPropertyData( - @JsonProperty("value") Object value, - @JsonProperty("valueType") String valueType - ) { - this.value = value; - this.valueType = valueType; - } - - public ArangoDBPropertyData(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 "ArangoDBPropertyValue{" + - "value=" + value + - ", valueType='" + valueType + '\'' + - '}'; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - ArangoDBPropertyData that = (ArangoDBPropertyData) 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/ArangoDBSimpleElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java new file mode 100644 index 0000000..0cd16f1 --- /dev/null +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBSimpleElement.java @@ -0,0 +1,35 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.persistence.AdbValue; +import com.arangodb.tinkerpop.gremlin.persistence.SimplePropertyData; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; + +public abstract class ArangoDBSimpleElement extends ArangoDBElement { + public ArangoDBSimpleElement(ArangoDBGraph graph, D data) { + super(graph, data); + } + + @Override + @SuppressWarnings("unchecked") + protected Property createProperty(String key, AdbValue value) { + return new ArangoDBProperty<>(this, key, (V) value.getValue()); + } + + @Override + public Property property(String key, V value) { + if (removed()) throw Exceptions.elementAlreadyRemoved(id()); + ElementHelper.validateProperty(key, value); + AdbValue dataValue = AdbValue.of(value); + data().add(key, dataValue); + doUpdate(); + return createProperty(key, dataValue); + } + + public void removeProperty(String key) { + if (removed()) throw Exceptions.elementAlreadyRemoved(id()); + data.remove(key); + doUpdate(); + } + +} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index 4057613..8a94bf5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -1,209 +1,116 @@ -/// /////////////////////////////////////////////////////////////////////////////////////// -// -// 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.*; -import java.util.regex.Matcher; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.ArrayUtils; -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; +import com.arangodb.tinkerpop.gremlin.persistence.VertexData; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; -import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; - - -/** - * The ArangoDB vertex class. - * - * @author Achim Brandt (http://www.triagens.de) - * @author Johannes Gocke (http://www.triagens.de) - * @author Guido Schwab (http://www.triagens.de) - * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - */ - -public class ArangoDBVertex implements Vertex { +import java.util.*; +import java.util.stream.Collectors; - private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertex.class); +import static com.arangodb.tinkerpop.gremlin.structure.ArangoDBElement.Exceptions.elementAlreadyRemoved; +import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.*; - private final ArangoDBGraph graph; - private final ArangoDBVertexData data; - private boolean removed; +public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { - public ArangoDBVertex(ArangoDBGraph graph, ArangoDBVertexData data) { - this.graph = graph; - this.data = data; - this.removed = false; + public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { + return new ArangoDBVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); } - public ArangoDBVertex(final String id, final String label, ArangoDBGraph graph) { - this.graph = graph; - String inferredLabel, key; - if (id != null) { - int separator = id.indexOf('/'); - if (separator > 0) { - inferredLabel = id.substring(0, separator); - key = id.substring(separator + 1); - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = id; - } - } else { - inferredLabel = label != null ? label : DEFAULT_LABEL; - key = null; - } - - data = new ArangoDBVertexData(inferredLabel, key); - removed = false; - } - - public boolean isRemoved() { - return removed; + public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { + super(graph, data); } @Override - public String id() { - String key = data.getKey(); - if (key == null) { - return null; - } - return graph.getPrefixedCollectioName(label()) + "/" + key; - } + public VertexProperty property( + final VertexProperty.Cardinality cardinality, + final String key, + final V value, + final Object... keyValues + ) { + if (removed()) throw elementAlreadyRemoved(id()); + ElementHelper.legalPropertyKeyValueArray(keyValues); + ElementHelper.validateProperty(key, value); - @Override - public String label() { - return data.getLabel(); - } + Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); + if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); - @Override - public ArangoDBGraph graph() { - return graph; - } + String idValue = ElementHelper.getIdValue(keyValues) + .map(it -> { + if (!graph.features().vertex().properties().willAllowId(it)) { + throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + return it.toString(); + }) + .orElseGet(() -> UUID.randomUUID().toString()); - @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; + VertexPropertyData prop = VertexPropertyData.of(idValue, value); + data.add(key, prop); + + ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); + // TODO: optmize writing only once + ElementHelper.attachProperties(vertexProperty, keyValues); + doUpdate(); + return vertexProperty; } @Override - public Edge addEdge(String label, Vertex inVertex, Object... keyValues) { - LOGGER.info("addEdge in collection {} to vertex {}", label, inVertex == null ? "?" : inVertex.id()); + public Edge addEdge(String label, Vertex vertex, Object... keyValues) { + if (null == vertex) throw Graph.Exceptions.argumentCanNotBeNull("vertex"); + if (removed() || ((ArangoDBVertex) vertex).removed()) throw elementAlreadyRemoved(id()); + ElementHelper.legalPropertyKeyValueArray(keyValues); ElementHelper.validateLabel(label); + 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(); + String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); + ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), graph); + // TODO: optmize writing only once + edge.doInsert(); ElementHelper.attachProperties(edge, keyValues); return edge; } @Override - public VertexProperty property( - final Cardinality cardinality, - final String key, - final V value, - final Object... keyValues - ) { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - ElementHelper.legalPropertyKeyValueArray(keyValues); - ElementHelper.validateProperty(key, value); - - final Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); - if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); - - Optional optionalId = ElementHelper.getIdValue(keyValues); - Object[] filteredKeyValues = ArrayUtils.clone(keyValues); - String idValue = null; - if (optionalId.isPresent()) { - if (graph.features().vertex().properties().willAllowId(optionalId.get())) { - idValue = optionalId.get().toString(); - } else { - throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); - } - int idIndex = 0; - for (int i = 0; i < filteredKeyValues.length; i += 2) { - if (filteredKeyValues[i] == T.id) { - idIndex = i; - break; - } - } - filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); - filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); - } - - if (idValue == null) { - idValue = UUID.randomUUID().toString(); - } - - ArangoDBVertexPropertyData prop = new ArangoDBVertexPropertyData(idValue, value); - - final List list = data.getProperties().getOrDefault(key, new ArrayList<>()); - list.add(prop); - data.getProperties().put(key, list); + protected void doRemove() { + edges(Direction.BOTH).forEachRemaining(Edge::remove); + graph.getClient().deleteVertex(this); + } - ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); - ElementHelper.attachProperties(vertexProperty, filteredKeyValues); - update(); - return vertexProperty; + @Override + protected String stringify() { + return StringFactory.vertexString(this); } + @Override + protected Property createProperty(String key, VertexPropertyData value) { + return new ArangoDBVertexProperty<>(key, value, this); + } @Override public Iterator edges(Direction direction, String... edgeLabels) { @@ -213,13 +120,10 @@ public Iterator edges(Direction direction, String... edgeLabels) { if (edgeCollections.isEmpty()) { return Collections.emptyIterator(); } - return graph.getClient().getVertexEdges(id(), edgeCollections, direction) - .stream() - .map(it -> (Edge) new ArangoDBEdge(graph, it)) - .iterator(); + return IteratorUtils.map(graph.getClient().getVertexEdges(id(), edgeCollections, direction), + it -> new ArangoDBEdge(graph, it)); } - @Override public Iterator vertices(Direction direction, String... edgeLabels) { List edgeCollections = getQueryEdgeCollections(edgeLabels); @@ -228,59 +132,44 @@ public Iterator vertices(Direction direction, String... edgeLabels) { 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(); + return IteratorUtils.map(graph.getClient().getVertexNeighbors(id(), edgeCollections, direction), + it -> new ArangoDBVertex(graph, it)); } - - @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(); + public void doInsert() { + graph.getClient().insertVertex(this); } - @Override - public String toString() { - return StringFactory.vertexString(this); + public void doUpdate() { + graph.getClient().updateVertex(this); } - private Stream> allProperties() { - return data.getProperties().entrySet().stream() - .flatMap(x -> x.getValue().stream() - .map(y -> new ArangoDBVertexProperty<>(x.getKey(), y, this)) - ); + @Override + public VertexProperty property(final String key) { + return Vertex.super.property(key); } - public void insert() { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - graph.getClient().insertVertex(data); + @Override + public VertexProperty property(final String key, final V value) { + return Vertex.super.property(key, value); } - - public void update() { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - graph.getClient().updateVertex(data); + @Override + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); } - public void removeProperty(ArangoDBVertexPropertyData prop) { - if (removed) throw elementAlreadyRemoved(Vertex.class, id()); - for (List it : data.getProperties().values()) { - if (it.remove(prop)) return; - } + public void removeProperty(ArangoDBVertexProperty prop) { + if (removed()) throw ArangoDBElement.Exceptions.elementAlreadyRemoved(id()); + data.remove(prop.key(), prop.data()); + doUpdate(); } /** * Query will raise an exception if the edge_collection name is not in the graph, so we need to filter out * edgeLabels not in the graph. - * - * @param edgeLabels - * @return */ private List getQueryEdgeCollections(String... edgeLabels) { List vertexCollections; @@ -295,17 +184,4 @@ private List getQueryEdgeCollections(String... edgeLabels) { } return vertexCollections; } - - @Override - @SuppressWarnings("EqualsDoesntCheckParameterClass") - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); - } - - @Override - public int hashCode() { - return ElementHelper.hashCode(this); - } - } - diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java deleted file mode 100644 index d7c2829..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.*; - -public class ArangoDBVertexData extends ArangoDBData> { - - public ArangoDBVertexData() { - } - - public ArangoDBVertexData(String label, String key) { - super(label, key); - } - - @Override - public String toString() { - return "ArangoDBVertexData{" + - super.toString() + - "}"; - } -} - diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java index 143b8ee..0283831 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java @@ -1,29 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package com.arangodb.tinkerpop.gremlin.structure; +import com.arangodb.tinkerpop.gremlin.persistence.VertexPropertyData; import org.apache.tinkerpop.gremlin.structure.*; -import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.apache.tinkerpop.gremlin.structure.util.StringFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; 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); +public class ArangoDBVertexProperty

extends ArangoDBSimpleElement implements VertexProperty

{ private final String key; private final ArangoDBVertex vertex; - private final ArangoDBVertexPropertyData data; - private boolean removed; - public ArangoDBVertexProperty(String key, ArangoDBVertexPropertyData data, ArangoDBVertex vertex) { + public ArangoDBVertexProperty(String key, VertexPropertyData data, ArangoDBVertex vertex) { + super(vertex.graph(), data); this.key = key; - this.data = data; this.vertex = vertex; - removed = false; + } + + @Override + protected boolean removed() { + return super.removed() || vertex.removed(); } @Override @@ -33,8 +49,8 @@ public String key() { @SuppressWarnings("unchecked") @Override - public V value() throws NoSuchElementException { - return (V) data.getValue(); + public P value() throws NoSuchElementException { + return (P) data.getValue(); } @Override @@ -43,67 +59,44 @@ public boolean isPresent() { } @Override - public Vertex element() { + public ArangoDBVertex element() { return vertex; } - @Override public Object id() { return data.getId(); } @Override - public Property property(String key, W value) { - if (removed) throw elementAlreadyRemoved(VertexProperty.class, id()); - LOGGER.info("set property {} = {}", key, value); - ElementHelper.validateProperty(key, value); - data.setProperty(key, value); - vertex.update(); - return new ArangoDBProperty<>(this, key, value); + protected void doUpdate() { + vertex.doUpdate(); } @Override - public void remove() { - if (removed) return; - vertex.removeProperty(data); - vertex.update(); - removed = true; + protected void doRemove() { + vertex.removeProperty(this); + vertex.doUpdate(); } @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(); - } + protected void doInsert() { + doUpdate(); } @Override - public String toString() { + public String stringify() { return StringFactory.propertyString(this); } - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override - public boolean equals(final Object object) { - return ElementHelper.areEqual(this, object); + public Iterator> properties(String... propertyKeys) { + return IteratorUtils.cast(super.properties(propertyKeys)); } @Override - public int hashCode() { - return ElementHelper.hashCode((Property) this); + public String label() { + return VertexProperty.super.label(); } - } + diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java deleted file mode 100644 index 05a22d7..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; -import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; - -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -public class ArangoDBVertexPropertyData extends ArangoDBPropertyData implements PropertiesContainer { - private final String id; - private final Map properties; - - @JsonCreator - ArangoDBVertexPropertyData( - @JsonProperty("id") String id, - @JsonProperty("value") Object value, - @JsonProperty("valueType") String valueType, - @JsonProperty("properties") Map properties) { - super(value, valueType); - this.id = id; - this.properties = properties; - } - - public ArangoDBVertexPropertyData(String id, Object value) { - super(value); - this.id = id; - this.properties = new HashMap<>(); - } - - public String getId() { - return id; - } - - public Map getProperties() { - return properties; - } - - @Override - public String toString() { - return "TinkerVertexPropertyData{" + - "id='" + id + '\'' + - ", value=" + getValue() + - ", valueType='" + getValueType() + '\'' + - ", properties=" + properties + - '}'; - } - - @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - ArangoDBVertexPropertyData that = (ArangoDBVertexPropertyData) o; - return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), id, properties); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java deleted file mode 100644 index 4f23904..0000000 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import java.util.AbstractMap; -import java.util.Map; -import java.util.stream.Stream; - -interface PropertiesContainer { - Map getProperties(); - - default Stream> properties() { - return getProperties().entrySet().stream() - .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); - } - - default boolean hasProperty(String key) { - return getProperties().containsKey(key); - } - - default void removeProperty(String key) { - getProperties().remove(key); - } - - default void setProperty(String key, Object value) { - getProperties().put(key, new ArangoDBPropertyData(value)); - } - - default Object getProperty(String key) { - return getProperties().get(key).getValue(); - } -} diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java index 3a32072..f5bdcda 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -1,10 +1,10 @@ -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// // // Implementation of the TinkerPop OLTP Provider API for ArangoDB // // Copyright triAGENS GmbH Cologne and The University of York // -////////////////////////////////////////////////////////////////////////////////////////// +/// /////////////////////////////////////////////////////////////////////////////////////// package com.arangodb.tinkerpop.gremlin.utils; @@ -21,354 +21,364 @@ import com.arangodb.tinkerpop.gremlin.structure.*; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.collections4.CollectionUtils; import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides utility methods for creating properties and for normalising property and * collections names (to satisfy Arango DB naming conventions. - * + * * @author Achim Brandt (http://www.triagens.de) * @author Johannes Gocke (http://www.triagens.de) * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) */ //FIXME We should add more util methods to validate attribute names, e.g. scape ".". public class ArangoDBUtil { - - /** The Logger. */ - - private static final Logger logger = LoggerFactory.getLogger(ArangoDBUtil.class); - - /** Utiliy mapper for conversions. **/ - - private static final ObjectMapper mapper = new ObjectMapper(); - - /** - * The prefix to denote that a collection is a hidden collection. - */ - - private final static String HIDDEN_PREFIX = "adbt_"; - - /** The Constant HIDDEN_PREFIX_LENGTH. */ - - private static final int HIDDEN_PREFIX_LENGTH = HIDDEN_PREFIX.length(); - - /** The regex to match DOCUMENT_KEY. */ - - public static final Pattern DOCUMENT_KEY = Pattern.compile("^[A-Za-z0-9_:\\.@()\\+,=;\\$!\\*'%-]*"); - - /** - * Instantiates a new ArangoDB Util. - */ - private ArangoDBUtil() { - // this is a helper class - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename name "_XXXX" to "«a»XXXX" for storage. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String normalizeKey(String key) { - if (key.charAt(0) == '_') { - return "«a»" + key.substring(1); - } - return key; - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String denormalizeKey(String key) { - if (key.startsWith("«a»")) { - return "_" + key.substring(3); - } - return key; - } - - /** - * Hidden keys, labels, etc. are prefixed in Tinkerpop with @link Graph.Hidden.HIDDEN_PREFIX). Since in ArangoDB - * collection names must always start with a letter, this method normalises Hidden collections name to valid - * ArangoDB names by replacing the "~" with - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String normalizeCollection(String key) { - String nname = isHidden(key) ? key : HIDDEN_PREFIX.concat(key); - if (!NamingConventions.COLLECTION.hasValidNameSize(nname)) { - throw ArangoDBGraphClient.ArangoDBExceptions.getNamingConventionError(ArangoDBGraphClient.ArangoDBExceptions.NAME_TO_LONG, key); - } - return nname; - } - - /** - * Since attributes that start with underscore are considered to be system attributes (), - * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. - * - * @param key the name to convert - * @return String the converted String - * @see Manual - */ - - public static String denormalizeCollection(String key) { - return isHidden(key) ? key.substring(HIDDEN_PREFIX_LENGTH) : key; - } - - /** - * The Enum NamingConventions. - */ - - public enum NamingConventions { - - /** The collection. */ - COLLECTION(64), - - /** The name. */ - KEY(256); - - /** The max length. */ - - private int maxLength; - - /** - * Instantiates a new naming conventions. - * - * @param maxLength the max length - */ - NamingConventions(int maxLength) { - this.maxLength = maxLength; - } - - /** - * Checks for valid name size. - * - * @param name the name - * @return true, if successful - */ - public boolean hasValidNameSize(String name) { - final byte[] utf8Bytes; - try { - utf8Bytes = name.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - return false; - } - return utf8Bytes.length <= maxLength; - } - } - - /** - * Create an EdgeDefinition from a relation in the Configuration. The format of a relation is: - *

-	 * collection:from->to
-	 * 
- * Where collection is the name of the Edge collection, and to and from are comma separated list of - * node collection names. - * - * @param graph the name of the graph - * @param relation the relation - * @return an EdgeDefinition that represents the relation. - * @throws ArangoDBGraphException if the relation is malformed - */ - - public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph graph, String relation) throws ArangoDBGraphException { - logger.info("Creating EdgeRelation from {}", relation); - EdgeDefinition result = new EdgeDefinition(); - String[] info = relation.split(":"); - if (info.length != 2) { - throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation); - } - result.collection(graph.getPrefixedCollectioName(info[0])); - info = info[1].split("->"); - if (info.length != 2) { - throw new ArangoDBGraphException("Error in configuration. Malformed relation> " + relation); - } - List trimmed = Arrays.stream(info[0].split(",")) - .map(String::trim) - .map(c -> graph.getPrefixedCollectioName(c)) - .collect(Collectors.toList()); - String[] from = new String[trimmed.size()]; - from = trimmed.toArray(from); - - trimmed = Arrays.stream(info[1].split(",")) - .map(String::trim) - .map(c -> graph.getPrefixedCollectioName(c)) - .collect(Collectors.toList()); - String[] to = new String[trimmed.size()]; - to = trimmed.toArray(to); - result.from(from).to(to); - return result; - } - - - /** - * Creates the default edge definitions. When no relations are provided, the graph schema is - * assumed to be fully connected, i.e. there is an EdgeDefintion for each possible combination - * of Vertex-Edge-Vertex triplets. - * - * @param verticesCollectionNames the vertex collection names - * @param edgesCollectionNames the edge collection names - * @return the list of edge definitions - */ - - public static List createDefaultEdgeDefinitions( - List verticesCollectionNames, - List edgesCollectionNames) { - List result = new ArrayList<>(); - for (String e : edgesCollectionNames) { - for (String from : verticesCollectionNames) { - for (String to : verticesCollectionNames) { - EdgeDefinition ed = new EdgeDefinition() - .collection(e) - .from(from) - .to(to); - result.add(ed); - } - } - } - return result; - } - - - /** - * Gets a collection that is unique for the given graph. - * - * @param graphName the graph name - * @param collectionName the collection name - * @param shouldPrefixWithGraphName flag to indicate if the name should be prefixed - * @return the unique collection name - */ - @Deprecated - public static String getCollectioName(String graphName, String collectionName, Boolean shouldPrefixWithGraphName) { - if(shouldPrefixWithGraphName) { - return String.format("%s_%s", graphName, collectionName); - }else{ - return collectionName; - } - } - - /** - * Validate if an existing graph is correctly configured to handle the desired vertex, edges - * and relations. - * - * @param verticesCollectionNames The names of collections for nodes - * @param edgesCollectionNames The names of collections for edges - * @param requiredDefinitions The description of edge definitions - * @param graph the graph - * @param options The options used to create the graph - * @throws ArangoDBGraphException If the graph settings do not match the configuration information - */ - - public static void checkGraphForErrors( - List verticesCollectionNames, - List edgesCollectionNames, - List requiredDefinitions, - ArangoGraph graph, - GraphCreateOptions options) throws ArangoDBGraphException { - - checkGraphVertexCollections(verticesCollectionNames, graph, options); - - GraphEntity ge = graph.getInfo(); + + /** + * The Logger. + */ + + private static final Logger logger = LoggerFactory.getLogger(ArangoDBUtil.class); + + /** + * Utiliy mapper for conversions. + **/ + + private static final ObjectMapper mapper = new ObjectMapper(); + + /** + * The prefix to denote that a collection is a hidden collection. + */ + + private final static String HIDDEN_PREFIX = "adbt_"; + + /** + * The Constant HIDDEN_PREFIX_LENGTH. + */ + + private static final int HIDDEN_PREFIX_LENGTH = HIDDEN_PREFIX.length(); + + /** + * The regex to match DOCUMENT_KEY. + */ + + public static final Pattern DOCUMENT_KEY = Pattern.compile("^[A-Za-z0-9_:\\.@()\\+,=;\\$!\\*'%-]*"); + + /** + * Instantiates a new ArangoDB Util. + */ + private ArangoDBUtil() { + // this is a helper class + } + + /** + * Since attributes that start with underscore are considered to be system attributes (), + * rename name "_XXXX" to "«a»XXXX" for storage. + * + * @param key the name to convert + * @return String the converted String + * @see Manual + */ + + public static String normalizeKey(String key) { + if (key.charAt(0) == '_') { + return "«a»" + key.substring(1); + } + return key; + } + + /** + * Since attributes that start with underscore are considered to be system attributes (), + * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. + * + * @param key the name to convert + * @return String the converted String + * @see Manual + */ + + public static String denormalizeKey(String key) { + if (key.startsWith("«a»")) { + return "_" + key.substring(3); + } + return key; + } + + /** + * Hidden keys, labels, etc. are prefixed in Tinkerpop with @link Graph.Hidden.HIDDEN_PREFIX). Since in ArangoDB + * collection names must always start with a letter, this method normalises Hidden collections name to valid + * ArangoDB names by replacing the "~" with + * + * @param key the name to convert + * @return String the converted String + * @see Manual + */ + + public static String normalizeCollection(String key) { + String nname = isHidden(key) ? key : HIDDEN_PREFIX.concat(key); + if (!NamingConventions.COLLECTION.hasValidNameSize(nname)) { + throw ArangoDBGraphClient.ArangoDBExceptions.getNamingConventionError(ArangoDBGraphClient.ArangoDBExceptions.NAME_TO_LONG, key); + } + return nname; + } + + /** + * Since attributes that start with underscore are considered to be system attributes (), + * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. + * + * @param key the name to convert + * @return String the converted String + * @see Manual + */ + + public static String denormalizeCollection(String key) { + return isHidden(key) ? key.substring(HIDDEN_PREFIX_LENGTH) : key; + } + + /** + * The Enum NamingConventions. + */ + + public enum NamingConventions { + + /** + * The collection. + */ + COLLECTION(64), + + /** + * The name. + */ + KEY(256); + + /** + * The max length. + */ + + private int maxLength; + + /** + * Instantiates a new naming conventions. + * + * @param maxLength the max length + */ + NamingConventions(int maxLength) { + this.maxLength = maxLength; + } + + /** + * Checks for valid name size. + * + * @param name the name + * @return true, if successful + */ + public boolean hasValidNameSize(String name) { + final byte[] utf8Bytes; + try { + utf8Bytes = name.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + return false; + } + return utf8Bytes.length <= maxLength; + } + } + + /** + * Create an EdgeDefinition from a relation in the Configuration. The format of a relation is: + *
+     * collection:from->to
+     * 
+ * Where collection is the name of the Edge collection, and to and from are comma separated list of + * node collection names. + * + * @param graph the name of the graph + * @param relation the relation + * @return an EdgeDefinition that represents the relation. + * @throws ArangoDBGraphException if the relation is malformed + */ + + public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph graph, String relation) throws ArangoDBGraphException { + logger.debug("Creating EdgeRelation from {}", relation); + EdgeDefinition result = new EdgeDefinition(); + String[] info = relation.split(":"); + if (info.length != 2) { + throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation); + } + result.collection(graph.getPrefixedCollectioName(info[0])); + info = info[1].split("->"); + if (info.length != 2) { + throw new ArangoDBGraphException("Error in configuration. Malformed relation> " + relation); + } + List trimmed = Arrays.stream(info[0].split(",")) + .map(String::trim) + .map(c -> graph.getPrefixedCollectioName(c)) + .collect(Collectors.toList()); + String[] from = new String[trimmed.size()]; + from = trimmed.toArray(from); + + trimmed = Arrays.stream(info[1].split(",")) + .map(String::trim) + .map(c -> graph.getPrefixedCollectioName(c)) + .collect(Collectors.toList()); + String[] to = new String[trimmed.size()]; + to = trimmed.toArray(to); + result.from(from).to(to); + return result; + } + + + /** + * Creates the default edge definitions. When no relations are provided, the graph schema is + * assumed to be fully connected, i.e. there is an EdgeDefintion for each possible combination + * of Vertex-Edge-Vertex triplets. + * + * @param verticesCollectionNames the vertex collection names + * @param edgesCollectionNames the edge collection names + * @return the list of edge definitions + */ + + public static List createDefaultEdgeDefinitions( + List verticesCollectionNames, + List edgesCollectionNames) { + List result = new ArrayList<>(); + for (String e : edgesCollectionNames) { + for (String from : verticesCollectionNames) { + for (String to : verticesCollectionNames) { + EdgeDefinition ed = new EdgeDefinition() + .collection(e) + .from(from) + .to(to); + result.add(ed); + } + } + } + return result; + } + + + /** + * Gets a collection that is unique for the given graph. + * + * @param graphName the graph name + * @param collectionName the collection name + * @param shouldPrefixWithGraphName flag to indicate if the name should be prefixed + * @return the unique collection name + */ + @Deprecated + public static String getCollectioName(String graphName, String collectionName, Boolean shouldPrefixWithGraphName) { + if (shouldPrefixWithGraphName) { + return String.format("%s_%s", graphName, collectionName); + } else { + return collectionName; + } + } + + /** + * Validate if an existing graph is correctly configured to handle the desired vertex, edges + * and relations. + * + * @param verticesCollectionNames The names of collections for nodes + * @param edgesCollectionNames The names of collections for edges + * @param requiredDefinitions The description of edge definitions + * @param graph the graph + * @param options The options used to create the graph + * @throws ArangoDBGraphException If the graph settings do not match the configuration information + */ + + public static void checkGraphForErrors( + List verticesCollectionNames, + List edgesCollectionNames, + List requiredDefinitions, + ArangoGraph graph, + GraphCreateOptions options) throws ArangoDBGraphException { + + checkGraphVertexCollections(verticesCollectionNames, graph, options); + + GraphEntity ge = graph.getInfo(); Collection graphEdgeDefinitions = ge.getEdgeDefinitions(); if (CollectionUtils.isEmpty(requiredDefinitions)) { - // If no relations are defined, vertices and edges can only have one value - if ((verticesCollectionNames.size() != 1) || (edgesCollectionNames.size() != 1)) { - throw new ArangoDBGraphException("No relations where specified but more than one vertex/edge where defined."); - } - if (graphEdgeDefinitions.size() != 2) { // There is always a edgeDefinition for ELEMENT_HAS_PROPERTIES - throw new ArangoDBGraphException("No relations where specified but the graph has more than one EdgeDefinition."); - } + // If no relations are defined, vertices and edges can only have one value + if ((verticesCollectionNames.size() != 1) || (edgesCollectionNames.size() != 1)) { + throw new ArangoDBGraphException("No relations where specified but more than one vertex/edge where defined."); + } + if (graphEdgeDefinitions.size() != 2) { // There is always a edgeDefinition for ELEMENT_HAS_PROPERTIES + throw new ArangoDBGraphException("No relations where specified but the graph has more than one EdgeDefinition."); + } } - Map eds = requiredDefinitions.stream().collect(Collectors.toMap(EdgeDefinition::getCollection, ed -> ed)); + Map eds = requiredDefinitions.stream().collect(Collectors.toMap(EdgeDefinition::getCollection, ed -> ed)); Iterator it = graphEdgeDefinitions.iterator(); while (it.hasNext()) { - EdgeDefinition existing = it.next(); - if (eds.containsKey(existing.getCollection())) { - EdgeDefinition requiredEdgeDefinition = eds.remove(existing.getCollection()); - HashSet existingSet = new HashSet(existing.getFrom()); - HashSet requiredSet = new HashSet(requiredEdgeDefinition.getFrom()); - if (!existingSet.equals(requiredSet)) { - throw new ArangoDBGraphException(String.format("The from collections dont match for edge definition %s", existing.getCollection())); - } - existingSet.clear(); - existingSet.addAll(existing.getTo()); - requiredSet.clear(); - requiredSet.addAll(requiredEdgeDefinition.getTo()); - if (!existingSet.equals(requiredSet)) { - throw new ArangoDBGraphException(String.format("The to collections dont match for edge definition %s", existing.getCollection())); - } - } else { - throw new ArangoDBGraphException(String.format("The graph has a surplus edge definition %s", edgeDefinitionString(existing))); - } + EdgeDefinition existing = it.next(); + if (eds.containsKey(existing.getCollection())) { + EdgeDefinition requiredEdgeDefinition = eds.remove(existing.getCollection()); + HashSet existingSet = new HashSet(existing.getFrom()); + HashSet requiredSet = new HashSet(requiredEdgeDefinition.getFrom()); + if (!existingSet.equals(requiredSet)) { + throw new ArangoDBGraphException(String.format("The from collections dont match for edge definition %s", existing.getCollection())); + } + existingSet.clear(); + existingSet.addAll(existing.getTo()); + requiredSet.clear(); + requiredSet.addAll(requiredEdgeDefinition.getTo()); + if (!existingSet.equals(requiredSet)) { + throw new ArangoDBGraphException(String.format("The to collections dont match for edge definition %s", existing.getCollection())); + } + } else { + throw new ArangoDBGraphException(String.format("The graph has a surplus edge definition %s", edgeDefinitionString(existing))); + } + } + + } + + private static void checkGraphVertexCollections(List verticesCollectionNames, ArangoGraph graph, GraphCreateOptions options) { + List allVertexCollections = new ArrayList<>(verticesCollectionNames); + final Collection orphanCollections = options.getOrphanCollections(); + if (orphanCollections != null) { + allVertexCollections.addAll(orphanCollections); } + if (!graph.getVertexCollections().containsAll(allVertexCollections)) { + Set avc = new HashSet<>(allVertexCollections); + avc.removeAll(graph.getVertexCollections()); + throw new ArangoDBGraphException("Not all declared vertex names appear in the graph. Missing " + avc); + } + } + + /** + * Get a string representation of the Edge definition that complies with the configuration options. + * + * @param ed the Edge definition + * @return the string that represents the edge definition + */ - } - - private static void checkGraphVertexCollections(List verticesCollectionNames, ArangoGraph graph, GraphCreateOptions options) { - List allVertexCollections = new ArrayList<>(verticesCollectionNames); - final Collection orphanCollections = options.getOrphanCollections(); - if (orphanCollections != null) { - allVertexCollections.addAll(orphanCollections); - } - if (!graph.getVertexCollections().containsAll(allVertexCollections)) { - Set avc = new HashSet<>(allVertexCollections); - avc.removeAll(graph.getVertexCollections()); - throw new ArangoDBGraphException("Not all declared vertex names appear in the graph. Missing " + avc); - } - } - - /** - * Get a string representation of the Edge definition that complies with the configuration options. - * - * @param ed the Edge definition - * @return the string that represents the edge definition - */ - - public static String edgeDefinitionString(EdgeDefinition ed) { - return String.format("[%s]: %s->%s", ed.getCollection(), ed.getFrom(), ed.getTo()); - } + public static String edgeDefinitionString(EdgeDefinition ed) { + return String.format("[%s]: %s->%s", ed.getCollection(), ed.getFrom(), ed.getTo()); + } /** * Create the EdgeDefinition for the graph properties. * - * @param graph The graph + * @param graph The graph * @param vertexCollections the vertex collections - * @param edgeCollections the edge collections + * @param edgeCollections the edge collections * @return the edge definition */ - + public static EdgeDefinition createPropertyEdgeDefinitions( - final ArangoDBGraph graph, - final List vertexCollections, - final List edgeCollections) { + final ArangoDBGraph graph, + final List vertexCollections, + final List edgeCollections) { final List from = new ArrayList<>(vertexCollections); from.addAll(edgeCollections); from.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); @@ -381,208 +391,233 @@ public static EdgeDefinition createPropertyEdgeDefinitions( } /** - * Creates an Arango DB vertex property. + * Gets the correct primitive. * - * @param the generic type - * @param id the id - * @param key the name - * @param value the value - * @param vertex the vertex - * @return the created Arango DB vertex property + * @param value the value + * @param valueClass the exoected class of the value + * @param the value type + * @return the correct Java primitive */ - -// public static TinkerVertexProperty createArangoDBVertexProperty(String id, String key, U value, ArangoDBVertex vertex) { -// TinkerVertexProperty p; -// p = new TinkerVertexProperty<>(id, key, value, vertex); -// insertElementAndProperty(vertex, p); -// return p; -// } + + @SuppressWarnings("unchecked") + public static Object getCorretctPrimitive(V value, String valueClass) { + + switch (valueClass) { + case "java.lang.Float": { + if (value instanceof Double) { + return ((Double) value).floatValue(); + } else if (value instanceof Long) { + return ((Long) value).floatValue(); + } else if (value instanceof Integer) { + return ((Integer) value).floatValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Double": { + if (value instanceof Double) { + return value; + } else if (value instanceof Long) { + return ((Long) value).doubleValue(); + } else if (value instanceof Integer) { + return ((Integer) value).doubleValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Long": { + if (value instanceof Long) { + return value; + } else if (value instanceof Double) { + return ((Double) value).longValue(); + } else if (value instanceof Integer) { + return ((Integer) value).longValue(); + } else { + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + break; + } + case "java.lang.Integer": { + if (value instanceof Long) { + return ((Long) value).intValue(); + } + break; + } + case "java.lang.String": + case "java.lang.Boolean": + case "": + return value; + case "java.util.HashMap": + //logger.debug(((Map)value).keySet().stream().map(Object::getClass).collect(Collectors.toList())); + //logger.debug("Add conversion for map values to " + valueClass); + // Maps are handled by ArangoOK, but we have an extra field, remove it + Map valueMap = (Map) value; + for (String key : valueMap.keySet()) { + if (key.startsWith("_")) { + valueMap.remove(key); + } + // We might need to check individual values... + } + break; + case "java.util.ArrayList": + // Should we save the type per item? + List list = new ArrayList<>(); + ((ArrayList) value).forEach(e -> list.add(getCorretctPrimitive(e, ""))); + return list; + case "boolean[]": + if (value instanceof List) { + List barray = (List) value; + boolean[] br = new boolean[barray.size()]; + IntStream.range(0, barray.size()) + .forEach(i -> br[i] = (boolean) barray.get(i)); + return br; + } else { + return value; + } + case "double[]": + if (value instanceof List) { + List darray = (List) value; + double[] dr = new double[darray.size()]; + IntStream.range(0, darray.size()) + .forEach(i -> dr[i] = (double) getCorretctPrimitive(darray.get(i), "java.lang.Double")); + return dr; + } else { + return value; + } + case "float[]": + if (value instanceof List) { + List farray = (List) value; + float[] fr = new float[farray.size()]; + IntStream.range(0, farray.size()) + .forEach(i -> fr[i] = (float) getCorretctPrimitive(farray.get(i), "java.lang.Float")); + return fr; + } else { + return value; + } + case "int[]": + if (value instanceof List) { + List iarray = (List) value; + int[] ir = new int[iarray.size()]; + IntStream.range(0, iarray.size()) + .forEach(i -> ir[i] = (int) getCorretctPrimitive(iarray.get(i), "java.lang.Integer")); + return ir; + } else { + return value; + } + case "long[]": + if (value instanceof List) { + List larray = (List) value; + long[] lr = new long[larray.size()]; + IntStream.range(0, larray.size()) + .forEach(i -> lr[i] = (long) getCorretctPrimitive(larray.get(i), "java.lang.Long")); + return lr; + } else { + return value; + } + case "java.lang.String[]": + if (value instanceof List) { + List sarray = (List) value; + String[] sr = new String[sarray.size()]; + IntStream.range(0, sarray.size()) + .forEach(i -> sr[i] = (String) sarray.get(i)); + return sr; + } else { + return value; + } + default: + Object result; + try { + result = mapper.convertValue(value, Class.forName(valueClass)); + return result; + } catch (IllegalArgumentException | ClassNotFoundException e1) { + logger.warn("Type not deserializable", e1); + } + logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); + } + return value; + } /** - * Gets the correct primitive. + * Translate a Gremlin direction to Arango direction * - * @param value the value - * @param valueClass the exoected class of the value - * @param the value type - * @return the correct Java primitive + * @param direction the direction to translate + * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction */ - - @SuppressWarnings("unchecked") - public static Object getCorretctPrimitive(V value, String valueClass) { - - switch(valueClass) { - case "java.lang.Float": - { - if (value instanceof Double) { - return ((Double) value).floatValue(); - } - else if (value instanceof Long) { - return ((Long) value).floatValue(); - } - else if (value instanceof Integer) { - return ((Integer) value).floatValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Double": - { - if (value instanceof Double) { - return value; - } - else if (value instanceof Long) { - return ((Long) value).doubleValue(); - } - else if (value instanceof Integer) { - return ((Integer) value).doubleValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Long": - { - if (value instanceof Long) { - return value; - } - else if (value instanceof Double) { - return ((Double)value).longValue(); - } - else if (value instanceof Integer) { - return ((Integer)value).longValue(); - } - else { - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - break; - } - case "java.lang.Integer": - { - if (value instanceof Long) { - return ((Long) value).intValue(); - } - break; - } - case "java.lang.String": - case "java.lang.Boolean": - case "": - return value; - case "java.util.HashMap": - //logger.debug(((Map)value).keySet().stream().map(Object::getClass).collect(Collectors.toList())); - //logger.debug("Add conversion for map values to " + valueClass); - // Maps are handled by ArangoOK, but we have an extra field, remove it - Map valueMap = (Map)value; - for (String key : valueMap.keySet()) { - if (key.startsWith("_")) { - valueMap.remove(key); - } - // We might need to check individual values... - } - break; - case "java.util.ArrayList": - // Should we save the type per item? - List list = new ArrayList<>(); - ((ArrayList)value).forEach(e -> list.add(getCorretctPrimitive(e, ""))); - return list; - case "boolean[]": - if(value instanceof List) { - List barray = (List)value; - boolean[] br = new boolean[barray.size()]; - IntStream.range(0, barray.size()) - .forEach(i -> br[i] = (boolean) barray.get(i)); - return br; - } else { - return value; - } - case "double[]": - if(value instanceof List) { - List darray = (List)value; - double[] dr = new double[darray.size()]; - IntStream.range(0, darray.size()) - .forEach(i -> dr[i] = (double) getCorretctPrimitive(darray.get(i), "java.lang.Double")); - return dr; - } else { - return value; - } - case "float[]": - if(value instanceof List) { - List farray = (List)value; - float[] fr = new float[farray.size()]; - IntStream.range(0, farray.size()) - .forEach(i -> fr[i] = (float) getCorretctPrimitive(farray.get(i), "java.lang.Float")); - return fr; - } else { - return value; - } - case "int[]": - if(value instanceof List) { - List iarray = (List)value; - int[] ir = new int[iarray.size()]; - IntStream.range(0, iarray.size()) - .forEach(i -> ir[i] = (int) getCorretctPrimitive(iarray.get(i), "java.lang.Integer")); - return ir; - } else { - return value; - } - case "long[]": - if(value instanceof List) { - List larray = (List)value; - long[] lr = new long[larray.size()]; - IntStream.range(0, larray.size()) - .forEach(i -> lr[i] = (long) getCorretctPrimitive(larray.get(i), "java.lang.Long")); - return lr; - } else { - return value; - } - case "java.lang.String[]": - if(value instanceof List) { - List sarray = (List)value; - String[] sr = new String[sarray.size()]; - IntStream.range(0, sarray.size()) - .forEach(i -> sr[i] = (String) sarray.get(i)); - return sr; - } else { - return value; - } - default: - Object result; - try { - result = mapper.convertValue(value, Class.forName(valueClass)); - return result; - } catch (IllegalArgumentException | ClassNotFoundException e1) { - logger.warn("Type not deserializable", e1); - } - logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); - } - return value; + public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirection(final Direction direction) { + switch (direction) { + case BOTH: + return ArangoDBQueryBuilder.Direction.ALL; + case IN: + return ArangoDBQueryBuilder.Direction.IN; + case OUT: + return ArangoDBQueryBuilder.Direction.OUT; + } + throw new IllegalArgumentException("Unsupported direction: " + direction); + } + + public static String extractKey(final String id) { + if (id == null) { + return null; + } + int separator = id.indexOf('/'); + if (separator > 0) { + return id.substring(separator + 1); + } else { + return id; + } + } + + public static String extractCollection(final String id) { + if (id == null) { + return null; + } + int separator = id.indexOf('/'); + if (separator > 0) { + return id.substring(0, separator); + } else { + return null; + } + } + + public static Optional extractLabel(final String id, final String label) { + String col = extractCollection(id); + if (col != null) { + if (label != null && !label.equals(col)) { + throw new IllegalArgumentException("Invalid label: [" + label + "] for id: [" + id + "]"); + } + return Optional.of(col); + } + return Optional.ofNullable(label); } - /** - * Translate a Gremlin direction to Arango direction - * @param direction the direction to translate - * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction - */ - public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirection(final Direction direction) { - switch (direction) { - case BOTH: - return ArangoDBQueryBuilder.Direction.ALL; - case IN: - return ArangoDBQueryBuilder.Direction.IN; - case OUT: - return ArangoDBQueryBuilder.Direction.OUT; - } - return null; - } - - public static IllegalStateException elementAlreadyRemoved(final Class clazz, final Object id) { - return new IllegalStateException(String.format("%s with id %s was removed.", clazz.getSimpleName(), id)); - } - - public static IllegalStateException unsupportedIdType(final Object id) { - return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); - } + public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { + Optional optionalId = ElementHelper.getIdValue(keyValues); + if (!optionalId.isPresent()) { + return null; + } + Object id = optionalId.get(); + if (features.willAllowId(id)) { + if (id.toString().contains("/")) { + String fullId = id.toString(); + String[] parts = fullId.split("/"); + // The collection name is the last part of the full name + String[] collectionParts = parts[0].split("_"); + String collectionName = collectionParts[collectionParts.length - 1]; + if (collectionName.contains(label)) { + id = parts[1]; + } + } + Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String) id); + if (m.matches()) { + return id.toString(); + } else { + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); + } + } else { + throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + } + } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java index 0a9ff28..2533967 100644 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java @@ -5,6 +5,7 @@ import java.util.Properties; import java.util.Set; +import com.arangodb.tinkerpop.gremlin.persistence.*; import com.arangodb.tinkerpop.gremlin.structure.*; import org.apache.commons.configuration2.Configuration; import org.apache.commons.configuration2.ConfigurationConverter; @@ -22,22 +23,22 @@ * The Class ArangoDBGraphProvider. This provider assumes that there is a local ArangoDB running (i.e. * http://127.0.0.1:8529) with a tinkerpop database and a gremlin user that has Administrate permissions * on the db. - * */ public class ArangoDBGraphProvider extends AbstractGraphProvider { - - /** The Constant IMPLEMENTATIONS. */ - private static final Set IMPLEMENTATIONS = new HashSet() {{ - add(ArangoDBEdge.class); - add(ArangoDBGraph.class); - add(ArangoDBGraphVariables.class); - add(ArangoDBProperty.class); - add(ArangoDBVertexPropertyData.class); - add(ArangoDBVertex.class); - add(ArangoDBVertexProperty.class); + + /** + * The Constant IMPLEMENTATIONS. + */ + private static final Set IMPLEMENTATIONS = new HashSet() {{ + add(PersistentData.class); + add(AdbValue.class); + add(EdgeData.class); + add(PropertyData.class); + add(VertexData.class); + add(VertexPropertyData.class); }}; - - + + @Override public Configuration newGraphConfiguration(final String graphName, final Class test, final String testMethodName, @@ -52,258 +53,240 @@ public Configuration newGraphConfiguration(final String graphName, final Class conf.setProperty(e.getKey(), e.getValue())); return conf; } - - private Configuration getConfiguration( - String graphName, - Class test, - String testMethodName, - GraphData loadGraphWith) { - ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() - .arangoHosts("127.0.0.1:8529") - .arangoUser("root") - .arangoPassword("test") - .graph(graphName); - if (loadGraphWith != null) { - switch(loadGraphWith) { - case CLASSIC: - System.out.println("CLASSIC"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "vertex", "vertex"); - builder.configureEdge("created", "vertex", "vertex"); - break; - case CREW: - System.out.println("CREW"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("uses"); - builder.withEdgeCollection("develops"); - builder.withEdgeCollection("traverses"); - builder.configureEdge("uses", "person", "software"); - builder.configureEdge("develops", "person", "software"); - builder.configureEdge("traverses", "software", "software"); - break; - case GRATEFUL: - System.out.println("GRATEFUL"); - break; - case MODERN: - System.out.println("MODERN"); - builder.withVertexCollection("dog"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("created", "person", "software"); - break; - default: - System.out.println("default"); - break; - } - } - else { - if (testMethodName.startsWith("shouldProcessVerticesEdges") - || testMethodName.startsWith("shouldGenerate") - || testMethodName.startsWith("shouldSetValueOnEdge") - || testMethodName.startsWith("shouldAutotype")) { - builder.withEdgeCollection("knows"); - } - else if(testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { - builder.withEdgeCollection("self"); - } - else if(testMethodName.startsWith("shouldSupportUserSuppliedIds")) { - builder.withEdgeCollection("test"); - } - else if(testMethodName.startsWith("shouldSupportUUID")) { - builder.withEdgeCollection("friend"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithINEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges")) { - builder.withEdgeCollection("friends"); - } - else if(testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } - else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldReadWriteEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } - else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { - builder.withEdgeCollection("self"); - } - else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { - builder.withEdgeCollection("self"); - } - else { - // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? - switch (testMethodName) { - case "shouldGetPropertyKeysOnEdge": - case "shouldNotGetConcurrentModificationException": - builder.withEdgeCollection("friend"); - builder.withEdgeCollection("knows"); - break; - case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": - case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": - builder.withEdgeCollection("hate"); - builder.withEdgeCollection("friend"); - break; - case "shouldPersistDataOnClose": - builder.withEdgeCollection("collaborator"); - break; - case "shouldTestTreeConnectivity": - builder.withEdgeCollection("test1"); - builder.withEdgeCollection("test2"); - builder.withEdgeCollection("test3"); - break; - case "shouldEvaluateConnectivityPatterns": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("knows"); - break; - case "shouldRemoveEdgesWithoutConcurrentModificationException": - builder.withEdgeCollection("link"); - break; - case "shouldGetValueThatIsNotPresentOnEdge": - case "shouldHaveStandardStringRepresentationForEdgeProperty": - case "shouldHaveTruncatedStringRepresentationForEdgeProperty": - case "shouldValidateIdEquality": - case "shouldValidateEquality": - case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": - case "shouldAddEdgeWithUserSuppliedStringId": - case "shouldAllowNullAddEdge": - builder.withEdgeCollection("self"); - break; - case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": - case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": - case "shouldProcessEdges": - case "shouldReturnOutThenInOnVertexIterator": - case "shouldReturnEmptyIteratorIfNoProperties": - builder.withEdgeCollection("knows"); - break; - case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("pets"); - builder.withEdgeCollection("walks"); - builder.withEdgeCollection("livesWith"); - break; - case "shouldHaveStandardStringRepresentation": - builder.withEdgeCollection("friends"); - break; - case "shouldReadWriteSelfLoopingEdges": - builder.withEdgeCollection("CONTROL"); - builder.withEdgeCollection("SELFLOOP"); - break; - case "shouldReadGraphML": - case "shouldReadGraphMLUnorderedElements": - case "shouldTransformGraphMLV2ToV3ViaXSLT": - case "shouldReadLegacyGraphSON": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - break; - case "shouldAddVertexWithLabel": - case "shouldAllowNullAddVertexProperty": - builder.withVertexCollection("person"); - break; - case "shouldNotAllowSetProperty": - case "shouldHashAndEqualCorrectly": - case "shouldNotAllowRemove": - case "shouldNotConstructNewWithSomethingAlreadyDetached": - case "shouldNotConstructNewWithSomethingAlreadyReferenced": - builder.withEdgeCollection("test"); - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertex": - builder.withEdgeCollection("tonothing"); - break; - case "shouldHandleSelfLoops": - builder.withVertexCollection("person"); - builder.withEdgeCollection("self"); - break; - case "shouldAttachWithCreateMethod": - case "testAttachableCreateMethod": - builder.withVertexCollection("person"); - builder.withVertexCollection("project"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("developedBy"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("developedBy", "project", "person"); - break; - case "shouldConstructReferenceVertex": - builder.withVertexCollection("blah"); - break; - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": - if (VertexTest.class.equals(test.getEnclosingClass())) { - builder.withVertexCollection("foo"); - } - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": - builder.withVertexCollection("foo"); - break; - case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": - builder.withEdgeCollection("created"); - builder.withEdgeCollection("knows"); - break; - default: - System.out.println("case \"" + testMethodName + "\":"); - } - } - } - return builder.build(); - } - @Override - public void clear(Graph graph, Configuration configuration) throws Exception { - ArangoDBGraphClient client; - if (graph ==null) { - Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 0, true); - client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); - } - else { - ArangoDBGraph agraph = (ArangoDBGraph) graph; - client = agraph.getClient(); - client.clear(agraph); - agraph.close(); - } - - } + private Configuration getConfiguration( + String graphName, + Class test, + String testMethodName, + GraphData loadGraphWith) { + ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() + .arangoHosts("127.0.0.1:8529") + .arangoUser("root") + .arangoPassword("test") + .graph(graphName); + if (loadGraphWith != null) { + switch (loadGraphWith) { + case CLASSIC: + System.out.println("CLASSIC"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "vertex", "vertex"); + builder.configureEdge("created", "vertex", "vertex"); + break; + case CREW: + System.out.println("CREW"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("uses"); + builder.withEdgeCollection("develops"); + builder.withEdgeCollection("traverses"); + builder.configureEdge("uses", "person", "software"); + builder.configureEdge("develops", "person", "software"); + builder.configureEdge("traverses", "software", "software"); + break; + case GRATEFUL: + System.out.println("GRATEFUL"); + break; + case MODERN: + System.out.println("MODERN"); + builder.withVertexCollection("dog"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + break; + default: + System.out.println("default"); + break; + } + } else { + if (testMethodName.startsWith("shouldProcessVerticesEdges") + || testMethodName.startsWith("shouldGenerate") + || testMethodName.startsWith("shouldSetValueOnEdge") + || testMethodName.startsWith("shouldAutotype")) { + builder.withEdgeCollection("knows"); + } else if (testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldSupportUserSuppliedIds")) { + builder.withEdgeCollection("test"); + } else if (testMethodName.startsWith("shouldSupportUUID")) { + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteVertexWithINEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteVertexNoEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { + builder.withEdgeCollection("friends"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { + builder.withEdgeCollection("self"); + } else { + // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? + switch (testMethodName) { + case "shouldGetPropertyKeysOnEdge": + case "shouldNotGetConcurrentModificationException": + builder.withEdgeCollection("friend"); + builder.withEdgeCollection("knows"); + break; + case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": + case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": + builder.withEdgeCollection("hate"); + builder.withEdgeCollection("friend"); + break; + case "shouldPersistDataOnClose": + builder.withEdgeCollection("collaborator"); + break; + case "shouldTestTreeConnectivity": + builder.withEdgeCollection("test1"); + builder.withEdgeCollection("test2"); + builder.withEdgeCollection("test3"); + break; + case "shouldEvaluateConnectivityPatterns": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("knows"); + break; + case "shouldRemoveEdgesWithoutConcurrentModificationException": + builder.withEdgeCollection("link"); + break; + case "shouldGetValueThatIsNotPresentOnEdge": + case "shouldHaveStandardStringRepresentationForEdgeProperty": + case "shouldHaveTruncatedStringRepresentationForEdgeProperty": + case "shouldValidateIdEquality": + case "shouldValidateEquality": + case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": + case "shouldAddEdgeWithUserSuppliedStringId": + case "shouldAllowNullAddEdge": + builder.withEdgeCollection("self"); + break; + case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": + case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": + case "shouldProcessEdges": + case "shouldReturnOutThenInOnVertexIterator": + case "shouldReturnEmptyIteratorIfNoProperties": + builder.withEdgeCollection("knows"); + break; + case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("pets"); + builder.withEdgeCollection("walks"); + builder.withEdgeCollection("livesWith"); + break; + case "shouldHaveStandardStringRepresentation": + builder.withEdgeCollection("friends"); + break; + case "shouldReadWriteSelfLoopingEdges": + builder.withEdgeCollection("CONTROL"); + builder.withEdgeCollection("SELFLOOP"); + break; + case "shouldReadGraphML": + case "shouldReadGraphMLUnorderedElements": + case "shouldTransformGraphMLV2ToV3ViaXSLT": + case "shouldReadLegacyGraphSON": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + break; + case "shouldAddVertexWithLabel": + case "shouldAllowNullAddVertexProperty": + builder.withVertexCollection("person"); + break; + case "shouldNotAllowSetProperty": + case "shouldHashAndEqualCorrectly": + case "shouldNotAllowRemove": + case "shouldNotConstructNewWithSomethingAlreadyDetached": + case "shouldNotConstructNewWithSomethingAlreadyReferenced": + builder.withEdgeCollection("test"); + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertex": + builder.withEdgeCollection("tonothing"); + break; + case "shouldHandleSelfLoops": + builder.withVertexCollection("person"); + builder.withEdgeCollection("self"); + break; + case "shouldAttachWithCreateMethod": + case "testAttachableCreateMethod": + builder.withVertexCollection("person"); + builder.withVertexCollection("project"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("developedBy"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("developedBy", "project", "person"); + break; + case "shouldConstructReferenceVertex": + builder.withVertexCollection("blah"); + break; + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": + if (VertexTest.class.equals(test.getEnclosingClass())) { + builder.withVertexCollection("foo"); + } + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": + builder.withVertexCollection("foo"); + break; + case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": + builder.withEdgeCollection("created"); + builder.withEdgeCollection("knows"); + break; + default: + System.out.println("case \"" + testMethodName + "\":"); + } + } + } + return builder.build(); + } + + @Override + public void clear(Graph graph, Configuration configuration) throws Exception { + ArangoDBGraphClient client; + if (graph == null) { + Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 0, true); + client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); + } else { + ArangoDBGraph agraph = (ArangoDBGraph) graph; + client = agraph.getClient(); + client.clear(agraph); + agraph.close(); + } + + } - @Override - public Set getImplementations() { - return IMPLEMENTATIONS; - } + @Override + public Set getImplementations() { + return IMPLEMENTATIONS; + } - @Override - public Map getBaseConfiguration(String graphName, Class test, String testMethodName, - GraphData loadGraphWith) { - // TODO Auto-generated method stub - return null; - } + @Override + public Map getBaseConfiguration(String graphName, Class test, String testMethodName, + GraphData loadGraphWith) { + // TODO Auto-generated method stub + return null; + } @Override public Object convertId(Object id, Class c) { From dfc97ebad20151443793998b041d17c144d2863d Mon Sep 17 00:00:00 2001 From: Michele Rastelli Date: Wed, 5 Mar 2025 10:49:31 +0100 Subject: [PATCH 2/2] tests refactoring --- README.md | 11 +- jautodoc_templates.xml | 237 -------- .../gremlin/client/ArangoDBGraphClient.java | 526 ++---------------- .../gremlin/client/ArangoDBQueryBuilder.java | 297 ---------- .../gremlin/persistence/VertexData.java | 9 +- .../gremlin/structure/ArangoDBGraph.java | 323 ++++------- .../structure/ArangoDBPersistentElement.java | 1 + .../gremlin/structure/ArangoDBVertex.java | 11 +- .../tinkerpop/gremlin/utils/ArangoDBUtil.java | 44 +- .../gremlin/ArangoDBGraphProvider.java | 295 ---------- .../tinkerpop/gremlin/ArangoDBGraphTest.java | 12 - .../tinkerpop/gremlin/ArangoDBTestSuite.java | 92 --- .../tinkerpop/gremlin/BaseGremlinTest.java | 11 + .../arangodb/tinkerpop/gremlin/Issue57.java | 27 - .../arangodb/tinkerpop/gremlin/TestGraph.java | 88 +++ .../gremlin/client/test/BaseTestCase.java | 45 -- .../gremlin/client/test/ClientTest.java | 302 ---------- .../gremlin/custom/CustomGraphProvider.java | 58 ++ .../gremlin/custom/CustomStandardSuite.java | 26 + .../custom/CustomStandardSuiteTest.java | 10 + .../gremlin/custom/CustomTestGraph.java | 54 ++ .../traversal/step/OrderabilityTest.java | 509 +++++++++++++++++ .../traversal/step/map/MergeEdgeTest.java | 271 +++++++++ .../util/detached/DetachedGraphTest.java | 81 +++ .../structure/util/star/StarGraphTest.java | 115 ++++ .../gremlin/process/ProcessGraphProvider.java | 112 ++++ .../process/ProcessStandardSuiteTest.java | 12 + .../structure/ArangoDBGremlinTest.java | 10 - .../structure/StructureGraphProvider.java | 177 ++++++ .../structure/StructureStandardSuiteTest.java | 12 + .../com/arangodb/tinkerpop/gremlin/test.conf | 11 - .../gremlin/util/BaseGraphProvider.java | 156 ++++++ .../gremlin/util/TestGraphClient.java | 40 ++ src/test/resources/arangodb.properties | 3 - tests/travis/setup_arangodb.sh | 54 -- 35 files changed, 1945 insertions(+), 2097 deletions(-) delete mode 100644 jautodoc_templates.xml delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java delete mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/test.conf create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java create mode 100644 src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java delete mode 100644 src/test/resources/arangodb.properties delete mode 100644 tests/travis/setup_arangodb.sh diff --git a/README.md b/README.md index 3361132..3bb0958 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ An implementation of the [Apache TinkerPop OLTP Provider](https://tinkerpop.apac ## Compatibility This Provider supports: -* Apache TinkerPop 3.3 +* Apache TinkerPop 3.7 * ArangoDB 3.11+ (via ArangoDB Java Driver 7.17.0). ## ArangoDB @@ -101,21 +101,24 @@ graph.close(); ## A note on element IDs The provider implementation supports user supplied IDs, i.e. provide an id property for graph -elements, but currently we only support String ids, that is: +elements, but we only support String ids, that is: ``` Vertex v1 = g.addV("person").property(T.id, "1"); ``` -will create a vertex with id "1". However, implementation wise, in ArangoDB we are only allowed to manipulate the documents `name`, not its `id`. For this reason, providing a TinkerPop vertex id (`T.id`) actually sets the vertex's ArangoDB `name`. As a result, retrieving the vertex by the given id will fail: +will create a vertex with id "1". However, implementation wise, in ArangoDB we are only allowed to manipulate the +documents `name`, not its `id`. For this reason, providing a TinkerPop vertex id (`T.id`) actually sets the vertex's +ArangoDB `name`. As a result, retrieving the vertex by the given id will fail: ``` Vertex v2 = g.V("1"); assert v2 == null; ``` -Since we know that documents IDs are created by concatenating (with a slash) the document's collection and its name, then we can find the vertex like so: +Since we know that documents IDs are created by concatenating (with a slash) the document's collection and its name, +then we can find the vertex like so: ``` Vertex v2 = g.V("person/1"); diff --git a/jautodoc_templates.xml b/jautodoc_templates.xml deleted file mode 100644 index ae8de96..0000000 --- a/jautodoc_templates.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java index e3c208b..5d0dbc5 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphClient.java @@ -13,6 +13,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.arangodb.*; import com.arangodb.config.ArangoConfigProperties; import com.arangodb.entity.*; import com.arangodb.model.*; @@ -21,17 +22,12 @@ import com.arangodb.tinkerpop.gremlin.persistence.EdgeData; import com.arangodb.tinkerpop.gremlin.persistence.VertexData; import com.arangodb.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Graph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.arangodb.ArangoCollection; -import com.arangodb.ArangoCursor; -import com.arangodb.ArangoDB; -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabase; -import com.arangodb.ArangoGraph; import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.getArangoDirectionFromGremlinDirection; @@ -57,7 +53,9 @@ public class ArangoDBGraphClient { public static class ArangoDBExceptions { - /** The error code regex. Matches response messages from the ArangoDB client */ + /** + * The error code regex. Matches response messages from the ArangoDB client + */ public static Pattern ERROR_CODE = Pattern.compile("^Response:\\s\\d+,\\sError:\\s(\\d+)\\s-\\s([a-z\\s]+).+"); @@ -74,14 +72,20 @@ private ArangoDBExceptions() { * @param ex the ex * @return The ArangoDBClientException */ - + // FIXME: match errors on code and error num instead of pattern matching on message string public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) { + if (ex.getCause() instanceof InterruptedException) { + TraversalInterruptedException ie = new TraversalInterruptedException(); + ie.initCause(ex); + throw ie; + } + String errorMessage = ex.getMessage(); Matcher m = ERROR_CODE.matcher(errorMessage); if (m.matches()) { int code = Integer.parseInt(m.group(1)); String msg = m.group(2); - switch ((int) code / 100) { + switch (code / 100) { case 10: // Internal ArangoDB storage errors return new ArangoDBGraphException(code, String.format("Internal ArangoDB storage error (%s): %s", code, msg), ex); case 11: @@ -101,14 +105,16 @@ public static ArangoDBGraphException getArangoDBException(ArangoDBException ex) return new ArangoDBGraphException("General ArangoDB error (unkown error code)", ex); } - /** "name to long" Message. */ + /** + * "name to long" Message. + */ public static String NAME_TO_LONG = "Name is too long: {} bytes (max 64 bytes for labels, 256 for keys)"; /** * Gets the naming convention error. * - * @param cause the cause + * @param cause the cause * @param details the details * @return the naming convention error */ @@ -122,84 +128,29 @@ public static ArangoDBGraphException getNamingConventionError(String cause, Stri private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraphClient.class); - private final ArangoDB driver; - - private final ArangoDatabase db; - - private final int batchSize; + protected final ArangoDatabase db; private final ArangoDBGraph graph; /** - * Create a simple graph client and connect to the provided db. If the DB does not exist, the driver will try to - * create one. - * - * @param graph the ArangoDB graph that uses this client - * @param properties the ArangoDB configuration properties - * @param dbname the ArangoDB name to connect to or create - * @param batchSize the size of the batch mode chunks - * @throws ArangoDBGraphException If the db does not exist and cannot be created - */ - - public ArangoDBGraphClient( - 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 + * Create a simple graph client and connect to the provided db. * - * @param graph the ArangoDB graph that uses this client - * @param properties the ArangoDB configuration properties - * @param dbname the ArangoDB name to connect to or create - * @param batchSize the size of the batch mode chunks - * @param createDatabase if true, the driver will attempt to crate the DB if it does not exist - * @throws ArangoDBGraphException If the db does not exist and cannot be created + * @param graph the ArangoDB graph that uses this client + * @param properties the ArangoDB configuration properties + * @param dbname the ArangoDB name to connect to or create + * @throws ArangoDBGraphException If the db does not exist and cannot be created */ - public ArangoDBGraphClient( ArangoDBGraph graph, Properties properties, - String dbname, - int batchSize, - boolean createDatabase) + String dbname) throws ArangoDBGraphException { logger.debug("Initiating the ArangoDb Client"); this.graph = graph; - driver = new ArangoDB.Builder() + db = new ArangoDB.Builder() .loadProperties(ArangoConfigProperties.fromProperties(properties)) - .build(); - db = driver.db(dbname); - if (createDatabase) { - if (!db.exists()) { - logger.debug("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; + .build() + .db(dbname); } /** @@ -208,23 +159,7 @@ public ArangoDBGraphClient( 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.debug("Clear {}", graph.name()); - deleteGraph(graph.name()); + db.arango().shutdown(); } /** @@ -242,44 +177,6 @@ public String getVersion() throws ArangoDBGraphException { } } - - /** - * Gets the database. - * - * @return the ArangoDB - */ - - public ArangoDatabase getDB() { - return db; - } - - /** - * Test if the db exists. - * - * @return true if the db exists - */ - - public boolean dbExists() { - return db == null ? false : db.exists(); - } - - /** - * Delete the current database accessed by the driver. - * - * @throws ArangoDBGraphException if there was an error - */ - - public void deleteDb() throws ArangoDBGraphException { - logger.debug("Delete current db"); - if (db != null) { - try { - db.drop(); - } catch (ArangoDBException e) { - throw ArangoDBExceptions.getArangoDBException(e); - } - } - } - public ArangoDBGraphVariables getGraphVariables() { logger.debug("Get graph variables"); ArangoDBGraphVariables result; @@ -298,8 +195,9 @@ public ArangoDBGraphVariables getGraphVariables() { /** * Insert a ArangoDBBaseDocument in the graph. The document is updated with the id, rev and name * (if not * present) - * @param document the document - * @throws ArangoDBGraphException If there was an error inserting the document + * + * @param document the document + * @throws ArangoDBGraphException If there was an error inserting the document */ public void insertGraphVariables(ArangoDBGraphVariables document) { @@ -333,8 +231,9 @@ public void insertGraphVariables(ArangoDBGraphVariables document) { /** * Delete a document from the graph. - * @param document the document to delete - * @throws ArangoDBGraphException If there was an error deleting the document + * + * @param document the document to delete + * @throws ArangoDBGraphException If there was an error deleting the document */ public void deleteGraphVariables(ArangoDBGraphVariables document) { @@ -351,14 +250,14 @@ public void deleteGraphVariables(ArangoDBGraphVariables document) { /** * Update the document in the graph. - * @param document the document * - * @throws ArangoDBGraphException If there was an error updating the document + * @param document the document + * @throws ArangoDBGraphException If there was an error updating the document */ public void updateGraphVariables(ArangoDBGraphVariables document) { logger.debug("Update variables {} in {}", document, graph.name()); - DocumentUpdateEntity updateEntity; + DocumentUpdateEntity updateEntity; try { updateEntity = db.collection(document.collection()) .updateDocument(document._key(), document); @@ -373,11 +272,12 @@ public void updateGraphVariables(ArangoDBGraphVariables document) { /** * Get vertices of a graph. If no ids are provided, get all vertices. * - * @param ids the ids to match + * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphVertices(final List ids) { + // FIXME: use multi-docs API + public ArangoIterable getGraphVertices(final List ids) { logger.debug("Get all {} graph vertices, filtered by ids: {}", graph.name(), ids); List prefixedColNames = graph.vertexCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, VertexData.class); @@ -386,16 +286,17 @@ public ArangoCursor getGraphVertices(final List ids) { /** * Get edges of a graph. If no ids are provided, get all edges. * - * @param ids the ids to match + * @param ids the ids to match * @return ArangoDBBaseQuery the query object */ - public ArangoCursor getGraphEdges(List ids) { + // FIXME: use multi-docs API + public ArangoIterable getGraphEdges(List ids) { logger.debug("Get all {} graph edges, filtered by ids: {}", graph.name(), ids); List prefixedColNames = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); return getGraphDocuments(ids, prefixedColNames, EdgeData.class); } - private ArangoCursor getGraphDocuments(List ids, List prefixedColNames, Class clazz) { + private ArangoIterable getGraphDocuments(List ids, List prefixedColNames, Class clazz) { Map bindVars = new HashMap<>(); ArangoDBQueryBuilder queryBuilder = new ArangoDBQueryBuilder(); if (ids.isEmpty()) { @@ -405,7 +306,11 @@ private ArangoCursor getGraphDocuments(List ids, List pre queryBuilder.iterateCollection("d", prefixedColNames.get(0), bindVars); } } else { - queryBuilder.with(prefixedColNames, bindVars).documentsById(ids, "d", bindVars); + List prunedIds = ids.stream() + .map(graph::getPrefixedCollectioName) + .filter(it -> prefixedColNames.contains(ArangoDBUtil.extractCollection(it))) + .collect(Collectors.toList()); + queryBuilder.with(prefixedColNames, bindVars).documentsById(prunedIds, "d", bindVars); } queryBuilder.ret("d"); String query = queryBuilder.toString(); @@ -413,101 +318,14 @@ private ArangoCursor getGraphDocuments(List ids, List pre return executeAqlQuery(query, bindVars, null, clazz); } - /** - * Delete a graph from the db, and all its collections. - * - * @param name the name of the graph to delete - * @return true, if the graph was deleted - */ - - public boolean deleteGraph(String name) { - return deleteGraph(name, true); - } - - /** - * Delete a graph from the db. If dropCollection is true, then all the graph collections are also - * dropped - * - * @param name the name - * @param dropCollections true to drop the graph collections too - * @return true if the graph was deleted - */ - - public boolean deleteGraph(String name, boolean dropCollections) { - if (db != null) { - ArangoGraph graph = db.graph(name); - if (graph.exists()) { - try { - Collection edgeDefinitions = dropCollections ? graph.getEdgeDefinitions() : Collections.emptyList(); - Collection vertexCollections = dropCollections ? graph.getVertexCollections() : Collections.emptyList(); - // Drop graph first because it will break if the graph collections do not exist - graph.drop(); - for (String definitionName : edgeDefinitions) { - String collectioName = definitionName; - if (db.collection(collectioName).exists()) { - db.collection(collectioName).drop(); - } - } - for (String vc : vertexCollections) { - String collectioName = vc; - if (db.collection(collectioName).exists()) { - db.collection(collectioName).drop(); - } - } - // Delete the graph variables - db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); - } catch (ArangoDBException e) { - System.out.println(e); - } - return true; - } else { - try { - graph.drop(); - } catch (ArangoDBException e) { - //throw ArangoDBExceptions.getArangoDBException(e); - } - } - } - return false; - } - - /** - * Delete collection. - * - * @param name the name - * @return true, if successful - */ - - public boolean deleteCollection(String name) { - ArangoCollection collection = db.collection(name); - if (collection.exists()) { - collection.drop(); - return collection.exists(); - } - return false; - } - - /** - * Create a new graph. - * - * @param name the name of the new graph - * @param edgeDefinitions the edge definitions for the graph - * @throws ArangoDBGraphException If the graph can not be created - */ - - public void createGraph(String name, List edgeDefinitions) - throws ArangoDBGraphException { - this.createGraph(name, edgeDefinitions, null); - } - /** * Create a new graph. * - * @param name the name of the new graph - * @param edgeDefinitions the edge definitions for the graph - * @param options additional graph options + * @param name the name of the new graph + * @param edgeDefinitions the edge definitions for the graph + * @param options additional graph options * @return the arango graph - * @throws ArangoDBGraphException If the graph can not be created + * @throws ArangoDBGraphException If the graph can not be created */ public ArangoGraph createGraph(String name, @@ -539,13 +357,13 @@ public ArangoGraph getArangoGraph() { /** * Execute AQL query. * - * @param the generic type of the returned values - * @param query the query string - * @param bindVars the value of the bind parameters - * @param aqlQueryOptions the aql query options - * @param type the type of the result (POJO class, VPackSlice, String for Json, or Collection/List/Map) + * @param the generic type of the returned values + * @param query the query string + * @param bindVars the value of the bind parameters + * @param aqlQueryOptions the aql query options + * @param type the type of the result (POJO class, VPackSlice, String for Json, or Collection/List/Map) * @return the cursor result - * @throws ArangoDBGraphException if executing the query raised an exception + * @throws ArangoDBGraphException if executing the query raised an exception */ public ArangoCursor executeAqlQuery( @@ -563,230 +381,6 @@ public ArangoCursor executeAqlQuery( } } - // TODO Decide what of these methods should be restored. -// -// /** -// * Creates vertices (bulk import). -// * -// * @param graph The graph -// * @param vertices The list of new vertices -// * @param details True, for details -// * @return a ImportResultEntity object -// * @throws ArangoDBException if an error occurs -// */ -// public ImportResultEntity createVertices( -// ArangoDBSimpleGraph graph, -// List vertices, -// boolean details) throws ArangoDBException { -// -// List> values = new ArrayList>(); -// -// for (ArangoDBSimpleVertex v : vertices) { -// values.add(v.getProperties()); -// } -// -// try { -// return driver.importDocuments(graph.getVertexCollection(), true, values); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } -// -// /** -// * Creates edges (bulk import). -// * -// * @param graph The graph -// * @param edges The list of new edges -// * @param details True, for details -// * @return a ImportResultEntity object -// * @throws ArangoDBException if an error occurs -// */ -// public ImportResultEntity createEdges(ArangoDBSimpleGraph graph, List edges, boolean details) -// throws ArangoDBException { -// -// List> values = new ArrayList>(); -// -// for (ArangoDBSimpleEdge e : edges) { -// values.add(e.getProperties()); -// } -// -// try { -// return driver.importDocuments(graph.getEdgeCollection(), true, values); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } - -// /** -// * Create an index on collection keys. -// * -// * @param graph the simple graph -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex createVertexIndex( -// ArangoDBSimpleGraph graph, -// IndexType type, -// boolean unique, -// List fields) throws ArangoDBException { -// return createIndex(graph.getVertexCollection(), type, unique, fields); -// } -// -// /** -// * Create an index on collection keys. -// * -// * @param graph the simple graph -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex createEdgeIndex(ArangoDBSimpleGraph graph, IndexType type, boolean unique, List fields) -// throws ArangoDBException { -// return createIndex(graph.getEdgeCollection(), type, unique, fields); -// } -// -// /** -// * Get an index. -// * -// * @param id id of the index -// * @return ArangoDBIndex the index, or null if the index was not found -// * @throws ArangoDBException if creation failed -// */ -// -// public ArangoDBIndex getIndex(String id) throws ArangoDBException { -// IndexEntity index; -// try { -// index = driver.getIndex(id); -// } catch (ArangoException e) { -// -// if (e.getErrorNumber() == ErrorNums.ERROR_ARANGO_INDEX_NOT_FOUND) { -// return null; -// } -// -// throw new ArangoDBException(e); -// } -// return new ArangoDBIndex(index); -// } -// -// /** -// * Returns the indices of the vertex collection. -// * -// * @param graph The graph -// * @return List of indices -// * @throws ArangoDBException if an error occurs -// */ -// public List getVertexIndices(ArangoDBSimpleGraph graph) throws ArangoDBException { -// return getIndices(graph.getVertexCollection()); -// } -// -// /** -// * Returns the indices of the edge collection. -// * -// * @param graph The graph -// * @return List of indices -// * @throws ArangoDBException if an error occurs -// */ -// public List getEdgeIndices(ArangoDBSimpleGraph graph) throws ArangoDBException { -// return getIndices(graph.getEdgeCollection()); -// } -// -// /** -// * Deletes an index. -// * -// * @param id The identifier of the index -// * @return true, if the index was deleted -// * @throws ArangoDBException if an error occurs -// */ -// public boolean deleteIndex(String id) throws ArangoDBException { -// try { -// driver.deleteIndex(id); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// return true; -// } -// -// /** -// * Create an index on collection keys. -// * -// * @param collectionName the collection name -// * @param type the index type ("cap", "geo", "hash", "skiplist") -// * @param unique true for a unique name -// * @param fields a list of name fields -// * @return ArangoDBIndex the index -// * @throws ArangoDBException if creation failed -// */ -// -// private ArangoDBIndex createIndex(String collectionName, IndexType type, boolean unique, List fields) -// throws ArangoDBException { -// -// IndexEntity indexEntity; -// try { -// indexEntity = driver.createIndex(collectionName, type, unique, fields.toArray(new String[0])); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// return new ArangoDBIndex(indexEntity); -// } -// -// /** -// * Get the List of indices of a collection. -// * -// * @param collectionName the collection name -// * @return Vector List of indices -// * @throws ArangoDBException if creation failed -// */ -// -// private List getIndices(String collectionName) throws ArangoDBException { -// List indices = new ArrayList(); -// -// IndexesEntity indexes; -// try { -// indexes = driver.getIndexes(collectionName); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// -// for (IndexEntity indexEntity : indexes.getIndexes()) { -// indices.add(new ArangoDBIndex(indexEntity)); -// } -// -// return indices; -// } -// -// /** -// * Returns the current connection configuration. -// * -// * @param collectionName the collection name -// * @return the configuration -// * @throws ArangoDBException the arango DB exception -// */ - - //// public ArangoDBConfiguration getConfiguration() { - //// return configuration; - //// } -// -// /** -// * Truncates a collection -// * -// * @param collectionName -// */ -// public void truncateCollection(String collectionName) throws ArangoDBException { -// try { -// driver.truncateCollection(collectionName); -// } catch (ArangoException e) { -// throw new ArangoDBException(e); -// } -// } public void insertEdge(ArangoDBEdge edge) { logger.debug("Insert edge {} in {} ", edge, graph.name()); EdgeEntity insertEntity; @@ -903,7 +497,7 @@ public Iterator getVertexNeighbors(String vertexId, List edg bindVars.put("graph", graph.name()); bindVars.put("edgeCollections", edgeCollections); String query = "FOR v IN 1..1 " + getArangoDirectionFromGremlinDirection(direction).getAqlName() + - " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections, uniqueVertices: 'global', order: 'bfs' } RETURN v"; + " @start GRAPH @graph OPTIONS { edgeCollections: @edgeCollections, order: 'bfs' } RETURN v"; return executeAqlQuery(query, bindVars, null, VertexData.class); } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java index eea7b66..f0f4fb2 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java @@ -8,13 +8,9 @@ package com.arangodb.tinkerpop.gremlin.client; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,83 +70,6 @@ String getAqlName() { } - /** - * Options for vertices in Graph Traversals. - * - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - */ - - enum UniqueVertices { - - /** It is guaranteed that there is no path returned with a duplicate vertex. */ - PATH("path"), - - /** it is guaranteed that each vertex is visited at most once during the traversal, no - * matter how many paths lead from the start vertex to this one. */ - GLOBAL("global"), - - /** No uniqueness check is applied on vertices - (default). */ - NONE("none"); - - /** The aql name. */ - private final String aqlName; - - /** - * Instantiates a new unique vertices. - * - * @param aqlName the aql name - */ - UniqueVertices(String aqlName) { - this.aqlName = aqlName; - } - - /** - * Gets the aql name. - * - * @return the aql name - */ - String getAqlName() { - return aqlName; - } - } - - /** - * Options for edges in Graph Traversals. - * - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - */ - - enum UniqueEdges { - - /** It is guaranteed that there is no path returned with a duplicate edge - (default). */ - PATH("path"), - - /** No uniqueness check is applied on edges. Note: Using this configuration the - * traversal will follow cycles in edges. */ - NONE("none"); - - /** The aql name. */ - private final String aqlName; - - /** - * Instantiates a new unique edges. - * - * @param aqlName the aql name - */ - UniqueEdges(String aqlName) { - this.aqlName = aqlName; - } - - /** - * Gets the aql name. - * - * @return the aql name - */ - String getAqlName() { - return aqlName; - } - } - /** * Create a new QueryBuilder with config of whether Collection Names should be prefixed with Graph name or not. */ @@ -206,26 +125,6 @@ public ArangoDBQueryBuilder documentsById( return this; } - /** - * Append a Document statement to find a single element in the graph. This segment should be - * used in conjunction with the {@link #with(List, Map)} segment. - * - * @param id the id to look for - * @param loopVariable the loop variable name - * @param bindVars the the map of bind parameters - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder documentById( - String id, - String loopVariable, - Map bindVars) { - queryBuilder.append(String.format("LET %s = Document(@id)\n", loopVariable)); - bindVars.put("id", id); - logger.debug("documentById", queryBuilder.toString()); - return this; - } - /** * Append an union segment. * @param collections the collections that participate in the union @@ -274,202 +173,6 @@ public ArangoDBQueryBuilder iterateCollection( return this; } - /** - * Add a graph iteration segment. - * @param graphName the graph name - * @param vertexVariable the vertex variable - * @param edgeVariable the edge variable - * @param pathVariable the path variable - * @param min edges and vertices returned by this query will start at the traversal depth of min - * @param max up to max length paths are traversed - * @param direction follow edges pointing in the direction - * @param startVertex the start vertex id - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder iterateGraph( - String graphName, - String vertexVariable, - Optional edgeVariable, - Optional pathVariable, - Optional min, - Optional max, - Direction direction, - String startVertex, - Map bindVars) { - queryBuilder.append(String.format("FOR %s", vertexVariable)); - if (edgeVariable.isPresent()) { - queryBuilder.append(String.format(", %s", edgeVariable.get())); - } - if (pathVariable.isPresent()) { - queryBuilder.append(String.format(", %s", pathVariable.get())); - } - queryBuilder.append("\n IN "); - if (min.isPresent()) { - queryBuilder.append(min.get()); - if (max.isPresent()) { - queryBuilder.append(String.format("..%s", max.get())); - } - queryBuilder.append(" "); - } - queryBuilder.append(direction.getAqlName()).append(" @startVertex\n") - .append(" GRAPH '").append(graphName).append("'\n"); // FIXME Graph could be a parameter - bindVars.put("startVertex", startVertex); - logger.debug("iterateGraph", queryBuilder.toString()); - return this; - } - - /** - * Iterate over a collection of edges. - * @param graphName the graph name - * @param vertexVariable the vertex variable - * @param edgeVariable the edge variable - * @param pathVariable the path variable - * @param min edges and vertices returned by this query will start at the traversal depth of min - * @param max up to max length paths are traversed - * @param direction follow edges pointing in the direction - * @param edgeCollections the edge collections - * @param startVertex the start vertex id - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder iterateEdges( - String graphName, - String vertexVariable, - Optional edgeVariable, - Optional pathVariable, - Optional min, - Optional max, - Direction direction, - List edgeCollections, - String startVertex, Map bindVars) { - queryBuilder.append(String.format("FOR %s", vertexVariable)); - edgeVariable.ifPresent(ev -> queryBuilder.append(String.format(", %s", ev))); - pathVariable.ifPresent(pv -> queryBuilder.append(String.format(", %s", pv))); - queryBuilder.append("\n IN "); - min.ifPresent(queryBuilder::append); - max.ifPresent(m -> queryBuilder.append(String.format("..%s", m))); - queryBuilder.append(direction.getAqlName()).append(" @startVertex\n"); - String separator = ""; - for (String c : edgeCollections) { - queryBuilder.append(separator); - separator = ", "; - queryBuilder.append(String.format("@@col%s", iterateCnt)); - bindVars.put(String.format("@col%s", iterateCnt++), c); - } - bindVars.put("@startVertex", startVertex); - logger.debug("iterateGraph", queryBuilder.toString()); - return this; - } - - /** - * Add a Graph options segment. - * - * @see UniqueVertices - * @see UniqueEdges - * @param onVertices the vertices options - * @param onEdges the edges options - * @param bfs if true, the traversal will be executed breadth-first, else it will - * be executed depth-first. - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder graphOptions( - Optional onVertices, - Optional onEdges, - boolean bfs) { - if (onVertices.isPresent() || onEdges.isPresent() || bfs) { - queryBuilder.append(" OPTIONS {"); - if (onVertices.isPresent()) { - queryBuilder.append(String.format("uniqueVertices: '%s', ", onVertices.get().getAqlName())); - } - if (onEdges.isPresent()) { - queryBuilder.append(String.format("uniqueEdges: '%s', ", onEdges.get().getAqlName())); - } - if (bfs) { - queryBuilder.append("bfs: true"); - } - queryBuilder.append("}\n"); - } - logger.debug("graphOptions", queryBuilder.toString()); - return this; - } - - /** - * Add a filter same collections segment, i.e. element represented by variable must be in any - * of the provided collections. - * @param filterVariable the filter variable - * @param collections the collections to filter by - * @param bindVars the map of bind parameters - * - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder filterSameCollections( - String filterVariable, - List collections, - Map bindVars) { - if (!collections.isEmpty()) { - queryBuilder.append(" FILTER (IS_SAME_COLLECTION("); - String separator = ""; - for (String c : collections) { - queryBuilder.append(separator); - separator = String.format(", %s) OR IS_SAME_COLLECTION(", filterVariable); - queryBuilder.append(String.format("@@col%s", iterateCnt)); - bindVars.put(String.format("@col%s", iterateCnt++), c); - } - queryBuilder.append(String.format(", %s))\n", filterVariable)); - } - filtered = true; - logger.debug("filterSameCollections", queryBuilder.toString()); - return this; - } - - /** - * Add a filter on element properties segment. The filter operations are defined using a - * #link {@link ArangoDBPropertyFilter}. - * - * @param propertyFilter the property filter - * @param filterVariable the filter variable - * @param bindVars the map of bind parameters - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder filterProperties( - ArangoDBPropertyFilter propertyFilter, - String filterVariable, - Map bindVars) { - List filterSegments = new ArrayList(); - propertyFilter.addAqlSegments(String.format("%s.", filterVariable), filterSegments, bindVars); - if (CollectionUtils.isNotEmpty(filterSegments)) { - if (filtered) { - queryBuilder.append(" AND "); - } else { - queryBuilder.append(" FILTER "); - } - queryBuilder.append(StringUtils.join(filterSegments, " AND ")).append("\n"); - } - logger.debug("filterProperties", queryBuilder.toString()); - return this; - } - - /** - * Add a limit segment to limit the number of elements returned. - * - * @param limit the limit number - * @return a reference to this object. - */ - - public ArangoDBQueryBuilder limit(Long limit) { - queryBuilder.append(" LIMIT " + limit.toString()); - logger.debug("limit", queryBuilder.toString()); - return this; - } - /** * Add a RETURN Segment. * TODO provide finer grained return statements diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java index 4aa0c24..34e09f0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/persistence/VertexData.java @@ -40,11 +40,14 @@ public class VertexData implements PropertyData, PersistentD public VertexData() { } - public VertexData(String label, String key) { + public static VertexData of(String label, String key) { ElementHelper.validateLabel(label); if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); - this.label = label; - this.key = key; + + VertexData data = new VertexData(); + data.label = label; + data.key = key; + return data; } @Override diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java index 9f8e535..13aff31 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraph.java @@ -33,7 +33,7 @@ /** * The ArangoDB graph class. - * + *

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

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

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

@@ -131,87 +131,16 @@ * @author Johannes Gocke (http://www.triagens.de) * @author Guido Schwab (http://www.triagens.de) * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) - * */ -@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD) -@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_INTEGRATE) -@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) -@Graph.OptIn("com.arangodb.tinkerpop.gremlin.ArangoDBTestSuite") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.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", - reason = "Test creates vertices with random labels, which does not work with our schema-based approach.") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldRemoveEdges", - reason = "Test creates edges with random labels, which does not work with our schema-based approach.") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.GraphTest", - method = "shouldEvaluateConnectivityPatterns", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest", - method = "shouldNotEvaluateToEqualDifferentId", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldAttachWithCreateMethod", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", - method = "shouldCopyFromGraphAToGraphB", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", - method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", - reason = "FIXME") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", - method = "shouldAddEdgeWithUserSuppliedStringId", - reason = "FIXME") public class ArangoDBGraph implements Graph { - /** - * The Class ArangoDBGraphFeatures. - */ - - public class ArangoDBGraphFeatures implements Features { - - /** - * The Class ArangoDBGraphGraphFeatures. - */ + public static class ArangoDBGraphFeatures implements Features { - private class ArangoDBGraphGraphFeatures implements GraphFeatures { + protected static class ArangoDBGraphGraphFeatures implements GraphFeatures { - /** The variable features. */ - private VariableFeatures variableFeatures = new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); - - /** - * Instantiates a new ArangoDB graph graph features. - */ - - ArangoDBGraphGraphFeatures () { } + protected ArangoDBGraphGraphFeatures() { + } @Override public boolean supportsComputer() { @@ -230,21 +159,14 @@ public boolean supportsTransactions() { @Override public VariableFeatures variables() { - return variableFeatures; + return new ArangoDBGraphVariables.ArangoDBGraphVariableFeatures(); } } - /** - * The Class ArangoDBGraphElementFeatures. - */ + protected static class ArangoDBGraphElementFeatures implements ElementFeatures { - private class ArangoDBGraphElementFeatures implements ElementFeatures { - - /** - * Instantiates a new ArangoDB graph element features. - */ - - ArangoDBGraphElementFeatures() { } + protected ArangoDBGraphElementFeatures() { + } @Override public boolean supportsAnyIds() { @@ -263,71 +185,36 @@ public boolean supportsNumericIds() { @Override public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer - * the string representation of these is fine for ArangoDB, which makes the test - * complain because it expects the actual class to be deserialized. We can test - * to see if a string is accepted for deserialization. - * TODO As with properties, a way to support this is to store the id value class - */ return false; } } - /** - * The Class ArangoDBGraphVertexFeatures. - */ - - private class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { - - /** The vertex property features. */ - - private final VertexPropertyFeatures vertexPropertyFeatures = new ArangoDBGraphVertexPropertyFeatures(); - - /** - * Instantiates a new ArangoDB graph vertex features. - */ - - ArangoDBGraphVertexFeatures () { } + protected static class ArangoDBGraphVertexFeatures extends ArangoDBGraphElementFeatures implements VertexFeatures { + protected ArangoDBGraphVertexFeatures() { + } @Override public VertexPropertyFeatures properties() { - return vertexPropertyFeatures; + return new ArangoDBGraphVertexPropertyFeatures(); } } - /** - * The Class ArangoDBGraphEdgeFeatures. - */ - public class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { - - /** The edge property features. */ - - private final EdgePropertyFeatures edgePropertyFeatures = new ArangoDBGraphEdgePropertyFeatures(); + public static class ArangoDBGraphEdgeFeatures extends ArangoDBGraphElementFeatures implements EdgeFeatures { - /** - * Instantiates a new ArangoDB graph edge features. - */ - - ArangoDBGraphEdgeFeatures() { } + protected ArangoDBGraphEdgeFeatures() { + } @Override public EdgePropertyFeatures properties() { - return edgePropertyFeatures; + return new ArangoDBGraphEdgePropertyFeatures(); } } - /** - * The Class ArangoDBGraphVertexPropertyFeatures. - */ - - private class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { + protected static class ArangoDBGraphVertexPropertyFeatures implements VertexPropertyFeatures { - /** - * Instantiates a new ArangoDB graph vertex property features. - */ - - ArangoDBGraphVertexPropertyFeatures() { } + protected ArangoDBGraphVertexPropertyFeatures() { + } @Override public boolean supportsAnyIds() { @@ -346,48 +233,25 @@ public boolean supportsNumericIds() { @Override public boolean supportsUuidIds() { - /* We can not use Java Objects as keys, ergo we can not support UUID and Integer - * the string representation of these is fine for ArangoDB, which makes the test - * complain because it expects the actual class to be deserialized. We can test - * to see if a string is accepted for deserialization. - * TODO As with properties, a way to support this is to store the id value class - */ return false; } } - /** - * The Class ArangoDBGraphEdgePropertyFeatures. - */ - private class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { + protected static class ArangoDBGraphEdgePropertyFeatures implements EdgePropertyFeatures { - /** - * Instantiates a new ArangoDB graph edge property features. - */ + protected ArangoDBGraphEdgePropertyFeatures() { + } - ArangoDBGraphEdgePropertyFeatures() { } } - /** The graph features. */ - - protected GraphFeatures graphFeatures = new ArangoDBGraphGraphFeatures(); - - /** The vertex features. */ - - protected VertexFeatures vertexFeatures = new ArangoDBGraphVertexFeatures(); - - /** The edge features. */ - - protected EdgeFeatures edgeFeatures = new ArangoDBGraphEdgeFeatures(); - @Override public EdgeFeatures edge() { - return edgeFeatures; + return new ArangoDBGraphEdgeFeatures(); } @Override public GraphFeatures graph() { - return graphFeatures; + return new ArangoDBGraphGraphFeatures(); } @Override @@ -397,98 +261,136 @@ public String toString() { @Override public VertexFeatures vertex() { - return vertexFeatures; + return new ArangoDBGraphVertexFeatures(); } } - /** The Logger. */ + /** + * The Logger. + */ private static final Logger logger = LoggerFactory.getLogger(ArangoDBGraph.class); - /** The properties name CONFIG_CONF. */ + /** + * The properties name CONFIG_CONF. + */ public static final String PROPERTY_KEY_PREFIX = "gremlin.arangodb.conf"; - /** The properties name CONFIG_DB. */ + /** + * The properties name CONFIG_DB. + */ public static final String PROPERTY_KEY_DB_NAME = "graph.db"; - /** The properties name CONFIG_NAME. */ + /** + * The properties name CONFIG_NAME. + */ public static final String PROPERTY_KEY_GRAPH_NAME = "graph.name"; - /** The properties name CONFIG_VERTICES. */ + /** + * The properties name CONFIG_VERTICES. + */ public static final String PROPERTY_KEY_VERTICES = "graph.vertex"; - /** The properties name CONFIG_EDGES. */ + /** + * The properties name CONFIG_EDGES. + */ public static final String PROPERTY_KEY_EDGES = "graph.edge"; - /** The properties name CONFIG_RELATIONS. */ + /** + * The properties name CONFIG_RELATIONS. + */ public static final String PROPERTY_KEY_RELATIONS = "graph.relation"; - /** The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES **/ + /** + * The properties name CONFIG_SHOULD_PREFIX_COLLECTION_NAMES + **/ public static final String PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES = "graph.shouldPrefixCollectionNames"; - /** The Constant DEFAULT_VERTEX_COLLECTION. */ + /** + * The Constant DEFAULT_VERTEX_COLLECTION. + */ public static final String DEFAULT_VERTEX_COLLECTION = "vertex"; - /** The Constant DEFAULT_VERTEX_COLLECTION. */ + /** + * The Constant DEFAULT_VERTEX_COLLECTION. + */ public static final String DEFAULT_EDGE_COLLECTION = "edge"; - /** The Constant GRAPH_VARIABLES_COLLECTION. */ + /** + * The Constant GRAPH_VARIABLES_COLLECTION. + */ public static final String GRAPH_VARIABLES_COLLECTION = "TINKERPOP-GRAPH-VARIABLES"; - /** The Constant ELEMENT_PROPERTIES_COLLECTION. */ + /** + * The Constant ELEMENT_PROPERTIES_COLLECTION. + */ public static final String ELEMENT_PROPERTIES_COLLECTION = "ELEMENT-PROPERTIES"; - /** The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. */ + /** + * The Constant ELEMENT_PROPERTIES_EDGE_COLLECTION. + */ public static final String ELEMENT_PROPERTIES_EDGE_COLLECTION = "ELEMENT-HAS-PROPERTIES"; public static Set GRAPH_COLLECTIONS = new HashSet<>(Arrays.asList(ELEMENT_PROPERTIES_EDGE_COLLECTION, ELEMENT_PROPERTIES_COLLECTION)); - /** The features. */ + /** + * The features. + */ private final Features FEATURES = new ArangoDBGraphFeatures(); - /** A ArangoDBGraphClient to handle the connection to the Database. */ + /** + * A ArangoDBGraphClient to handle the connection to the Database. + */ private ArangoDBGraphClient client = null; - /** The name. */ + /** + * The name. + */ private String name; - /** The vertex collections. */ + /** + * The vertex collections. + */ private final List vertexCollections; - /** The edge collections. */ + /** + * The edge collections. + */ private final List edgeCollections; - /** The relations. */ + /** + * The relations. + */ private final List relations; - /** Flat to indicate that the graph has no schema. */ - - private boolean schemaless = false; - - /** The configuration. */ + /** + * The configuration. + */ - private Configuration configuration; + private final Configuration configuration; - /** If collection names should be prefixed with graph name */ + /** + * If collection names should be prefixed with graph name + */ private final boolean shouldPrefixCollectionNames; @@ -525,7 +427,6 @@ public ArangoDBGraph(Configuration configuration) { name = arangoConfig.getString(PROPERTY_KEY_GRAPH_NAME); checkValues(arangoConfig.getString(PROPERTY_KEY_DB_NAME), name, vertexCollections, edgeCollections, relations); if (CollectionUtils.isEmpty(vertexCollections)) { - schemaless = true; vertexCollections.add(DEFAULT_VERTEX_COLLECTION); } if (CollectionUtils.isEmpty(edgeCollections)) { @@ -534,9 +435,7 @@ public ArangoDBGraph(Configuration configuration) { shouldPrefixCollectionNames = arangoConfig.getBoolean(PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, true); Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - int batchSize = 0; - client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME), - batchSize, shouldPrefixCollectionNames); + client = new ArangoDBGraphClient(this, arangoProperties, arangoConfig.getString(PROPERTY_KEY_DB_NAME)); ArangoGraph graph = client.getArangoGraph(); GraphCreateOptions options = new GraphCreateOptions(); @@ -589,19 +488,12 @@ public ArangoDBGraph(Configuration configuration) { @Override public Vertex addVertex(Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); - String label; - if (!schemaless) { - label = ElementHelper.getLabelValue(keyValues).orElse(null); - ElementHelper.validateLabel(label); - } else { - label = DEFAULT_VERTEX_COLLECTION; - } - if (!vertexCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", label, name)); - } - + String label = ElementHelper.getLabelValue(keyValues).orElse(null); String id = ArangoDBUtil.getId(features().vertex(), label, keyValues); ArangoDBVertex vertex = ArangoDBVertex.of(id, label, this); + if (!vertexCollections().contains(vertex.label())) { + throw new IllegalArgumentException(String.format("Vertex label (%s) not in graph (%s) vertex collections.", vertex.label(), name)); + } // TODO: optmize writing only once vertex.doInsert(); @@ -750,6 +642,7 @@ public List vertexCollections() { /** * Return the collection name correctly prefixed according to the shouldPrefixCollectionNames flag + * * @param collectionName the collection name * @return the Collection name prefixed */ diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java index dc04b76..a63b93b 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPersistentElement.java @@ -53,6 +53,7 @@ default String collection() { default String id() { return Optional.ofNullable(key()) .map(it -> collection() + '/' + it) + // TODO: review .orElse(label()); } } diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java index 8a94bf5..2960db0 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java @@ -36,7 +36,7 @@ public class ArangoDBVertex extends ArangoDBElement implements Vertex, ArangoDBPersistentElement { public static ArangoDBVertex of(final String id, final String label, ArangoDBGraph graph) { - return new ArangoDBVertex(graph, new VertexData(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); + return new ArangoDBVertex(graph, VertexData.of(extractLabel(id, label).orElse(DEFAULT_LABEL), extractKey(id))); } public ArangoDBVertex(ArangoDBGraph graph, VertexData data) { @@ -83,13 +83,12 @@ public Edge addEdge(String label, Vertex vertex, Object... keyValues) { ElementHelper.legalPropertyKeyValueArray(keyValues); ElementHelper.validateLabel(label); - - if (!graph.edgeCollections().contains(label)) { - throw new IllegalArgumentException(String.format("Edge label (%s)not in graph (%s) edge collections.", label, graph.name())); - } - String id = ArangoDBUtil.getId(graph.features().edge(), label, keyValues); ArangoDBEdge edge = ArangoDBEdge.of(id, label, id(), (String) vertex.id(), graph); + if (!graph.edgeCollections().contains(edge.label())) { + throw new IllegalArgumentException(String.format("Edge label (%s) not in graph (%s) edge collections.", edge.label(), graph.name())); + } + // TODO: optmize writing only once edge.doInsert(); ElementHelper.attachProperties(edge, keyValues); diff --git a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java index f5bdcda..9d08e16 100644 --- a/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java +++ b/src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java @@ -581,42 +581,50 @@ public static String extractCollection(final String id) { } } + // FIXME: review public static Optional extractLabel(final String id, final String label) { String col = extractCollection(id); if (col != null) { - if (label != null && !label.equals(col)) { + String labelFromId = col.replaceFirst("^.*_", ""); + if (label != null && !label.equals(labelFromId)) { throw new IllegalArgumentException("Invalid label: [" + label + "] for id: [" + id + "]"); } - return Optional.of(col); + return Optional.of(labelFromId); } return Optional.ofNullable(label); } + // FIXME: use com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractKey() and com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.extractLabel() public static String getId(Graph.Features.ElementFeatures features, String label, Object... keyValues) { Optional optionalId = ElementHelper.getIdValue(keyValues); if (!optionalId.isPresent()) { return null; } - Object id = optionalId.get(); - if (features.willAllowId(id)) { - if (id.toString().contains("/")) { - String fullId = id.toString(); - String[] parts = fullId.split("/"); - // The collection name is the last part of the full name - String[] collectionParts = parts[0].split("_"); - String collectionName = collectionParts[collectionParts.length - 1]; - if (collectionName.contains(label)) { + String id = optionalId + .filter(features::willAllowId) + .map(Object::toString) + .orElseThrow(Vertex.Exceptions::userSuppliedIdsOfThisTypeNotSupported); + + if (id.contains("/")) { + String fullId = id; + String[] parts = fullId.split("/"); + // The collection name is the last part of the full name + String[] collectionParts = parts[0].split("_"); + String collectionName = collectionParts[collectionParts.length - 1]; + Optional inferredLabel = extractLabel(id, label); + if(inferredLabel.isPresent()) { + if (collectionName.contains(inferredLabel.get())) { id = parts[1]; } } - Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String) id); - if (m.matches()) { - return id.toString(); - } else { - throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); - } + } + + // FIXME: review + Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher(id); + if (m.matches()) { + return id; } else { - throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); + throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); } } diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java deleted file mode 100644 index 2533967..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java +++ /dev/null @@ -1,295 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import java.util.HashSet; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import com.arangodb.tinkerpop.gremlin.persistence.*; -import com.arangodb.tinkerpop.gremlin.structure.*; -import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.tinkerpop.gremlin.AbstractGraphProvider; -import org.apache.tinkerpop.gremlin.LoadGraphWith; -import org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData; -import org.apache.tinkerpop.gremlin.structure.Element; -import org.apache.tinkerpop.gremlin.structure.Graph; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; -import org.apache.tinkerpop.gremlin.structure.VertexTest; - -/** - * The Class ArangoDBGraphProvider. This provider assumes that there is a local ArangoDB running (i.e. - * http://127.0.0.1:8529) with a tinkerpop database and a gremlin user that has Administrate permissions - * on the db. - */ -public class ArangoDBGraphProvider extends AbstractGraphProvider { - - /** - * The Constant IMPLEMENTATIONS. - */ - private static final Set IMPLEMENTATIONS = new HashSet() {{ - add(PersistentData.class); - add(AdbValue.class); - add(EdgeData.class); - add(PropertyData.class); - add(VertexData.class); - add(VertexPropertyData.class); - }}; - - - @Override - public Configuration newGraphConfiguration(final String graphName, final Class test, - final String testMethodName, - final Map configurationOverrides, - final LoadGraphWith.GraphData loadGraphWith) { - Configuration conf = getConfiguration(graphName, test, testMethodName, loadGraphWith); - - // assign overrides but don't allow gremlin.graph setting to be overridden. the test suite should - // not be able to override that. - configurationOverrides.entrySet().stream() - .filter(c -> !c.getKey().equals(Graph.GRAPH)) - .forEach(e -> conf.setProperty(e.getKey(), e.getValue())); - return conf; - } - - private Configuration getConfiguration( - String graphName, - Class test, - String testMethodName, - GraphData loadGraphWith) { - ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() - .arangoHosts("127.0.0.1:8529") - .arangoUser("root") - .arangoPassword("test") - .graph(graphName); - if (loadGraphWith != null) { - switch (loadGraphWith) { - case CLASSIC: - System.out.println("CLASSIC"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "vertex", "vertex"); - builder.configureEdge("created", "vertex", "vertex"); - break; - case CREW: - System.out.println("CREW"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("uses"); - builder.withEdgeCollection("develops"); - builder.withEdgeCollection("traverses"); - builder.configureEdge("uses", "person", "software"); - builder.configureEdge("develops", "person", "software"); - builder.configureEdge("traverses", "software", "software"); - break; - case GRATEFUL: - System.out.println("GRATEFUL"); - break; - case MODERN: - System.out.println("MODERN"); - builder.withVertexCollection("dog"); - builder.withVertexCollection("software"); - builder.withVertexCollection("person"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("created", "person", "software"); - break; - default: - System.out.println("default"); - break; - } - } else { - if (testMethodName.startsWith("shouldProcessVerticesEdges") - || testMethodName.startsWith("shouldGenerate") - || testMethodName.startsWith("shouldSetValueOnEdge") - || testMethodName.startsWith("shouldAutotype")) { - builder.withEdgeCollection("knows"); - } else if (testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { - builder.withEdgeCollection("self"); - } else if (testMethodName.startsWith("shouldSupportUserSuppliedIds")) { - builder.withEdgeCollection("test"); - } else if (testMethodName.startsWith("shouldSupportUUID")) { - builder.withEdgeCollection("friend"); - } else if (testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteVertexWithINEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { - builder.withEdgeCollection("friends"); - } else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } else if (testMethodName.startsWith("shouldReadWriteEdge")) { - builder.withVertexCollection("person"); - builder.withEdgeCollection("friend"); - } else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { - builder.withEdgeCollection("self"); - } else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { - builder.withEdgeCollection("self"); - } else { - // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? - switch (testMethodName) { - case "shouldGetPropertyKeysOnEdge": - case "shouldNotGetConcurrentModificationException": - builder.withEdgeCollection("friend"); - builder.withEdgeCollection("knows"); - break; - case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": - case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": - builder.withEdgeCollection("hate"); - builder.withEdgeCollection("friend"); - break; - case "shouldPersistDataOnClose": - builder.withEdgeCollection("collaborator"); - break; - case "shouldTestTreeConnectivity": - builder.withEdgeCollection("test1"); - builder.withEdgeCollection("test2"); - builder.withEdgeCollection("test3"); - break; - case "shouldEvaluateConnectivityPatterns": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("knows"); - break; - case "shouldRemoveEdgesWithoutConcurrentModificationException": - builder.withEdgeCollection("link"); - break; - case "shouldGetValueThatIsNotPresentOnEdge": - case "shouldHaveStandardStringRepresentationForEdgeProperty": - case "shouldHaveTruncatedStringRepresentationForEdgeProperty": - case "shouldValidateIdEquality": - case "shouldValidateEquality": - case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": - case "shouldAddEdgeWithUserSuppliedStringId": - case "shouldAllowNullAddEdge": - builder.withEdgeCollection("self"); - break; - case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": - case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": - case "shouldProcessEdges": - case "shouldReturnOutThenInOnVertexIterator": - case "shouldReturnEmptyIteratorIfNoProperties": - builder.withEdgeCollection("knows"); - break; - case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("pets"); - builder.withEdgeCollection("walks"); - builder.withEdgeCollection("livesWith"); - break; - case "shouldHaveStandardStringRepresentation": - builder.withEdgeCollection("friends"); - break; - case "shouldReadWriteSelfLoopingEdges": - builder.withEdgeCollection("CONTROL"); - builder.withEdgeCollection("SELFLOOP"); - break; - case "shouldReadGraphML": - case "shouldReadGraphMLUnorderedElements": - case "shouldTransformGraphMLV2ToV3ViaXSLT": - case "shouldReadLegacyGraphSON": - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("created"); - break; - case "shouldAddVertexWithLabel": - case "shouldAllowNullAddVertexProperty": - builder.withVertexCollection("person"); - break; - case "shouldNotAllowSetProperty": - case "shouldHashAndEqualCorrectly": - case "shouldNotAllowRemove": - case "shouldNotConstructNewWithSomethingAlreadyDetached": - case "shouldNotConstructNewWithSomethingAlreadyReferenced": - builder.withEdgeCollection("test"); - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertex": - builder.withEdgeCollection("tonothing"); - break; - case "shouldHandleSelfLoops": - builder.withVertexCollection("person"); - builder.withEdgeCollection("self"); - break; - case "shouldAttachWithCreateMethod": - case "testAttachableCreateMethod": - builder.withVertexCollection("person"); - builder.withVertexCollection("project"); - builder.withEdgeCollection("knows"); - builder.withEdgeCollection("developedBy"); - builder.configureEdge("knows", "person", "person"); - builder.configureEdge("developedBy", "project", "person"); - break; - case "shouldConstructReferenceVertex": - builder.withVertexCollection("blah"); - break; - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": - case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": - if (VertexTest.class.equals(test.getEnclosingClass())) { - builder.withVertexCollection("foo"); - } - break; - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": - case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": - builder.withVertexCollection("foo"); - break; - case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": - builder.withEdgeCollection("created"); - builder.withEdgeCollection("knows"); - break; - default: - System.out.println("case \"" + testMethodName + "\":"); - } - } - } - return builder.build(); - } - - @Override - public void clear(Graph graph, Configuration configuration) throws Exception { - ArangoDBGraphClient client; - if (graph == null) { - Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); - Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); - client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 0, true); - client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); - } else { - ArangoDBGraph agraph = (ArangoDBGraph) graph; - client = agraph.getClient(); - client.clear(agraph); - agraph.close(); - } - - } - - @Override - public Set getImplementations() { - return IMPLEMENTATIONS; - } - - @Override - public Map getBaseConfiguration(String graphName, Class test, String testMethodName, - GraphData loadGraphWith) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Object convertId(Object id, Class c) { - return id.toString(); - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java deleted file mode 100644 index 3224571..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import org.apache.tinkerpop.gremlin.GraphProviderClass; -import org.junit.runner.RunWith; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; - -@RunWith(ArangoDBTestSuite.class) -@GraphProviderClass(provider = ArangoDBGraphProvider.class, graph = ArangoDBGraph.class) -public class ArangoDBGraphTest { - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java deleted file mode 100644 index a60343c..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; -import org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest; -import org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest; -import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; -import org.apache.tinkerpop.gremlin.structure.*; -import org.apache.tinkerpop.gremlin.structure.io.IoCustomTest; -import org.apache.tinkerpop.gremlin.structure.io.IoEdgeTest; -import org.apache.tinkerpop.gremlin.structure.io.IoGraphTest; -import org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest; -import org.apache.tinkerpop.gremlin.structure.io.IoTest; -import org.apache.tinkerpop.gremlin.structure.io.IoVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdgeTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdgeTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceGraphTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexPropertyTest; -import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest; -import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - - -/** - * Run with {@code GREMLIN_TESTS} environment variable set to a list of any of these to enable - * particular tests: - * org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest, - * org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest, - * org.apache.tinkerpop.gremlin.structure.EdgeTest, - * org.apache.tinkerpop.gremlin.structure.FeatureSupportTest, - * org.apache.tinkerpop.gremlin.structure.io.IoCustomTest, - * org.apache.tinkerpop.gremlin.structure.io.IoGraphTest, - * org.apache.tinkerpop.gremlin.structure.io.IoVertexTest, - * org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest, - * org.apache.tinkerpop.gremlin.structure.GraphTest, - * org.apache.tinkerpop.gremlin.structure.GraphConstructionTest, - * org.apache.tinkerpop.gremlin.structure.io.IoTest, - * org.apache.tinkerpop.gremlin.structure.VertexPropertyTest - * - */ -public class ArangoDBTestSuite extends AbstractGremlinSuite { - - /** - * This list of tests in the suite that will be executed. Gremlin developers should add to this list - * as needed to enforce tests upon implementations. - */ - private static final Class[] allTests = new Class[]{ - CommunityGeneratorTest.class, - DetachedGraphTest.class, - DetachedEdgeTest.class, - DetachedVertexPropertyTest.class, - DetachedPropertyTest.class, - DetachedVertexTest.class, - DistributionGeneratorTest.class, - EdgeTest.class, - FeatureSupportTest.class, - IoCustomTest.class, - IoEdgeTest.class, - IoGraphTest.class, - IoVertexTest.class, - IoPropertyTest.class, - GraphTest.class, - GraphConstructionTest.class, - IoTest.class, - VertexPropertyTest.class, - VariablesTest.class, - PropertyTest.class, - ReferenceGraphTest.class, - ReferenceEdgeTest.class, - ReferenceVertexPropertyTest.class, - ReferenceVertexTest.class, - SerializationTest.class, - StarGraphTest.class, - TransactionTest.class, - TransactionMultiThreadedTest.class, - VertexTest.class, - //ArangoDBIndexCheck.class, - //ArangoDBCypherCheck.class, - }; - - public ArangoDBTestSuite( - Class klass, - RunnerBuilder builder) - throws InitializationError { - super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); - } - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java new file mode 100644 index 0000000..5753614 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/BaseGremlinTest.java @@ -0,0 +1,11 @@ +package com.arangodb.tinkerpop.gremlin; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; + +public abstract class BaseGremlinTest extends AbstractGremlinTest { + + protected ArangoDBGraph getGraph() { + return (ArangoDBGraph) this.graph; + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java b/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java deleted file mode 100644 index 752d726..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.arangodb.tinkerpop.gremlin; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; -import org.apache.commons.configuration2.Configuration; - -import java.io.File; - - -public class Issue57 { - - public static void main(String... args) { - ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder(); - builder.dataBase("Test02") - .graph("Test02Graph01") - .arangoUser("gremlin") - .arangoPassword("gremlin") - .arangoHosts("127.0.0.1:8529") - .withEdgeCollection("testedge") - .withVertexCollection("testfrom") - .withVertexCollection("testto") - .shouldPrefixCollectionNamesWithGraphName(false) - .configureEdge("testedge", "testfrom", "testto"); - - ArangoDBGraph g = ArangoDBGraph.open(builder.build()); - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java new file mode 100644 index 0000000..eb7c593 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/TestGraph.java @@ -0,0 +1,88 @@ +package com.arangodb.tinkerpop.gremlin; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.structure.Graph; + +@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD) +@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest", + method = "testAttachableCreateMethod", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.detached.DetachedGraphTest#testAttachableCreateMethod()") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldAddVertexWithUserSuppliedStringId", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveVertices", + reason = "FIXME: DE-998") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldRemoveEdges", + reason = "FIXME: DE-998") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.GraphTest", + method = "shouldEvaluateConnectivityPatterns", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldAttachWithCreateMethod", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest.shouldAttachWithCreateMethod") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest", + method = "shouldCopyFromGraphAToGraphB", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest.shouldCopyFromGraphAToGraphB") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateEquivalentVertexHashCodeWithSuppliedIds", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaTraversal", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$BasicVertexTest", + method = "shouldEvaluateVerticesEquivalentWithSuppliedIdsViaIterators", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.structure.VertexTest$AddEdgeTest", + method = "shouldAddEdgeWithUserSuppliedStringId", + reason = "FIXME: DE-996") +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeEdgeTest$Traversals", + method = "*", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map.MergeEdgeTest" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexTest$Traversals", + method = "g_withSideEffectXc_label_person_name_markoX_withSideEffectXm_age_19X_mergeVXselectXcXX_optionXonMatch_selectXmXX_option", + reason = "FIXME: DE-995" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MergeVertexTest$Traversals", + method = "g_mergeVXlabel_person_name_markoX_optionXonMatch_age_19X_option", + reason = "FIXME: DE-995" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.OrderabilityTest$Traversals", + method = "*", + reason = "replaced by com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.OrderabilityTest" +) +@Graph.OptOut( + test = "org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest$Traversals", + method = "*", + reason = "FIXME: DE-996" +) +public class TestGraph extends ArangoDBGraph { + + @SuppressWarnings("unused") + public static TestGraph open(Configuration configuration) { + return new TestGraph(configuration); + } + + public TestGraph(Configuration configuration) { + super(configuration); + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java b/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java deleted file mode 100644 index 6a2139c..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.client.test; - -import java.util.Properties; - -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.junit.After; -import org.junit.Before; - -public abstract class BaseTestCase { - - protected ArangoDBGraphClient client; - protected final String graphName = "test_graph1"; - protected final String vertices = "test_vertices1"; - protected final String edges = "test_edges1"; - - @Before - public void setUp() throws Exception { - - // host name and port see: arangodb.properties - PropertiesConfiguration configuration = new PropertiesConfiguration(); - configuration.setProperty("arangodb.hosts", "127.0.0.1:8529"); - configuration.setProperty("arangodb.user", "gremlin"); - configuration.setProperty("arangodb.password", "gremlin"); - Properties arangoProperties = ConfigurationConverter.getProperties(configuration); - - client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 30000, true); - - client.deleteGraph(graphName); - client.deleteCollection(vertices); - client.deleteCollection(edges); - - } - - @After - public void tearDown() { - - client.deleteCollection(vertices); - client.deleteCollection(edges); - client.deleteGraph(graphName); - client = null; - } - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java deleted file mode 100644 index b05af42..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java +++ /dev/null @@ -1,302 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.client.test; - -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; - -import com.arangodb.ArangoDBException; -import com.arangodb.ArangoDatabase; -import com.arangodb.ArangoGraph; -import com.arangodb.entity.EdgeDefinition; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; -import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; - -import java.util.*; -import java.util.stream.Collectors; - -import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; -import org.apache.commons.configuration2.ConfigurationConverter; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.junit.*; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/** - * This tests require four users: - *
    - *
  • root: To test DB creation, requires root access - *
  • gremlin: To test graph/collection creation+access in the db, requires "Administrate" permission to DB - *
  • reader: To test graph/collection access in the db, requires "Access" permission to DB - *
  • limited: To test no access to the db, requires "No access" permission to DB - *
- * For all users (except root) the password is expected to be the same as the username. For the root, - * the password must be set via environment variable ARANGODB_ROOT_PWD (so no system password is shared - * via code). - * @author Horacio Hoyos Rodriguez (@horaciohoyosr) - * - */ -@RunWith(Parameterized.class) -@Ignore -public class ClientTest { - - @Parameterized.Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { true, "knows_", "social_", "routeplanner_" }, - { false, "", "", "" } - }); - } - - @Rule - public ExpectedException exception = ExpectedException.none(); - - private PropertiesConfiguration configuration; - private ArangoDBGraphClient client; - - @Parameterized.Parameter - public boolean shouldPrefixCollectionWithGraphName; - - @Parameterized.Parameter(1) - public String knowsPrefix; - - @Parameterized.Parameter(2) - public String socialPrefix; - - @Parameterized.Parameter(3) - public String routeplannerPrefix; - - @Before - public void setUp() throws Exception { - - configuration = new PropertiesConfiguration(); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_DB_NAME, "tinkerpop"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME, "standard"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.hosts", "127.0.0.1:8529"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.user", "gremlin"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.password", "gremlin"); - configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, shouldPrefixCollectionWithGraphName); - Properties arangoProperties = ConfigurationConverter.getProperties(configuration); - ArangoDBGraph g = new ArangoDBGraph(configuration); - System.out.println(g.features()); - // client = new ArangoDBGraphClient(g, arangoProperties, "tinkerpop", 30000); - } - - @Test - public void dummy() { - assert true; - } -// -// @After -// public void tearDown() { -// // Drop used graphs and collections, if present -// ArangoDatabase db = client.getDB(); -// // know_graph -// deleteGraph(db, "knows", true); -// // social graph -// deleteGraph(db, "social", true); -// // city graph -// deleteGraph(db, "routeplanner", true); -// client.shutdown(); -// client = null; -// } -// -// private boolean deleteGraph( -// ArangoDatabase db, -// String name, -// boolean dropCollections) { -// if (db != null) { -// ArangoGraph graph = db.graph(name); -// if (graph.exists()) { -// Collection edgeDefinitions = dropCollections ? graph.getEdgeDefinitions() : Collections.emptyList(); -// Collection vertexCollections = dropCollections ? graph.getVertexCollections(): Collections.emptyList();; -// // Drop graph first because it will break if the graph collections do not exist -// graph.drop(); -// for (String definitionName : edgeDefinitions) { -// String collectioName = definitionName; -// if (db.collection(collectioName).exists()) { -// db.collection(collectioName).drop(); -// } -// } -// for (String vc : vertexCollections) { -// String collectioName = vc; -// if (db.collection(collectioName).exists()) { -// db.collection(collectioName).drop(); -// } -// } -// return true; -// } else { -// try { -// graph.drop(); -// } catch (ArangoDBException e) { -// //throw ArangoDBExceptions.getArangoDBException(e); -// } -// } -// } -// return false; -// } -// -// -// // ********* The following methods test a local ArangoDBGraphClient ********* -// -// @Test -// public void test_RestrictedUserNewDatabase_should_throw_ArangoDBGraphException() throws Exception { -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); -// ArangoDBGraph g = new ArangoDBGraph(configuration); -// new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); -// } -// -// @Test -// public void test_AuthorizedUserNewDatabase_can_create_new_database() throws Exception { -// org.junit.Assume.assumeTrue(System.getenv("ARANGODB_ROOT_PWD") != null); -// configuration.setProperty("arangodb.user", "root"); -// String pwd = System.getenv("ARANGODB_ROOT_PWD"); -// configuration.setProperty("arangodb.password", pwd); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// ArangoDBGraph g = new ArangoDBGraph(configuration); -// ArangoDBGraphClient localClient = new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); -// assertThat(localClient.dbExists(), is(true)); -// localClient.deleteDb(); -// localClient.shutdown(); -// } -// -// @Test -// public void test_RestrictedUserExistingDb_should_throw_ArangoDBGraphException() throws Exception { -// configuration.setProperty("arangodb.user", "limited"); -// configuration.setProperty("arangodb.password", "limited"); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("DB not found or user has no access:")); -// new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); -// } -// -// @Test -// public void test_ReadAccessUserCreateGraph_should_throw_ArangoDBGraphException() throws Exception { -// configuration.setProperty("arangodb.user", "reader"); -// configuration.setProperty("arangodb.password", "reader"); -// Properties arangoProperties = ConfigurationConverter.getProperties(configuration); -// ArangoDBGraphClient localClient = new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); -// assertThat(localClient.dbExists(), is(true)); -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// verticesCollectionNames.add("person"); -// edgesCollectionNames.add("knows"); -// -// exception.expect(ArangoDBGraphException.class); -// exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); -// localClient.createGraph("knows", verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); -// localClient.shutdown(); -// } -// -// // ********* The following tests use the ArangoDBGraphClient from @Setup ********* -// -// @Test -// public void test_ServerVersion() throws Exception { -// String version = client.getVersion(); -// assertThat(version, is(notNullValue())); -// } -// -// @Test -// public void test_CreateSimpleGraph() throws Exception { -// -// String graph_name = "knows"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// verticesCollectionNames.add("person"); -// edgesCollectionNames.add("knows"); -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); -// ArangoDatabase db = client.getDB(); -// assertThat("Graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection found in db", db.collection(String.format("%sperson", knowsPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sknows", knowsPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat(defs, hasSize(2)); // +1 for ELEMENT_HAS_PROPERTIES -// EdgeDefinition d = defs.iterator().next(); -// assertThat(d.getCollection(), is(String.format("%sknows", knowsPrefix))); -// assertThat(d.getFrom(), contains(String.format("%sperson", knowsPrefix))); -// assertThat(d.getTo(), contains(String.format("%sperson", knowsPrefix))); -// } -// -// @Test -// public void test_CreateMultiVertexGraph() throws Exception { -// String graph_name = "social"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// List relations = new ArrayList<>(); -// verticesCollectionNames.add("male"); -// verticesCollectionNames.add("female"); -// edgesCollectionNames.add("relation"); -// relations.add("relation:male,female->male,female"); -// -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); -// ArangoDatabase db = client.getDB(); -// assertThat("Created graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%smale", socialPrefix)).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sfemale", socialPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%srelation", socialPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat(defs, hasSize(2)); -// EdgeDefinition d = defs.iterator().next(); -// assertThat(d.getCollection(), is(String.format("%srelation", socialPrefix))); -// assertThat(d.getFrom(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); -// assertThat(d.getTo(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); -// } -// -// @Test -// public void test_CreateMultiVertexMultiEdgeGraph() throws Exception { -// String graph_name = "routeplanner"; -// List verticesCollectionNames = new ArrayList<>(); -// List edgesCollectionNames = new ArrayList<>(); -// List relations = new ArrayList<>(); -// verticesCollectionNames.add("germanCity"); -// verticesCollectionNames.add("frenchCity"); -// edgesCollectionNames.add("frenchHighway"); -// edgesCollectionNames.add("germanHighway"); -// edgesCollectionNames.add("internationalHighway"); -// relations.add("frenchHighway:frenchCity->frenchCity"); -// relations.add("germanHighway:germanCity->germanCity"); -// relations.add("internationalHighway:germanCity,frenchCity->germanCity,frenchCity"); -// -// -// client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); -// ArangoDatabase db = client.getDB(); -// assertThat("Craeted graph not found in db", db.graph(graph_name).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sgermanCity", routeplannerPrefix)).exists(), is(true)); -// assertThat("Vertex collection not found in db", db.collection(String.format("%sfrenchCity", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sfrenchHighway", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sgermanHighway", routeplannerPrefix)).exists(), is(true)); -// assertThat("Edge collection found in db", db.collection(String.format("%sinternationalHighway", routeplannerPrefix)).exists(), is(true)); -// ArangoGraph g = db.graph(graph_name); -// Collection defs = g.getInfo().getEdgeDefinitions(); -// assertThat("Not all edge definitions created", defs, hasSize(4)); -// List edgeNames = defs.stream().map(EdgeDefinition::getCollection).collect(Collectors.toList()); -// assertThat("Missmatch name in edge collecion names", edgeNames, -// containsInAnyOrder(String.format("%sfrenchHighway", routeplannerPrefix), -// String.format("%sgermanHighway", routeplannerPrefix), -// String.format("%sinternationalHighway", routeplannerPrefix), "routeplanner_ELEMENT-HAS-PROPERTIES")); -// EdgeDefinition fh = defs.stream().filter(ed -> String.format("%sfrenchHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("FrenchHighway connects incorrect collections", fh.getFrom(), contains(String.format("%sfrenchCity", routeplannerPrefix))); -// assertThat("FrenchHighway connects incorrect collections", fh.getTo(), contains(String.format("%sfrenchCity", routeplannerPrefix))); -// EdgeDefinition gh = defs.stream().filter(ed -> String.format("%sgermanHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("GermanHighway connects incorrect collections", gh.getFrom(), contains(String.format("%sgermanCity", routeplannerPrefix))); -// assertThat("GermanHighway connects incorrect collections", gh.getTo(), contains(String.format("%sgermanCity", routeplannerPrefix))); -// EdgeDefinition ih = defs.stream().filter(ed -> String.format("%sinternationalHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); -// assertThat("InternationalHighway connects incorrect collections", ih.getFrom(), -// containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); -// assertThat("InternationalHighway connects incorrect collections", ih.getTo(), -// containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); -// } -// -// - -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java new file mode 100644 index 0000000..2718340 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomGraphProvider.java @@ -0,0 +1,58 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; + +public class CustomGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, CustomTestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + switch (testMethodName) { + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists": + case "g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadasX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists": + case "g_V_mergeEXlabel_self_weight_05X": + case "g_injectXlabel_knows_out_marko_in_vadasX_mergeE": + case "g_mergeE_with_outV_inV_options": + builder.withVertexCollection("person"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("self"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("self", "person", "person"); + break; + case "testAttachableCreateMethod": + case "shouldAttachWithCreateMethod": + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withVertexCollection("project"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("developedBy"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("developedBy", "project", "person"); + break; + case "shouldCopyFromGraphAToGraphB": + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withVertexCollection("software"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + break; + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java new file mode 100644 index 0000000..60bba07 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuite.java @@ -0,0 +1,26 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.OrderabilityTest; +import com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map.MergeEdgeTest; +import com.arangodb.tinkerpop.gremlin.custom.structure.util.detached.DetachedGraphTest; +import com.arangodb.tinkerpop.gremlin.custom.structure.util.star.StarGraphTest; +import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + + +public class CustomStandardSuite extends AbstractGremlinSuite { + + private static final Class[] allTests = new Class[]{ + MergeEdgeTest.Traversals.class, + OrderabilityTest.Traversals.class, + DetachedGraphTest.class, + StarGraphTest.class + }; + + public CustomStandardSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { + super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java new file mode 100644 index 0000000..d810b70 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomStandardSuiteTest.java @@ -0,0 +1,10 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.junit.runner.RunWith; + +@RunWith(CustomStandardSuite.class) +@GraphProviderClass(provider = CustomGraphProvider.class, graph = CustomTestGraph.class) +public class CustomStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java new file mode 100644 index 0000000..56e4df5 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/CustomTestGraph.java @@ -0,0 +1,54 @@ +package com.arangodb.tinkerpop.gremlin.custom; + +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.structure.Graph; + +@Graph.OptIn("com.arangodb.tinkerpop.gremlin.custom.CustomStandardSuite") +public class CustomTestGraph extends ArangoDBGraph { + + @SuppressWarnings("unused") + public static CustomTestGraph open(Configuration configuration) { + return new CustomTestGraph(configuration); + } + + public CustomTestGraph(Configuration configuration) { + super(configuration); + } + + @Override + public Features features() { + return new ArangoDBGraph.ArangoDBGraphFeatures() { + + @Override + public Features.EdgeFeatures edge() { + return new ArangoDBGraphFeatures.ArangoDBGraphEdgeFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + + @Override + public Features.VertexFeatures vertex() { + return new ArangoDBGraphFeatures.ArangoDBGraphVertexFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + + @Override + public Features.VertexPropertyFeatures properties() { + return new ArangoDBGraphFeatures.ArangoDBGraphVertexPropertyFeatures() { + @Override + public boolean supportsNumericIds() { + return true; + } + }; + } + }; + } + }; + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java new file mode 100644 index 0000000..384c6c1 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/OrderabilityTest.java @@ -0,0 +1,509 @@ +package com.arangodb.tinkerpop.gremlin.custom.process.traversal.step; + + +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest; +import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; +import org.apache.tinkerpop.gremlin.process.traversal.Order; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.util.CollectionUtil; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.EdgeFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.GraphFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexFeatures; +import static org.apache.tinkerpop.gremlin.structure.Graph.Features.VertexPropertyFeatures; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +@RunWith(GremlinProcessRunner.class) +public abstract class OrderabilityTest extends AbstractGremlinProcessTest { + + private interface Constants { + UUID uuid = UUID.randomUUID(); + Date date = new Date(); + List list1 = Arrays.asList(1, 2, 3); + List list2 = Arrays.asList(1, 2, 3, 4); + Set set1 = CollectionUtil.asSet(list1); + Set set2 = CollectionUtil.asSet(list2); + Map map1 = CollectionUtil.asMap(1, 11, 2, 22, 3, false, 4, 44); + Map map2 = CollectionUtil.asMap(1, 11, 2, 22, 33); + + Object[] unordered = { map2, 1, map1, "foo", null, list1, date, set1, list2, true, uuid, "bar", 2.0, false, set2 }; + } + + public abstract Traversal get_g_V_values_order(); + + public abstract Traversal get_g_V_properties_order(); + + public abstract Traversal get_g_E_properties_order_value(); + + public abstract Traversal get_g_E_properties_order_byXdescX_value(); + + public abstract Traversal get_g_inject_order(); + + // order asc by vertex: v[3], v[5] + public abstract Traversal get_g_V_out_out_order_byXascX(); + + // order asc by vertex in path: v[3], v[5] + public abstract Traversal get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX(); + + // order asc by edge: e[10], v[e11] + public abstract Traversal get_g_V_out_outE_order_byXascX(); + + // order asc by edge in path: e[10], e[11] + public abstract Traversal get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX(); + + // order asc by vertex and then vertex property id in path. + public abstract Traversal get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value(); + + // order asc by vertex and then vertex property value in path. + public abstract Traversal get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX(); + + // order desc by vertex: v[3], v[5] + public abstract Traversal get_g_V_out_out_order_byXdescX(); + + // order desc by vertex in path: v[3], v[5] + public abstract Traversal get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX(); + + // order desc by edge: e[10], v[e11] + public abstract Traversal get_g_V_out_outE_order_byXdescX(); + + // order desc by edge in path: e[10], e[11] + public abstract Traversal get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX(); + + // order desc by vertex and then vertex property id in path. + public abstract Traversal get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value(); + + // order desc by vertex and then vertex property value in path. + public abstract Traversal get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX(); + + /** + * Order by property value (mixed types). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_V_values_order() { + final Traversal traversal = get_g_V_values_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + 27, 29, 32, 35, "java", "java", "josh", "lop", "marko", "peter", "ripple", "vadas" + ), traversal); + } + + /** + * Order by vertex property (orders by id). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_properties_order() { + final Traversal traversal = get_g_V_properties_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertexProperty("marko", "name", "marko"), // vpid = 0 + convertToVertexProperty("marko", "age", 29), // vpid = 1 + convertToVertexProperty("peter", "name", "peter"), // vpid = 10 + convertToVertexProperty("peter", "age", 35), // vpid = 11 + convertToVertexProperty("vadas", "name", "vadas"), // vpid = 2 + convertToVertexProperty("vadas", "age", 27), // vpid = 3 + convertToVertexProperty("lop", "name", "lop"), // vpid = 4 + convertToVertexProperty("lop", "lang", "java"), // vpid = 5 + convertToVertexProperty("josh", "name", "josh"), // vpid = 6 + convertToVertexProperty("josh", "age", 32), // vpid = 7 + convertToVertexProperty("ripple", "name", "ripple"), // vpid = 8 + convertToVertexProperty("ripple", "lang", "java") // vpid = 9 + ), traversal); + } + + /** + * Order by edge property (orders by key, then value). + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_ADD_PROPERTY) + public void g_E_properties_order_value() { + { // add some more edge properties + final AtomicInteger a = new AtomicInteger(); + g.E().forEachRemaining(e -> e.property("a", a.getAndIncrement())); + } + + final Traversal asc = get_g_E_properties_order_value(); + printTraversalForm(asc); + checkOrderedResults(Arrays.asList( + 0, 1, 2, 3, 4, 5, 0.2, 0.4, 0.4, 0.5, 1.0, 1.0 + ), asc); + + final Traversal desc = get_g_E_properties_order_byXdescX_value(); + printTraversalForm(desc); + checkOrderedResults(Arrays.asList( + 1.0, 1.0, 0.5, 0.4, 0.4, 0.2, 5, 4, 3, 2, 1, 0 + ), desc); + } + + /** + * Mixed type values including list, set, map, uuid, date, boolean, numeric, string, null. + */ + @Test + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_inject_order() { + final Traversal traversal = get_g_inject_order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + null, + false, true, + 1, 2.0, + OrderabilityTest.Constants.date, + "bar", "foo", + OrderabilityTest.Constants.uuid, + OrderabilityTest.Constants.set1, OrderabilityTest.Constants.set2, + OrderabilityTest.Constants.list1, OrderabilityTest.Constants.list2, + OrderabilityTest.Constants.map1, OrderabilityTest.Constants.map2 + ), traversal); + } + + /** + * More mixed type values including a Java Object (unknown type). + */ + @Test + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + public void g_inject_order_with_unknown_type() { + final Object unknown = new Object(); + final Object[] unordered = new Object[OrderabilityTest.Constants.unordered.length+1]; + unordered[0] = unknown; + System.arraycopy(OrderabilityTest.Constants.unordered, 0, unordered, 1, OrderabilityTest.Constants.unordered.length); + + final Traversal traversal = g.inject(unordered).order(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + null, + false, true, + 1, 2.0, + OrderabilityTest.Constants.date, + "bar", "foo", + OrderabilityTest.Constants.uuid, + OrderabilityTest.Constants.set1, OrderabilityTest.Constants.set2, + OrderabilityTest.Constants.list1, OrderabilityTest.Constants.list2, + OrderabilityTest.Constants.map1, OrderabilityTest.Constants.map2, + unknown + ), traversal); + } + + /** + * Order asc by vertex: v[3], v[5] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_order_byXascX() { + final Traversal traversal = get_g_V_out_out_order_byXascX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("lop"), // vid = 3 + convertToVertex("ripple") // vid = 5 + ), traversal); + } + + /** + * Order asc by vertex in path: v[3], v[5] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("lop"), // vid = 3 + convertToVertex("ripple") // vid = 5 + ), traversal); + } + + /** + * Order asc by edge: e[10], v[e11] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_order_byXascX() { + final Traversal traversal = get_g_V_out_outE_order_byXascX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "ripple"), // eid = 10 + convertToEdge("josh", "created", "lop") // eid = 11 + ), traversal); + } + + /** + * Order asc by edge in path: e[10], e[11] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "ripple"), // eid = 10 + convertToEdge("josh", "created", "lop") // eid = 11 + ), traversal); + } + + /** + * Order asc by vertex and then vertex property id in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value() { + final Traversal traversal = get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "lop", // vid = 3, vpid = 4 + "java", // vid = 3, vpid = 5 + "ripple", // vid = 5, vpid = 8 + "java" // vid = 5, vpid = 9 + ), traversal); + } + + /** + * Order asc by vertex and then vertex property value in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "java", // vid = 3, val = "java" + "lop", // vid = 3, val = "lop" + "java", // vid = 5, val = "java" + "ripple" // vid = 5, val = "ripple" + ), traversal); + } + + /** + * Order desc by vertex: v[5], v[3] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_order_byXdescX() { + final Traversal traversal = get_g_V_out_out_order_byXdescX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("ripple"), // vid = 5 + convertToVertex("lop") // vid = 3 + ), traversal); + } + + /** + * Order desc by vertex in path: v[5], v[3] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToVertex("ripple"), // vid = 5 + convertToVertex("lop") // vid = 3 + ), traversal); + } + + /** + * Order desc by edge: e[11], v[e10] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_order_byXdescX() { + final Traversal traversal = get_g_V_out_outE_order_byXdescX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "lop"), // eid = 11 + convertToEdge("josh", "created", "ripple") // eid = 10 + ), traversal); + } + + /** + * Order desc by edge in path: e[11], e[10] + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = EdgeFeatures.class, feature = EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + convertToEdge("josh", "created", "lop"), // eid = 11 + convertToEdge("josh", "created", "ripple") // eid = 10 + ), traversal); + } + + /** + * Order desc by vertex and then vertex property id in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = VertexPropertyFeatures.class, feature = VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value() { + final Traversal traversal = get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "java", // vid = 5, vpid = 9 + "ripple", // vid = 5, vpid = 8 + "java", // vid = 3, vpid = 5 + "lop" // vid = 3, vpid = 4 + ), traversal); + } + + /** + * Order desc by vertex and then vertex property value in path. + */ + @Test + @LoadGraphWith(MODERN) + @FeatureRequirement(featureClass = GraphFeatures.class, feature = GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS) + @FeatureRequirement(featureClass = VertexFeatures.class, feature = VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + public void g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX() { + final Traversal traversal = get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX(); + printTraversalForm(traversal); + checkOrderedResults(Arrays.asList( + "ripple", // vid = 5, val = "ripple" + "java", // vid = 5, val = "java" + "lop", // vid = 3, val = "lop" + "java" // vid = 3, val = "java" + ), traversal); + } + + public static class Traversals extends OrderabilityTest implements OrderabilityTest.Constants { + + @Override + public Traversal get_g_V_values_order() { + return g.V().values().order(); + } + + @Override + public Traversal get_g_V_properties_order() { + return g.V().properties().order(); + } + + @Override + public Traversal get_g_E_properties_order_value() { + return g.E().properties().order().value(); + } + + @Override + public Traversal get_g_E_properties_order_byXdescX_value() { + return g.E().properties().order().by(Order.desc).value(); + } + + @Override + public Traversal get_g_inject_order() { + return g.inject(unordered).order(); + } + + // order asc by vertex: v[3], v[5] + @Override + public Traversal get_g_V_out_out_order_byXascX() { + return g.V().out().out().order().by(Order.asc); + } + + // order asc by vertex in path: v[3], v[5] + @Override + public Traversal get_g_V_out_out_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().out().as("head").path().order().by(Order.asc).select("head"); + } + + // order asc by edge: e[10], v[e11] + @Override + public Traversal get_g_V_out_outE_order_byXascX() { + return g.V().out().outE().order().by(Order.asc); + } + + // order asc by edge in path: e[10], e[11] + @Override + public Traversal get_g_V_out_outE_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().outE().as("head").path().order().by(Order.asc).select("head"); + } + + // order asc by vertex and then vertex property id in path. + @Override + public Traversal get_g_V_out_out_properties_asXheadX_path_order_byXascX_selectXheadX_value() { + return g.V().out().out().properties().as("head").path().order().by(Order.asc).select("head").value(); + } + + // order asc by vertex and then vertex property value in path. + @Override + public Traversal get_g_V_out_out_values_asXheadX_path_order_byXascX_selectXheadX() { + return g.V().out().out().values().as("head").path().order().by(Order.asc).select("head"); + } + + // order desc by vertex: v[5], v[3] + @Override + public Traversal get_g_V_out_out_order_byXdescX() { + return g.V().out().out().order().by(Order.desc); + } + + // order desc by vertex in path: v[5], v[3] + @Override + public Traversal get_g_V_out_out_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().out().as("head").path().order().by(Order.desc).select("head"); + } + + // order desc by edge: e[11], v[e10] + @Override + public Traversal get_g_V_out_outE_order_byXdescX() { + return g.V().out().outE().order().by(Order.desc); + } + + // order desc by edge in path: e[11], e[10] + @Override + public Traversal get_g_V_out_outE_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().outE().as("head").path().order().by(Order.desc).select("head"); + } + + // order desc by vertex and then vertex property id in path. + @Override + public Traversal get_g_V_out_out_properties_asXheadX_path_order_byXdescX_selectXheadX_value() { + return g.V().out().out().properties().as("head").path().order().by(Order.desc).select("head").value(); + } + + // order desc by vertex and then vertex property value in path. + @Override + public Traversal get_g_V_out_out_values_asXheadX_path_order_byXdescX_selectXheadX() { + return g.V().out().out().values().as("head").path().order().by(Order.desc).select("head"); + } + + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java new file mode 100644 index 0000000..6616379 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/process/traversal/step/map/MergeEdgeTest.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.arangodb.tinkerpop.gremlin.custom.process.traversal.step.map; + +import java.util.Map; + +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.FeatureRequirementSet; +import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner; +import org.apache.tinkerpop.gremlin.process.traversal.Merge; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.select; +import static org.apache.tinkerpop.gremlin.util.CollectionUtil.asMap; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.StringEndsWith.endsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; + +@RunWith(GremlinProcessRunner.class) +public abstract class MergeEdgeTest extends AbstractGremlinTest { + + public abstract Traversal get_g_V_mergeEXlabel_self_weight_05X(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists(); + + public abstract Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + + public abstract Traversal get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + + public abstract Traversal, Edge> get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE(); + + public abstract Traversal get_g_mergeE_with_outV_inV_options(); + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_V_mergeEXlabel_self_weight_05X() { + g.addV("person").property("name", "stephen").iterate(); + final Traversal traversal = get_g_V_mergeEXlabel_self_weight_05X(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("self", edge.label()); + assertEquals(0.5d, edge.value("weight").doubleValue(), 0.0001d); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX() { + g.addV("person").property(T.id, "100").property("name", "marko"). + addV("person").property(T.id, "101").property("name", "vadas").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("standard_person/100", edge.outVertex().id()); + assertEquals("standard_person/101", edge.inVertex().id()); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists() { + g.addV("person").property(T.id, "100").property("name", "marko").as("a"). + addV("person").property(T.id, "101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("standard_person/100", edge.outVertex().id()); + assertEquals("standard_person/101", edge.inVertex().id()); + assertEquals(0.5d, edge.value("weight").doubleValue(), 0.0001d); + assertFalse(traversal.hasNext()); + assertEquals(2, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX() { + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX(); + printTraversalForm(traversal); + try { + traversal.next(); + fail("Should have failed as vertices are not created"); + } catch (Exception ex) { + assertThat(ex.getMessage(), endsWith("Vertex id could not be resolved from mergeE: standard_person/100")); + } + assertEquals(0, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists() { + g.addV("person").property(T.id, "100").property("name", "marko").as("a"). + addV("person").property(T.id, "101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("standard_person/100", edge.outVertex().id()); + assertEquals("standard_person/101", edge.inVertex().id()); + assertEquals("N", edge.value("created")); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + g.addV("person").property(T.id, "standard_person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "standard_person/101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").property("created","Y").iterate(); + final Traversal traversal = get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + printTraversalForm(traversal); + final Edge edge = traversal.next(); + assertEquals("knows", edge.label()); + assertEquals("standard_person/100", edge.outVertex().id()); + assertEquals("standard_person/101", edge.inVertex().id()); + assertEquals("N", edge.value("created")); + assertFalse(traversal.hasNext()); + assertEquals(1, IteratorUtils.count(g.E())); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + g.addV("person").property(T.id, "standard_person/100").property("name", "marko").as("a"). + addV("person").property(T.id, "standard_person/101").property("name", "vadas").as("b"). + addE("knows").from("a").to("b").property("created","Y"). + addE("knows").from("a").to("b").iterate(); + final Traversal traversal = get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated(); + printTraversalForm(traversal); + + assertEquals(2, IteratorUtils.count(traversal)); + assertEquals(2, IteratorUtils.count(g.V())); + assertEquals(2, IteratorUtils.count(g.E())); + assertEquals(2, IteratorUtils.count(g.E().hasLabel("knows").has("created", "N"))); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_injectXlabel_knows_out_marko_in_vadasX_mergeE() { + g.addV("person").property(T.id, "standard_person/100").property("name", "marko"). + addV("person").property(T.id, "standard_person/101").property("name", "vadas").iterate(); + final Traversal, Edge> traversal = get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE(); + printTraversalForm(traversal); + + assertEquals(1, IteratorUtils.count(traversal)); + assertEquals(2, IteratorUtils.count(g.V())); + assertEquals(1, IteratorUtils.count(g.E())); + assertEquals(1, IteratorUtils.count(g.V("standard_person/100").out("knows").hasId("standard_person/101"))); + } + + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE) + public void g_mergeE_with_outV_inV_options() { + g.addV("person").property(T.id, "standard_person/1") + .addV("person").property(T.id, "standard_person/2") + .iterate(); + + final Traversal traversal = get_g_mergeE_with_outV_inV_options(); + printTraversalForm(traversal); + + assertEquals(1, IteratorUtils.count(traversal)); + assertEquals(1, IteratorUtils.count(g.V("standard_person/1").out("knows").hasId("standard_person/2"))); + } + + public static class Traversals extends MergeEdgeTest { + + @Override + public Traversal get_g_V_mergeEXlabel_self_weight_05X() { + return g.V().as("v").mergeE(asMap(T.label, "self", "weight", 0.5d, Direction.OUT, Merge.outV, Direction.IN, Merge.inV)).option(Merge.outV, select("v")).option(Merge.inV, select("v")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "weight", 0.5d)); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + return g.mergeE(asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal get_g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated() { + return g.V().has("person","name","marko"). + mergeE(asMap(T.label, "knows")). + option(Merge.onCreate, asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"), "created", "Y")). + option(Merge.onMatch, asMap("created", "N")); + } + + @Override + public Traversal, Edge> get_g_injectXlabel_knows_out_marko_in_vadasX_mergeE() { + return g.inject((Map) asMap(T.label, "knows", Direction.IN, new ReferenceVertex("standard_person/101"), Direction.OUT, new ReferenceVertex("standard_person/100"))).mergeE(); + } + + @Override + public Traversal get_g_mergeE_with_outV_inV_options() { + return g.mergeE(asMap(T.label, "knows", Direction.OUT, Merge.outV, Direction.IN, Merge.inV)) + .option(Merge.outV, asMap(T.id, "standard_person/1")) + .option(Merge.inV, asMap(T.id, "standard_person/2")); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java new file mode 100644 index 0000000..d2e50b8 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/detached/DetachedGraphTest.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.arangodb.tinkerpop.gremlin.custom.structure.util.detached; + +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.TestHelper; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Random; +import java.util.UUID; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class DetachedGraphTest extends AbstractGremlinTest { + + // FIXME: review after DE-996 + @Ignore("FIXME") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + public void testAttachableCreateMethod() { + final Random random = TestHelper.RANDOM; + StarGraph starGraph = StarGraph.open(); + final String label = "person"; + final String id = label + "/" + UUID.randomUUID(); + Vertex starVertex = starGraph.addVertex(T.id, id, T.label, label, "name", "stephen", "name", "spmallete"); + starVertex.property("acl", true, "timestamp", random.nextLong(), "creator", "marko"); + for (int i = 0; i < 100; i++) { + starVertex.addEdge("knows", starGraph.addVertex("person", "name", new UUID(random.nextLong(), random.nextLong()), "since", random.nextLong())); + starGraph.addVertex(T.label, "project").addEdge("developedBy", starVertex, "public", random.nextBoolean()); + } + final DetachedVertex detachedVertex = DetachedFactory.detach(starGraph.getStarVertex(), true); + final Vertex createdVertex = detachedVertex.attach(Attachable.Method.create(graph)); + TestHelper.validateVertexEquality(detachedVertex, createdVertex, false); + TestHelper.validateVertexEquality(detachedVertex, starVertex, false); + + starGraph.getStarVertex().edges(Direction.BOTH).forEachRemaining(starEdge -> { + final DetachedEdge detachedEdge = DetachedFactory.detach(starEdge, true); + final Edge createdEdge = detachedEdge.attach(Attachable.Method.create(random.nextBoolean() ? graph : createdVertex)); + TestHelper.validateEdgeEquality(detachedEdge, starEdge); + TestHelper.validateEdgeEquality(detachedEdge, createdEdge); + }); + + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java new file mode 100644 index 0000000..0474b7a --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/custom/structure/util/star/StarGraphTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.arangodb.tinkerpop.gremlin.custom.structure.util.star; + +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.TestHelper; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class StarGraphTest extends AbstractGremlinTest { + + @Ignore("FIXME: DE-996") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + public void shouldCopyFromGraphAToGraphB() throws Exception { + final List starGraphs = IteratorUtils.stream(graph.vertices()).map(StarGraph::of).collect(Collectors.toList()); + + // via vertices and then edges + final Configuration g1Configuration = graphProvider.newGraphConfiguration("readGraph", this.getClass(), name.getMethodName(), null); + Graph g1 = graphProvider.openTestGraph(g1Configuration); + starGraphs.stream().map(StarGraph::getStarVertex).forEach(vertex -> vertex.attach(Attachable.Method.getOrCreate(g1))); + starGraphs.stream().forEach(starGraph -> starGraph.edges().forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.getOrCreate(g1)))); + assertEquals(IteratorUtils.count(graph.vertices()), IteratorUtils.count(g1.vertices())); + assertEquals(IteratorUtils.count(graph.edges()), IteratorUtils.count(g1.edges())); + graph.vertices().forEachRemaining(vertex -> TestHelper.validateVertexEquality(vertex, g1.vertices(vertex.id()).next(), true)); + graphProvider.clear(g1, g1Configuration); + + // via edges only + final Configuration g2Configuration = graphProvider.newGraphConfiguration("readGraph", this.getClass(), name.getMethodName(), null); + final Graph g2 = graphProvider.openTestGraph(g2Configuration); + starGraphs.stream().forEach(starGraph -> starGraph.edges().forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.getOrCreate(g2)))); + assertEquals(IteratorUtils.count(graph.vertices()), IteratorUtils.count(g2.vertices())); + assertEquals(IteratorUtils.count(graph.edges()), IteratorUtils.count(g2.edges())); + // TODO: you can't get adjacent labels -- graph.vertices().forEachRemaining(vertex -> TestHelper.validateVertexEquality(vertex, g1.vertices(vertex.id()).next(), true)); + graphProvider.clear(g2, g2Configuration); + } + + @Ignore("FIXME: DE-996") + @Test + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexPropertyFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_USER_SUPPLIED_IDS) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_PROPERTY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_META_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_MULTI_PROPERTIES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES) + @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_PROPERTY) + public void shouldAttachWithCreateMethod() { + final Random random = TestHelper.RANDOM; + StarGraph starGraph = StarGraph.open(); + Vertex starVertex = starGraph.addVertex(T.id, "person/" + UUID.randomUUID(), T.label, "person", "name", "stephen", "name", "spmallete"); + starVertex.property("acl", true, "timestamp", random.nextLong(), "creator", "marko"); + for (int i = 0; i < 100; i++) { + starVertex.addEdge( + "knows", + starGraph.addVertex(T.id, "person/" + UUID.randomUUID(), T.label, "person", "name", new UUID(random.nextLong(), random.nextLong()), "since", random.nextLong()), + T.id, "knows/" + UUID.randomUUID() + ); + starGraph + .addVertex(T.id, "project/" + UUID.randomUUID(), T.label, "project") + .addEdge("developedBy", starVertex, T.id, "developedBy/" + UUID.randomUUID(), "public", random.nextBoolean()); + } + final Vertex createdVertex = starGraph.getStarVertex().attach(Attachable.Method.create(graph)); + starGraph.getStarVertex().edges(Direction.BOTH).forEachRemaining(edge -> ((Attachable) edge).attach(Attachable.Method.create(random.nextBoolean() ? graph : createdVertex))); + TestHelper.validateEquality(starVertex, createdVertex); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java new file mode 100644 index 0000000..1562cd1 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessGraphProvider.java @@ -0,0 +1,112 @@ +package com.arangodb.tinkerpop.gremlin.process; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; + +public class ProcessGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, TestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + switch (testMethodName) { + case "g_addV_asXfirstX_repeatXaddEXnextX_toXaddVX_inVX_timesX5X_addEXnextX_toXselectXfirstXX": + builder.withEdgeCollection("next"); + break; + case "shouldGenerateDefaultIdOnAddEWithSpecifiedId": + case "shouldSetIdOnAddEWithNamePropertyKeySpecifiedAndNameSuppliedAsProperty": + case "shouldSetIdOnAddEWithIdPropertyKeySpecifiedAndNameSuppliedAsProperty": + case "shouldGenerateDefaultIdOnAddEWithGeneratedId": + case "shouldTriggerAddEdgePropertyAdded": + case "shouldReferencePropertyOfEdgeWhenRemoved": + case "shouldTriggerAddEdge": + case "shouldTriggerRemoveEdge": + case "shouldTriggerRemoveEdgeProperty": + case "shouldReferenceEdgeWhenRemoved": + case "shouldUseActualEdgeWhenAdded": + case "shouldDetachEdgeWhenAdded": + case "shouldUseActualEdgeWhenRemoved": + case "shouldDetachPropertyOfEdgeWhenNew": + case "shouldDetachPropertyOfEdgeWhenChanged": + case "shouldUseActualPropertyOfEdgeWhenChanged": + case "shouldDetachEdgeWhenRemoved": + case "shouldTriggerUpdateEdgePropertyAddedViaMergeE": + case "shouldDetachPropertyOfEdgeWhenRemoved": + case "shouldUseActualPropertyOfEdgeWhenRemoved": + case "shouldUseActualPropertyOfEdgeWhenNew": + case "shouldTriggerEdgePropertyChanged": + case "shouldTriggerAddEdgeViaMergeE": + case "shouldReferencePropertyOfEdgeWhenNew": + case "shouldReferenceEdgeWhenAdded": + case "shouldReferencePropertyOfEdgeWhenChanged": + case "shouldTriggerAddEdgeByPath": + case "shouldWriteToMultiplePartitions": + case "shouldAppendPartitionToEdge": + case "shouldThrowExceptionOnEInDifferentPartition": + builder.withEdgeCollection("self"); + builder.withEdgeCollection("self-but-different"); + builder.withEdgeCollection("aTOa"); + builder.withEdgeCollection("aTOb"); + builder.withEdgeCollection("aTOc"); + builder.withEdgeCollection("bTOc"); + builder.withEdgeCollection("connectsTo"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("relatesTo"); + break; + case "g_io_read_withXreader_graphsonX": + case "g_io_read_withXreader_gryoX": + case "g_io_read_withXreader_graphmlX": + case "g_io_readXjsonX": + case "g_io_readXkryoX": + case "g_io_readXxmlX": + builder.withVertexCollection("person"); + builder.withVertexCollection("software"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + break; + case "g_addV_propertyXlabel_personX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadas_weight_05X_exists": + case "g_V_hasXperson_name_marko_X_mergeEXlabel_knowsX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists_updated": + case "g_mergeEXlabel_knows_out_marko_in_vadasX": + case "g_mergeEXlabel_knows_out_marko_in_vadasX_optionXonCreate_created_YX_optionXonMatch_created_NX_exists": + case "g_V_mergeEXlabel_self_weight_05X": + case "g_mergeE_with_outV_inV_options": + case "g_injectXlabel_knows_out_marko_in_vadasX_mergeE": + builder.withVertexCollection("person"); + builder.withEdgeCollection("self"); + break; + case "g_V_hasXname_regexXTinkerXX": + case "g_V_hasXname_regexXTinkerUnicodeXX": + builder.withVertexCollection("software"); + break; + case "shouldDetachVertexWhenAdded": + case "shouldReferenceVertexWhenAdded": + case "shouldUseActualVertexWhenAdded": + builder.withVertexCollection("thing"); + break; + case "shouldAppendPartitionToAllVertexProperties": + builder.withVertexCollection("person"); + builder.withVertexCollection("vertex"); + builder.configureEdge("edge", "person", "person"); + break; + case "shouldPartitionWithAbstractLambdaChildTraversal": + builder.withVertexCollection("testV"); + builder.withEdgeCollection("self"); + break; + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java new file mode 100644 index 0000000..19f8451 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/process/ProcessStandardSuiteTest.java @@ -0,0 +1,12 @@ +package com.arangodb.tinkerpop.gremlin.process; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.apache.tinkerpop.gremlin.process.ProcessStandardSuite; +import org.junit.runner.RunWith; + +@RunWith(ProcessStandardSuite.class) +@GraphProviderClass(provider = ProcessGraphProvider.class, graph = TestGraph.class) +public class ProcessStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java deleted file mode 100644 index 0bf0dca..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.arangodb.tinkerpop.gremlin.structure; - -import org.apache.tinkerpop.gremlin.AbstractGremlinTest; - -public class ArangoDBGremlinTest extends AbstractGremlinTest { - - protected ArangoDBGraph getGraph() { - return (ArangoDBGraph) this.graph; - } -} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java new file mode 100644 index 0000000..889c769 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureGraphProvider.java @@ -0,0 +1,177 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.util.BaseGraphProvider; + +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.VertexTest; + +import java.util.Map; + +public class StructureGraphProvider extends BaseGraphProvider { + + @Override + public Configuration newGraphConfiguration(String graphName, Class test, String testMethodName, Map configurationOverrides, LoadGraphWith.GraphData loadGraphWith) { + Configuration conf = super.newGraphConfiguration(graphName, test, testMethodName, configurationOverrides, loadGraphWith); + conf.setProperty(Graph.GRAPH, TestGraph.class.getName()); + return conf; + } + + @Override + protected void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName) { + if (testMethodName.startsWith("shouldProcessVerticesEdges") + || testMethodName.startsWith("shouldGenerate") + || testMethodName.startsWith("shouldSetValueOnEdge") + || testMethodName.startsWith("shouldAutotype")) { + builder.withEdgeCollection("knows"); + } else if (testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldSupportUserSuppliedIds")) { + builder.withEdgeCollection("test"); + } else if (testMethodName.startsWith("shouldSupportUUID")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldReadWriteEdge")) { + builder.withVertexCollection("person"); + builder.withEdgeCollection("friend"); + } else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { + builder.withEdgeCollection("self"); + } else if (testMethodName.startsWith("shouldReadWriteVerticesNoEdgesToGryoManual") || + testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges") || + testMethodName.startsWith("shouldReadWriteVerticesNoEdgesToGraphSONManual") || + testMethodName.startsWith("shouldReadWriteVerticesNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexWithINEdges") || + testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges") || + testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexNoEdges") || + testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges") || + testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { + builder.withVertexCollection("vertex"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("friends"); + builder.configureEdge("friends", "person", "person"); + } else { + // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? + switch (testMethodName) { + case "shouldGetPropertyKeysOnEdge": + case "shouldNotGetConcurrentModificationException": + builder.withEdgeCollection("friend"); + builder.withEdgeCollection("knows"); + break; + case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": + case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": + builder.withEdgeCollection("hate"); + builder.withEdgeCollection("friend"); + break; + case "shouldPersistDataOnClose": + builder.withEdgeCollection("collaborator"); + break; + case "shouldTestTreeConnectivity": + builder.withEdgeCollection("test1"); + builder.withEdgeCollection("test2"); + builder.withEdgeCollection("test3"); + break; + case "shouldEvaluateConnectivityPatterns": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("hates"); + break; + case "shouldRemoveEdgesWithoutConcurrentModificationException": + builder.withEdgeCollection("link"); + break; + case "shouldGetValueThatIsNotPresentOnEdge": + case "shouldHaveStandardStringRepresentationForEdgeProperty": + case "shouldHaveTruncatedStringRepresentationForEdgeProperty": + case "shouldValidateIdEquality": + case "shouldValidateEquality": + case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": + case "shouldAddEdgeWithUserSuppliedStringId": + case "shouldAllowNullAddEdge": + builder.withEdgeCollection("self"); + break; + case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": + case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": + case "shouldProcessEdges": + case "shouldReturnOutThenInOnVertexIterator": + case "shouldReturnEmptyIteratorIfNoProperties": + builder.withEdgeCollection("knows"); + break; + case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("pets"); + builder.withEdgeCollection("walks"); + builder.withEdgeCollection("livesWith"); + break; + case "shouldHaveStandardStringRepresentation": + builder.withEdgeCollection("friends"); + break; + case "shouldReadWriteSelfLoopingEdges": + builder.withEdgeCollection("CONTROL"); + builder.withEdgeCollection("SELFLOOP"); + break; + case "shouldReadGraphML": + case "shouldReadGraphMLUnorderedElements": + case "shouldTransformGraphMLV2ToV3ViaXSLT": + case "shouldReadLegacyGraphSON": + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + break; + case "shouldAddVertexWithLabel": + case "shouldAllowNullAddVertexProperty": + builder.withVertexCollection("person"); + break; + case "shouldNotAllowSetProperty": + case "shouldHashAndEqualCorrectly": + case "shouldNotAllowRemove": + case "shouldNotConstructNewWithSomethingAlreadyDetached": + case "shouldNotConstructNewWithSomethingAlreadyReferenced": + builder.withEdgeCollection("test"); + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertex": + builder.withEdgeCollection("tonothing"); + break; + case "shouldHandleSelfLoops": + builder.withVertexCollection("person"); + builder.withEdgeCollection("self"); + break; + case "testAttachableCreateMethod": + builder.withVertexCollection("person"); + builder.withVertexCollection("project"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("developedBy"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("developedBy", "project", "person"); + break; + case "shouldConstructReferenceVertex": + builder.withVertexCollection("blah"); + break; + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": + case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": + if (VertexTest.class.equals(test.getEnclosingClass())) { + builder.withVertexCollection("foo"); + } + break; + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": + case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": + builder.withVertexCollection("foo"); + break; + case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": + builder.withEdgeCollection("created"); + builder.withEdgeCollection("knows"); + break; + } + } + } +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java new file mode 100644 index 0000000..5a330be --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/structure/StructureStandardSuiteTest.java @@ -0,0 +1,12 @@ +package com.arangodb.tinkerpop.gremlin.structure; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import org.apache.tinkerpop.gremlin.GraphProviderClass; +import org.apache.tinkerpop.gremlin.structure.StructureStandardSuite; +import org.junit.runner.RunWith; + +@RunWith(StructureStandardSuite.class) +@GraphProviderClass(provider = StructureGraphProvider.class, graph = TestGraph.class) +public class StructureStandardSuiteTest { + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf b/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf deleted file mode 100644 index 30c495d..0000000 --- a/src/test/java/com/arangodb/tinkerpop/gremlin/test.conf +++ /dev/null @@ -1,11 +0,0 @@ -gremlin.graph = com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph -gremlin.arangodb.conf.graph.db = Test02 -gremlin.arangodb.conf.graph.name = Test02Graph01 -gremlin.arangodb.conf.graph.vertex = testfrom -gremlin.arangodb.conf.graph.vertex = testto -gremlin.arangodb.conf.graph.edge = testedge -gremlin.arangodb.conf.graph.relation = testedge:testfrom->testto -gremlin.arangodb.conf.graph.shouldPrefixCollectionNames = false -gremlin.arangodb.conf.arangodb.hosts = 127.0.0.1:8529 -gremlin.arangodb.conf.arangodb.user = xxxx -gremlin.arangodb.conf.arangodb.password = xxxx \ No newline at end of file diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java new file mode 100644 index 0000000..086a8d7 --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/BaseGraphProvider.java @@ -0,0 +1,156 @@ +package com.arangodb.tinkerpop.gremlin.util; + +import com.arangodb.tinkerpop.gremlin.TestGraph; +import com.arangodb.tinkerpop.gremlin.custom.CustomTestGraph; +import com.arangodb.tinkerpop.gremlin.structure.*; +import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.ConfigurationConverter; +import org.apache.tinkerpop.gremlin.AbstractGraphProvider; +import org.apache.tinkerpop.gremlin.LoadGraphWith; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class BaseGraphProvider extends AbstractGraphProvider { + + private final String dbName = getClass().getSimpleName(); + + protected abstract void configure(ArangoDBConfigurationBuilder builder, Class test, String testMethodName); + + @Override + public Configuration newGraphConfiguration(final String graphName, final Class test, + final String testMethodName, + final Map configurationOverrides, + final LoadGraphWith.GraphData loadGraphWith) { + return getConfiguration(graphName, test, testMethodName, loadGraphWith); + } + + private Configuration getConfiguration( + String graphName, + Class test, + String testMethodName, + LoadGraphWith.GraphData loadGraphWith) { + System.out.println("case \"" + test.getCanonicalName() + "." + testMethodName + "\":"); + ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() + .arangoHosts("127.0.0.1:8529") + .arangoUser("root") + .arangoPassword("test") + .dataBase(dbName) + .graph(graphName); + if (loadGraphWith != null) { + switch (loadGraphWith) { + case CLASSIC: + System.out.println("CLASSIC"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.configureEdge("knows", "vertex", "vertex"); + builder.configureEdge("created", "vertex", "vertex"); + break; + case MODERN: + System.out.println("MODERN"); + builder.withVertexCollection("name"); + builder.withVertexCollection("vertex"); + builder.withVertexCollection("animal"); + builder.withVertexCollection("dog"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("knows"); + builder.withEdgeCollection("created"); + builder.withEdgeCollection("createdBy"); + builder.withEdgeCollection("existsWith"); + builder.withEdgeCollection("codeveloper"); + builder.withEdgeCollection("uses"); + builder.configureEdge("knows", "person", "person"); + builder.configureEdge("created", "person", "software"); + builder.configureEdge("createdBy", "software", "person"); + builder.configureEdge("existsWith", "software", "software"); + builder.configureEdge("codeveloper", "person", "person"); + builder.configureEdge("uses", "person", "software"); + break; + case CREW: + System.out.println("CREW"); + builder.withVertexCollection("software"); + builder.withVertexCollection("person"); + builder.withEdgeCollection("uses"); + builder.withEdgeCollection("develops"); + builder.withEdgeCollection("traverses"); + builder.configureEdge("uses", "person", "software"); + builder.configureEdge("develops", "person", "software"); + builder.configureEdge("traverses", "software", "software"); + break; + case GRATEFUL: + System.out.println("GRATEFUL"); + builder.withVertexCollection("vertex"); + builder.withVertexCollection("song"); + builder.withVertexCollection("artist"); + builder.withEdgeCollection("followedBy"); + builder.withEdgeCollection("sungBy"); + builder.withEdgeCollection("writtenBy"); + builder.configureEdge("followedBy", "vertex", "vertex"); + builder.configureEdge("sungBy", "song", "artist"); + builder.configureEdge("writtenBy", "song", "artist"); + break; + case SINK: + System.out.println("SINK"); + builder.withVertexCollection("loops"); + builder.withVertexCollection("message"); + builder.withEdgeCollection("link"); + builder.withEdgeCollection("self"); + builder.configureEdge("self", "loops", "loops"); + builder.configureEdge("link", "message", "message"); + break; + } + } else { + configure(builder, test, testMethodName); + } + return builder.build(); + } + + @Override + public void clear(Graph graph, Configuration configuration) throws Exception { + Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); + Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); + TestGraphClient client = new TestGraphClient(arangoProperties, dbName); + client.clear(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); + if (graph != null) { + graph.close(); + } + } + + @Override + @SuppressWarnings("rawtypes") + public Set getImplementations() { + return Stream.of( + ArangoDBEdge.class, + ArangoDBElement.class, + TestGraph.class, + CustomTestGraph.class, + ArangoDBGraph.class, + ArangoDBGraphVariables.class, + ArangoDBPersistentElement.class, + ArangoDBProperty.class, + ArangoDBSimpleElement.class, + ArangoDBVertex.class, + ArangoDBVertexProperty.class + ).collect(Collectors.toSet()); + } + + @Override + public Map getBaseConfiguration(String graphName, Class test, String testMethodName, + LoadGraphWith.GraphData loadGraphWith) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object convertId(Object id, Class c) { + return id.toString(); + } + +} diff --git a/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java new file mode 100644 index 0000000..d6a537a --- /dev/null +++ b/src/test/java/com/arangodb/tinkerpop/gremlin/util/TestGraphClient.java @@ -0,0 +1,40 @@ +package com.arangodb.tinkerpop.gremlin.util; + +import com.arangodb.ArangoDBException; +import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; +import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; +import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; + +import java.util.Properties; + +public class TestGraphClient extends ArangoDBGraphClient { + + public TestGraphClient(Properties properties, String dbname) throws ArangoDBGraphException { + super(null, properties, dbname); + if (!db.exists()) { + if (!db.create()) { + throw new ArangoDBGraphException("Unable to crate the database " + dbname); + } + } + } + + public void clear(String name) { + try { + db.collection(ArangoDBGraph.GRAPH_VARIABLES_COLLECTION).deleteDocument(name); + } catch (ArangoDBException e) { + if (e.getErrorNum() != 1202 // document not found + && e.getErrorNum() != 1203 // collection not found + ) throw e; + } + + try { + db.graph(name).drop(true); + } catch (ArangoDBException e) { + if (e.getErrorNum() != 1924) // graph not found + throw e; + } + + db.clearQueryCache(); + } + +} diff --git a/src/test/resources/arangodb.properties b/src/test/resources/arangodb.properties deleted file mode 100644 index 56c9a88..0000000 --- a/src/test/resources/arangodb.properties +++ /dev/null @@ -1,3 +0,0 @@ -port=8529 -host=localhost -enableCURLLogger=false diff --git a/tests/travis/setup_arangodb.sh b/tests/travis/setup_arangodb.sh deleted file mode 100644 index c02f450..0000000 --- a/tests/travis/setup_arangodb.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd $DIR - -VERSION=2.8.11 -NAME=ArangoDB-$VERSION - -if [ ! -d "$DIR/$NAME" ]; then - # download ArangoDB - echo "wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz" - wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz - echo "tar zxf $NAME.tar.gz" - tar zvxf $NAME.tar.gz -fi - -ARCH=$(arch) -PID=$(echo $PPID) -TMP_DIR="/tmp/arangodb.$PID" -PID_FILE="/tmp/arangodb.$PID.pid" -ARANGODB_DIR="$DIR/$NAME" -ARANGOD="${ARANGODB_DIR}/bin/arangod_x86_64" - -# create database directory -mkdir ${TMP_DIR} - -echo "Starting ArangoDB '${ARANGOD}'" - -${ARANGOD} \ - --database.directory ${TMP_DIR} \ - --configuration none \ - --server.endpoint tcp://127.0.0.1:8529 \ - --javascript.app-path ${ARANGODB_DIR}/js/apps \ - --javascript.startup-directory ${ARANGODB_DIR}/js \ - --server.disable-authentication true & - -sleep 2 - -echo "Check for arangod process" -process=$(ps auxww | grep "bin/arangod" | grep -v grep) - -if [ "x$process" == "x" ]; then - echo "no 'arangod' process found" - echo "ARCH = $ARCH" - exit 1 -fi - -echo "Waiting until ArangoDB is ready on port 8529" -while [[ -z `curl -uroot: -s 'http://127.0.0.1:8529/_api/version' ` ]] ; do - echo -n "." - sleep 2s -done - -echo "ArangoDB is up"