├── docker ├── jwtSecret ├── jwtHeader ├── server.pem └── start_db.sh ├── src ├── test │ ├── resources │ │ ├── arangodb.properties │ │ └── logback-test.xml │ └── java │ │ └── com │ │ └── arangodb │ │ └── tinkerpop │ │ └── gremlin │ │ ├── structure │ │ └── ArangoDBGremlinTest.java │ │ ├── ArangoDBGraphTest.java │ │ ├── test.conf │ │ ├── Issue57.java │ │ ├── client │ │ └── test │ │ │ ├── BaseTestCase.java │ │ │ └── ClientTest.java │ │ ├── ArangoDBTestSuite.java │ │ └── ArangoDBGraphProvider.java ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin │ └── java │ │ └── com │ │ └── arangodb │ │ └── tinkerpop │ │ └── gremlin │ │ ├── structure │ │ ├── ArangoDBVertexData.java │ │ ├── PropertiesContainer.java │ │ ├── ArangoDBPropertyData.java │ │ ├── ArangoDBEdgeData.java │ │ ├── ArangoDBVertexPropertyData.java │ │ ├── ArangoDBData.java │ │ ├── ArangoDBProperty.java │ │ ├── ArangoDBVertexProperty.java │ │ ├── ArangoDBGraphVariables.java │ │ ├── ArangoDBEdge.java │ │ └── ArangoDBVertex.java │ │ ├── client │ │ ├── ArangoDBGraphException.java │ │ ├── ArangoDBBaseDocument.java │ │ ├── ArangoDBPropertyFilter.java │ │ └── ArangoDBQueryBuilder.java │ │ ├── jsr223 │ │ └── ArangoDBGremlinPlugin.java │ │ └── utils │ │ ├── ArangoDBConfigurationBuilder.java │ │ └── ArangoDBUtil.java └── assembly │ ├── distribution.xml │ └── standalone.xml ├── .github ├── FUNDING.yml └── workflows │ └── maven.yml ├── .gitignore ├── tests └── travis │ └── setup_arangodb.sh ├── CHANGELOG.md ├── ci-prod └── pom.xml ├── README.md ├── pom.xml ├── jautodoc_templates.xml └── LICENSE /docker/jwtSecret: -------------------------------------------------------------------------------- 1 | Averysecretword 2 | -------------------------------------------------------------------------------- /src/test/resources/arangodb.properties: -------------------------------------------------------------------------------- 1 | port=8529 2 | host=localhost 3 | enableCURLLogger=false 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: arcanefoam 4 | custom: https://www.buymeacoffee.com/KinoriTech 5 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin: -------------------------------------------------------------------------------- 1 | com.arangodb.tinkerpop.gremlin.jsr223.ArangoDBGremlinPlugin 2 | -------------------------------------------------------------------------------- /docker/jwtHeader: -------------------------------------------------------------------------------- 1 | Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhcmFuZ29kYiIsInNlcnZlcl9pZCI6ImZvbyJ9.QmuhPHkmRPJuHGxsEqggHGRyVXikV44tb5YU_yWEvEM 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.iml 3 | .idea 4 | target 5 | .classpath 6 | .project 7 | .settings/ 8 | 9 | # Package Files # 10 | *.war 11 | *.ear 12 | /bin/ 13 | 14 | \.DS_Store 15 | 16 | old/ 17 | 18 | tikerpopTests\.log 19 | 20 | ArangoDBGraphTest* -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGremlinTest.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import org.apache.tinkerpop.gremlin.AbstractGremlinTest; 4 | 5 | public class ArangoDBGremlinTest extends AbstractGremlinTest { 6 | 7 | protected ArangoDBGraph getGraph() { 8 | return (ArangoDBGraph) this.graph; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphTest.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin; 2 | 3 | import org.apache.tinkerpop.gremlin.GraphProviderClass; 4 | import org.junit.runner.RunWith; 5 | 6 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 7 | 8 | @RunWith(ArangoDBTestSuite.class) 9 | @GraphProviderClass(provider = ArangoDBGraphProvider.class, graph = ArangoDBGraph.class) 10 | public class ArangoDBGraphTest { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexData.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import java.util.*; 4 | 5 | public class ArangoDBVertexData extends ArangoDBData> { 6 | 7 | public ArangoDBVertexData() { 8 | } 9 | 10 | public ArangoDBVertexData(String label, String key) { 11 | super(label, key); 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return "ArangoDBVertexData{" + 17 | super.toString() + 18 | "}"; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/test.conf: -------------------------------------------------------------------------------- 1 | gremlin.graph = com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph 2 | gremlin.arangodb.conf.graph.db = Test02 3 | gremlin.arangodb.conf.graph.name = Test02Graph01 4 | gremlin.arangodb.conf.graph.vertex = testfrom 5 | gremlin.arangodb.conf.graph.vertex = testto 6 | gremlin.arangodb.conf.graph.edge = testedge 7 | gremlin.arangodb.conf.graph.relation = testedge:testfrom->testto 8 | gremlin.arangodb.conf.graph.shouldPrefixCollectionNames = false 9 | gremlin.arangodb.conf.arangodb.hosts = 127.0.0.1:8529 10 | gremlin.arangodb.conf.arangodb.user = xxxx 11 | gremlin.arangodb.conf.arangodb.password = xxxx -------------------------------------------------------------------------------- /src/assembly/distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | distribution 3 | 4 | zip 5 | 6 | 7 | 8 | src 9 | 10 | 11 | target/${project.artifactId}-${project.version}-standalone/lib 12 | lib 13 | 14 | 15 | 16 | 17 | pom.xml 18 | src 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven 3 | 4 | name: Test 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | test: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up JDK 8 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: '8' 23 | distribution: 'temurin' 24 | cache: maven 25 | - name: Start Database 26 | run: ./docker/start_db.sh 27 | - name: Test 28 | run: mvn test 29 | -------------------------------------------------------------------------------- /src/assembly/standalone.xml: -------------------------------------------------------------------------------- 1 | 2 | standalone 3 | 4 | dir 5 | 6 | false 7 | 8 | 9 | 10 | target/*.jar 11 | /lib 12 | 13 | 14 | 15 | 16 | 17 | /lib 18 | false 19 | compile 20 | 21 | 22 | /lib 23 | false 24 | provided 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/PropertiesContainer.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.Map; 5 | import java.util.stream.Stream; 6 | 7 | interface PropertiesContainer { 8 | Map getProperties(); 9 | 10 | default Stream> properties() { 11 | return getProperties().entrySet().stream() 12 | .map(it -> new AbstractMap.SimpleEntry<>(it.getKey(), it.getValue().getValue())); 13 | } 14 | 15 | default boolean hasProperty(String key) { 16 | return getProperties().containsKey(key); 17 | } 18 | 19 | default void removeProperty(String key) { 20 | getProperties().remove(key); 21 | } 22 | 23 | default void setProperty(String key, Object value) { 24 | getProperties().put(key, new ArangoDBPropertyData(value)); 25 | } 26 | 27 | default Object getProperty(String key) { 28 | return getProperties().get(key).getValue(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/Issue57.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin; 2 | 3 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 4 | import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; 5 | import org.apache.commons.configuration2.Configuration; 6 | 7 | import java.io.File; 8 | 9 | 10 | public class Issue57 { 11 | 12 | public static void main(String... args) { 13 | ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder(); 14 | builder.dataBase("Test02") 15 | .graph("Test02Graph01") 16 | .arangoUser("gremlin") 17 | .arangoPassword("gremlin") 18 | .arangoHosts("127.0.0.1:8529") 19 | .withEdgeCollection("testedge") 20 | .withVertexCollection("testfrom") 21 | .withVertexCollection("testto") 22 | .shouldPrefixCollectionNamesWithGraphName(false) 23 | .configureEdge("testedge", "testfrom", "testto"); 24 | 25 | ArangoDBGraph g = ArangoDBGraph.open(builder.build()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n 8 | 9 | 10 | 11 | 12 | tikerpopTests.log 13 | false 14 | 15 | true 16 | 18 | 19 | %-4relative [%thread] %-5level %logger{35} - %msg%n 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/travis/setup_arangodb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | cd $DIR 5 | 6 | VERSION=2.8.11 7 | NAME=ArangoDB-$VERSION 8 | 9 | if [ ! -d "$DIR/$NAME" ]; then 10 | # download ArangoDB 11 | echo "wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz" 12 | wget https://www.arangodb.com/repositories/travisCI/$NAME.tar.gz 13 | echo "tar zxf $NAME.tar.gz" 14 | tar zvxf $NAME.tar.gz 15 | fi 16 | 17 | ARCH=$(arch) 18 | PID=$(echo $PPID) 19 | TMP_DIR="/tmp/arangodb.$PID" 20 | PID_FILE="/tmp/arangodb.$PID.pid" 21 | ARANGODB_DIR="$DIR/$NAME" 22 | ARANGOD="${ARANGODB_DIR}/bin/arangod_x86_64" 23 | 24 | # create database directory 25 | mkdir ${TMP_DIR} 26 | 27 | echo "Starting ArangoDB '${ARANGOD}'" 28 | 29 | ${ARANGOD} \ 30 | --database.directory ${TMP_DIR} \ 31 | --configuration none \ 32 | --server.endpoint tcp://127.0.0.1:8529 \ 33 | --javascript.app-path ${ARANGODB_DIR}/js/apps \ 34 | --javascript.startup-directory ${ARANGODB_DIR}/js \ 35 | --server.disable-authentication true & 36 | 37 | sleep 2 38 | 39 | echo "Check for arangod process" 40 | process=$(ps auxww | grep "bin/arangod" | grep -v grep) 41 | 42 | if [ "x$process" == "x" ]; then 43 | echo "no 'arangod' process found" 44 | echo "ARCH = $ARCH" 45 | exit 1 46 | fi 47 | 48 | echo "Waiting until ArangoDB is ready on port 8529" 49 | while [[ -z `curl -uroot: -s 'http://127.0.0.1:8529/_api/version' ` ]] ; do 50 | echo -n "." 51 | sleep 2s 52 | done 53 | 54 | echo "ArangoDB is up" 55 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/client/test/BaseTestCase.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.client.test; 2 | 3 | import java.util.Properties; 4 | 5 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; 6 | import org.apache.commons.configuration2.ConfigurationConverter; 7 | import org.apache.commons.configuration2.PropertiesConfiguration; 8 | import org.junit.After; 9 | import org.junit.Before; 10 | 11 | public abstract class BaseTestCase { 12 | 13 | protected ArangoDBGraphClient client; 14 | protected final String graphName = "test_graph1"; 15 | protected final String vertices = "test_vertices1"; 16 | protected final String edges = "test_edges1"; 17 | 18 | @Before 19 | public void setUp() throws Exception { 20 | 21 | // host name and port see: arangodb.properties 22 | PropertiesConfiguration configuration = new PropertiesConfiguration(); 23 | configuration.setProperty("arangodb.hosts", "127.0.0.1:8529"); 24 | configuration.setProperty("arangodb.user", "gremlin"); 25 | configuration.setProperty("arangodb.password", "gremlin"); 26 | Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 27 | 28 | client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 30000, true); 29 | 30 | client.deleteGraph(graphName); 31 | client.deleteCollection(vertices); 32 | client.deleteCollection(edges); 33 | 34 | } 35 | 36 | @After 37 | public void tearDown() { 38 | 39 | client.deleteCollection(vertices); 40 | client.deleteCollection(edges); 41 | client.deleteGraph(graphName); 42 | client = null; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBPropertyData.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; 4 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; 5 | import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; 6 | 7 | import java.util.Objects; 8 | 9 | public class ArangoDBPropertyData { 10 | private final Object value; 11 | private final String valueType; 12 | 13 | @JsonCreator 14 | ArangoDBPropertyData( 15 | @JsonProperty("value") Object value, 16 | @JsonProperty("valueType") String valueType 17 | ) { 18 | this.value = value; 19 | this.valueType = valueType; 20 | } 21 | 22 | public ArangoDBPropertyData(Object value) { 23 | this.value = value; 24 | valueType = (value != null ? value.getClass() : Void.class).getCanonicalName(); 25 | } 26 | 27 | public Object getValue() { 28 | return ArangoDBUtil.getCorretctPrimitive(value, valueType); 29 | } 30 | 31 | public String getValueType() { 32 | return valueType; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "ArangoDBPropertyValue{" + 38 | "value=" + value + 39 | ", valueType='" + valueType + '\'' + 40 | '}'; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (o == null || getClass() != o.getClass()) return false; 46 | ArangoDBPropertyData that = (ArangoDBPropertyData) o; 47 | return Objects.equals(value, that.value) && Objects.equals(valueType, that.valueType); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(value, valueType); 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdgeData.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import com.arangodb.serde.*; 4 | 5 | import java.util.*; 6 | 7 | public class ArangoDBEdgeData extends ArangoDBData implements PropertiesContainer { 8 | 9 | @InternalFrom 10 | private String from; 11 | 12 | @InternalTo 13 | private String to; 14 | 15 | public ArangoDBEdgeData() { 16 | } 17 | 18 | public ArangoDBEdgeData( 19 | String label, 20 | String key, 21 | String from, 22 | String to 23 | ) { 24 | super(label, key); 25 | Objects.requireNonNull(from, "from"); 26 | Objects.requireNonNull(to, "to"); 27 | 28 | this.from = from; 29 | this.to = to; 30 | } 31 | 32 | public String getFrom() { 33 | return from; 34 | } 35 | 36 | public void setFrom(String from) { 37 | this.from = from; 38 | } 39 | 40 | public String getTo() { 41 | return to; 42 | } 43 | 44 | public void setTo(String to) { 45 | this.to = to; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "ArangoDBEdgeData{" + 51 | super.toString() + 52 | ", from='" + from + '\'' + 53 | ", to='" + to + '\'' + 54 | ", properties=" + getProperties() + 55 | '}'; 56 | } 57 | 58 | @Override 59 | public boolean equals(Object o) { 60 | if (o == null || getClass() != o.getClass()) return false; 61 | if (!super.equals(o)) return false; 62 | ArangoDBEdgeData that = (ArangoDBEdgeData) o; 63 | return Objects.equals(from, that.from) && Objects.equals(to, that.to); 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | return Objects.hash(super.hashCode(), from, to); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexPropertyData.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonCreator; 4 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Objects; 9 | 10 | public class ArangoDBVertexPropertyData extends ArangoDBPropertyData implements PropertiesContainer { 11 | private final String id; 12 | private final Map properties; 13 | 14 | @JsonCreator 15 | ArangoDBVertexPropertyData( 16 | @JsonProperty("id") String id, 17 | @JsonProperty("value") Object value, 18 | @JsonProperty("valueType") String valueType, 19 | @JsonProperty("properties") Map properties) { 20 | super(value, valueType); 21 | this.id = id; 22 | this.properties = properties; 23 | } 24 | 25 | public ArangoDBVertexPropertyData(String id, Object value) { 26 | super(value); 27 | this.id = id; 28 | this.properties = new HashMap<>(); 29 | } 30 | 31 | public String getId() { 32 | return id; 33 | } 34 | 35 | public Map getProperties() { 36 | return properties; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "TinkerVertexPropertyData{" + 42 | "id='" + id + '\'' + 43 | ", value=" + getValue() + 44 | ", valueType='" + getValueType() + '\'' + 45 | ", properties=" + properties + 46 | '}'; 47 | } 48 | 49 | @Override 50 | public boolean equals(Object o) { 51 | if (o == null || getClass() != o.getClass()) return false; 52 | if (!super.equals(o)) return false; 53 | ArangoDBVertexPropertyData that = (ArangoDBVertexPropertyData) o; 54 | return Objects.equals(id, that.id) && Objects.equals(properties, that.properties); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(super.hashCode(), id, properties); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBData.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import com.arangodb.serde.InternalKey; 4 | import com.arangodb.serde.InternalRev; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.Objects; 9 | 10 | abstract class ArangoDBData { 11 | private String label; 12 | 13 | @InternalKey 14 | private String key; 15 | 16 | @InternalRev 17 | private String rev; 18 | 19 | private Map properties = new HashMap<>(); 20 | 21 | public ArangoDBData() { 22 | } 23 | 24 | public ArangoDBData(String label, String key) { 25 | Objects.requireNonNull(label, "label"); 26 | if (label.isEmpty()) throw new IllegalArgumentException("empty label"); 27 | if (key != null && key.isEmpty()) throw new IllegalArgumentException("empty key"); 28 | 29 | this.label = label; 30 | this.key = key; 31 | } 32 | 33 | public String getKey() { 34 | return key; 35 | } 36 | 37 | public void setKey(String key) { 38 | this.key = key; 39 | } 40 | 41 | public String getRev() { 42 | return rev; 43 | } 44 | 45 | public void setRev(String rev) { 46 | this.rev = rev; 47 | } 48 | 49 | public String getLabel() { 50 | return label; 51 | } 52 | 53 | public void setLabel(String label) { 54 | this.label = label; 55 | } 56 | 57 | public Map getProperties() { 58 | if (properties == null) { 59 | properties = new HashMap<>(); 60 | } 61 | return properties; 62 | } 63 | 64 | public void setProperties(Map properties) { 65 | this.properties = properties; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "key='" + key + '\'' + 71 | ", label='" + label + '\'' + 72 | ", rev='" + rev + '\'' + 73 | ", properties=" + properties; 74 | } 75 | 76 | @Override 77 | public boolean equals(Object o) { 78 | if (o == null || getClass() != o.getClass()) return false; 79 | ArangoDBData that = (ArangoDBData) o; 80 | return Objects.equals(label, that.label) && Objects.equals(key, that.key) && Objects.equals(rev, that.rev) && Objects.equals(properties, that.properties); 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | return Objects.hash(label, key, rev, properties); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBGraphException.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.client; 10 | 11 | /** 12 | * The ArangoDBGraphException is used to signal all exceptions related to the graph operations. 13 | * 14 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 15 | */ 16 | 17 | public class ArangoDBGraphException extends RuntimeException { 18 | 19 | /** The Constant serialVersionUID. */ 20 | 21 | private static final long serialVersionUID = -8478050406116402002L; 22 | 23 | private int errorCode; 24 | 25 | /** 26 | * Instantiates a new Arango DB graph exception. 27 | * 28 | * @param errorCode the ArangoDB error errorCode 29 | * @param message the exception message 30 | * @param cause the exception cause 31 | */ 32 | 33 | public ArangoDBGraphException(int errorCode, String message, Throwable cause) { 34 | super(message, cause); 35 | this.errorCode = errorCode; 36 | } 37 | 38 | /** 39 | * Instantiates a new Arango DB graph exception. 40 | * 41 | * @param message the message 42 | * @param cause the cause 43 | */ 44 | 45 | public ArangoDBGraphException(String message, Throwable cause) { 46 | super(message, cause); 47 | } 48 | 49 | /** 50 | * Instantiates a new Arango DB graph exception. 51 | * 52 | * @param errorCode the ArangoDB error errorCode 53 | * @param message the exception message 54 | */ 55 | 56 | public ArangoDBGraphException(int errorCode, String message) { 57 | super(message); 58 | } 59 | 60 | /** 61 | * Instantiates a new Arango DB graph exception. 62 | * 63 | * @param message the message 64 | */ 65 | 66 | public ArangoDBGraphException(String message) { 67 | super(message); 68 | } 69 | 70 | /** 71 | * Instantiates a new Arango DB graph exception. 72 | * 73 | * @param cause the cause 74 | */ 75 | 76 | public ArangoDBGraphException(Throwable cause) { 77 | super(cause); 78 | } 79 | 80 | public int getErrorCode() { 81 | return errorCode; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [2.0.3] - 2020-11-16 10 | 11 | ### Fixed 12 | - #68 Create Graph Variables collection if not present 13 | - Bump junit from 4.12 to 4.13.1 14 | 15 | ## [2.0.2] - 2019-03-22 16 | 17 | ### Added 18 | - Collection name prefix is now optional to support existing database collections/graphs 19 | - Maven surefire runs supported tests 20 | - Changelong file 21 | 22 | ### Fixed 23 | - Corrected information on ID management 24 | - Edge.getEdgeVertices() returns correct edges 25 | - Deleting a Property deletes its incoming edges 26 | - Fixed Javadocs 27 | 28 | ## [2.0.1] - 2018-12-25 29 | ### Fixed 30 | - Spelling mistakes 31 | - ConfigurationBuilder sets gremlim.graph property 32 | 33 | ## [2.0.0] - 2018-10-30 34 | ### Added 35 | - Official Gremlin Test Suite is used for testing 36 | ### Changed 37 | - Move to tinkerpop 3 38 | - Move to ArangoDB 3.3 39 | - Move to ArangoDB Java driver 5.0 40 | 41 | ## [1.0.15] - 2016-08-29 42 | ### Fixed 43 | - #25 44 | 45 | ## [1.0.14] - 2016-04-22 46 | ### Added 47 | - Changed to Apache httpclient 4.2.5 48 | 49 | ## [1.0.13] - 2016-03-25 50 | ### Added 51 | - Updated to ArangoDB java client 2.7.3 52 | 53 | ## [1.0.12] - 2016-01-08 54 | ### Added 55 | - Update to java driver 2.7.0 56 | 57 | ## [1.0.11] - 2015-07-09 58 | ### Added 59 | - Support of ArangoDB 2.6 60 | 61 | ## [1.0.10] - 2015-05-23 62 | ### Changed 63 | - Changed version to 1.0.10 for Blueprints 2.6 64 | 65 | ## [1.0.9] - 2014-08-29 66 | ### Added 67 | - Updates for new graph document format in arangodb 2.2 68 | 69 | ## [1.0.8] - 2014-06-06 70 | ### Changed 71 | - Changes for gremlin 2.5.0 72 | 73 | ## [1.0.7] - 2014-03-14 74 | ### Fixed 75 | - Honor batch size in BatchGraph 76 | - Honor client connection configuration options 77 | 78 | ## [1.0.6] - 2014-03-11 79 | ### Fixed 80 | - Fixed setup 81 | 82 | ## [1.0.5] - 2014-02-21 83 | ### Added 84 | - Updates for ArangoDBGraph.query() 85 | 86 | ## [1.0.3] - 2013-02-12 87 | ### Added 88 | - Default logging config 89 | 90 | ## [1.0.2] - 2013-02-06 91 | ### Added 92 | - Rexter config 93 | 94 | ## [1.0.1] - 2013-02-05 95 | ### Changed 96 | - Changed caching of changed elements 97 | 98 | ## [1.0.0] - 2013-02-04 99 | ### Added 100 | - Initial Version -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/jsr223/ArangoDBGremlinPlugin.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.jsr223; 10 | 11 | import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; 12 | import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; 13 | import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; 14 | 15 | import com.arangodb.tinkerpop.gremlin.client.*; 16 | import com.arangodb.tinkerpop.gremlin.structure.*; 17 | import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; 18 | 19 | /** 20 | * The Class ArangoDBGremlinPlugin. 21 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 22 | */ 23 | public class ArangoDBGremlinPlugin extends AbstractGremlinPlugin { 24 | 25 | /** The Constant NAME. */ 26 | private static final String NAME = "tinkerpop.arangodb"; 27 | 28 | /** The Constant IMPORTS. */ 29 | private static final ImportCustomizer IMPORTS; 30 | 31 | static { 32 | try { 33 | IMPORTS = DefaultImportCustomizer.build().addClassImports( 34 | ArangoDBBaseDocument.class, 35 | ArangoDBGraphClient.class, 36 | ArangoDBGraphException.class, 37 | ArangoDBPropertyFilter.class, 38 | ArangoDBQueryBuilder.class, 39 | ArangoDBEdge.class, 40 | ArangoDBEdgeData.class, 41 | ArangoDBProperty.class, 42 | ArangoDBGraph.class, 43 | ArangoDBGraphVariables.class, 44 | ArangoDBVertexPropertyData.class, 45 | ArangoDBVertex.class, 46 | ArangoDBVertexProperty.class, 47 | ArangoDBUtil.class 48 | ) 49 | .create(); 50 | } catch (Exception ex) { 51 | throw new RuntimeException(ex); 52 | } 53 | } 54 | 55 | /** The Constant INSTANCE. */ 56 | private static final ArangoDBGremlinPlugin INSTANCE = new ArangoDBGremlinPlugin(); 57 | 58 | /** 59 | * Instantiates a new Arango DB gremlin plugin. 60 | */ 61 | public ArangoDBGremlinPlugin() { 62 | super(NAME, IMPORTS); 63 | } 64 | 65 | /** 66 | * Instance. 67 | * 68 | * @return the arango DB gremlin plugin 69 | */ 70 | public static ArangoDBGremlinPlugin instance() { 71 | return INSTANCE; 72 | } 73 | 74 | @Override 75 | public boolean requireRestart() { 76 | return true; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.arangodb.tinkerpop.gremlin.structure; 20 | 21 | import org.apache.tinkerpop.gremlin.structure.Element; 22 | import org.apache.tinkerpop.gremlin.structure.Property; 23 | import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; 24 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 25 | 26 | public final class ArangoDBProperty implements Property { 27 | 28 | private final String key; 29 | private final V value; 30 | private final Element element; 31 | 32 | public ArangoDBProperty(final Element element, final String key, final V value) { 33 | this.element = element; 34 | this.key = key; 35 | this.value = value; 36 | } 37 | 38 | @Override 39 | public Element element() { 40 | return element; 41 | } 42 | 43 | @Override 44 | public String key() { 45 | return key; 46 | } 47 | 48 | @Override 49 | public V value() { 50 | return value; 51 | } 52 | 53 | @Override 54 | public boolean isPresent() { 55 | return true; 56 | } 57 | 58 | @Override 59 | public void remove() { 60 | if (element instanceof ArangoDBEdge) { 61 | ((ArangoDBEdge) element).removeProperty(key); 62 | } else if (element instanceof ArangoDBVertexProperty) { 63 | ((ArangoDBVertexProperty) element).removeProperty(key); 64 | } else { 65 | throw new UnsupportedOperationException("Property " + this.key() + " is not an Edge"); 66 | } 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return StringFactory.propertyString(this); 72 | } 73 | 74 | @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 75 | @Override 76 | public boolean equals(final Object object) { 77 | return ElementHelper.areEqual(this, object); 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | return ElementHelper.hashCode(this); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /docker/server.pem: -------------------------------------------------------------------------------- 1 | Bag Attributes 2 | friendlyName: arangotest 3 | localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 4 | Key Attributes: 5 | -----BEGIN PRIVATE KEY----- 6 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1WiDnd4+uCmMG 7 | 539ZNZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMAL 8 | X9euSseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7Nzmvdo 9 | gNXoJQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiR 10 | dJVPwUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf 11 | 2MGO64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzb 12 | f8KnRY4PAgMBAAECggEAKi1d/bdW2TldMpvgiFTm15zLjHCpllbKBWFqRj3T9+X7 13 | Duo6Nh9ehopD0YDDe2DNhYr3DsH4sLjUWVDfDpAhutMsU1wlBzmOuC+EuRv/CeDB 14 | 4DFr+0sgCwlti+YAtwWcR05SF7A0Ai0GYW2lUipbtbFSBSjCfM08BlPDsPCRhdM8 15 | DhBn3S45aP7oC8BdhG/etg+DfXW+/nyNwEcMCYG97bzXNjzYpCQjo/bTHdh2UPYM 16 | 4WEAqFzZ5jir8LVS3v7GqpqPmk6FnHJOJpfpOSZoPqnfpIw7SVlNsXHvDaHGcgYZ 17 | Xec7rLQlBuv4RZU7OlGJpK2Ng5kvS9q3nfqqn7YIMQKBgQDqSsYnE+k6CnrSpa2W 18 | B9W/+PChITgkA46XBUUjAueJ7yVZQQEOzl0VI6RoVBp3t66eO8uM9omO8/ogHXku 19 | Ei9UUIIfH4BsSP7G5A06UC/FgReDxwBfbRuS+lupnmc348vPDkFlJZ4hDgWflNev 20 | 7tpUbljSAqUea1VhdBy146V4qwKBgQDGJ6iL1+A9uUM+1UklOAPpPhTQ8ZQDRCj7 21 | 7IMVcbzWYvCMuVNXzOWuiz+VYr3IGCJZIbxbFDOHxGF4XKJnk0vm1qhQQME0PtAF 22 | i1jIfsxpj8KKJl9Uad+XLQCYRV8mIZlhsd/ErRJuz6FyqevKH3nFIb0ggF3x2d06 23 | odTHuj4ILQKBgCUsI/BDSne4/e+59aaeK52/w33tJVkhb1gqr+N0LIRH+ycEF0Tg 24 | HQijlQwwe9qOvBfC6PK+kuipcP/zbSyQGg5Ij7ycZOXJVxL7T9X2rv2pE7AGvNpn 25 | Fz7klfJ9fWbyr310h4+ivkoETYQaO3ZgcSeAMntvi/8djHhf0cZSDgjtAoGBAKvQ 26 | TUNcHjJGxfjgRLkB1dpSmwgEv7sJSaQOkiZw5TTauwq50nsJzYlHcg1cfYPW8Ulp 27 | iAFNBdVNwNn1MFgwjpqMO4rCawObBxIXnhbSYvmQzjStSvFNj7JsMdzWIcdVUMI1 28 | 0fmdu6LbY3ihvzIVkqcMNwnMZCjFKB6jnXTElu7NAoGAS0gNPD/bfzWAhZBBYp9/ 29 | SLGOvjHKrSVWGwDiqdAGuh6xg+1C3F+XpiITP6d3Wv3PCJ/Gia5isQPSMaXG+xTt 30 | 6huBgFlksHqr0tsQA9dcgGW7BDr5VhRq5/WinaLhGGy1R+i2zbDmQXgHbCO+RH/s 31 | bD9F4LZ3RoXmGHLW0IUggPw= 32 | -----END PRIVATE KEY----- 33 | Bag Attributes 34 | friendlyName: arangotest 35 | localKeyID: 54 69 6D 65 20 31 36 30 34 32 35 36 36 37 39 38 35 34 36 | subject=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost 37 | 38 | issuer=C = Unknown, ST = Unknown, L = Unknown, O = Unknown, OU = Unknown, CN = localhost 39 | 40 | -----BEGIN CERTIFICATE----- 41 | MIIDezCCAmOgAwIBAgIEeDCzXzANBgkqhkiG9w0BAQsFADBuMRAwDgYDVQQGEwdV 42 | bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD 43 | VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv 44 | c3QwHhcNMjAxMTAxMTg1MTE5WhcNMzAxMDMwMTg1MTE5WjBuMRAwDgYDVQQGEwdV 45 | bmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYD 46 | VQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhv 47 | c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1WiDnd4+uCmMG539Z 48 | NZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMALX9eu 49 | SseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7NzmvdogNXo 50 | JQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiRdJVP 51 | wUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf2MGO 52 | 64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzbf8Kn 53 | RY4PAgMBAAGjITAfMB0GA1UdDgQWBBTBrv9Awynt3C5IbaCNyOW5v4DNkTANBgkq 54 | hkiG9w0BAQsFAAOCAQEAIm9rPvDkYpmzpSIhR3VXG9Y71gxRDrqkEeLsMoEyqGnw 55 | /zx1bDCNeGg2PncLlW6zTIipEBooixIE9U7KxHgZxBy0Et6EEWvIUmnr6F4F+dbT 56 | D050GHlcZ7eOeqYTPYeQC502G1Fo4tdNi4lDP9L9XZpf7Q1QimRH2qaLS03ZFZa2 57 | tY7ah/RQqZL8Dkxx8/zc25sgTHVpxoK853glBVBs/ENMiyGJWmAXQayewY3EPt/9 58 | wGwV4KmU3dPDleQeXSUGPUISeQxFjy+jCw21pYviWVJTNBA9l5ny3GhEmcnOT/gQ 59 | HCvVRLyGLMbaMZ4JrPwb+aAtBgrgeiK4xeSMMvrbhw== 60 | -----END CERTIFICATE----- 61 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertexProperty.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.structure; 2 | 3 | import org.apache.tinkerpop.gremlin.structure.*; 4 | import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; 5 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.*; 10 | import java.util.stream.Collectors; 11 | 12 | import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; 13 | 14 | public class ArangoDBVertexProperty implements Element, VertexProperty { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertexProperty.class); 16 | 17 | private final String key; 18 | private final ArangoDBVertex vertex; 19 | private final ArangoDBVertexPropertyData data; 20 | private boolean removed; 21 | 22 | public ArangoDBVertexProperty(String key, ArangoDBVertexPropertyData data, ArangoDBVertex vertex) { 23 | this.key = key; 24 | this.data = data; 25 | this.vertex = vertex; 26 | removed = false; 27 | } 28 | 29 | @Override 30 | public String key() { 31 | return key; 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | @Override 36 | public V value() throws NoSuchElementException { 37 | return (V) data.getValue(); 38 | } 39 | 40 | @Override 41 | public boolean isPresent() { 42 | return true; 43 | } 44 | 45 | @Override 46 | public Vertex element() { 47 | return vertex; 48 | } 49 | 50 | 51 | @Override 52 | public Object id() { 53 | return data.getId(); 54 | } 55 | 56 | @Override 57 | public Property property(String key, W value) { 58 | if (removed) throw elementAlreadyRemoved(VertexProperty.class, id()); 59 | LOGGER.info("set property {} = {}", key, value); 60 | ElementHelper.validateProperty(key, value); 61 | data.setProperty(key, value); 62 | vertex.update(); 63 | return new ArangoDBProperty<>(this, key, value); 64 | } 65 | 66 | @Override 67 | public void remove() { 68 | if (removed) return; 69 | vertex.removeProperty(data); 70 | vertex.update(); 71 | removed = true; 72 | } 73 | 74 | @Override 75 | @SuppressWarnings("unchecked") 76 | public Iterator> properties(String... propertyKeys) { 77 | return data.getProperties() 78 | .entrySet() 79 | .stream() 80 | .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) 81 | .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue().getValue())) 82 | .collect(Collectors.toList()).iterator(); 83 | } 84 | 85 | public void removeProperty(String key) { 86 | if (removed) throw elementAlreadyRemoved(Edge.class, id()); 87 | if (data.hasProperty(key)) { 88 | data.removeProperty(key); 89 | vertex.update(); 90 | } 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return StringFactory.propertyString(this); 96 | } 97 | 98 | @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 99 | @Override 100 | public boolean equals(final Object object) { 101 | return ElementHelper.areEqual(this, object); 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return ElementHelper.hashCode((Property) this); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /ci-prod/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | arangodb-tinkerpop-provider 7 | org.arangodb 8 | 2.0.1 9 | 10 | 4.0.0 11 | ci-prod 12 | 13 | 14 | 15 | 17 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-source-plugin 28 | 2.4 29 | 30 | 31 | 32 | jar 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-javadoc-plugin 41 | 3.0.1 42 | 43 | 44 | attach-javadocs 45 | 46 | jar 47 | 48 | 49 | ${javadoc.opts} 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-gpg-plugin 58 | 1.5 59 | 60 | 61 | sign-artifacts 62 | verify 63 | 64 | sign 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-assembly-plugin 74 | 3.1.0 75 | 76 | 77 | assembly 78 | package 79 | 80 | single 81 | 82 | 83 | 84 | 85 | 86 | ${project.artifactId}-${project.version}-standalone 87 | 88 | false 89 | false 90 | 91 | jar-with-dependencies 92 | 93 | 94 | 95 | 96 | 97 | 98 | org.sonatype.plugins 99 | nexus-staging-maven-plugin 100 | 1.6.7 101 | true 102 | 103 | ossrh 104 | https://oss.sonatype.org/ 105 | true 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docker/start_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Configuration environment variables: 4 | # STARTER_MODE: (single|cluster|activefailover), default single 5 | # DOCKER_IMAGE: ArangoDB docker image, default docker.io/arangodb/arangodb:latest 6 | # STARTER_DOCKER_IMAGE: ArangoDB Starter docker image, default docker.io/arangodb/arangodb-starter:latest 7 | # SSL: (true|false), default false 8 | # ARANGO_LICENSE_KEY: only required for ArangoDB Enterprise 9 | 10 | # EXAMPLE: 11 | # STARTER_MODE=cluster SSL=true ./start_db.sh 12 | 13 | STARTER_MODE=${STARTER_MODE:=single} 14 | DOCKER_IMAGE=${DOCKER_IMAGE:=docker.io/arangodb/arangodb:latest} 15 | STARTER_DOCKER_IMAGE=${STARTER_DOCKER_IMAGE:=docker.io/arangodb/arangodb-starter:latest} 16 | SSL=${SSL:=false} 17 | COMPRESSION=${COMPRESSION:=false} 18 | 19 | GW=172.28.0.1 20 | docker network create arangodb --subnet 172.28.0.0/16 21 | 22 | # exit when any command fails 23 | set -e 24 | 25 | docker pull $STARTER_DOCKER_IMAGE 26 | docker pull $DOCKER_IMAGE 27 | 28 | LOCATION=$(pwd)/$(dirname "$0") 29 | AUTHORIZATION_HEADER=$(cat "$LOCATION"/jwtHeader) 30 | 31 | STARTER_ARGS= 32 | SCHEME=http 33 | ARANGOSH_SCHEME=http+tcp 34 | COORDINATORS=("$GW:8529" "$GW:8539" "$GW:8549") 35 | 36 | if [ "$STARTER_MODE" == "single" ]; then 37 | COORDINATORS=("$GW:8529") 38 | fi 39 | 40 | if [ "$SSL" == "true" ]; then 41 | STARTER_ARGS="$STARTER_ARGS --ssl.keyfile=/data/server.pem" 42 | SCHEME=https 43 | ARANGOSH_SCHEME=http+ssl 44 | fi 45 | 46 | if [ "$COMPRESSION" == "true" ]; then 47 | STARTER_ARGS="${STARTER_ARGS} --all.http.compress-response-threshold=1" 48 | fi 49 | 50 | # data volume 51 | docker create -v /data --name arangodb-data alpine:3 /bin/true 52 | docker cp "$LOCATION"/jwtSecret arangodb-data:/data 53 | docker cp "$LOCATION"/server.pem arangodb-data:/data 54 | 55 | docker run -d \ 56 | --name=adb \ 57 | -p 8528:8528 \ 58 | --volumes-from arangodb-data \ 59 | -v /var/run/docker.sock:/var/run/docker.sock \ 60 | -e ARANGO_LICENSE_KEY="$ARANGO_LICENSE_KEY" \ 61 | $STARTER_DOCKER_IMAGE \ 62 | $STARTER_ARGS \ 63 | --docker.net-mode=default \ 64 | --docker.container=adb \ 65 | --auth.jwt-secret=/data/jwtSecret \ 66 | --starter.address="${GW}" \ 67 | --docker.image="${DOCKER_IMAGE}" \ 68 | --starter.local --starter.mode=${STARTER_MODE} --all.log.level=debug --all.log.output=+ --log.verbose \ 69 | --all.server.descriptors-minimum=1024 --all.javascript.allow-admin-execute=true \ 70 | --all.query.require-with=true --all.server.options-api=admin 71 | 72 | 73 | wait_server() { 74 | # shellcheck disable=SC2091 75 | until $(curl --output /dev/null --insecure --fail --silent --head -i -H "$AUTHORIZATION_HEADER" "$SCHEME://$1/_api/version"); do 76 | printf '.' 77 | sleep 1 78 | done 79 | } 80 | 81 | echo "Waiting..." 82 | 83 | for a in ${COORDINATORS[*]} ; do 84 | wait_server "$a" 85 | done 86 | 87 | set +e 88 | for a in ${COORDINATORS[*]} ; do 89 | echo "" 90 | echo "Setting username and password..." 91 | docker run --rm ${DOCKER_IMAGE} arangosh --server.endpoint="$ARANGOSH_SCHEME://$a" --server.authentication=false --javascript.execute-string='require("org/arangodb/users").update("root", "test")' 92 | done 93 | set -e 94 | 95 | for a in ${COORDINATORS[*]} ; do 96 | echo "" 97 | echo "Requesting endpoint version..." 98 | curl -u root:test --insecure --fail "$SCHEME://$a/_api/version" 99 | done 100 | 101 | echo "" 102 | echo "" 103 | echo "Done, your deployment is reachable at: " 104 | for a in ${COORDINATORS[*]} ; do 105 | echo "$SCHEME://$a" 106 | echo "" 107 | done 108 | 109 | if [ "$STARTER_MODE" == "activefailover" ]; then 110 | LEADER=$("$LOCATION"/find_active_endpoint.sh) 111 | echo "Leader: $SCHEME://$LEADER" 112 | echo "" 113 | fi 114 | 115 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBGraphVariables.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.structure; 10 | 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.Optional; 15 | import java.util.Set; 16 | 17 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; 18 | import org.apache.tinkerpop.gremlin.structure.Graph; 19 | import org.apache.tinkerpop.gremlin.structure.util.GraphVariableHelper; 20 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 21 | 22 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBBaseDocument; 23 | 24 | /** 25 | * The Class ArangoDBGraphVariables. 26 | * 27 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 28 | */ 29 | 30 | public class ArangoDBGraphVariables extends ArangoDBBaseDocument implements Graph.Variables { 31 | 32 | 33 | /** 34 | * The Class ArangoDBGraphVariableFeatures. 35 | */ 36 | 37 | public static class ArangoDBGraphVariableFeatures implements Graph.Features.VariableFeatures { 38 | 39 | } 40 | 41 | /** The key:value store for properties. */ 42 | 43 | @JsonProperty 44 | private final Map store = new HashMap<>(4); 45 | 46 | /** 47 | * Constructor used for ArabgoDB JavaBeans de-/serialisation. 48 | */ 49 | 50 | public ArangoDBGraphVariables() { 51 | super(); 52 | } 53 | 54 | /** 55 | * Instantiates a new Arango DB graph variables. 56 | * 57 | * @param key the key to assign to the document 58 | * @param label the document label 59 | * @param graph the graph that contains the document 60 | */ 61 | public ArangoDBGraphVariables(String key, String label, ArangoDBGraph graph) { 62 | super(key, label, graph); 63 | } 64 | 65 | @Override 66 | public Set keys() { 67 | return store.keySet(); 68 | } 69 | 70 | @SuppressWarnings("unchecked") 71 | @Override 72 | public Optional get(String key) { 73 | Object value = store.get(key); 74 | return Optional.of((R)value); 75 | } 76 | 77 | @Override 78 | public void set(String key, Object value) { 79 | GraphVariableHelper.validateVariable(key, value); 80 | Object oldValue = this.store.put(key, value); 81 | if (oldValue != null) { 82 | if (!oldValue.equals(value)) { 83 | graph.getClient().updateGraphVariables(this); 84 | } 85 | } 86 | else { 87 | graph.getClient().updateGraphVariables(this); 88 | } 89 | } 90 | 91 | @Override 92 | public void remove(String key) { 93 | Object oldValue = this.store.remove(key); 94 | if (oldValue != null) { 95 | graph.getClient().deleteGraphVariables(this); 96 | } 97 | } 98 | 99 | @Override 100 | public int hashCode() { 101 | final int prime = 31; 102 | int result = super.hashCode(); 103 | result = prime * result + ((store == null) ? 0 : store.hashCode()); 104 | return result; 105 | } 106 | 107 | @Override 108 | public boolean equals(Object obj) { 109 | if (this == obj) 110 | return true; 111 | if (!super.equals(obj)) 112 | return false; 113 | if (getClass() != obj.getClass()) 114 | return false; 115 | ArangoDBGraphVariables other = (ArangoDBGraphVariables) obj; 116 | if (store == null) { 117 | if (other.store != null) 118 | return false; 119 | } else if (!store.equals(other.store)) 120 | return false; 121 | return true; 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return StringFactory.graphVariablesString(this); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBTestSuite.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin; 2 | 3 | import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; 4 | import org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest; 5 | import org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest; 6 | import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; 7 | import org.apache.tinkerpop.gremlin.structure.*; 8 | import org.apache.tinkerpop.gremlin.structure.io.IoCustomTest; 9 | import org.apache.tinkerpop.gremlin.structure.io.IoEdgeTest; 10 | import org.apache.tinkerpop.gremlin.structure.io.IoGraphTest; 11 | import org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest; 12 | import org.apache.tinkerpop.gremlin.structure.io.IoTest; 13 | import org.apache.tinkerpop.gremlin.structure.io.IoVertexTest; 14 | import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdgeTest; 15 | import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedGraphTest; 16 | import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedPropertyTest; 17 | import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexPropertyTest; 18 | import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexTest; 19 | import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceEdgeTest; 20 | import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceGraphTest; 21 | import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexPropertyTest; 22 | import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexTest; 23 | import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphTest; 24 | import org.junit.runners.model.InitializationError; 25 | import org.junit.runners.model.RunnerBuilder; 26 | 27 | 28 | /** 29 | * Run with {@code GREMLIN_TESTS} environment variable set to a list of any of these to enable 30 | * particular tests: 31 | * org.apache.tinkerpop.gremlin.algorithm.generator.CommunityGeneratorTest, 32 | * org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGeneratorTest, 33 | * org.apache.tinkerpop.gremlin.structure.EdgeTest, 34 | * org.apache.tinkerpop.gremlin.structure.FeatureSupportTest, 35 | * org.apache.tinkerpop.gremlin.structure.io.IoCustomTest, 36 | * org.apache.tinkerpop.gremlin.structure.io.IoGraphTest, 37 | * org.apache.tinkerpop.gremlin.structure.io.IoVertexTest, 38 | * org.apache.tinkerpop.gremlin.structure.io.IoPropertyTest, 39 | * org.apache.tinkerpop.gremlin.structure.GraphTest, 40 | * org.apache.tinkerpop.gremlin.structure.GraphConstructionTest, 41 | * org.apache.tinkerpop.gremlin.structure.io.IoTest, 42 | * org.apache.tinkerpop.gremlin.structure.VertexPropertyTest 43 | * 44 | */ 45 | public class ArangoDBTestSuite extends AbstractGremlinSuite { 46 | 47 | /** 48 | * This list of tests in the suite that will be executed. Gremlin developers should add to this list 49 | * as needed to enforce tests upon implementations. 50 | */ 51 | private static final Class[] allTests = new Class[]{ 52 | CommunityGeneratorTest.class, 53 | DetachedGraphTest.class, 54 | DetachedEdgeTest.class, 55 | DetachedVertexPropertyTest.class, 56 | DetachedPropertyTest.class, 57 | DetachedVertexTest.class, 58 | DistributionGeneratorTest.class, 59 | EdgeTest.class, 60 | FeatureSupportTest.class, 61 | IoCustomTest.class, 62 | IoEdgeTest.class, 63 | IoGraphTest.class, 64 | IoVertexTest.class, 65 | IoPropertyTest.class, 66 | GraphTest.class, 67 | GraphConstructionTest.class, 68 | IoTest.class, 69 | VertexPropertyTest.class, 70 | VariablesTest.class, 71 | PropertyTest.class, 72 | ReferenceGraphTest.class, 73 | ReferenceEdgeTest.class, 74 | ReferenceVertexPropertyTest.class, 75 | ReferenceVertexTest.class, 76 | SerializationTest.class, 77 | StarGraphTest.class, 78 | TransactionTest.class, 79 | TransactionMultiThreadedTest.class, 80 | VertexTest.class, 81 | //ArangoDBIndexCheck.class, 82 | //ArangoDBCypherCheck.class, 83 | }; 84 | 85 | public ArangoDBTestSuite( 86 | Class klass, 87 | RunnerBuilder builder) 88 | throws InitializationError { 89 | super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ArangoDB-Logo](https://docs.arangodb.com/assets/arangodb_logo_2016_inverted.png) 2 | 3 | # arangodb-tinkerpop-provider 4 | 5 | 6 | > [!IMPORTANT] 7 | > ArangoDB now provides an official Tinkerpop [integration](https://github.com/arangodb/arangodb-tinkerpop-provider) 8 | 9 | An implementation of the [Apache TinkerPop OLTP Provider](https://tinkerpop.apache.org/docs/3.3.3/dev/provider/#_provider_documentation) API for ArangoDB 10 | 11 | ## Compatibility 12 | 13 | This Provider supports: 14 | * Apache TinkerPop 3.3 15 | * ArangoDB 3.11+ (via ArangoDB Java Driver 7.17.0). 16 | 17 | ## ArangoDB 18 | 19 | Please check the 20 | [ArangoDB Installation Manual](https://docs.arangodb.com/latest/Manual/Deployment/) for guides on how to install ArangoDB. 21 | 22 | ## Maven 23 | 24 | To add the provider to your project via maven you need to add the following dependency (shown is the latest version - you can replace the version with the one you need) 25 | 26 | ```XML 27 | 28 | 29 | org.arangodb 30 | arangodb-tinkerpop-provider 31 | 2.0.3 32 | 33 | .... 34 | 35 | ``` 36 | 37 | The same coordinates can be used with Gradle and any other build system that uses maven repositories. 38 | 39 | 40 | ## Using ArangoDBGraph via the TinkerPop API 41 | This example is based on the TinkerPop documentation ([Creating a graph](http://tinkerpop.apache.org/docs/3.3.3/tutorials/getting-started/#_creating_a_graph)): 42 | 43 | ```java 44 | ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder(); 45 | builder.graph("modern") 46 | .withVertexCollection("software") 47 | .withVertexCollection("person") 48 | .withEdgeCollection("knows") 49 | .withEdgeCollection("created") 50 | .configureEdge("knows", "person", "person") 51 | .configureEdge("created", "person", "software"); 52 | 53 | // use the default database (and user:password) or configure a different database 54 | // builder.arangoHosts("172.168.1.10:4456") 55 | // .arangoUser("stripe") 56 | // .arangoPassword("gizmo") 57 | 58 | // create a ArangoDB graph 59 | BaseConfiguration conf = builder.build(); 60 | Graph graph = GraphFactory.open(conf); 61 | GraphTraversalSource gts = new GraphTraversalSource(graph); 62 | 63 | // Clone to avoid setup time 64 | GraphTraversalSource g = gts.clone(); 65 | // Add vertices 66 | Vertex v1 = g.addV("person").property(T.id, "1").property("name", "marko") 67 | .property("age", 29).next(); 68 | g = gts.clone(); 69 | Vertex v2 = g.addV("software").property(T.id, "3").property("name", "lop") 70 | .property("lang", "java").next(); 71 | 72 | // Add edges 73 | g = gts.clone(); 74 | Edge e1 = g.addE("created").from(v1).to(v2).property(T.id, "9") 75 | .property("weight", 0.4).next(); 76 | 77 | // Graph traversal 78 | // Find "marko" in the graph 79 | g = gts.clone(); 80 | Vertex rv = g.V().has("name","marko").next(); 81 | assert v1 == rv; 82 | 83 | // Walk along the "created" edges to "software" vertices 84 | g = gts.clone(); 85 | Edge re = g.V().has("name","marko").outE("created").next(); 86 | assert re == e1; 87 | 88 | g = gts.clone(); 89 | rv = g.V().has("name","marko").outE("created").inV().next(); 90 | // If the edge is irrelevant 91 | // rv = g.V().has("name","marko").out("created").next(); 92 | assert rv == v2; 93 | 94 | 95 | // Select the "name" property of the "software" vertices 96 | g = gts.clone(); 97 | String name = (String) g.V().has("name","marko").out("created").values("name").next(); 98 | assert name.equals("lop"); 99 | 100 | // close the graph and the traversal source 101 | gts.close(); 102 | graph.close(); 103 | ``` 104 | 105 | ## A note on element IDs 106 | 107 | The provider implementation supports user supplied IDs, i.e. provide an id property for graph 108 | elements, but currently we only support String ids, that is: 109 | 110 | ``` 111 | Vertex v1 = g.addV("person").property(T.id, "1"); 112 | ``` 113 | 114 | 115 | 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: 116 | 117 | ``` 118 | Vertex v2 = g.V("1"); 119 | assert v2 == null; 120 | ``` 121 | 122 | 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: 123 | 124 | ``` 125 | Vertex v2 = g.V("person/1"); 126 | assert v2 == v1; 127 | ``` 128 | 129 | ## Contributing 130 | 131 | We welcome bug reports (bugs, feature requests, etc.) as well as patches. When reporting a bug try to include as much information as possible (expected behaviour, seen behaviour, steps to reproduce, etc.). 132 | 133 | 134 | ## More 135 | 136 | Please visit our Wiki for additional information on how to use the latest version, build locally, run tests, etc. 137 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBBaseDocument.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.client; 10 | 11 | import com.arangodb.serde.InternalId; 12 | import com.arangodb.serde.InternalKey; 13 | import com.arangodb.serde.InternalRev; 14 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 15 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonIgnore; 16 | import com.arangodb.shaded.fasterxml.jackson.annotation.JsonProperty; 17 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBVertexPropertyData; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | /** 23 | * The ArangoDB BaseBaseDocument provides the internal fields required for the driver to correctly 24 | * serialize and deserialize vertices and edges. 25 | * 26 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 27 | */ 28 | 29 | public abstract class ArangoDBBaseDocument { 30 | 31 | /** ArangoDB internal id. */ 32 | 33 | @InternalId 34 | protected String _id; 35 | 36 | /** ArangoDB internal revision. */ 37 | 38 | @InternalRev 39 | protected String _rev; 40 | 41 | /** ArangoDB internal name - mapped to TinkerPop's ID. */ 42 | 43 | @InternalKey 44 | protected String _key; 45 | 46 | /** The label of the document */ 47 | 48 | @JsonProperty 49 | protected String label; 50 | 51 | @JsonProperty 52 | protected Map> properties; 53 | 54 | /** The collection in which the element is placed. */ 55 | 56 | @JsonIgnore 57 | protected String collection; 58 | 59 | /** the graph of the document. */ 60 | 61 | @JsonIgnore 62 | protected ArangoDBGraph graph; 63 | 64 | /** Flag to indicate if the element is paired to a document in the DB. */ 65 | 66 | @JsonIgnore 67 | protected boolean paired = false; 68 | 69 | /** 70 | * Constructor used for Arango DB JavaBeans de-/serialisation.. 71 | */ 72 | public ArangoDBBaseDocument() { 73 | super(); 74 | } 75 | 76 | /** 77 | * Instantiates a new Arango DB base document. The document's collection is assigned by requesting the graph to 78 | * prefix the collection. 79 | * 80 | * @param key the key to assign to the document 81 | * @param label the document label 82 | * @param graph the graph that contains the document 83 | */ 84 | 85 | public ArangoDBBaseDocument(String key, String label, ArangoDBGraph graph) { 86 | this._key = key; 87 | this.label = label; 88 | this.graph = graph; 89 | this.collection = graph.getPrefixedCollectioName(label); 90 | } 91 | 92 | /** 93 | * Get the Document's ArangoDB Id. 94 | * 95 | * @return the id 96 | */ 97 | 98 | public String _id() { 99 | return _id; 100 | } 101 | 102 | /** 103 | * Set the Document's ArangoDB Id. 104 | * This method is not for public use as ids must be final. It is only provided to allow the deserialization to 105 | * assign the value. 106 | * 107 | * @param id the id 108 | */ 109 | 110 | public void _id(String id) { 111 | this._id = id; 112 | } 113 | 114 | /** 115 | * Get the Document's ArangoDB Key. 116 | * 117 | * @return the name 118 | */ 119 | 120 | public String _key() { 121 | return _key; 122 | } 123 | 124 | /** 125 | * Set the Document's ArangoDB Key. 126 | * This method is only provided to allow the deserialization to assign the value. 127 | * 128 | * @param key the key 129 | */ 130 | 131 | protected void _key(String key) { 132 | this._key = key; 133 | } 134 | 135 | /** 136 | * Get the Document's ArangoDB Revision. 137 | * 138 | * @return the revision 139 | */ 140 | 141 | public String _rev() { 142 | return _rev; 143 | } 144 | 145 | /** 146 | * Set the Document's ArangoDB Revision. 147 | * This method is only provided to allow the deserialization to assign the value. 148 | * 149 | * @param revision the revision 150 | */ 151 | 152 | protected void _rev(String revision) { 153 | this._rev = revision; 154 | } 155 | 156 | /** 157 | * Get the document's collection. 158 | * 159 | * @return the collection 160 | */ 161 | 162 | public String collection() { 163 | return collection; 164 | } 165 | 166 | /** 167 | * Set the Documents collection. 168 | * This method is only provided to allow the deserialization to assign the value. 169 | * 170 | * @param collection the collection 171 | */ 172 | 173 | protected void collection(String collection) { 174 | this.collection = collection; 175 | } 176 | 177 | /** 178 | * The graph in which the document is contained. 179 | * 180 | * @return the Arango DB graph 181 | */ 182 | 183 | public ArangoDBGraph graph() { 184 | return graph; 185 | } 186 | 187 | /** 188 | * Set the document's graph 189 | * 190 | * @param graph the graph 191 | */ 192 | 193 | public void graph(ArangoDBGraph graph) { 194 | this.graph = graph; 195 | } 196 | 197 | 198 | /** 199 | * Checks if the document is paired. 200 | * 201 | * @return true, if is paired 202 | */ 203 | 204 | public boolean isPaired() { 205 | return paired; 206 | } 207 | 208 | /** 209 | * Sets the paired value of the document. 210 | * 211 | * @param paired the new paired status 212 | */ 213 | 214 | public void setPaired(boolean paired) { 215 | this.paired = paired; 216 | } 217 | 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.arangodb.tinkerpop.gremlin.structure; 20 | 21 | import org.apache.tinkerpop.gremlin.structure.*; 22 | import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; 23 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import java.util.*; 28 | import java.util.stream.Collectors; 29 | 30 | import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; 31 | 32 | 33 | public class ArangoDBEdge implements Edge { 34 | 35 | private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBEdge.class); 36 | 37 | private final ArangoDBGraph graph; 38 | private final ArangoDBEdgeData data; 39 | private boolean removed; 40 | 41 | public ArangoDBEdge(ArangoDBGraph graph, ArangoDBEdgeData data) { 42 | this.graph = graph; 43 | this.data = data; 44 | this.removed = false; 45 | } 46 | 47 | public ArangoDBEdge(final String id, final String label, final String outVertexId, final String inVertexId, ArangoDBGraph graph) { 48 | this.graph = graph; 49 | String inferredLabel, key; 50 | if (id != null) { 51 | int separator = id.indexOf('/'); 52 | if (separator > 0) { 53 | inferredLabel = id.substring(0, separator); 54 | key = id.substring(separator + 1); 55 | } else { 56 | inferredLabel = label != null ? label : DEFAULT_LABEL; 57 | key = id; 58 | } 59 | } else { 60 | inferredLabel = label != null ? label : DEFAULT_LABEL; 61 | key = null; 62 | } 63 | 64 | data = new ArangoDBEdgeData(inferredLabel, key, outVertexId, inVertexId); 65 | removed = false; 66 | } 67 | 68 | @Override 69 | public String id() { 70 | String key = data.getKey(); 71 | if (key == null) { 72 | return null; 73 | } 74 | return graph.getPrefixedCollectioName(label()) + "/" + key; 75 | } 76 | 77 | @Override 78 | public String label() { 79 | return data.getLabel(); 80 | } 81 | 82 | @Override 83 | public ArangoDBGraph graph() { 84 | return graph; 85 | } 86 | 87 | public void insert() { 88 | if (removed) throw elementAlreadyRemoved(Edge.class, id()); 89 | graph.getClient().insertEdge(data); 90 | } 91 | 92 | public void update() { 93 | if (removed) throw elementAlreadyRemoved(Edge.class, id()); 94 | graph.getClient().updateEdge(data); 95 | } 96 | 97 | public void removeProperty(String key) { 98 | if (removed) throw elementAlreadyRemoved(Edge.class, id()); 99 | if (data.hasProperty(key)) { 100 | data.removeProperty(key); 101 | update(); 102 | } 103 | } 104 | 105 | @Override 106 | @SuppressWarnings("unchecked") 107 | public Iterator> properties(final String... propertyKeys) { 108 | return data.properties() 109 | .filter(entry -> ElementHelper.keyExists(entry.getKey(), propertyKeys)) 110 | .map(entry -> (Property) new ArangoDBProperty<>(this, entry.getKey(), entry.getValue())) 111 | .collect(Collectors.toList()).iterator(); 112 | } 113 | 114 | @Override 115 | public Property property(final String key, final V value) { 116 | if (removed) throw elementAlreadyRemoved(Edge.class, id()); 117 | LOGGER.info("set property {} = {}", key, value); 118 | ElementHelper.validateProperty(key, value); 119 | data.setProperty(key, value); 120 | update(); 121 | return new ArangoDBProperty<>(this, key, value); 122 | } 123 | 124 | @SuppressWarnings("unchecked") 125 | @Override 126 | public Property property(final String key) { 127 | if (data.hasProperty(key)) { 128 | Object value = data.getProperty(key); 129 | return new ArangoDBProperty<>(this, key, (V) value); 130 | } 131 | return Property.empty(); 132 | } 133 | 134 | @Override 135 | public Set keys() { 136 | return data.getProperties().keySet(); 137 | } 138 | 139 | @Override 140 | public void remove() { 141 | LOGGER.info("removing {} from graph {}.", id(), graph.name()); 142 | graph.getClient().deleteEdge(data); 143 | this.removed = true; 144 | } 145 | 146 | @Override 147 | public Iterator values(String... propertyKeys) { 148 | return Edge.super.values(propertyKeys); 149 | } 150 | 151 | @Override 152 | public String toString() { 153 | return StringFactory.edgeString(this); 154 | } 155 | 156 | @Override 157 | public Iterator vertices(Direction direction) { 158 | if (removed) return Collections.emptyIterator(); 159 | List ids = new ArrayList<>(); 160 | switch (direction) { 161 | case BOTH: 162 | ids.add(data.getFrom()); 163 | ids.add(data.getTo()); 164 | break; 165 | case IN: 166 | ids.add(data.getTo()); 167 | break; 168 | case OUT: 169 | ids.add(data.getFrom()); 170 | break; 171 | } 172 | return graph.getClient().getGraphVertices(ids, Collections.emptyList()).stream() 173 | .map(it -> (Vertex) new ArangoDBVertex(graph, it)) 174 | .iterator(); 175 | } 176 | 177 | @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 178 | @Override 179 | public boolean equals(final Object object) { 180 | return ElementHelper.areEqual(this, object); 181 | } 182 | 183 | @Override 184 | public int hashCode() { 185 | return ElementHelper.hashCode(this); 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBPropertyFilter.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.client; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | import org.apache.commons.lang3.StringUtils; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | /** 21 | * The ArangoDB property filter class constructs AQL segments for comparing a document property 22 | * with a given value. 23 | * 24 | * @author Achim Brandt (http://www.triagens.de) 25 | * @author Johannes Gocke (http://www.triagens.de) 26 | * @author Guido Schwab (http://www.triagens.de) 27 | * @author Jan Steemann (http://www.triagens.de) 28 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 29 | */ 30 | 31 | public class ArangoDBPropertyFilter { 32 | 33 | /** 34 | * The Compare Operators. 35 | */ 36 | 37 | public enum Compare { 38 | 39 | /** The equal. */ 40 | EQUAL, 41 | 42 | /** The not equal. */ 43 | NOT_EQUAL, 44 | 45 | /** The greater than. */ 46 | GREATER_THAN, 47 | 48 | /** The greater than equal. */ 49 | GREATER_THAN_EQUAL, 50 | 51 | /** The less than. */ 52 | LESS_THAN, 53 | 54 | /** The less than equal. */ 55 | LESS_THAN_EQUAL, 56 | 57 | /** The has. */ 58 | HAS, 59 | 60 | /** The has not. */ 61 | HAS_NOT, 62 | 63 | /** The in. */ 64 | IN, 65 | 66 | /** The not in. */ 67 | NOT_IN 68 | }; 69 | 70 | /** The Constant PROPERTY. */ 71 | 72 | private static final String PROPERTY = "property"; 73 | 74 | /** The Constant logger. */ 75 | 76 | private static final Logger logger = LoggerFactory.getLogger(ArangoDBPropertyFilter.class); 77 | 78 | 79 | public static ArangoDBPropertyFilter empty() { 80 | return new ArangoDBPropertyFilter(); 81 | } 82 | 83 | /** The property containers. */ 84 | 85 | private List propertyContainers = new ArrayList(); 86 | 87 | /** 88 | * Adds a new "has" filter and returns the object. This is a fluent method that allows 89 | * adding multiple property filters to an ArangoDBPropertyFilter. 90 | * 91 | * @param key Name of the attribute to compare 92 | * @param value Value of the attribute to compare against 93 | * @param compare the compare operator 94 | * @return return the property filter 95 | */ 96 | 97 | public ArangoDBPropertyFilter has( 98 | final String key, 99 | final Object value, 100 | final Compare compare) { 101 | this.propertyContainers.add(new PropertyContainer(key, value, compare)); 102 | return this; 103 | } 104 | 105 | /** 106 | * Constructs the the AQL segment for each property filter and adds the required name-value 107 | * entries to the bind parameters map. 108 | * 109 | * @param prefix the iterator/variable to which the property filter will be applied 110 | * @param filterSegments the list to populate with the AQL segments 111 | * @param bindVars the map to populate with the name-value bindings 112 | */ 113 | 114 | public void addAqlSegments( 115 | String prefix, 116 | List filterSegments, 117 | Map bindVars) { 118 | logger.debug("addAqlSegments"); 119 | int count = 0; 120 | for (final PropertyContainer container : propertyContainers) { 121 | String key = escapeKey(container.key); 122 | switch (container.compare) { 123 | case EQUAL: 124 | filterSegments.add(prefix + key + " == @property" + count); 125 | bindVars.put(PROPERTY + count, container.value); 126 | break; 127 | case NOT_EQUAL: 128 | filterSegments.add(prefix + key + " != @property" + count); 129 | bindVars.put(PROPERTY + count, container.value); 130 | break; 131 | case GREATER_THAN: 132 | filterSegments.add(prefix + key + " > @property" + count); 133 | bindVars.put(PROPERTY + count, container.value); 134 | break; 135 | case LESS_THAN: 136 | filterSegments.add(prefix + key + " < @property" + count); 137 | bindVars.put(PROPERTY + count, container.value); 138 | break; 139 | case GREATER_THAN_EQUAL: 140 | filterSegments.add(prefix + key + " >= @property" + count); 141 | bindVars.put(PROPERTY + count, container.value); 142 | break; 143 | case LESS_THAN_EQUAL: 144 | filterSegments.add(prefix + key + " <= @property" + count); 145 | bindVars.put(PROPERTY + count, container.value); 146 | break; 147 | case HAS: 148 | filterSegments.add(prefix + container.key + " != null"); 149 | break; 150 | case HAS_NOT: 151 | filterSegments.add(prefix + container.key + " == null"); 152 | break; 153 | case IN: 154 | filterSegments.add( 155 | prefix + container.key + " IN [" + addArray(bindVars, PROPERTY + count, container.value) + "]"); 156 | break; 157 | case NOT_IN: 158 | filterSegments.add( 159 | prefix + container.key + " NOT IN [" + addArray(bindVars, PROPERTY + count, container.value) + "]"); 160 | break; 161 | default: 162 | // do nothing 163 | } 164 | count++; 165 | } 166 | } 167 | 168 | /** 169 | * Adds the array. 170 | * 171 | * @param bindVars the bind vars 172 | * @param propertyName the property name 173 | * @param value the value 174 | * @return the string 175 | */ 176 | 177 | private String addArray(Map bindVars, String propertyName, Object value) { 178 | int c = 0; 179 | List elements = new ArrayList(); 180 | if (value instanceof Iterable) { 181 | Iterable iterable = (Iterable) value; 182 | Iterator iter = iterable.iterator(); 183 | while (iter.hasNext()) { 184 | String prop = propertyName + "_" + c++; 185 | elements.add("@" + prop); 186 | bindVars.put(prop, iter.next()); 187 | } 188 | } else { 189 | elements.add("@" + propertyName); 190 | bindVars.put(propertyName, value); 191 | } 192 | 193 | return StringUtils.join(elements, ", "); 194 | } 195 | 196 | /** 197 | * Escape name. 198 | * 199 | * @param key the name 200 | * @return the string 201 | */ 202 | private String escapeKey(String key) { 203 | return "`" + key.replaceAll("`", "") + "`"; 204 | } 205 | 206 | /** 207 | * The Class PropertyContainer. 208 | */ 209 | 210 | private class PropertyContainer { 211 | 212 | /** The name. */ 213 | 214 | public final String key; 215 | 216 | /** The value. */ 217 | 218 | public final Object value; 219 | 220 | /** The compare. */ 221 | 222 | public final Compare compare; 223 | 224 | /** 225 | * Instantiates a new property container. 226 | * 227 | * @param key the name 228 | * @param value the value 229 | * @param compare the compare 230 | */ 231 | public PropertyContainer(final String key, final Object value, final Compare compare) { 232 | this.key = key; 233 | this.value = value; 234 | this.compare = compare; 235 | } 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.arangodb 8 | arangodb-tinkerpop-provider 9 | 2.0.3 10 | jar 11 | 12 | ArangoDB-TinkerPop-Provider 13 | An implementation of the Tinkerpop Provider OLTP for ArangoDB 14 | https://github.com/ArangoDB-Community 15 | 16 | 17 | 18 | Apache License 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0 20 | repo 21 | 22 | 23 | 24 | 25 | UTF-8 26 | 7.17.0 27 | 3.7.3 28 | 4.13.1 29 | 1.3.15 30 | 3.4 31 | 32 | 33 | 34 | 35 | com.arangodb 36 | arangodb-java-driver-shaded 37 | ${arangodb-java-driver.version} 38 | 39 | 40 | org.apache.tinkerpop 41 | gremlin-core 42 | ${tinkerpop.version} 43 | 44 | 45 | org.apache.commons 46 | commons-lang3 47 | ${commons-lang3.version} 48 | 49 | 50 | org.apache.commons 51 | commons-collections4 52 | 4.1 53 | 54 | 55 | com.google.code.gson 56 | gson 57 | 2.8.9 58 | 59 | 60 | 61 | 62 | junit 63 | junit 64 | ${junit.version} 65 | test 66 | 67 | 68 | org.apache.tinkerpop 69 | gremlin-test 70 | ${tinkerpop.version} 71 | test 72 | 73 | 74 | ch.qos.logback 75 | logback-classic 76 | ${logback-classic.version} 77 | jar 78 | test 79 | 80 | 81 | org.mockito 82 | mockito-core 83 | 4.11.0 84 | test 85 | 86 | 87 | org.hamcrest 88 | hamcrest-all 89 | 1.3 90 | test 91 | 92 | 93 | 94 | 95 | 96 | gschwab 97 | gschwab 98 | https://github.com/gschwab 99 | 100 | 101 | scottashton 102 | scottashton 103 | https://github.com/scottashton 104 | 105 | 106 | a-brandt 107 | a-brandt 108 | https://github.com/a-brandt 109 | 110 | 111 | arcanefoam 112 | Horacio Hoyos Rodriguez 113 | https://github.com/arcanefoam 114 | 115 | 116 | 117 | 118 | 119 | doclint-java8-disable 120 | 121 | [1.8,) 122 | 123 | 124 | -Xdoclint:none 125 | 126 | 127 | 128 | 129 | release 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-source-plugin 135 | 3.2.1 136 | 137 | 138 | 139 | jar 140 | 141 | 142 | 143 | 144 | 145 | 146 | org.apache.maven.plugins 147 | maven-javadoc-plugin 148 | 3.2.0 149 | 150 | 151 | attach-javadocs 152 | 153 | jar 154 | 155 | 156 | ${javadoc.opts} 157 | 8 158 | 159 | 160 | 161 | 162 | 163 | 164 | org.apache.maven.plugins 165 | maven-gpg-plugin 166 | 1.6 167 | 168 | 169 | sign-artifacts 170 | verify 171 | 172 | sign 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | org.apache.maven.plugins 187 | maven-surefire-plugin 188 | 3.0.0-M3 189 | 190 | 191 | 192 | 193 | 194 | org.apache.maven.plugins 195 | maven-compiler-plugin 196 | 3.8.0 197 | 198 | 1.8 199 | 1.8 200 | 201 | 202 | 203 | 204 | 205 | org.apache.maven.plugins 206 | maven-surefire-plugin 207 | 3.0.0-M3 208 | 209 | 210 | 211 | org.sonatype.plugins 212 | nexus-staging-maven-plugin 213 | 1.6.8 214 | true 215 | 216 | ossrh 217 | https://oss.sonatype.org/ 218 | true 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | https://github.com/ArangoDB-Community/arangodb-tinkerpop-provider 227 | scm:git:https://github.com/ArangoDB-Community/arangodb-tinkerpop-provider.git 228 | scm:git:git@github.com:ArangoDB-Community/arangodb-tinkerpop-provider.git 229 | 230 | 231 | 232 | Kinori Tech 233 | http://kinori.tech 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /jautodoc_templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 35 | 64 | 72 | 79 | 86 | 97 | 106 | 115 | 124 | 133 | 142 | 149 | 156 | 165 | 174 | 183 | 200 | 208 | 215 | 222 | 229 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 ArangoDB GmbH 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/structure/ArangoDBVertex.java: -------------------------------------------------------------------------------- 1 | /// /////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | /// /////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.structure; 10 | 11 | import java.util.*; 12 | import java.util.regex.Matcher; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.Stream; 15 | 16 | import org.apache.commons.lang3.ArrayUtils; 17 | import org.apache.tinkerpop.gremlin.structure.Direction; 18 | import org.apache.tinkerpop.gremlin.structure.Edge; 19 | import org.apache.tinkerpop.gremlin.structure.Graph; 20 | import org.apache.tinkerpop.gremlin.structure.T; 21 | import org.apache.tinkerpop.gremlin.structure.Vertex; 22 | import org.apache.tinkerpop.gremlin.structure.VertexProperty; 23 | import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; 24 | import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; 25 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | 29 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; 30 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBPropertyFilter; 31 | import com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil; 32 | 33 | import static com.arangodb.tinkerpop.gremlin.utils.ArangoDBUtil.elementAlreadyRemoved; 34 | 35 | 36 | /** 37 | * The ArangoDB vertex class. 38 | * 39 | * @author Achim Brandt (http://www.triagens.de) 40 | * @author Johannes Gocke (http://www.triagens.de) 41 | * @author Guido Schwab (http://www.triagens.de) 42 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 43 | */ 44 | 45 | public class ArangoDBVertex implements Vertex { 46 | 47 | private static final Logger LOGGER = LoggerFactory.getLogger(ArangoDBVertex.class); 48 | 49 | private final ArangoDBGraph graph; 50 | private final ArangoDBVertexData data; 51 | private boolean removed; 52 | 53 | public ArangoDBVertex(ArangoDBGraph graph, ArangoDBVertexData data) { 54 | this.graph = graph; 55 | this.data = data; 56 | this.removed = false; 57 | } 58 | 59 | public ArangoDBVertex(final String id, final String label, ArangoDBGraph graph) { 60 | this.graph = graph; 61 | String inferredLabel, key; 62 | if (id != null) { 63 | int separator = id.indexOf('/'); 64 | if (separator > 0) { 65 | inferredLabel = id.substring(0, separator); 66 | key = id.substring(separator + 1); 67 | } else { 68 | inferredLabel = label != null ? label : DEFAULT_LABEL; 69 | key = id; 70 | } 71 | } else { 72 | inferredLabel = label != null ? label : DEFAULT_LABEL; 73 | key = null; 74 | } 75 | 76 | data = new ArangoDBVertexData(inferredLabel, key); 77 | removed = false; 78 | } 79 | 80 | public boolean isRemoved() { 81 | return removed; 82 | } 83 | 84 | @Override 85 | public String id() { 86 | String key = data.getKey(); 87 | if (key == null) { 88 | return null; 89 | } 90 | return graph.getPrefixedCollectioName(label()) + "/" + key; 91 | } 92 | 93 | @Override 94 | public String label() { 95 | return data.getLabel(); 96 | } 97 | 98 | @Override 99 | public ArangoDBGraph graph() { 100 | return graph; 101 | } 102 | 103 | @Override 104 | public void remove() { 105 | if (removed) return; 106 | LOGGER.info("removing {} from graph {}.", id(), graph.name()); 107 | edges(Direction.BOTH).forEachRemaining(Edge::remove); 108 | graph.getClient().deleteVertex(data); 109 | this.removed = true; 110 | } 111 | 112 | @Override 113 | public Edge addEdge(String label, Vertex inVertex, Object... keyValues) { 114 | LOGGER.info("addEdge in collection {} to vertex {}", label, inVertex == null ? "?" : inVertex.id()); 115 | ElementHelper.legalPropertyKeyValueArray(keyValues); 116 | ElementHelper.validateLabel(label); 117 | if (!graph.edgeCollections().contains(label)) { 118 | throw new IllegalArgumentException(String.format("Edge label (%s)not in graph (%s) edge collections.", label, graph.name())); 119 | } 120 | if (inVertex == null) { 121 | throw Graph.Exceptions.argumentCanNotBeNull("vertex"); 122 | } 123 | Object id; 124 | ArangoDBEdge edge = null; 125 | if (ElementHelper.getIdValue(keyValues).isPresent()) { 126 | id = ElementHelper.getIdValue(keyValues).get(); 127 | if (graph.features().edge().willAllowId(id)) { 128 | if (id.toString().contains("/")) { 129 | String fullId = id.toString(); 130 | String[] parts = fullId.split("/"); 131 | // The collection name is the last part of the full name 132 | String[] collectionParts = parts[0].split("_"); 133 | String collectionName = collectionParts[collectionParts.length - 1]; 134 | if (collectionName.contains(label)) { 135 | id = parts[1]; 136 | 137 | } 138 | } 139 | Matcher m = ArangoDBUtil.DOCUMENT_KEY.matcher((String) id); 140 | if (m.matches()) { 141 | edge = new ArangoDBEdge(id.toString(), label, (String) this.id(), (String) inVertex.id(), graph); 142 | } else { 143 | throw new ArangoDBGraphException(String.format("Given id (%s) has unsupported characters.", id)); 144 | } 145 | } else { 146 | throw Vertex.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); 147 | } 148 | } else { 149 | edge = new ArangoDBEdge(null, label, (String) this.id(), (String) inVertex.id(), graph); 150 | } 151 | // The vertex needs to exist before we can attach properties 152 | edge.insert(); 153 | ElementHelper.attachProperties(edge, keyValues); 154 | return edge; 155 | } 156 | 157 | @Override 158 | public VertexProperty property( 159 | final Cardinality cardinality, 160 | final String key, 161 | final V value, 162 | final Object... keyValues 163 | ) { 164 | if (removed) throw elementAlreadyRemoved(Vertex.class, id()); 165 | ElementHelper.legalPropertyKeyValueArray(keyValues); 166 | ElementHelper.validateProperty(key, value); 167 | 168 | final Optional> optionalVertexProperty = ElementHelper.stageVertexProperty(this, cardinality, key, value, keyValues); 169 | if (optionalVertexProperty.isPresent()) return optionalVertexProperty.get(); 170 | 171 | Optional optionalId = ElementHelper.getIdValue(keyValues); 172 | Object[] filteredKeyValues = ArrayUtils.clone(keyValues); 173 | String idValue = null; 174 | if (optionalId.isPresent()) { 175 | if (graph.features().vertex().properties().willAllowId(optionalId.get())) { 176 | idValue = optionalId.get().toString(); 177 | } else { 178 | throw VertexProperty.Exceptions.userSuppliedIdsOfThisTypeNotSupported(); 179 | } 180 | int idIndex = 0; 181 | for (int i = 0; i < filteredKeyValues.length; i += 2) { 182 | if (filteredKeyValues[i] == T.id) { 183 | idIndex = i; 184 | break; 185 | } 186 | } 187 | filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); 188 | filteredKeyValues = ArrayUtils.remove(filteredKeyValues, idIndex); 189 | } 190 | 191 | if (idValue == null) { 192 | idValue = UUID.randomUUID().toString(); 193 | } 194 | 195 | ArangoDBVertexPropertyData prop = new ArangoDBVertexPropertyData(idValue, value); 196 | 197 | final List list = data.getProperties().getOrDefault(key, new ArrayList<>()); 198 | list.add(prop); 199 | data.getProperties().put(key, list); 200 | 201 | ArangoDBVertexProperty vertexProperty = new ArangoDBVertexProperty<>(key, prop, this); 202 | ElementHelper.attachProperties(vertexProperty, filteredKeyValues); 203 | update(); 204 | return vertexProperty; 205 | } 206 | 207 | 208 | @Override 209 | public Iterator edges(Direction direction, String... edgeLabels) { 210 | List edgeCollections = getQueryEdgeCollections(edgeLabels); 211 | // If edgeLabels was not empty but all were discarded, this means that we should 212 | // return an empty iterator, i.e. no edges for that edgeLabels exist. 213 | if (edgeCollections.isEmpty()) { 214 | return Collections.emptyIterator(); 215 | } 216 | return graph.getClient().getVertexEdges(id(), edgeCollections, direction) 217 | .stream() 218 | .map(it -> (Edge) new ArangoDBEdge(graph, it)) 219 | .iterator(); 220 | } 221 | 222 | 223 | @Override 224 | public Iterator vertices(Direction direction, String... edgeLabels) { 225 | List edgeCollections = getQueryEdgeCollections(edgeLabels); 226 | // If edgeLabels was not empty but all were discarded, this means that we should 227 | // return an empty iterator, i.e. no edges for that edgeLabels exist. 228 | if (edgeCollections.isEmpty()) { 229 | return Collections.emptyIterator(); 230 | } 231 | return graph.getClient().getDocumentNeighbors(id(), edgeCollections, direction, ArangoDBPropertyFilter.empty(), ArangoDBVertexData.class).stream() 232 | .map(it -> (Vertex) new ArangoDBVertex(graph, it)) 233 | .iterator(); 234 | } 235 | 236 | 237 | @SuppressWarnings("unchecked") 238 | @Override 239 | public Iterator> properties(String... propertyKeys) { 240 | LOGGER.debug("Get properties {}", (Object[]) propertyKeys); 241 | return allProperties() 242 | .filter(it -> ElementHelper.keyExists(it.key(), propertyKeys)) 243 | .map(it -> (VertexProperty) it) 244 | .iterator(); 245 | } 246 | 247 | 248 | @Override 249 | public String toString() { 250 | return StringFactory.vertexString(this); 251 | } 252 | 253 | private Stream> allProperties() { 254 | return data.getProperties().entrySet().stream() 255 | .flatMap(x -> x.getValue().stream() 256 | .map(y -> new ArangoDBVertexProperty<>(x.getKey(), y, this)) 257 | ); 258 | } 259 | 260 | public void insert() { 261 | if (removed) throw elementAlreadyRemoved(Vertex.class, id()); 262 | graph.getClient().insertVertex(data); 263 | } 264 | 265 | 266 | public void update() { 267 | if (removed) throw elementAlreadyRemoved(Vertex.class, id()); 268 | graph.getClient().updateVertex(data); 269 | } 270 | 271 | public void removeProperty(ArangoDBVertexPropertyData prop) { 272 | if (removed) throw elementAlreadyRemoved(Vertex.class, id()); 273 | for (List it : data.getProperties().values()) { 274 | if (it.remove(prop)) return; 275 | } 276 | } 277 | 278 | /** 279 | * Query will raise an exception if the edge_collection name is not in the graph, so we need to filter out 280 | * edgeLabels not in the graph. 281 | * 282 | * @param edgeLabels 283 | * @return 284 | */ 285 | private List getQueryEdgeCollections(String... edgeLabels) { 286 | List vertexCollections; 287 | if (edgeLabels.length == 0) { 288 | vertexCollections = graph.edgeCollections().stream().map(graph::getPrefixedCollectioName).collect(Collectors.toList()); 289 | } else { 290 | vertexCollections = Arrays.stream(edgeLabels) 291 | .filter(el -> graph.edgeCollections().contains(el)) 292 | .map(graph::getPrefixedCollectioName) 293 | .collect(Collectors.toList()); 294 | 295 | } 296 | return vertexCollections; 297 | } 298 | 299 | @Override 300 | @SuppressWarnings("EqualsDoesntCheckParameterClass") 301 | public boolean equals(final Object object) { 302 | return ElementHelper.areEqual(this, object); 303 | } 304 | 305 | @Override 306 | public int hashCode() { 307 | return ElementHelper.hashCode(this); 308 | } 309 | 310 | } 311 | 312 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/ArangoDBGraphProvider.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin; 2 | 3 | import java.util.HashSet; 4 | import java.util.Map; 5 | import java.util.Properties; 6 | import java.util.Set; 7 | 8 | import com.arangodb.tinkerpop.gremlin.structure.*; 9 | import org.apache.commons.configuration2.Configuration; 10 | import org.apache.commons.configuration2.ConfigurationConverter; 11 | import org.apache.tinkerpop.gremlin.AbstractGraphProvider; 12 | import org.apache.tinkerpop.gremlin.LoadGraphWith; 13 | import org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData; 14 | import org.apache.tinkerpop.gremlin.structure.Element; 15 | import org.apache.tinkerpop.gremlin.structure.Graph; 16 | 17 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; 18 | import com.arangodb.tinkerpop.gremlin.utils.ArangoDBConfigurationBuilder; 19 | import org.apache.tinkerpop.gremlin.structure.VertexTest; 20 | 21 | /** 22 | * The Class ArangoDBGraphProvider. This provider assumes that there is a local ArangoDB running (i.e. 23 | * http://127.0.0.1:8529) with a tinkerpop database and a gremlin user that has Administrate permissions 24 | * on the db. 25 | * 26 | */ 27 | public class ArangoDBGraphProvider extends AbstractGraphProvider { 28 | 29 | /** The Constant IMPLEMENTATIONS. */ 30 | private static final Set IMPLEMENTATIONS = new HashSet() {{ 31 | add(ArangoDBEdge.class); 32 | add(ArangoDBGraph.class); 33 | add(ArangoDBGraphVariables.class); 34 | add(ArangoDBProperty.class); 35 | add(ArangoDBVertexPropertyData.class); 36 | add(ArangoDBVertex.class); 37 | add(ArangoDBVertexProperty.class); 38 | }}; 39 | 40 | 41 | @Override 42 | public Configuration newGraphConfiguration(final String graphName, final Class test, 43 | final String testMethodName, 44 | final Map configurationOverrides, 45 | final LoadGraphWith.GraphData loadGraphWith) { 46 | Configuration conf = getConfiguration(graphName, test, testMethodName, loadGraphWith); 47 | 48 | // assign overrides but don't allow gremlin.graph setting to be overridden. the test suite should 49 | // not be able to override that. 50 | configurationOverrides.entrySet().stream() 51 | .filter(c -> !c.getKey().equals(Graph.GRAPH)) 52 | .forEach(e -> conf.setProperty(e.getKey(), e.getValue())); 53 | return conf; 54 | } 55 | 56 | private Configuration getConfiguration( 57 | String graphName, 58 | Class test, 59 | String testMethodName, 60 | GraphData loadGraphWith) { 61 | ArangoDBConfigurationBuilder builder = new ArangoDBConfigurationBuilder() 62 | .arangoHosts("127.0.0.1:8529") 63 | .arangoUser("root") 64 | .arangoPassword("test") 65 | .graph(graphName); 66 | if (loadGraphWith != null) { 67 | switch(loadGraphWith) { 68 | case CLASSIC: 69 | System.out.println("CLASSIC"); 70 | builder.withEdgeCollection("knows"); 71 | builder.withEdgeCollection("created"); 72 | builder.configureEdge("knows", "vertex", "vertex"); 73 | builder.configureEdge("created", "vertex", "vertex"); 74 | break; 75 | case CREW: 76 | System.out.println("CREW"); 77 | builder.withVertexCollection("software"); 78 | builder.withVertexCollection("person"); 79 | builder.withEdgeCollection("uses"); 80 | builder.withEdgeCollection("develops"); 81 | builder.withEdgeCollection("traverses"); 82 | builder.configureEdge("uses", "person", "software"); 83 | builder.configureEdge("develops", "person", "software"); 84 | builder.configureEdge("traverses", "software", "software"); 85 | break; 86 | case GRATEFUL: 87 | System.out.println("GRATEFUL"); 88 | break; 89 | case MODERN: 90 | System.out.println("MODERN"); 91 | builder.withVertexCollection("dog"); 92 | builder.withVertexCollection("software"); 93 | builder.withVertexCollection("person"); 94 | builder.withEdgeCollection("knows"); 95 | builder.withEdgeCollection("created"); 96 | builder.configureEdge("knows", "person", "person"); 97 | builder.configureEdge("created", "person", "software"); 98 | break; 99 | default: 100 | System.out.println("default"); 101 | break; 102 | } 103 | } 104 | else { 105 | if (testMethodName.startsWith("shouldProcessVerticesEdges") 106 | || testMethodName.startsWith("shouldGenerate") 107 | || testMethodName.startsWith("shouldSetValueOnEdge") 108 | || testMethodName.startsWith("shouldAutotype")) { 109 | builder.withEdgeCollection("knows"); 110 | } 111 | else if(testMethodName.startsWith("shouldIterateEdgesWithStringIdSupport")) { 112 | builder.withEdgeCollection("self"); 113 | } 114 | else if(testMethodName.startsWith("shouldSupportUserSuppliedIds")) { 115 | builder.withEdgeCollection("test"); 116 | } 117 | else if(testMethodName.startsWith("shouldSupportUUID")) { 118 | builder.withEdgeCollection("friend"); 119 | } 120 | else if(testMethodName.startsWith("shouldReadWriteVertexWithBOTHEdges")) { 121 | builder.withEdgeCollection("friends"); 122 | } 123 | else if(testMethodName.startsWith("shouldReadWriteVertexWithINEdges")) { 124 | builder.withEdgeCollection("friends"); 125 | } 126 | else if(testMethodName.startsWith("shouldReadWriteVertexMultiPropsNoEdges")) { 127 | builder.withEdgeCollection("friends"); 128 | } 129 | else if(testMethodName.startsWith("shouldReadWriteDetachedVertexAsReferenceNoEdges")) { 130 | builder.withEdgeCollection("friends"); 131 | } 132 | else if(testMethodName.startsWith("shouldReadWriteVertexNoEdges")) { 133 | builder.withEdgeCollection("friends"); 134 | } 135 | else if(testMethodName.startsWith("shouldReadWriteVertexWithOUTEdges")) { 136 | builder.withEdgeCollection("friends"); 137 | } 138 | else if(testMethodName.startsWith("shouldReadWriteDetachedVertexNoEdges")) { 139 | builder.withEdgeCollection("friends"); 140 | } 141 | else if (testMethodName.startsWith("shouldReadWriteDetachedEdge")) { 142 | builder.withVertexCollection("person"); 143 | builder.withEdgeCollection("friend"); 144 | } 145 | else if (testMethodName.startsWith("shouldReadWriteDetachedEdgeAsReference")) { 146 | builder.withVertexCollection("person"); 147 | builder.withEdgeCollection("friend"); 148 | } 149 | else if (testMethodName.startsWith("shouldReadWriteEdge")) { 150 | builder.withVertexCollection("person"); 151 | builder.withEdgeCollection("friend"); 152 | } 153 | else if (testMethodName.startsWith("shouldThrowOnGraphEdgeSetPropertyStandard")) { 154 | builder.withEdgeCollection("self"); 155 | } 156 | else if (testMethodName.startsWith("shouldThrowOnGraphAddEdge")) { 157 | builder.withEdgeCollection("self"); 158 | } 159 | else { 160 | // Perhaps change for startsWith, but then it would be more verbose. Perhaps a set? 161 | switch (testMethodName) { 162 | case "shouldGetPropertyKeysOnEdge": 163 | case "shouldNotGetConcurrentModificationException": 164 | builder.withEdgeCollection("friend"); 165 | builder.withEdgeCollection("knows"); 166 | break; 167 | case "shouldTraverseInOutFromVertexWithMultipleEdgeLabelFilter": 168 | case "shouldTraverseInOutFromVertexWithSingleEdgeLabelFilter": 169 | builder.withEdgeCollection("hate"); 170 | builder.withEdgeCollection("friend"); 171 | break; 172 | case "shouldPersistDataOnClose": 173 | builder.withEdgeCollection("collaborator"); 174 | break; 175 | case "shouldTestTreeConnectivity": 176 | builder.withEdgeCollection("test1"); 177 | builder.withEdgeCollection("test2"); 178 | builder.withEdgeCollection("test3"); 179 | break; 180 | case "shouldEvaluateConnectivityPatterns": 181 | builder.withEdgeCollection("knows"); 182 | builder.withEdgeCollection("knows"); 183 | break; 184 | case "shouldRemoveEdgesWithoutConcurrentModificationException": 185 | builder.withEdgeCollection("link"); 186 | break; 187 | case "shouldGetValueThatIsNotPresentOnEdge": 188 | case "shouldHaveStandardStringRepresentationForEdgeProperty": 189 | case "shouldHaveTruncatedStringRepresentationForEdgeProperty": 190 | case "shouldValidateIdEquality": 191 | case "shouldValidateEquality": 192 | case "shouldHaveExceptionConsistencyWhenAssigningSameIdOnEdge": 193 | case "shouldAddEdgeWithUserSuppliedStringId": 194 | case "shouldAllowNullAddEdge": 195 | builder.withEdgeCollection("self"); 196 | break; 197 | case "shouldAllowRemovalFromEdgeWhenAlreadyRemoved": 198 | case "shouldRespectWhatAreEdgesAndWhatArePropertiesInMultiProperties": 199 | case "shouldProcessEdges": 200 | case "shouldReturnOutThenInOnVertexIterator": 201 | case "shouldReturnEmptyIteratorIfNoProperties": 202 | builder.withEdgeCollection("knows"); 203 | break; 204 | case "shouldNotHaveAConcurrentModificationExceptionWhenIteratingAndRemovingAddingEdges": 205 | builder.withEdgeCollection("knows"); 206 | builder.withEdgeCollection("pets"); 207 | builder.withEdgeCollection("walks"); 208 | builder.withEdgeCollection("livesWith"); 209 | break; 210 | case "shouldHaveStandardStringRepresentation": 211 | builder.withEdgeCollection("friends"); 212 | break; 213 | case "shouldReadWriteSelfLoopingEdges": 214 | builder.withEdgeCollection("CONTROL"); 215 | builder.withEdgeCollection("SELFLOOP"); 216 | break; 217 | case "shouldReadGraphML": 218 | case "shouldReadGraphMLUnorderedElements": 219 | case "shouldTransformGraphMLV2ToV3ViaXSLT": 220 | case "shouldReadLegacyGraphSON": 221 | builder.withEdgeCollection("knows"); 222 | builder.withEdgeCollection("created"); 223 | break; 224 | case "shouldAddVertexWithLabel": 225 | case "shouldAllowNullAddVertexProperty": 226 | builder.withVertexCollection("person"); 227 | break; 228 | case "shouldNotAllowSetProperty": 229 | case "shouldHashAndEqualCorrectly": 230 | case "shouldNotAllowRemove": 231 | case "shouldNotConstructNewWithSomethingAlreadyDetached": 232 | case "shouldNotConstructNewWithSomethingAlreadyReferenced": 233 | builder.withEdgeCollection("test"); 234 | break; 235 | case "shouldHaveExceptionConsistencyWhenUsingNullVertex": 236 | builder.withEdgeCollection("tonothing"); 237 | break; 238 | case "shouldHandleSelfLoops": 239 | builder.withVertexCollection("person"); 240 | builder.withEdgeCollection("self"); 241 | break; 242 | case "shouldAttachWithCreateMethod": 243 | case "testAttachableCreateMethod": 244 | builder.withVertexCollection("person"); 245 | builder.withVertexCollection("project"); 246 | builder.withEdgeCollection("knows"); 247 | builder.withEdgeCollection("developedBy"); 248 | builder.configureEdge("knows", "person", "person"); 249 | builder.configureEdge("developedBy", "project", "person"); 250 | break; 251 | case "shouldConstructReferenceVertex": 252 | builder.withVertexCollection("blah"); 253 | break; 254 | case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabel": 255 | case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabel": 256 | case "shouldHaveExceptionConsistencyWhenUsingEmptyVertexLabelOnOverload": 257 | case "shouldHaveExceptionConsistencyWhenUsingSystemVertexLabelOnOverload": 258 | if (VertexTest.class.equals(test.getEnclosingClass())) { 259 | builder.withVertexCollection("foo"); 260 | } 261 | break; 262 | case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabelOnOverload": 263 | case "shouldHaveExceptionConsistencyWhenUsingNullVertexLabel": 264 | builder.withVertexCollection("foo"); 265 | break; 266 | case "shouldReadGraphMLWithCommonVertexAndEdgePropertyNames": 267 | builder.withEdgeCollection("created"); 268 | builder.withEdgeCollection("knows"); 269 | break; 270 | default: 271 | System.out.println("case \"" + testMethodName + "\":"); 272 | } 273 | } 274 | } 275 | return builder.build(); 276 | } 277 | 278 | @Override 279 | public void clear(Graph graph, Configuration configuration) throws Exception { 280 | ArangoDBGraphClient client; 281 | if (graph ==null) { 282 | Configuration arangoConfig = configuration.subset(ArangoDBGraph.PROPERTY_KEY_PREFIX); 283 | Properties arangoProperties = ConfigurationConverter.getProperties(arangoConfig); 284 | client = new ArangoDBGraphClient(null, arangoProperties, "tinkerpop", 0, true); 285 | client.deleteGraph(arangoConfig.getString(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME)); 286 | } 287 | else { 288 | ArangoDBGraph agraph = (ArangoDBGraph) graph; 289 | client = agraph.getClient(); 290 | client.clear(agraph); 291 | agraph.close(); 292 | } 293 | 294 | } 295 | 296 | @Override 297 | public Set getImplementations() { 298 | return IMPLEMENTATIONS; 299 | } 300 | 301 | @Override 302 | public Map getBaseConfiguration(String graphName, Class test, String testMethodName, 303 | GraphData loadGraphWith) { 304 | // TODO Auto-generated method stub 305 | return null; 306 | } 307 | 308 | @Override 309 | public Object convertId(Object id, Class c) { 310 | return id.toString(); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/test/java/com/arangodb/tinkerpop/gremlin/client/test/ClientTest.java: -------------------------------------------------------------------------------- 1 | package com.arangodb.tinkerpop.gremlin.client.test; 2 | 3 | import static org.hamcrest.Matchers.contains; 4 | import static org.hamcrest.Matchers.containsInAnyOrder; 5 | import static org.hamcrest.Matchers.hasSize; 6 | import static org.hamcrest.Matchers.is; 7 | import static org.hamcrest.Matchers.notNullValue; 8 | import static org.hamcrest.Matchers.startsWith; 9 | import static org.junit.Assert.assertThat; 10 | 11 | import com.arangodb.ArangoDBException; 12 | import com.arangodb.ArangoDatabase; 13 | import com.arangodb.ArangoGraph; 14 | import com.arangodb.entity.EdgeDefinition; 15 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; 16 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; 17 | 18 | import java.util.*; 19 | import java.util.stream.Collectors; 20 | 21 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 22 | import org.apache.commons.configuration2.ConfigurationConverter; 23 | import org.apache.commons.configuration2.PropertiesConfiguration; 24 | import org.junit.*; 25 | import org.junit.rules.ExpectedException; 26 | import org.junit.runner.RunWith; 27 | import org.junit.runners.Parameterized; 28 | 29 | /** 30 | * This tests require four users: 31 | *
    32 | *
  • root: To test DB creation, requires root access 33 | *
  • gremlin: To test graph/collection creation+access in the db, requires "Administrate" permission to DB 34 | *
  • reader: To test graph/collection access in the db, requires "Access" permission to DB 35 | *
  • limited: To test no access to the db, requires "No access" permission to DB 36 | *
37 | * For all users (except root) the password is expected to be the same as the username. For the root, 38 | * the password must be set via environment variable ARANGODB_ROOT_PWD (so no system password is shared 39 | * via code). 40 | * @author Horacio Hoyos Rodriguez (@horaciohoyosr) 41 | * 42 | */ 43 | @RunWith(Parameterized.class) 44 | @Ignore 45 | public class ClientTest { 46 | 47 | @Parameterized.Parameters 48 | public static Collection data() { 49 | return Arrays.asList(new Object[][] { 50 | { true, "knows_", "social_", "routeplanner_" }, 51 | { false, "", "", "" } 52 | }); 53 | } 54 | 55 | @Rule 56 | public ExpectedException exception = ExpectedException.none(); 57 | 58 | private PropertiesConfiguration configuration; 59 | private ArangoDBGraphClient client; 60 | 61 | @Parameterized.Parameter 62 | public boolean shouldPrefixCollectionWithGraphName; 63 | 64 | @Parameterized.Parameter(1) 65 | public String knowsPrefix; 66 | 67 | @Parameterized.Parameter(2) 68 | public String socialPrefix; 69 | 70 | @Parameterized.Parameter(3) 71 | public String routeplannerPrefix; 72 | 73 | @Before 74 | public void setUp() throws Exception { 75 | 76 | configuration = new PropertiesConfiguration(); 77 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_DB_NAME, "tinkerpop"); 78 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME, "standard"); 79 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.hosts", "127.0.0.1:8529"); 80 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.user", "gremlin"); 81 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + "arangodb.password", "gremlin"); 82 | configuration.setProperty(ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + ArangoDBGraph.PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES, shouldPrefixCollectionWithGraphName); 83 | Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 84 | ArangoDBGraph g = new ArangoDBGraph(configuration); 85 | System.out.println(g.features()); 86 | // client = new ArangoDBGraphClient(g, arangoProperties, "tinkerpop", 30000); 87 | } 88 | 89 | @Test 90 | public void dummy() { 91 | assert true; 92 | } 93 | // 94 | // @After 95 | // public void tearDown() { 96 | // // Drop used graphs and collections, if present 97 | // ArangoDatabase db = client.getDB(); 98 | // // know_graph 99 | // deleteGraph(db, "knows", true); 100 | // // social graph 101 | // deleteGraph(db, "social", true); 102 | // // city graph 103 | // deleteGraph(db, "routeplanner", true); 104 | // client.shutdown(); 105 | // client = null; 106 | // } 107 | // 108 | // private boolean deleteGraph( 109 | // ArangoDatabase db, 110 | // String name, 111 | // boolean dropCollections) { 112 | // if (db != null) { 113 | // ArangoGraph graph = db.graph(name); 114 | // if (graph.exists()) { 115 | // Collection edgeDefinitions = dropCollections ? graph.getEdgeDefinitions() : Collections.emptyList(); 116 | // Collection vertexCollections = dropCollections ? graph.getVertexCollections(): Collections.emptyList();; 117 | // // Drop graph first because it will break if the graph collections do not exist 118 | // graph.drop(); 119 | // for (String definitionName : edgeDefinitions) { 120 | // String collectioName = definitionName; 121 | // if (db.collection(collectioName).exists()) { 122 | // db.collection(collectioName).drop(); 123 | // } 124 | // } 125 | // for (String vc : vertexCollections) { 126 | // String collectioName = vc; 127 | // if (db.collection(collectioName).exists()) { 128 | // db.collection(collectioName).drop(); 129 | // } 130 | // } 131 | // return true; 132 | // } else { 133 | // try { 134 | // graph.drop(); 135 | // } catch (ArangoDBException e) { 136 | // //throw ArangoDBExceptions.getArangoDBException(e); 137 | // } 138 | // } 139 | // } 140 | // return false; 141 | // } 142 | // 143 | // 144 | // // ********* The following methods test a local ArangoDBGraphClient ********* 145 | // 146 | // @Test 147 | // public void test_RestrictedUserNewDatabase_should_throw_ArangoDBGraphException() throws Exception { 148 | // Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 149 | // exception.expect(ArangoDBGraphException.class); 150 | // exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); 151 | // ArangoDBGraph g = new ArangoDBGraph(configuration); 152 | // new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); 153 | // } 154 | // 155 | // @Test 156 | // public void test_AuthorizedUserNewDatabase_can_create_new_database() throws Exception { 157 | // org.junit.Assume.assumeTrue(System.getenv("ARANGODB_ROOT_PWD") != null); 158 | // configuration.setProperty("arangodb.user", "root"); 159 | // String pwd = System.getenv("ARANGODB_ROOT_PWD"); 160 | // configuration.setProperty("arangodb.password", pwd); 161 | // Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 162 | // ArangoDBGraph g = new ArangoDBGraph(configuration); 163 | // ArangoDBGraphClient localClient = new ArangoDBGraphClient(g, arangoProperties, "demo", 30000, true); 164 | // assertThat(localClient.dbExists(), is(true)); 165 | // localClient.deleteDb(); 166 | // localClient.shutdown(); 167 | // } 168 | // 169 | // @Test 170 | // public void test_RestrictedUserExistingDb_should_throw_ArangoDBGraphException() throws Exception { 171 | // configuration.setProperty("arangodb.user", "limited"); 172 | // configuration.setProperty("arangodb.password", "limited"); 173 | // Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 174 | // exception.expect(ArangoDBGraphException.class); 175 | // exception.expectMessage(startsWith("DB not found or user has no access:")); 176 | // new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); 177 | // } 178 | // 179 | // @Test 180 | // public void test_ReadAccessUserCreateGraph_should_throw_ArangoDBGraphException() throws Exception { 181 | // configuration.setProperty("arangodb.user", "reader"); 182 | // configuration.setProperty("arangodb.password", "reader"); 183 | // Properties arangoProperties = ConfigurationConverter.getProperties(configuration); 184 | // ArangoDBGraphClient localClient = new ArangoDBGraphClient(, arangoProperties, "tinkerpop", 30000); 185 | // assertThat(localClient.dbExists(), is(true)); 186 | // List verticesCollectionNames = new ArrayList<>(); 187 | // List edgesCollectionNames = new ArrayList<>(); 188 | // verticesCollectionNames.add("person"); 189 | // edgesCollectionNames.add("knows"); 190 | // 191 | // exception.expect(ArangoDBGraphException.class); 192 | // exception.expectMessage(startsWith("General ArangoDB error (unkown error code)")); 193 | // localClient.createGraph("knows", verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); 194 | // localClient.shutdown(); 195 | // } 196 | // 197 | // // ********* The following tests use the ArangoDBGraphClient from @Setup ********* 198 | // 199 | // @Test 200 | // public void test_ServerVersion() throws Exception { 201 | // String version = client.getVersion(); 202 | // assertThat(version, is(notNullValue())); 203 | // } 204 | // 205 | // @Test 206 | // public void test_CreateSimpleGraph() throws Exception { 207 | // 208 | // String graph_name = "knows"; 209 | // List verticesCollectionNames = new ArrayList<>(); 210 | // List edgesCollectionNames = new ArrayList<>(); 211 | // verticesCollectionNames.add("person"); 212 | // edgesCollectionNames.add("knows"); 213 | // 214 | // client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, Collections.emptyList()); 215 | // ArangoDatabase db = client.getDB(); 216 | // assertThat("Graph not found in db", db.graph(graph_name).exists(), is(true)); 217 | // assertThat("Vertex collection found in db", db.collection(String.format("%sperson", knowsPrefix)).exists(), is(true)); 218 | // assertThat("Edge collection found in db", db.collection(String.format("%sknows", knowsPrefix)).exists(), is(true)); 219 | // ArangoGraph g = db.graph(graph_name); 220 | // Collection defs = g.getInfo().getEdgeDefinitions(); 221 | // assertThat(defs, hasSize(2)); // +1 for ELEMENT_HAS_PROPERTIES 222 | // EdgeDefinition d = defs.iterator().next(); 223 | // assertThat(d.getCollection(), is(String.format("%sknows", knowsPrefix))); 224 | // assertThat(d.getFrom(), contains(String.format("%sperson", knowsPrefix))); 225 | // assertThat(d.getTo(), contains(String.format("%sperson", knowsPrefix))); 226 | // } 227 | // 228 | // @Test 229 | // public void test_CreateMultiVertexGraph() throws Exception { 230 | // String graph_name = "social"; 231 | // List verticesCollectionNames = new ArrayList<>(); 232 | // List edgesCollectionNames = new ArrayList<>(); 233 | // List relations = new ArrayList<>(); 234 | // verticesCollectionNames.add("male"); 235 | // verticesCollectionNames.add("female"); 236 | // edgesCollectionNames.add("relation"); 237 | // relations.add("relation:male,female->male,female"); 238 | // 239 | // 240 | // client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); 241 | // ArangoDatabase db = client.getDB(); 242 | // assertThat("Created graph not found in db", db.graph(graph_name).exists(), is(true)); 243 | // assertThat("Vertex collection not found in db", db.collection(String.format("%smale", socialPrefix)).exists(), is(true)); 244 | // assertThat("Vertex collection not found in db", db.collection(String.format("%sfemale", socialPrefix)).exists(), is(true)); 245 | // assertThat("Edge collection found in db", db.collection(String.format("%srelation", socialPrefix)).exists(), is(true)); 246 | // ArangoGraph g = db.graph(graph_name); 247 | // Collection defs = g.getInfo().getEdgeDefinitions(); 248 | // assertThat(defs, hasSize(2)); 249 | // EdgeDefinition d = defs.iterator().next(); 250 | // assertThat(d.getCollection(), is(String.format("%srelation", socialPrefix))); 251 | // assertThat(d.getFrom(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); 252 | // assertThat(d.getTo(), containsInAnyOrder(String.format("%smale", socialPrefix), String.format("%sfemale", socialPrefix))); 253 | // } 254 | // 255 | // @Test 256 | // public void test_CreateMultiVertexMultiEdgeGraph() throws Exception { 257 | // String graph_name = "routeplanner"; 258 | // List verticesCollectionNames = new ArrayList<>(); 259 | // List edgesCollectionNames = new ArrayList<>(); 260 | // List relations = new ArrayList<>(); 261 | // verticesCollectionNames.add("germanCity"); 262 | // verticesCollectionNames.add("frenchCity"); 263 | // edgesCollectionNames.add("frenchHighway"); 264 | // edgesCollectionNames.add("germanHighway"); 265 | // edgesCollectionNames.add("internationalHighway"); 266 | // relations.add("frenchHighway:frenchCity->frenchCity"); 267 | // relations.add("germanHighway:germanCity->germanCity"); 268 | // relations.add("internationalHighway:germanCity,frenchCity->germanCity,frenchCity"); 269 | // 270 | // 271 | // client.createGraph(graph_name, verticesCollectionNames, edgesCollectionNames, relations); 272 | // ArangoDatabase db = client.getDB(); 273 | // assertThat("Craeted graph not found in db", db.graph(graph_name).exists(), is(true)); 274 | // assertThat("Vertex collection not found in db", db.collection(String.format("%sgermanCity", routeplannerPrefix)).exists(), is(true)); 275 | // assertThat("Vertex collection not found in db", db.collection(String.format("%sfrenchCity", routeplannerPrefix)).exists(), is(true)); 276 | // assertThat("Edge collection found in db", db.collection(String.format("%sfrenchHighway", routeplannerPrefix)).exists(), is(true)); 277 | // assertThat("Edge collection found in db", db.collection(String.format("%sgermanHighway", routeplannerPrefix)).exists(), is(true)); 278 | // assertThat("Edge collection found in db", db.collection(String.format("%sinternationalHighway", routeplannerPrefix)).exists(), is(true)); 279 | // ArangoGraph g = db.graph(graph_name); 280 | // Collection defs = g.getInfo().getEdgeDefinitions(); 281 | // assertThat("Not all edge definitions created", defs, hasSize(4)); 282 | // List edgeNames = defs.stream().map(EdgeDefinition::getCollection).collect(Collectors.toList()); 283 | // assertThat("Missmatch name in edge collecion names", edgeNames, 284 | // containsInAnyOrder(String.format("%sfrenchHighway", routeplannerPrefix), 285 | // String.format("%sgermanHighway", routeplannerPrefix), 286 | // String.format("%sinternationalHighway", routeplannerPrefix), "routeplanner_ELEMENT-HAS-PROPERTIES")); 287 | // EdgeDefinition fh = defs.stream().filter(ed -> String.format("%sfrenchHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); 288 | // assertThat("FrenchHighway connects incorrect collections", fh.getFrom(), contains(String.format("%sfrenchCity", routeplannerPrefix))); 289 | // assertThat("FrenchHighway connects incorrect collections", fh.getTo(), contains(String.format("%sfrenchCity", routeplannerPrefix))); 290 | // EdgeDefinition gh = defs.stream().filter(ed -> String.format("%sgermanHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); 291 | // assertThat("GermanHighway connects incorrect collections", gh.getFrom(), contains(String.format("%sgermanCity", routeplannerPrefix))); 292 | // assertThat("GermanHighway connects incorrect collections", gh.getTo(), contains(String.format("%sgermanCity", routeplannerPrefix))); 293 | // EdgeDefinition ih = defs.stream().filter(ed -> String.format("%sinternationalHighway", routeplannerPrefix).equals(ed.getCollection())).findFirst().get(); 294 | // assertThat("InternationalHighway connects incorrect collections", ih.getFrom(), 295 | // containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); 296 | // assertThat("InternationalHighway connects incorrect collections", ih.getTo(), 297 | // containsInAnyOrder(String.format("%sfrenchCity", routeplannerPrefix), String.format("%sgermanCity", routeplannerPrefix))); 298 | // } 299 | // 300 | // 301 | 302 | } 303 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/client/ArangoDBQueryBuilder.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.client; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Optional; 15 | 16 | import org.apache.commons.collections4.CollectionUtils; 17 | import org.apache.commons.lang3.StringUtils; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | 22 | /** 23 | * The ArangoDB Query Builder class provides static methods for building AQL fragments that can be concatenated to build 24 | * complete AQL queries. Note that all parameters used to create query fragments are used as is, hence, all 25 | * pre-processing (e.g. prefix collection names) must be done in the callee. 26 | * 27 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 28 | */ 29 | public class ArangoDBQueryBuilder { 30 | 31 | private static final Logger logger = LoggerFactory.getLogger(ArangoDBQueryBuilder.class); 32 | 33 | private StringBuilder queryBuilder; 34 | 35 | private int iterateCnt = 1; 36 | 37 | private boolean filtered = false; 38 | 39 | /** 40 | * Direction to navigate for vertex paths. 41 | */ 42 | 43 | public enum Direction { 44 | 45 | /** direction into a element. */ 46 | IN("INBOUND"), 47 | 48 | /** direction out of a element. */ 49 | OUT("OUTBOUND"), 50 | 51 | /** direction out and in of a element. */ 52 | ALL("ANY"); 53 | 54 | /** The aql name. */ 55 | private final String aqlName; 56 | 57 | /** 58 | * Instantiates a new direction. 59 | * 60 | * @param aqlName the aql name 61 | */ 62 | Direction(String aqlName) { 63 | this.aqlName = aqlName; 64 | } 65 | 66 | /** 67 | * Gets the aql name. 68 | * 69 | * @return the aql name 70 | */ 71 | String getAqlName() { 72 | return aqlName; 73 | } 74 | 75 | } 76 | 77 | /** 78 | * Options for vertices in Graph Traversals. 79 | * 80 | * @author Horacio Hoyos Rodriguez (@horaciohoyosr) 81 | */ 82 | 83 | enum UniqueVertices { 84 | 85 | /** It is guaranteed that there is no path returned with a duplicate vertex. */ 86 | PATH("path"), 87 | 88 | /** it is guaranteed that each vertex is visited at most once during the traversal, no 89 | * matter how many paths lead from the start vertex to this one. */ 90 | GLOBAL("global"), 91 | 92 | /** No uniqueness check is applied on vertices - (default). */ 93 | NONE("none"); 94 | 95 | /** The aql name. */ 96 | private final String aqlName; 97 | 98 | /** 99 | * Instantiates a new unique vertices. 100 | * 101 | * @param aqlName the aql name 102 | */ 103 | UniqueVertices(String aqlName) { 104 | this.aqlName = aqlName; 105 | } 106 | 107 | /** 108 | * Gets the aql name. 109 | * 110 | * @return the aql name 111 | */ 112 | String getAqlName() { 113 | return aqlName; 114 | } 115 | } 116 | 117 | /** 118 | * Options for edges in Graph Traversals. 119 | * 120 | * @author Horacio Hoyos Rodriguez (@horaciohoyosr) 121 | */ 122 | 123 | enum UniqueEdges { 124 | 125 | /** It is guaranteed that there is no path returned with a duplicate edge - (default). */ 126 | PATH("path"), 127 | 128 | /** No uniqueness check is applied on edges. Note: Using this configuration the 129 | * traversal will follow cycles in edges. */ 130 | NONE("none"); 131 | 132 | /** The aql name. */ 133 | private final String aqlName; 134 | 135 | /** 136 | * Instantiates a new unique edges. 137 | * 138 | * @param aqlName the aql name 139 | */ 140 | UniqueEdges(String aqlName) { 141 | this.aqlName = aqlName; 142 | } 143 | 144 | /** 145 | * Gets the aql name. 146 | * 147 | * @return the aql name 148 | */ 149 | String getAqlName() { 150 | return aqlName; 151 | } 152 | } 153 | 154 | /** 155 | * Create a new QueryBuilder with config of whether Collection Names should be prefixed with Graph name or not. 156 | */ 157 | public ArangoDBQueryBuilder() { 158 | this.queryBuilder = new StringBuilder(); 159 | } 160 | 161 | /** 162 | * Append a WITH statement to the query builder for the given collections. The required bindVars are 163 | * added to the bindVars map. 164 | * @param collections the list of Collections to use in the statement 165 | * @param bindVars the map of bind parameters 166 | * 167 | * @return a reference to this object. 168 | */ 169 | 170 | public ArangoDBQueryBuilder with(List collections, Map bindVars) { 171 | queryBuilder.append("WITH "); 172 | String separator = ""; 173 | int colId = 1; 174 | for (String c : collections) { 175 | queryBuilder.append(separator); 176 | separator = ","; 177 | String varName = String.format("@with%s", colId); 178 | queryBuilder.append("@").append(varName); 179 | bindVars.put(varName, c); 180 | } 181 | queryBuilder.append("\n"); 182 | logger.debug("with", queryBuilder.toString()); 183 | return this; 184 | } 185 | 186 | /** 187 | * Append a Document and FILTER statements to the query builder. Use this to find a single or 188 | * group of elements in the graph. This segment should be used in conjunction with the 189 | * {@link #with(List, Map)} segment. 190 | * 191 | * @param ids the id(s) to look for 192 | * @param loopVariable the loop variable name 193 | * @param bindVars the map of bind parameters 194 | * @return a reference to this object. 195 | */ 196 | 197 | public ArangoDBQueryBuilder documentsById( 198 | List ids, 199 | String loopVariable, 200 | Map bindVars) { 201 | queryBuilder.append("LET docs = FLATTEN(RETURN Document(@ids))\n"); 202 | queryBuilder.append(String.format("FOR %s IN docs\n", loopVariable)); 203 | queryBuilder.append(String.format(" FILTER NOT IS_NULL(%s)\n", loopVariable)); // Not needed? 204 | bindVars.put("ids", ids); 205 | logger.debug("documentsById", queryBuilder.toString()); 206 | return this; 207 | } 208 | 209 | /** 210 | * Append a Document statement to find a single element in the graph. This segment should be 211 | * used in conjunction with the {@link #with(List, Map)} segment. 212 | * 213 | * @param id the id to look for 214 | * @param loopVariable the loop variable name 215 | * @param bindVars the the map of bind parameters 216 | * @return a reference to this object. 217 | */ 218 | 219 | public ArangoDBQueryBuilder documentById( 220 | String id, 221 | String loopVariable, 222 | Map bindVars) { 223 | queryBuilder.append(String.format("LET %s = Document(@id)\n", loopVariable)); 224 | bindVars.put("id", id); 225 | logger.debug("documentById", queryBuilder.toString()); 226 | return this; 227 | } 228 | 229 | /** 230 | * Append an union segment. 231 | * @param collections the collections that participate in the union 232 | * @param loopVariable the loop variable 233 | * @param bindVars the map of bind parameters 234 | * 235 | * @return a reference to this object. 236 | */ 237 | 238 | public ArangoDBQueryBuilder union( 239 | List collections, 240 | String loopVariable, 241 | Map bindVars) { 242 | int count = 1; 243 | String separator = ""; 244 | queryBuilder.append(String.format("FOR %s in UNION( \n", loopVariable)); 245 | queryBuilder.append(" ("); 246 | for (String c : collections) { 247 | queryBuilder.append(separator); 248 | separator = "),\n ("; 249 | queryBuilder.append(String.format("FOR x%1$s IN @@col%1$s RETURN x%1$s", count)); 250 | bindVars.put(String.format("@col%s", count++), c); 251 | } 252 | queryBuilder.append(" )\n"); 253 | queryBuilder.append(")\n"); 254 | logger.debug("union", queryBuilder.toString()); 255 | return this; 256 | } 257 | 258 | /** 259 | * Add a FOR x IN y iteration to the query. A global collection counter is used so this operation 260 | * can be used to created nested loops. 261 | * @param loopVariable the loop variable 262 | * @param collectionName the collection name 263 | * @param bindVars the map of bind parameters 264 | * 265 | * @return a reference to this object. 266 | */ 267 | 268 | public ArangoDBQueryBuilder iterateCollection( 269 | String loopVariable, 270 | String collectionName, Map bindVars) { 271 | queryBuilder.append(String.format("FOR %1$s IN @@col%2$s", loopVariable, iterateCnt)).append("\n"); 272 | bindVars.put(String.format("@col%s", iterateCnt++), collectionName); 273 | logger.debug("iterateCollection", queryBuilder.toString()); 274 | return this; 275 | } 276 | 277 | /** 278 | * Add a graph iteration segment. 279 | * @param graphName the graph name 280 | * @param vertexVariable the vertex variable 281 | * @param edgeVariable the edge variable 282 | * @param pathVariable the path variable 283 | * @param min edges and vertices returned by this query will start at the traversal depth of min 284 | * @param max up to max length paths are traversed 285 | * @param direction follow edges pointing in the direction 286 | * @param startVertex the start vertex id 287 | * @param bindVars the map of bind parameters 288 | * 289 | * @return a reference to this object. 290 | */ 291 | 292 | public ArangoDBQueryBuilder iterateGraph( 293 | String graphName, 294 | String vertexVariable, 295 | Optional edgeVariable, 296 | Optional pathVariable, 297 | Optional min, 298 | Optional max, 299 | Direction direction, 300 | String startVertex, 301 | Map bindVars) { 302 | queryBuilder.append(String.format("FOR %s", vertexVariable)); 303 | if (edgeVariable.isPresent()) { 304 | queryBuilder.append(String.format(", %s", edgeVariable.get())); 305 | } 306 | if (pathVariable.isPresent()) { 307 | queryBuilder.append(String.format(", %s", pathVariable.get())); 308 | } 309 | queryBuilder.append("\n IN "); 310 | if (min.isPresent()) { 311 | queryBuilder.append(min.get()); 312 | if (max.isPresent()) { 313 | queryBuilder.append(String.format("..%s", max.get())); 314 | } 315 | queryBuilder.append(" "); 316 | } 317 | queryBuilder.append(direction.getAqlName()).append(" @startVertex\n") 318 | .append(" GRAPH '").append(graphName).append("'\n"); // FIXME Graph could be a parameter 319 | bindVars.put("startVertex", startVertex); 320 | logger.debug("iterateGraph", queryBuilder.toString()); 321 | return this; 322 | } 323 | 324 | /** 325 | * Iterate over a collection of edges. 326 | * @param graphName the graph name 327 | * @param vertexVariable the vertex variable 328 | * @param edgeVariable the edge variable 329 | * @param pathVariable the path variable 330 | * @param min edges and vertices returned by this query will start at the traversal depth of min 331 | * @param max up to max length paths are traversed 332 | * @param direction follow edges pointing in the direction 333 | * @param edgeCollections the edge collections 334 | * @param startVertex the start vertex id 335 | * @param bindVars the map of bind parameters 336 | * 337 | * @return a reference to this object. 338 | */ 339 | 340 | public ArangoDBQueryBuilder iterateEdges( 341 | String graphName, 342 | String vertexVariable, 343 | Optional edgeVariable, 344 | Optional pathVariable, 345 | Optional min, 346 | Optional max, 347 | Direction direction, 348 | List edgeCollections, 349 | String startVertex, Map bindVars) { 350 | queryBuilder.append(String.format("FOR %s", vertexVariable)); 351 | edgeVariable.ifPresent(ev -> queryBuilder.append(String.format(", %s", ev))); 352 | pathVariable.ifPresent(pv -> queryBuilder.append(String.format(", %s", pv))); 353 | queryBuilder.append("\n IN "); 354 | min.ifPresent(queryBuilder::append); 355 | max.ifPresent(m -> queryBuilder.append(String.format("..%s", m))); 356 | queryBuilder.append(direction.getAqlName()).append(" @startVertex\n"); 357 | String separator = ""; 358 | for (String c : edgeCollections) { 359 | queryBuilder.append(separator); 360 | separator = ", "; 361 | queryBuilder.append(String.format("@@col%s", iterateCnt)); 362 | bindVars.put(String.format("@col%s", iterateCnt++), c); 363 | } 364 | bindVars.put("@startVertex", startVertex); 365 | logger.debug("iterateGraph", queryBuilder.toString()); 366 | return this; 367 | } 368 | 369 | /** 370 | * Add a Graph options segment. 371 | * 372 | * @see UniqueVertices 373 | * @see UniqueEdges 374 | * @param onVertices the vertices options 375 | * @param onEdges the edges options 376 | * @param bfs if true, the traversal will be executed breadth-first, else it will 377 | * be executed depth-first. 378 | * @return a reference to this object. 379 | */ 380 | 381 | public ArangoDBQueryBuilder graphOptions( 382 | Optional onVertices, 383 | Optional onEdges, 384 | boolean bfs) { 385 | if (onVertices.isPresent() || onEdges.isPresent() || bfs) { 386 | queryBuilder.append(" OPTIONS {"); 387 | if (onVertices.isPresent()) { 388 | queryBuilder.append(String.format("uniqueVertices: '%s', ", onVertices.get().getAqlName())); 389 | } 390 | if (onEdges.isPresent()) { 391 | queryBuilder.append(String.format("uniqueEdges: '%s', ", onEdges.get().getAqlName())); 392 | } 393 | if (bfs) { 394 | queryBuilder.append("bfs: true"); 395 | } 396 | queryBuilder.append("}\n"); 397 | } 398 | logger.debug("graphOptions", queryBuilder.toString()); 399 | return this; 400 | } 401 | 402 | /** 403 | * Add a filter same collections segment, i.e. element represented by variable must be in any 404 | * of the provided collections. 405 | * @param filterVariable the filter variable 406 | * @param collections the collections to filter by 407 | * @param bindVars the map of bind parameters 408 | * 409 | * @return a reference to this object. 410 | */ 411 | 412 | public ArangoDBQueryBuilder filterSameCollections( 413 | String filterVariable, 414 | List collections, 415 | Map bindVars) { 416 | if (!collections.isEmpty()) { 417 | queryBuilder.append(" FILTER (IS_SAME_COLLECTION("); 418 | String separator = ""; 419 | for (String c : collections) { 420 | queryBuilder.append(separator); 421 | separator = String.format(", %s) OR IS_SAME_COLLECTION(", filterVariable); 422 | queryBuilder.append(String.format("@@col%s", iterateCnt)); 423 | bindVars.put(String.format("@col%s", iterateCnt++), c); 424 | } 425 | queryBuilder.append(String.format(", %s))\n", filterVariable)); 426 | } 427 | filtered = true; 428 | logger.debug("filterSameCollections", queryBuilder.toString()); 429 | return this; 430 | } 431 | 432 | /** 433 | * Add a filter on element properties segment. The filter operations are defined using a 434 | * #link {@link ArangoDBPropertyFilter}. 435 | * 436 | * @param propertyFilter the property filter 437 | * @param filterVariable the filter variable 438 | * @param bindVars the map of bind parameters 439 | * @return a reference to this object. 440 | */ 441 | 442 | public ArangoDBQueryBuilder filterProperties( 443 | ArangoDBPropertyFilter propertyFilter, 444 | String filterVariable, 445 | Map bindVars) { 446 | List filterSegments = new ArrayList(); 447 | propertyFilter.addAqlSegments(String.format("%s.", filterVariable), filterSegments, bindVars); 448 | if (CollectionUtils.isNotEmpty(filterSegments)) { 449 | if (filtered) { 450 | queryBuilder.append(" AND "); 451 | } else { 452 | queryBuilder.append(" FILTER "); 453 | } 454 | queryBuilder.append(StringUtils.join(filterSegments, " AND ")).append("\n"); 455 | } 456 | logger.debug("filterProperties", queryBuilder.toString()); 457 | return this; 458 | } 459 | 460 | /** 461 | * Add a limit segment to limit the number of elements returned. 462 | * 463 | * @param limit the limit number 464 | * @return a reference to this object. 465 | */ 466 | 467 | public ArangoDBQueryBuilder limit(Long limit) { 468 | queryBuilder.append(" LIMIT " + limit.toString()); 469 | logger.debug("limit", queryBuilder.toString()); 470 | return this; 471 | } 472 | 473 | /** 474 | * Add a RETURN Segment. 475 | * TODO provide finer grained return statements 476 | * 477 | * @param returnStatement the return statement 478 | * @return a reference to this object. 479 | */ 480 | 481 | public ArangoDBQueryBuilder ret(String returnStatement) { 482 | queryBuilder.append("RETURN ").append(returnStatement).append("\n"); 483 | logger.debug("ret", queryBuilder.toString()); 484 | return this; 485 | } 486 | 487 | /** 488 | * Appends the specified string to this character sequence. 489 | *

490 | * The characters of the String argument are appended, in order, increasing the length of this 491 | * sequence by the length of the argument. If str is null, then the four characters "null" are 492 | * appended. 493 | *

494 | * Let n be the length of this character sequence just prior to execution of the append method. 495 | * Then the character at index k in the new character sequence is equal to the character at 496 | * index k in the old character sequence, if k is less than n; otherwise, it is equal to the 497 | * character at index k-n in the argument str. 498 | * @param segment a str 499 | * @return a reference to this object. 500 | */ 501 | 502 | public ArangoDBQueryBuilder append(String segment) { 503 | queryBuilder.append(segment); 504 | return this; 505 | } 506 | 507 | @Override 508 | public String toString() { 509 | return queryBuilder.toString(); 510 | } 511 | 512 | } 513 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBConfigurationBuilder.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.utils; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Set; 16 | import java.util.stream.Collectors; 17 | 18 | import org.apache.commons.configuration2.BaseConfiguration; 19 | import org.apache.commons.configuration2.convert.LegacyListDelimiterHandler; 20 | import org.apache.commons.lang3.tuple.ImmutableTriple; 21 | import org.apache.commons.lang3.tuple.Triple; 22 | import org.apache.tinkerpop.gremlin.structure.Graph; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import com.arangodb.Protocol; 27 | import com.arangodb.entity.LoadBalancingStrategy; 28 | import com.arangodb.tinkerpop.gremlin.structure.ArangoDBGraph; 29 | 30 | 31 | /** 32 | * The Class ArangoDBConfigurationBuilder provides a convenient method for creating ArangoDB graph 33 | * configurations. 34 | * If no parameters are set the default values used are: 35 | *

    36 | *
  • dbName: tinkerpop 37 | *
  • graphName: graph 38 | *
  • vertices: (empty - use default ArangoDBGraph settings) 39 | *
  • edges: (empty - use default ArangoDBGraph settings) 40 | *
  • relations: (empty - use default ArangoDBGraph settigns) 41 | *
  • user: gremlin 42 | *
  • password: gremlin 43 | *
  • other db settings: (default ArangoDB Java driver settings). 44 | *
  • collectionNames: prefixed with graphName
  • 45 | *
46 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 47 | */ 48 | public class ArangoDBConfigurationBuilder { 49 | 50 | private static final Logger logger = LoggerFactory.getLogger(ArangoDBConfigurationBuilder.class); 51 | 52 | private static final String PROPERTY_KEY_HOSTS = "arangodb.hosts"; 53 | private static final String PROPERTY_KEY_TIMEOUT = "arangodb.timeout"; 54 | private static final String PROPERTY_KEY_USER = "arangodb.user"; 55 | private static final String PROPERTY_KEY_PASSWORD = "arangodb.password"; 56 | private static final String PROPERTY_KEY_USE_SSL = "arangodb.usessl"; 57 | private static final String PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE = "arangodb.chunksize"; 58 | private static final String PROPERTY_KEY_MAX_CONNECTIONS = "arangodb.connections.max"; 59 | private static final String PROPERTY_KEY_CONNECTION_TTL = "arangodb.connections.ttl"; 60 | private static final String PROPERTY_KEY_ACQUIRE_HOST_LIST = "arangodb.acquireHostList"; 61 | private static final String PROPERTY_KEY_LOAD_BALANCING_STRATEGY = "arangodb.loadBalancingStrategy"; 62 | private static final String PROPERTY_KEY_PROTOCOL = "arangodb.protocol"; 63 | 64 | /** The db name. */ 65 | private String dbName = "tinkerpop"; 66 | 67 | /** The graph name. */ 68 | private String graphName = "graph"; 69 | 70 | /** The user. */ 71 | private String user = "gremlin"; 72 | 73 | /** The password. */ 74 | private String password = "gremlin"; 75 | 76 | /** The acquire host list flag. */ 77 | private boolean hostList; 78 | 79 | /** The use ssl flag. */ 80 | private boolean useSsl; 81 | 82 | /** The max number of connections. */ 83 | private Integer connections; 84 | 85 | /** The timeout. */ 86 | private Integer timeout; 87 | 88 | /** The VelocyStream chunk size. */ 89 | private Long velocyStreamChunk; 90 | 91 | /** The connection ttl. */ 92 | private Long connectionTtl; 93 | 94 | /** The protocol. */ 95 | private Protocol protocol; 96 | 97 | /** The strategy. */ 98 | private LoadBalancingStrategy strategy; 99 | 100 | /** The edges. */ 101 | private Set edges = new HashSet<>(); 102 | 103 | /** The vertices. */ 104 | private Set vertices = new HashSet<>(); 105 | 106 | /** The relations. */ 107 | private Set, Set>> relations = new HashSet<>(); 108 | 109 | /** The hosts. */ 110 | private Set hosts = new HashSet<>(); 111 | 112 | /** If Collection Names should be prefixed with Graph name. **/ 113 | private Boolean shouldPrefixCollectionNames = true; 114 | 115 | /** 116 | * Instantiates a new arango DB configuration builder. 117 | */ 118 | 119 | public ArangoDBConfigurationBuilder() { 120 | 121 | } 122 | 123 | /** 124 | * Build the configuration. 125 | * 126 | * @return a configuration that can be used to instantiate a new {@link ArangoDBGraph}. 127 | * @see ArangoDBGraph#open(org.apache.commons.configuration2.Configuration) 128 | */ 129 | 130 | public BaseConfiguration build() { 131 | BaseConfiguration config = new BaseConfiguration(); 132 | config.setListDelimiterHandler(new LegacyListDelimiterHandler('/')); 133 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_DB_NAME), dbName); 134 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_GRAPH_NAME), graphName); 135 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_VERTICES), vertices); 136 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_EDGES), edges); 137 | List rels = new ArrayList<>(); 138 | for (Triple, Set> r : relations) { 139 | // Make sure edge and vertex collections have been added 140 | StringBuilder rVal = new StringBuilder(); 141 | rVal.append(r.getLeft()); 142 | if (!edges.contains(r.getLeft())) { 143 | logger.warn("Missing edege collection {} from relations added to edge collections"); 144 | edges.add(r.getLeft()); 145 | } 146 | rVal.append(":"); 147 | for (String sv : r.getMiddle()) { 148 | if (!vertices.contains(sv)) { 149 | logger.warn("Missing vertex collection {} from relations added to vertex collections"); 150 | vertices.add(r.getLeft()); 151 | } 152 | } 153 | rVal.append(r.getMiddle().stream().collect(Collectors.joining(","))); 154 | rVal.append("->"); 155 | for (String sv : r.getRight()) { 156 | if (!vertices.contains(sv)) { 157 | logger.warn("Missing vertex collection {} from relations added to vertex collections"); 158 | vertices.add(r.getLeft()); 159 | } 160 | } 161 | rVal.append(r.getRight().stream().collect(Collectors.joining(","))); 162 | rels.add(rVal.toString()); 163 | 164 | } 165 | if (!rels.isEmpty()) { 166 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_RELATIONS), rels.stream().collect(Collectors.joining("/"))); 167 | } 168 | config.addProperty(fullPropertyKey(PROPERTY_KEY_USER), user); 169 | config.addProperty(fullPropertyKey(PROPERTY_KEY_PASSWORD), password); 170 | if (hostList) { 171 | config.addProperty(fullPropertyKey(PROPERTY_KEY_ACQUIRE_HOST_LIST), Boolean.toString(hostList)); 172 | } 173 | if(useSsl) { 174 | config.addProperty(fullPropertyKey(PROPERTY_KEY_USE_SSL), Boolean.toString(useSsl)); 175 | } 176 | if (connections != null) { 177 | config.addProperty(fullPropertyKey(PROPERTY_KEY_MAX_CONNECTIONS), connections); 178 | } 179 | if (timeout != null) { 180 | config.addProperty(fullPropertyKey(PROPERTY_KEY_TIMEOUT), timeout); 181 | } 182 | if (velocyStreamChunk != null) { 183 | config.addProperty(fullPropertyKey(PROPERTY_KEY_V_STREAM_CHUNK_CONTENT_SIZE), velocyStreamChunk); 184 | } 185 | if (connectionTtl != null) { 186 | config.addProperty(fullPropertyKey(PROPERTY_KEY_CONNECTION_TTL), connectionTtl); 187 | } 188 | if (protocol != null) { 189 | config.addProperty(fullPropertyKey(PROPERTY_KEY_PROTOCOL), protocol.name()); 190 | } 191 | if (strategy != null) { 192 | config.addProperty(fullPropertyKey(PROPERTY_KEY_LOAD_BALANCING_STRATEGY), strategy.name()); 193 | } 194 | if (!hosts.isEmpty()) { 195 | config.addProperty(fullPropertyKey(PROPERTY_KEY_HOSTS), hosts.stream().collect(Collectors.joining(","))); 196 | } 197 | if(shouldPrefixCollectionNames != null){ 198 | config.addProperty(fullPropertyKey(ArangoDBGraph.PROPERTY_KEY_SHOULD_PREFIX_COLLECTION_NAMES), shouldPrefixCollectionNames); 199 | } 200 | 201 | config.addProperty(Graph.GRAPH, ArangoDBGraph.class.getName()); 202 | return config; 203 | } 204 | 205 | private String fullPropertyKey(String key) { 206 | return ArangoDBGraph.PROPERTY_KEY_PREFIX + "." + key; 207 | } 208 | 209 | /** 210 | * Name of the database to use. 211 | * 212 | * @param name the db name 213 | * @return a reference to this object. 214 | */ 215 | 216 | public ArangoDBConfigurationBuilder dataBase(String name) { 217 | dbName = name; 218 | return this; 219 | } 220 | 221 | /** 222 | * Name of the graph to use. 223 | * 224 | * @param name the graph name 225 | * @return a reference to this object. 226 | */ 227 | 228 | public ArangoDBConfigurationBuilder graph(String name) { 229 | graphName = name; 230 | return this; 231 | } 232 | 233 | /** 234 | * Add vertex collection. 235 | * 236 | * @param name the vertex collection name 237 | * @return a reference to this object. 238 | */ 239 | 240 | public ArangoDBConfigurationBuilder withVertexCollection(String name) { 241 | vertices.add(name); 242 | return this; 243 | } 244 | 245 | /** 246 | * Add edge collection. 247 | * 248 | * @param name the edge collection name 249 | * @return a reference to this object. 250 | */ 251 | 252 | public ArangoDBConfigurationBuilder withEdgeCollection(String name) { 253 | edges.add(name); 254 | return this; 255 | } 256 | 257 | /** 258 | * Configure a 1-to-1 edge, i.e. for a given edge collection define the source and target vertex 259 | * collections. 260 | * 261 | * @param edgeCollection the edge collection 262 | * @param sourceCollection the source vertex collection 263 | * @param targetCollection the target vertex collection 264 | * @return a reference to this object. 265 | */ 266 | 267 | public ArangoDBConfigurationBuilder configureEdge( 268 | String edgeCollection, 269 | String sourceCollection, 270 | String targetCollection) { 271 | Set source = new HashSet<>(); 272 | Set target = new HashSet<>(); 273 | source.add(sourceCollection); 274 | target.add(targetCollection); 275 | ImmutableTriple, Set> triple = new ImmutableTriple<>(edgeCollection, source, target); 276 | relations.add(triple); 277 | return this; 278 | } 279 | 280 | /** 281 | * Configure a 1-to-many edge, i.e. for a given edge collection define the source and target vertex 282 | * collections. 283 | * 284 | * @param edgeCollection the edge collection 285 | * @param sourceCollection the source vertex collection 286 | * @param targetCollections the target vertices collections 287 | * @return a reference to this object. 288 | */ 289 | 290 | public ArangoDBConfigurationBuilder configureEdge( 291 | String edgeCollection, 292 | String sourceCollection, 293 | Set targetCollections) { 294 | Set source = new HashSet<>(); 295 | source.add(sourceCollection); 296 | ImmutableTriple, Set> triple = new ImmutableTriple<>(edgeCollection, source, Collections.unmodifiableSet(targetCollections)); 297 | relations.add(triple); 298 | return this; 299 | } 300 | 301 | 302 | /** 303 | * Configure a many-to-1 edge, i.e. for a given edge collection define the source and target vertex 304 | * collections. 305 | * 306 | * @param edgeCollection the edge collection 307 | * @param sourceCollections the source vertices collections 308 | * @param targetCollection the target vertex collection 309 | * @return a reference to this object. 310 | */ 311 | 312 | public ArangoDBConfigurationBuilder configureEdge( 313 | String edgeCollection, 314 | Set sourceCollections, 315 | String targetCollection) { 316 | Set target = new HashSet<>(); 317 | target.add(targetCollection); 318 | ImmutableTriple, Set> triple = new ImmutableTriple<>(edgeCollection, Collections.unmodifiableSet(sourceCollections), target); 319 | relations.add(triple); 320 | return this; 321 | } 322 | 323 | 324 | /** 325 | * Configure a many-to-many edge, i.e. for a given edge collection define the source and target vertex 326 | * collections. 327 | * 328 | * @param edgeCollection the edge collection 329 | * @param sourceCollections the source vertices collections 330 | * @param targetCollections the target vertices collections 331 | * @return a reference to this object. 332 | */ 333 | 334 | public ArangoDBConfigurationBuilder configureEdge( 335 | String edgeCollection, 336 | Set sourceCollections, 337 | Set targetCollections) { 338 | ImmutableTriple, Set> triple = new ImmutableTriple<>(edgeCollection, Collections.unmodifiableSet(sourceCollections), Collections.unmodifiableSet(targetCollections)); 339 | relations.add(triple); 340 | return this; 341 | } 342 | 343 | 344 | /** 345 | * ArangoDB hosts. 346 | *

347 | * This method can be used multiple times to add multiple hosts (i.e. fallback hosts). 348 | * 349 | * @param host the host in the form url:port 350 | * @return a reference to this object. 351 | * @see ArangoDB Java Driver 352 | */ 353 | 354 | public ArangoDBConfigurationBuilder arangoHosts(String host) { 355 | this.hosts.add(host); 356 | return this; 357 | } 358 | 359 | /** 360 | * ArangoDB socket connect timeout(milliseconds). 361 | * 362 | * @param timeout the tiemout 363 | * @return a reference to this object. 364 | * @see ArangoDB Java Driver 365 | */ 366 | 367 | public ArangoDBConfigurationBuilder arangoTimeout(int timeout) { 368 | this.timeout = timeout; 369 | return this; 370 | } 371 | 372 | /** 373 | * ArangoDB Basic Authentication User. 374 | * 375 | * @param user the user 376 | * @return a reference to this object. 377 | * @see ArangoDB Java Driver 378 | */ 379 | 380 | public ArangoDBConfigurationBuilder arangoUser(String user) { 381 | this.user = user; 382 | return this; 383 | } 384 | 385 | 386 | /** 387 | * ArangoDB Basic Authentication Password. 388 | * 389 | * @param password the password 390 | * @return a reference to this object. 391 | * @see ArangoDB Java Driver 392 | */ 393 | 394 | public ArangoDBConfigurationBuilder arangoPassword(String password) { 395 | this.password = password; 396 | return this; 397 | } 398 | 399 | /** 400 | * ArangoDB use SSL connection. 401 | * 402 | * @param useSsl true, to use SSL connection. 403 | * @return a reference to this object. 404 | * @see ArangoDB Java Driver 405 | */ 406 | 407 | public ArangoDBConfigurationBuilder arangoSSL(boolean useSsl) { 408 | this.useSsl = useSsl; 409 | return this; 410 | } 411 | 412 | /** 413 | * ArangoDB VelocyStream Chunk content-size(bytes). 414 | * 415 | * @param size the size 416 | * @return a reference to this object. 417 | * @see ArangoDB Java Driver 418 | */ 419 | 420 | public ArangoDBConfigurationBuilder arangoVelocyStreamChunk(long size) { 421 | this.velocyStreamChunk = size; 422 | return this; 423 | } 424 | 425 | 426 | /** 427 | * ArangoDB max number of connections. 428 | * 429 | * @param connections the connections 430 | * @return a reference to this object. 431 | * @see ArangoDB Java Driver 432 | */ 433 | 434 | public ArangoDBConfigurationBuilder arangoMaxConnections(int connections) { 435 | this.connections = connections; 436 | return this; 437 | } 438 | 439 | /** 440 | * ArangoDB Connection time to live (ms). 441 | * 442 | * @param time the time 443 | * @return a reference to this object. 444 | * @see ArangoDB Java Driver 445 | */ 446 | 447 | public ArangoDBConfigurationBuilder arangoTTL(long time) { 448 | this.connectionTtl = time; 449 | return this; 450 | } 451 | 452 | 453 | /** 454 | * ArangoDB used network protocol. 455 | *

456 | * Note: If you are using ArangoDB 3.0.x you have to set the protocol to Protocol.HTTP_JSON 457 | * because it is the only one supported. 458 | * 459 | * @param protocol the protocol 460 | * @return a reference to this object. 461 | * @see ArangoDB Java Driver 462 | */ 463 | 464 | public ArangoDBConfigurationBuilder arangoNetworkProtocol(Protocol protocol) { 465 | this.protocol = protocol; 466 | return this; 467 | } 468 | 469 | 470 | /** 471 | * ArangoDB load balancing strategy. 472 | * 473 | * @param strategy the strategy 474 | * @return a reference to this object. 475 | * @see ArangoDB Java Driver 476 | */ 477 | 478 | public ArangoDBConfigurationBuilder arangoNetworkProtocol(LoadBalancingStrategy strategy) { 479 | this.strategy = strategy; 480 | return this; 481 | } 482 | 483 | /** 484 | * ArangoDB acquire a list of all known hosts in the cluster. 485 | * 486 | * @param hostList the host list 487 | * @return a reference to this object. 488 | * @see ArangoDB Java Driver 489 | */ 490 | 491 | public ArangoDBConfigurationBuilder arangoAcquireHostList(boolean hostList) { 492 | this.hostList = hostList; 493 | return this; 494 | } 495 | 496 | /** 497 | * In case of colliding collection names in Graph, these names can be prefixed with Graph Name. 498 | * If set to true collection names are in {@code %s_%s} format (where first %s is graph name, second %s is collection name). 499 | * If set to false, collection names are without any prefix. 500 | * Default set to true. 501 | * @param shouldPrefixCollectionNames whether it should prefixed or not. 502 | * @return a reference to this object. 503 | */ 504 | public ArangoDBConfigurationBuilder shouldPrefixCollectionNamesWithGraphName(boolean shouldPrefixCollectionNames){ 505 | this.shouldPrefixCollectionNames = shouldPrefixCollectionNames; 506 | return this; 507 | } 508 | 509 | } 510 | -------------------------------------------------------------------------------- /src/main/java/com/arangodb/tinkerpop/gremlin/utils/ArangoDBUtil.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Implementation of the TinkerPop OLTP Provider API for ArangoDB 4 | // 5 | // Copyright triAGENS GmbH Cologne and The University of York 6 | // 7 | ////////////////////////////////////////////////////////////////////////////////////////// 8 | 9 | package com.arangodb.tinkerpop.gremlin.utils; 10 | 11 | import static org.apache.tinkerpop.gremlin.structure.Graph.Hidden.isHidden; 12 | 13 | import com.arangodb.ArangoGraph; 14 | import com.arangodb.entity.EdgeDefinition; 15 | import com.arangodb.entity.GraphEntity; 16 | import com.arangodb.model.GraphCreateOptions; 17 | import com.arangodb.shaded.fasterxml.jackson.databind.ObjectMapper; 18 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphClient; 19 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBGraphException; 20 | import com.arangodb.tinkerpop.gremlin.client.ArangoDBQueryBuilder; 21 | import com.arangodb.tinkerpop.gremlin.structure.*; 22 | 23 | import java.io.UnsupportedEncodingException; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.Collection; 27 | import java.util.HashSet; 28 | import java.util.Iterator; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Set; 32 | import java.util.regex.Pattern; 33 | import java.util.stream.Collectors; 34 | import java.util.stream.IntStream; 35 | 36 | import org.apache.commons.collections4.CollectionUtils; 37 | import org.apache.tinkerpop.gremlin.structure.Direction; 38 | import org.apache.tinkerpop.gremlin.structure.Element; 39 | import org.slf4j.Logger; 40 | import org.slf4j.LoggerFactory; 41 | 42 | /** 43 | * This class provides utility methods for creating properties and for normalising property and 44 | * collections names (to satisfy Arango DB naming conventions. 45 | * 46 | * @author Achim Brandt (http://www.triagens.de) 47 | * @author Johannes Gocke (http://www.triagens.de) 48 | * @author Horacio Hoyos Rodriguez (https://www.york.ac.uk) 49 | */ 50 | //FIXME We should add more util methods to validate attribute names, e.g. scape ".". 51 | public class ArangoDBUtil { 52 | 53 | /** The Logger. */ 54 | 55 | private static final Logger logger = LoggerFactory.getLogger(ArangoDBUtil.class); 56 | 57 | /** Utiliy mapper for conversions. **/ 58 | 59 | private static final ObjectMapper mapper = new ObjectMapper(); 60 | 61 | /** 62 | * The prefix to denote that a collection is a hidden collection. 63 | */ 64 | 65 | private final static String HIDDEN_PREFIX = "adbt_"; 66 | 67 | /** The Constant HIDDEN_PREFIX_LENGTH. */ 68 | 69 | private static final int HIDDEN_PREFIX_LENGTH = HIDDEN_PREFIX.length(); 70 | 71 | /** The regex to match DOCUMENT_KEY. */ 72 | 73 | public static final Pattern DOCUMENT_KEY = Pattern.compile("^[A-Za-z0-9_:\\.@()\\+,=;\\$!\\*'%-]*"); 74 | 75 | /** 76 | * Instantiates a new ArangoDB Util. 77 | */ 78 | private ArangoDBUtil() { 79 | // this is a helper class 80 | } 81 | 82 | /** 83 | * Since attributes that start with underscore are considered to be system attributes (), 84 | * rename name "_XXXX" to "«a»XXXX" for storage. 85 | * 86 | * @param key the name to convert 87 | * @return String the converted String 88 | * @see Manual 89 | */ 90 | 91 | public static String normalizeKey(String key) { 92 | if (key.charAt(0) == '_') { 93 | return "«a»" + key.substring(1); 94 | } 95 | return key; 96 | } 97 | 98 | /** 99 | * Since attributes that start with underscore are considered to be system attributes (), 100 | * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. 101 | * 102 | * @param key the name to convert 103 | * @return String the converted String 104 | * @see Manual 105 | */ 106 | 107 | public static String denormalizeKey(String key) { 108 | if (key.startsWith("«a»")) { 109 | return "_" + key.substring(3); 110 | } 111 | return key; 112 | } 113 | 114 | /** 115 | * Hidden keys, labels, etc. are prefixed in Tinkerpop with @link Graph.Hidden.HIDDEN_PREFIX). Since in ArangoDB 116 | * collection names must always start with a letter, this method normalises Hidden collections name to valid 117 | * ArangoDB names by replacing the "~" with 118 | * 119 | * @param key the name to convert 120 | * @return String the converted String 121 | * @see Manual 122 | */ 123 | 124 | public static String normalizeCollection(String key) { 125 | String nname = isHidden(key) ? key : HIDDEN_PREFIX.concat(key); 126 | if (!NamingConventions.COLLECTION.hasValidNameSize(nname)) { 127 | throw ArangoDBGraphClient.ArangoDBExceptions.getNamingConventionError(ArangoDBGraphClient.ArangoDBExceptions.NAME_TO_LONG, key); 128 | } 129 | return nname; 130 | } 131 | 132 | /** 133 | * Since attributes that start with underscore are considered to be system attributes (), 134 | * rename Attribute "«a»XXXX" to "_XXXX" for retrieval. 135 | * 136 | * @param key the name to convert 137 | * @return String the converted String 138 | * @see Manual 139 | */ 140 | 141 | public static String denormalizeCollection(String key) { 142 | return isHidden(key) ? key.substring(HIDDEN_PREFIX_LENGTH) : key; 143 | } 144 | 145 | /** 146 | * The Enum NamingConventions. 147 | */ 148 | 149 | public enum NamingConventions { 150 | 151 | /** The collection. */ 152 | COLLECTION(64), 153 | 154 | /** The name. */ 155 | KEY(256); 156 | 157 | /** The max length. */ 158 | 159 | private int maxLength; 160 | 161 | /** 162 | * Instantiates a new naming conventions. 163 | * 164 | * @param maxLength the max length 165 | */ 166 | NamingConventions(int maxLength) { 167 | this.maxLength = maxLength; 168 | } 169 | 170 | /** 171 | * Checks for valid name size. 172 | * 173 | * @param name the name 174 | * @return true, if successful 175 | */ 176 | public boolean hasValidNameSize(String name) { 177 | final byte[] utf8Bytes; 178 | try { 179 | utf8Bytes = name.getBytes("UTF-8"); 180 | } catch (UnsupportedEncodingException e) { 181 | return false; 182 | } 183 | return utf8Bytes.length <= maxLength; 184 | } 185 | } 186 | 187 | /** 188 | * Create an EdgeDefinition from a relation in the Configuration. The format of a relation is: 189 | *

190 | 	 * collection:from->to
191 | 	 * 
192 | * Where collection is the name of the Edge collection, and to and from are comma separated list of 193 | * node collection names. 194 | * 195 | * @param graph the name of the graph 196 | * @param relation the relation 197 | * @return an EdgeDefinition that represents the relation. 198 | * @throws ArangoDBGraphException if the relation is malformed 199 | */ 200 | 201 | public static EdgeDefinition relationPropertyToEdgeDefinition(ArangoDBGraph graph, String relation) throws ArangoDBGraphException { 202 | logger.info("Creating EdgeRelation from {}", relation); 203 | EdgeDefinition result = new EdgeDefinition(); 204 | String[] info = relation.split(":"); 205 | if (info.length != 2) { 206 | throw new ArangoDBGraphException("Error in configuration. Malformed relation " + relation); 207 | } 208 | result.collection(graph.getPrefixedCollectioName(info[0])); 209 | info = info[1].split("->"); 210 | if (info.length != 2) { 211 | throw new ArangoDBGraphException("Error in configuration. Malformed relation> " + relation); 212 | } 213 | List trimmed = Arrays.stream(info[0].split(",")) 214 | .map(String::trim) 215 | .map(c -> graph.getPrefixedCollectioName(c)) 216 | .collect(Collectors.toList()); 217 | String[] from = new String[trimmed.size()]; 218 | from = trimmed.toArray(from); 219 | 220 | trimmed = Arrays.stream(info[1].split(",")) 221 | .map(String::trim) 222 | .map(c -> graph.getPrefixedCollectioName(c)) 223 | .collect(Collectors.toList()); 224 | String[] to = new String[trimmed.size()]; 225 | to = trimmed.toArray(to); 226 | result.from(from).to(to); 227 | return result; 228 | } 229 | 230 | 231 | /** 232 | * Creates the default edge definitions. When no relations are provided, the graph schema is 233 | * assumed to be fully connected, i.e. there is an EdgeDefintion for each possible combination 234 | * of Vertex-Edge-Vertex triplets. 235 | * 236 | * @param verticesCollectionNames the vertex collection names 237 | * @param edgesCollectionNames the edge collection names 238 | * @return the list of edge definitions 239 | */ 240 | 241 | public static List createDefaultEdgeDefinitions( 242 | List verticesCollectionNames, 243 | List edgesCollectionNames) { 244 | List result = new ArrayList<>(); 245 | for (String e : edgesCollectionNames) { 246 | for (String from : verticesCollectionNames) { 247 | for (String to : verticesCollectionNames) { 248 | EdgeDefinition ed = new EdgeDefinition() 249 | .collection(e) 250 | .from(from) 251 | .to(to); 252 | result.add(ed); 253 | } 254 | } 255 | } 256 | return result; 257 | } 258 | 259 | 260 | /** 261 | * Gets a collection that is unique for the given graph. 262 | * 263 | * @param graphName the graph name 264 | * @param collectionName the collection name 265 | * @param shouldPrefixWithGraphName flag to indicate if the name should be prefixed 266 | * @return the unique collection name 267 | */ 268 | @Deprecated 269 | public static String getCollectioName(String graphName, String collectionName, Boolean shouldPrefixWithGraphName) { 270 | if(shouldPrefixWithGraphName) { 271 | return String.format("%s_%s", graphName, collectionName); 272 | }else{ 273 | return collectionName; 274 | } 275 | } 276 | 277 | /** 278 | * Validate if an existing graph is correctly configured to handle the desired vertex, edges 279 | * and relations. 280 | * 281 | * @param verticesCollectionNames The names of collections for nodes 282 | * @param edgesCollectionNames The names of collections for edges 283 | * @param requiredDefinitions The description of edge definitions 284 | * @param graph the graph 285 | * @param options The options used to create the graph 286 | * @throws ArangoDBGraphException If the graph settings do not match the configuration information 287 | */ 288 | 289 | public static void checkGraphForErrors( 290 | List verticesCollectionNames, 291 | List edgesCollectionNames, 292 | List requiredDefinitions, 293 | ArangoGraph graph, 294 | GraphCreateOptions options) throws ArangoDBGraphException { 295 | 296 | checkGraphVertexCollections(verticesCollectionNames, graph, options); 297 | 298 | GraphEntity ge = graph.getInfo(); 299 | Collection graphEdgeDefinitions = ge.getEdgeDefinitions(); 300 | if (CollectionUtils.isEmpty(requiredDefinitions)) { 301 | // If no relations are defined, vertices and edges can only have one value 302 | if ((verticesCollectionNames.size() != 1) || (edgesCollectionNames.size() != 1)) { 303 | throw new ArangoDBGraphException("No relations where specified but more than one vertex/edge where defined."); 304 | } 305 | if (graphEdgeDefinitions.size() != 2) { // There is always a edgeDefinition for ELEMENT_HAS_PROPERTIES 306 | throw new ArangoDBGraphException("No relations where specified but the graph has more than one EdgeDefinition."); 307 | } 308 | } 309 | Map eds = requiredDefinitions.stream().collect(Collectors.toMap(EdgeDefinition::getCollection, ed -> ed)); 310 | 311 | Iterator it = graphEdgeDefinitions.iterator(); 312 | while (it.hasNext()) { 313 | EdgeDefinition existing = it.next(); 314 | if (eds.containsKey(existing.getCollection())) { 315 | EdgeDefinition requiredEdgeDefinition = eds.remove(existing.getCollection()); 316 | HashSet existingSet = new HashSet(existing.getFrom()); 317 | HashSet requiredSet = new HashSet(requiredEdgeDefinition.getFrom()); 318 | if (!existingSet.equals(requiredSet)) { 319 | throw new ArangoDBGraphException(String.format("The from collections dont match for edge definition %s", existing.getCollection())); 320 | } 321 | existingSet.clear(); 322 | existingSet.addAll(existing.getTo()); 323 | requiredSet.clear(); 324 | requiredSet.addAll(requiredEdgeDefinition.getTo()); 325 | if (!existingSet.equals(requiredSet)) { 326 | throw new ArangoDBGraphException(String.format("The to collections dont match for edge definition %s", existing.getCollection())); 327 | } 328 | } else { 329 | throw new ArangoDBGraphException(String.format("The graph has a surplus edge definition %s", edgeDefinitionString(existing))); 330 | } 331 | } 332 | 333 | } 334 | 335 | private static void checkGraphVertexCollections(List verticesCollectionNames, ArangoGraph graph, GraphCreateOptions options) { 336 | List allVertexCollections = new ArrayList<>(verticesCollectionNames); 337 | final Collection orphanCollections = options.getOrphanCollections(); 338 | if (orphanCollections != null) { 339 | allVertexCollections.addAll(orphanCollections); 340 | } 341 | if (!graph.getVertexCollections().containsAll(allVertexCollections)) { 342 | Set avc = new HashSet<>(allVertexCollections); 343 | avc.removeAll(graph.getVertexCollections()); 344 | throw new ArangoDBGraphException("Not all declared vertex names appear in the graph. Missing " + avc); 345 | } 346 | } 347 | 348 | /** 349 | * Get a string representation of the Edge definition that complies with the configuration options. 350 | * 351 | * @param ed the Edge definition 352 | * @return the string that represents the edge definition 353 | */ 354 | 355 | public static String edgeDefinitionString(EdgeDefinition ed) { 356 | return String.format("[%s]: %s->%s", ed.getCollection(), ed.getFrom(), ed.getTo()); 357 | } 358 | 359 | /** 360 | * Create the EdgeDefinition for the graph properties. 361 | * 362 | * @param graph The graph 363 | * @param vertexCollections the vertex collections 364 | * @param edgeCollections the edge collections 365 | * @return the edge definition 366 | */ 367 | 368 | public static EdgeDefinition createPropertyEdgeDefinitions( 369 | final ArangoDBGraph graph, 370 | final List vertexCollections, 371 | final List edgeCollections) { 372 | final List from = new ArrayList<>(vertexCollections); 373 | from.addAll(edgeCollections); 374 | from.add(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); 375 | String[] f = from.toArray(new String[from.size()]); 376 | EdgeDefinition ed = new EdgeDefinition() 377 | .collection(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_EDGE_COLLECTION)) 378 | .from(f) 379 | .to(graph.getPrefixedCollectioName(ArangoDBGraph.ELEMENT_PROPERTIES_COLLECTION)); 380 | return ed; 381 | } 382 | 383 | /** 384 | * Creates an Arango DB vertex property. 385 | * 386 | * @param the generic type 387 | * @param id the id 388 | * @param key the name 389 | * @param value the value 390 | * @param vertex the vertex 391 | * @return the created Arango DB vertex property 392 | */ 393 | 394 | // public static TinkerVertexProperty createArangoDBVertexProperty(String id, String key, U value, ArangoDBVertex vertex) { 395 | // TinkerVertexProperty p; 396 | // p = new TinkerVertexProperty<>(id, key, value, vertex); 397 | // insertElementAndProperty(vertex, p); 398 | // return p; 399 | // } 400 | 401 | /** 402 | * Gets the correct primitive. 403 | * 404 | * @param value the value 405 | * @param valueClass the exoected class of the value 406 | * @param the value type 407 | * @return the correct Java primitive 408 | */ 409 | 410 | @SuppressWarnings("unchecked") 411 | public static Object getCorretctPrimitive(V value, String valueClass) { 412 | 413 | switch(valueClass) { 414 | case "java.lang.Float": 415 | { 416 | if (value instanceof Double) { 417 | return ((Double) value).floatValue(); 418 | } 419 | else if (value instanceof Long) { 420 | return ((Long) value).floatValue(); 421 | } 422 | else if (value instanceof Integer) { 423 | return ((Integer) value).floatValue(); 424 | } 425 | else { 426 | logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); 427 | } 428 | break; 429 | } 430 | case "java.lang.Double": 431 | { 432 | if (value instanceof Double) { 433 | return value; 434 | } 435 | else if (value instanceof Long) { 436 | return ((Long) value).doubleValue(); 437 | } 438 | else if (value instanceof Integer) { 439 | return ((Integer) value).doubleValue(); 440 | } 441 | else { 442 | logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); 443 | } 444 | break; 445 | } 446 | case "java.lang.Long": 447 | { 448 | if (value instanceof Long) { 449 | return value; 450 | } 451 | else if (value instanceof Double) { 452 | return ((Double)value).longValue(); 453 | } 454 | else if (value instanceof Integer) { 455 | return ((Integer)value).longValue(); 456 | } 457 | else { 458 | logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); 459 | } 460 | break; 461 | } 462 | case "java.lang.Integer": 463 | { 464 | if (value instanceof Long) { 465 | return ((Long) value).intValue(); 466 | } 467 | break; 468 | } 469 | case "java.lang.String": 470 | case "java.lang.Boolean": 471 | case "": 472 | return value; 473 | case "java.util.HashMap": 474 | //logger.debug(((Map)value).keySet().stream().map(Object::getClass).collect(Collectors.toList())); 475 | //logger.debug("Add conversion for map values to " + valueClass); 476 | // Maps are handled by ArangoOK, but we have an extra field, remove it 477 | Map valueMap = (Map)value; 478 | for (String key : valueMap.keySet()) { 479 | if (key.startsWith("_")) { 480 | valueMap.remove(key); 481 | } 482 | // We might need to check individual values... 483 | } 484 | break; 485 | case "java.util.ArrayList": 486 | // Should we save the type per item? 487 | List list = new ArrayList<>(); 488 | ((ArrayList)value).forEach(e -> list.add(getCorretctPrimitive(e, ""))); 489 | return list; 490 | case "boolean[]": 491 | if(value instanceof List) { 492 | List barray = (List)value; 493 | boolean[] br = new boolean[barray.size()]; 494 | IntStream.range(0, barray.size()) 495 | .forEach(i -> br[i] = (boolean) barray.get(i)); 496 | return br; 497 | } else { 498 | return value; 499 | } 500 | case "double[]": 501 | if(value instanceof List) { 502 | List darray = (List)value; 503 | double[] dr = new double[darray.size()]; 504 | IntStream.range(0, darray.size()) 505 | .forEach(i -> dr[i] = (double) getCorretctPrimitive(darray.get(i), "java.lang.Double")); 506 | return dr; 507 | } else { 508 | return value; 509 | } 510 | case "float[]": 511 | if(value instanceof List) { 512 | List farray = (List)value; 513 | float[] fr = new float[farray.size()]; 514 | IntStream.range(0, farray.size()) 515 | .forEach(i -> fr[i] = (float) getCorretctPrimitive(farray.get(i), "java.lang.Float")); 516 | return fr; 517 | } else { 518 | return value; 519 | } 520 | case "int[]": 521 | if(value instanceof List) { 522 | List iarray = (List)value; 523 | int[] ir = new int[iarray.size()]; 524 | IntStream.range(0, iarray.size()) 525 | .forEach(i -> ir[i] = (int) getCorretctPrimitive(iarray.get(i), "java.lang.Integer")); 526 | return ir; 527 | } else { 528 | return value; 529 | } 530 | case "long[]": 531 | if(value instanceof List) { 532 | List larray = (List)value; 533 | long[] lr = new long[larray.size()]; 534 | IntStream.range(0, larray.size()) 535 | .forEach(i -> lr[i] = (long) getCorretctPrimitive(larray.get(i), "java.lang.Long")); 536 | return lr; 537 | } else { 538 | return value; 539 | } 540 | case "java.lang.String[]": 541 | if(value instanceof List) { 542 | List sarray = (List)value; 543 | String[] sr = new String[sarray.size()]; 544 | IntStream.range(0, sarray.size()) 545 | .forEach(i -> sr[i] = (String) sarray.get(i)); 546 | return sr; 547 | } else { 548 | return value; 549 | } 550 | default: 551 | Object result; 552 | try { 553 | result = mapper.convertValue(value, Class.forName(valueClass)); 554 | return result; 555 | } catch (IllegalArgumentException | ClassNotFoundException e1) { 556 | logger.warn("Type not deserializable", e1); 557 | } 558 | logger.debug("Add conversion for " + value.getClass().getName() + " to " + valueClass); 559 | } 560 | return value; 561 | } 562 | 563 | /** 564 | * Translate a Gremlin direction to Arango direction 565 | * @param direction the direction to translate 566 | * @return the ArangoDBQueryBuilder.Direction that represents the gremlin direction 567 | */ 568 | public static ArangoDBQueryBuilder.Direction getArangoDirectionFromGremlinDirection(final Direction direction) { 569 | switch (direction) { 570 | case BOTH: 571 | return ArangoDBQueryBuilder.Direction.ALL; 572 | case IN: 573 | return ArangoDBQueryBuilder.Direction.IN; 574 | case OUT: 575 | return ArangoDBQueryBuilder.Direction.OUT; 576 | } 577 | return null; 578 | } 579 | 580 | public static IllegalStateException elementAlreadyRemoved(final Class clazz, final Object id) { 581 | return new IllegalStateException(String.format("%s with id %s was removed.", clazz.getSimpleName(), id)); 582 | } 583 | 584 | public static IllegalStateException unsupportedIdType(final Object id) { 585 | return new IllegalStateException(String.format("Unsupported id type [%s]: %s", id.getClass().getSimpleName(), id)); 586 | } 587 | 588 | } 589 | --------------------------------------------------------------------------------