refact: merge client into toolchains
diff --git a/hugegraph-client/.github/ISSUE_TEMPLATE/bug_report.yml b/hugegraph-client/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..c5645d8
--- /dev/null
+++ b/hugegraph-client/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,100 @@
+name: Bug report (反馈 Bug)
+description: Create a bug report to help HugeGraph improve
+title: '[Bug] describe the main problem'
+labels:
+ - bug
+
+body:
+ - type: markdown
+ attributes:
+ value: >-
+ ### Note (特别注意) :
+
+ > 1. 请先**搜索**现有的[Server-Issues](https://github.com/hugegraph/hugegraph/issues) 与
+ [Client-Issues](https://github.com/hugegraph/hugegraph-client/issues) 中没有与您相同
+ / 相关的问题 (请勿重复提交)
+
+ > 2. 我们需要尽可能**详细**的信息来**复现**问题, 越详细的信息 (包括**日志 / 截图 / 配置**等)
+ 会**越快**被响应和处理
+
+ > 3. Issue 标题请保持原有模板分类(例如:`[Bug]`), 长段描述之间可以增加`空行`或使用`序号`标记, 保持排版清晰
+
+ > 4. 请在对应的模块提交 issue, 缺乏有效信息 / 长时间 (> 14 天) 没有回复的 issue 可能会被 **关闭**
+ (更新时会再开启)
+
+ - type: dropdown
+ attributes:
+ label: Bug Type (问题类型)
+ options:
+ - gremlin (结果不合预期)
+ - rest-api (结果不合预期)
+ - other exception / error (其他异常报错)
+ - server status (连接异常)
+ - logic (逻辑设计问题)
+ - performence (性能下降)
+ - others (please edit later)
+
+ - type: checkboxes
+ attributes:
+ label: Before submit
+ options:
+ - label: 我已经确认现有的 [Server-Issues](https://github.com/hugegraph/hugegraph/issues) 与 [Client-Issues](https://github.com/hugegraph/hugegraph-client/issues) 中没有相同 / 重复问题
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Environment (环境信息)
+ description: |
+ > server version could get from [rest-api](https://hugegraph.github.io/hugegraph-doc/clients/restful-api/other.html) (http://localhost:8080/versions)
+ value: |
+ - Server Version: v0.11.x
+ - Client Version: v1.x
+ - Data Size: xx vertices, xx edges <!-- (like 1000W 点, 9000W 边) -->
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Expected & Actual behavior (期望与实际表现)
+ description: |
+ > we can refer [How to create a minimal reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) (如何提供最简的可复现用例)
+ > if possible, please provide screenshots or GIF (请提供清晰的截图, 动图录屏更佳)
+ placeholder: |
+ type the main problem here
+
+ ```java
+ // Exception / Error info (尽可能详细的日志 + 完整异常栈)
+
+ ```
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Vertex/Edge example (问题点 / 边数据举例)
+ description: |
+ > 如果问题与具体的点 / 边数据相关, 请提供完整的`查询语句 + 返回 JSON 结果`
+ placeholder: |
+ // Query URL
+ GET http://localhost:8080/gremlin?gremlin=hugegraph.traversal().V('1:tom')
+
+ // JSON of Vertex / Edge
+ {
+ "vertex": { "id": "xxx" }
+ }
+ render: javascript
+
+ - type: textarea
+ attributes:
+ label: Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)
+ description: |
+ > 如果问题与具体的点类型 / 边类型 / 索引类型相关, 请提供完整的 `Schema 返回 JSON 结果`
+ placeholder: |
+ // Query URL
+ GET http://localhost:8080/graphs/hugegraph/schema/vertexlabels
+
+ // JSON of GraphSchema
+ {
+ "vertex": { "id": "xxx" }
+ }
+ render: javascript
diff --git a/hugegraph-client/.github/ISSUE_TEMPLATE/config.yml b/hugegraph-client/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..7aa5dc1
--- /dev/null
+++ b/hugegraph-client/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,10 @@
+blank_issues_enabled: false
+
+# 设置提 issue 前的参考文档
+contact_links:
+ - name: HugeGraph Client Doc
+ url: https://hugegraph.github.io/hugegraph-doc/quickstart/hugegraph-client.html
+ about: Please search question here before opening a new issue
+ - name: HugeGraph API Doc
+ url: https://hugegraph.github.io/hugegraph-doc/clients/hugegraph-api.html
+ about: Please search usage here before opening a new issue
diff --git a/hugegraph-client/.github/ISSUE_TEMPLATE/feature_request.yml b/hugegraph-client/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..4012606
--- /dev/null
+++ b/hugegraph-client/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,15 @@
+name: Feature request (新需求 / 功能)
+description: Give an idea for HugeGraph
+title: '[Feature] describe the new feature'
+labels:
+ - feature
+
+body:
+ - type: textarea
+ attributes:
+ label: Feature Description (功能描述)
+ description: |
+ > 请简要描述新功能 / 需求的使用场景或上下文, 最好能给个具体的例子说明
+ placeholder: type the feature description here
+ validations:
+ required: true
diff --git a/hugegraph-client/.github/ISSUE_TEMPLATE/question_ask.yml b/hugegraph-client/.github/ISSUE_TEMPLATE/question_ask.yml
new file mode 100644
index 0000000..16811ae
--- /dev/null
+++ b/hugegraph-client/.github/ISSUE_TEMPLATE/question_ask.yml
@@ -0,0 +1,99 @@
+name: Ask question (提问)
+description: Question about usage or configs in HugeGraph
+title: '[Question] describe your problem'
+
+body:
+ - type: markdown
+ attributes:
+ value: >-
+ ### Note (特别注意) :
+
+ > 1. 请先**搜索**现有的[Server-Issues](https://github.com/hugegraph/hugegraph/issues) 与
+ [Client-Issues](https://github.com/hugegraph/hugegraph-client/issues) 中没有与您相同
+ / 相关的问题 (请勿重复提交)
+
+ > 2. 我们需要尽可能**详细**的信息来**复现**问题, 越详细的信息 (包括**日志 / 截图 / 配置**等)
+ 会**越快**被响应和处理
+
+ > 3. Issue 标题请保持原有模板分类(例如:`[Bug]`), 长段描述之间可以增加`空行`或使用`序号`标记, 保持排版清晰
+
+ > 4. 请在对应的模块提交 issue, 缺乏有效信息 / 长时间 (> 14 天) 没有回复的 issue 可能会被 **关闭**
+ (更新时会再开启)
+
+ - type: dropdown
+ attributes:
+ label: Problem Type (问题类型)
+ options:
+ - gremlin (结果不合预期)
+ - rest-api (结果不合预期)
+ - server status (连接异常)
+ - configs (配置项 / 文档相关)
+ - struct / logic (架构 / 逻辑设计问题)
+ - performence (性能优化)
+ - other exception / error (其他异常报错)
+ - others (please edit later)
+
+ - type: checkboxes
+ attributes:
+ label: Before submit
+ options:
+ - label: 我已经确认现有的 [Server-Issues](https://github.com/hugegraph/hugegraph/issues) 与 [Client-Issues](https://github.com/hugegraph/hugegraph-client/issues) 中没有相同 / 重复问题
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Environment (环境信息)
+ description: |
+ > server version could get from [rest-api](https://hugegraph.github.io/hugegraph-doc/clients/restful-api/other.html) (http://localhost:8080/versions)
+ value: |
+ - Server Version: v0.11.x
+ - Client Version: v1.x
+ - Data Size: xx vertices, xx edges <!-- (like 1000W 点, 9000W 边) -->
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Your Question (问题描述)
+ description: |
+ > 图使用 / 配置相关问题,请优先参考 [REST-API 文档](https://hugegraph.github.io/hugegraph-doc/clients/hugegraph-api.html), 以及 [Server 配置文档](https://hugegraph.github.io/hugegraph-doc/config/config-option.html)
+ > if possible, please provide screenshots or GIF (请提供清晰的截图, 动图录屏更佳)
+ placeholder: |
+ type the main problem here
+
+ ```java
+ // Exception / Error info (尽可能详细的日志 + 完整异常栈)
+
+ ```
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Vertex/Edge example (问题点 / 边数据举例)
+ description: |
+ > 如果问题与具体的点 / 边数据相关, 请提供完整的`查询语句 + 返回 JSON 结果`
+ placeholder: |
+ // Query URL
+ GET http://localhost:8080/gremlin?gremlin=hugegraph.traversal().V('1:tom')
+
+ // JSON of Vertex / Edge
+ {
+ "vertex": { "id": "xxx" }
+ }
+ render: javascript
+
+ - type: textarea
+ attributes:
+ label: Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)
+ description: |
+ > 如果问题与具体的点类型 / 边类型 / 索引类型相关, 请提供完整的 `Schema 返回 JSON 结果`
+ placeholder: |
+ // Query URL
+ GET http://localhost:8080/graphs/hugegraph/schema/vertexlabels
+
+ // JSON of GraphSchema
+ {
+ "vertex": { "id": "xxx" }
+ }
+ render: javascript
diff --git a/hugegraph-client/.github/workflows/ci.yml b/hugegraph-client/.github/workflows/ci.yml
new file mode 100644
index 0000000..2be805f
--- /dev/null
+++ b/hugegraph-client/.github/workflows/ci.yml
@@ -0,0 +1,61 @@
+name: hugegraph-client ci
+
+on:
+ push:
+ branches:
+ - master
+ - /^release-.*$/
+ - /^test-.*$/
+ pull_request:
+ branches:
+ - master
+ - /^release-.*$/
+ - /^test-.*$/
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ env:
+ TRAVIS_DIR: assembly/travis
+ COMMIT_ID: 1d031c5905cbef008dd5fb468576b0e6a9445181
+ strategy:
+ fail-fast: false
+ matrix:
+ JAVA_VERSION: ['8']
+ steps:
+ - name: Install JDK 8
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{ matrix.JAVA_VERSION }}
+ distribution: 'zulu'
+
+ - name: Cache Maven packages
+ uses: actions/cache@v2
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 2
+
+ - name: Compile
+ run: |
+ mvn compile -Dmaven.javadoc.skip=true | grep -v "Downloading\|Downloaded"
+
+ - name: Prepare env and service
+ run: |
+ $TRAVIS_DIR/install-hugegraph-from-source.sh $COMMIT_ID
+
+ - name: Run test
+ run: |
+ mvn test -Dtest=UnitTestSuite
+ mvn test -Dtest=ApiTestSuite
+ mvn test -Dtest=FuncTestSuite
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v1
+ with:
+ file: target/jacoco.xml
diff --git a/hugegraph-client/.github/workflows/release.yml b/hugegraph-client/.github/workflows/release.yml
new file mode 100644
index 0000000..b0c20d2
--- /dev/null
+++ b/hugegraph-client/.github/workflows/release.yml
@@ -0,0 +1,39 @@
+name: release maven package
+
+on:
+ release:
+ types: [ published ]
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ steps:
+ - name: Install JDK 8
+ uses: actions/setup-java@v2
+ with:
+ java-version: '8'
+ distribution: 'zulu'
+
+ - name: Cache Maven packages
+ uses: actions/cache@v2
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 2
+
+ - name: Release Maven package
+ uses: samuelmeuli/action-maven-publish@v1
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg_passphrase: ${{ secrets.GPG_PASSPHRASE }}
+ nexus_username: ${{ secrets.NEXUS_USERNAME }}
+ nexus_password: ${{ secrets.NEXUS_PASSWORD }}
+ server_id: sonatype-nexus-staging
+ maven_profiles: "release"
+ maven_args: >
+ -Dmaven.test.skip=true
diff --git a/hugegraph-client/.github/workflows/stale.yml b/hugegraph-client/.github/workflows/stale.yml
new file mode 100644
index 0000000..1813bd3
--- /dev/null
+++ b/hugegraph-client/.github/workflows/stale.yml
@@ -0,0 +1,36 @@
+name: Mark stale issues and pull requests
+
+on:
+ schedule:
+ - cron: "0 21 * * *"
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+
+ steps:
+ - uses: actions/stale@v3
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-issue-message: 'Due to the lack of activity, the current issue is marked as stale and will be closed after 20 days, any update will remove the stale label'
+ stale-pr-message: 'Due to the lack of activity, the current pr is marked as stale and will be closed after 180 days, any update will remove the stale label'
+ stale-issue-label: 'inactive'
+ stale-pr-label: 'inactive'
+ exempt-issue-labels: 'feature,bug,enhancement,improvement,wontfix,todo'
+
+ days-before-issue-stale: 15
+ days-before-issue-close: 20
+ days-before-pr-stale: 30
+ days-before-pr-close: 180
+ operations-per-run: 10
+ start-date: '2018-12-01T00:00:00Z'
+
+ exempt-all-assignees: true
+ remove-stale-when-updated: true
+ exempt-all-pr-milestones: true
+ delete-branch: false
+ enable-statistics: true
diff --git a/hugegraph-client/.travis.yml b/hugegraph-client/.travis.yml
new file mode 100644
index 0000000..7babb8d
--- /dev/null
+++ b/hugegraph-client/.travis.yml
@@ -0,0 +1,30 @@
+language: java
+
+jdk:
+ - openjdk8
+
+sudo: required
+
+branches:
+ only:
+ - master
+ - /^release-.*$/
+ - /^test-.*$/
+
+install: mvn compile -Dmaven.javadoc.skip=true | grep -v "Downloading\|Downloaded"
+
+before_script:
+ - $TRAVIS_DIR/install-hugegraph-from-source.sh $TRAVIS_BRANCH | grep -v "Downloading\|Downloaded"
+
+script:
+ - mvn test -Dtest=UnitTestSuite
+ - mvn test -Dtest=ApiTestSuite
+ - mvn test -Dtest=FuncTestSuite
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
+
+env:
+ global:
+ - TRAVIS_DIR=assembly/travis
+ - COMMIT_ID=461948ee262cc696853a8c8ba1306e6b371e3e89
diff --git a/hugegraph-client/README.md b/hugegraph-client/README.md
new file mode 100644
index 0000000..21a752a
--- /dev/null
+++ b/hugegraph-client/README.md
@@ -0,0 +1,19 @@
+# hugegraph-client
+
+[![License](https://img.shields.io/badge/license-Apache%202-0E78BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
+[![Build Status](https://travis-ci.org/hugegraph/hugegraph-client.svg?branch=master)](https://travis-ci.org/hugegraph/hugegraph-client)
+[![codecov](https://codecov.io/gh/hugegraph/hugegraph-client/branch/master/graph/badge.svg)](https://codecov.io/gh/hugegraph/hugegraph-client)
+[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.baidu.hugegraph/hugegraph-client/badge.svg)](https://mvnrepository.com/artifact/com.baidu.hugegraph/hugegraph-client)
+
+hugegraph-client is a Java-written client of [HugeGraph](https://github.com/hugegraph/hugegraph), providing operations of graph, schema, gremlin, variables and traversals etc. All these operations are interpreted and translated into RESTful requests to HugeGraph Server. Besides, hugegraph-client also checks arguments, serializes and deserializes structures and encapsulates server exceptions.
+
+## Features
+
+- Graph Operation, CRUD of vertexes and edges, batch load of vertexes and edges
+- Schema Operation, CRUD of vertex label, edge label, index label and property key
+- Gremlin Traversal Statements
+- RESTful Traversals, shortest path, k-out, k-neighbor, paths and crosspoints etc.
+- Variables, CRUD of variables
+
+## Licence
+The same as HugeGraph, hugegraph-client is also licensed under Apache 2.0 License.
diff --git a/hugegraph-client/assembly/travis/codecov.yml b/hugegraph-client/assembly/travis/codecov.yml
new file mode 100644
index 0000000..8a2a5bb
--- /dev/null
+++ b/hugegraph-client/assembly/travis/codecov.yml
@@ -0,0 +1,2 @@
+ignore:
+ - "src/main/java/com/baidu/hugegraph/example"
diff --git a/hugegraph-client/assembly/travis/conf/graphs/hugegraph.properties b/hugegraph-client/assembly/travis/conf/graphs/hugegraph.properties
new file mode 100644
index 0000000..9c683e9
--- /dev/null
+++ b/hugegraph-client/assembly/travis/conf/graphs/hugegraph.properties
@@ -0,0 +1,61 @@
+# gremlin entrence to create graph
+gremlin.graph=com.baidu.hugegraph.auth.HugeFactoryAuthProxy
+
+# cache config
+#schema.cache_capacity=100000
+# vertex-cache default is 1000w, 10min expired
+#vertex.cache_capacity=10000000
+#vertex.cache_expire=600
+# edge-cache default is 100w, 10min expired
+#edge.cache_capacity=1000000
+#edge.cache_expire=600
+
+
+# schema illegal name template
+#schema.illegal_name_regex=\s+|~.*
+
+#vertex.default_label=vertex
+
+backend=rocksdb
+serializer=binary
+
+store=hugegraph
+
+search.text_analyzer=jieba
+search.text_analyzer_mode=INDEX
+
+# rocksdb backend config
+#rocksdb.data_path=/path/to/disk
+#rocksdb.wal_path=/path/to/disk
+
+
+# cassandra backend config
+cassandra.host=localhost
+cassandra.port=9042
+cassandra.username=
+cassandra.password=
+#cassandra.connect_timeout=5
+#cassandra.read_timeout=20
+#cassandra.keyspace.strategy=SimpleStrategy
+#cassandra.keyspace.replication=3
+
+# hbase backend config
+#hbase.hosts=localhost
+#hbase.port=2181
+#hbase.znode_parent=/hbsae
+#hbase.threads_max=64
+
+# mysql backend config
+#jdbc.driver=com.mysql.jdbc.Driver
+#jdbc.url=jdbc:mysql://127.0.0.1:3306
+#jdbc.username=root
+#jdbc.password=
+#jdbc.reconnect_max_times=3
+#jdbc.reconnect_interval=3
+#jdbc.sslmode=false
+
+# palo backend config
+#palo.host=127.0.0.1
+#palo.poll_interval=10
+#palo.temp_dir=./palo-data
+#palo.file_limit_size=32
diff --git a/hugegraph-client/assembly/travis/conf/gremlin-server.yaml b/hugegraph-client/assembly/travis/conf/gremlin-server.yaml
new file mode 100644
index 0000000..69e8990
--- /dev/null
+++ b/hugegraph-client/assembly/travis/conf/gremlin-server.yaml
@@ -0,0 +1,104 @@
+# host and port of gremlin server, need to be consistent with host and port in rest-server.properties
+#host: 127.0.0.1
+#port: 8182
+
+# timeout in ms of gremlin query
+scriptEvaluationTimeout: 30000
+
+channelizer: org.apache.tinkerpop.gremlin.server.channel.WsAndHttpChannelizer
+# don't set graph at here, this happens after support for dynamically adding graph
+graphs: {
+}
+scriptEngines: {
+ gremlin-groovy: {
+ plugins: {
+ com.baidu.hugegraph.plugin.HugeGraphGremlinPlugin: {},
+ org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
+ org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {
+ classImports: [
+ java.lang.Math,
+ com.baidu.hugegraph.backend.id.IdGenerator,
+ com.baidu.hugegraph.type.define.Directions,
+ com.baidu.hugegraph.type.define.NodeRole,
+ com.baidu.hugegraph.traversal.algorithm.CollectionPathsTraverser,
+ com.baidu.hugegraph.traversal.algorithm.CountTraverser,
+ com.baidu.hugegraph.traversal.algorithm.CustomizedCrosspointsTraverser,
+ com.baidu.hugegraph.traversal.algorithm.CustomizePathsTraverser,
+ com.baidu.hugegraph.traversal.algorithm.FusiformSimilarityTraverser,
+ com.baidu.hugegraph.traversal.algorithm.HugeTraverser,
+ com.baidu.hugegraph.traversal.algorithm.JaccardSimilarTraverser,
+ com.baidu.hugegraph.traversal.algorithm.KneighborTraverser,
+ com.baidu.hugegraph.traversal.algorithm.KoutTraverser,
+ com.baidu.hugegraph.traversal.algorithm.MultiNodeShortestPathTraverser,
+ com.baidu.hugegraph.traversal.algorithm.NeighborRankTraverser,
+ com.baidu.hugegraph.traversal.algorithm.PathsTraverser,
+ com.baidu.hugegraph.traversal.algorithm.PersonalRankTraverser,
+ com.baidu.hugegraph.traversal.algorithm.SameNeighborTraverser,
+ com.baidu.hugegraph.traversal.algorithm.ShortestPathTraverser,
+ com.baidu.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser,
+ com.baidu.hugegraph.traversal.algorithm.SubGraphTraverser,
+ com.baidu.hugegraph.traversal.algorithm.TemplatePathsTraverser,
+ com.baidu.hugegraph.traversal.algorithm.steps.EdgeStep,
+ com.baidu.hugegraph.traversal.algorithm.steps.RepeatEdgeStep,
+ com.baidu.hugegraph.traversal.algorithm.steps.WeightedEdgeStep,
+ com.baidu.hugegraph.traversal.optimize.Text,
+ com.baidu.hugegraph.traversal.optimize.TraversalUtil,
+ com.baidu.hugegraph.util.DateUtil
+ ],
+ methodImports: [java.lang.Math#*]
+ },
+ org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {
+ files: [scripts/empty-sample.groovy]
+ }
+ }
+ }
+}
+serializers:
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1,
+ config: {
+ serializeResultToString: false,
+ ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
+ }
+ }
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0,
+ config: {
+ serializeResultToString: false,
+ ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
+ }
+ }
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0,
+ config: {
+ serializeResultToString: false,
+ ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
+ }
+ }
+ - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0,
+ config: {
+ serializeResultToString: false,
+ ioRegistries: [com.baidu.hugegraph.io.HugeGraphIoRegistry]
+ }
+ }
+metrics: {
+ consoleReporter: {enabled: false, interval: 180000},
+ csvReporter: {enabled: false, interval: 180000, fileName: ./metrics/gremlin-server-metrics.csv},
+ jmxReporter: {enabled: false},
+ slf4jReporter: {enabled: false, interval: 180000},
+ gangliaReporter: {enabled: false, interval: 180000, addressingMode: MULTICAST},
+ graphiteReporter: {enabled: false, interval: 180000}
+}
+maxInitialLineLength: 4096
+maxHeaderSize: 8192
+maxChunkSize: 8192
+maxContentLength: 65536
+maxAccumulationBufferComponents: 1024
+resultIterationBatchSize: 64
+writeBufferLowWaterMark: 32768
+writeBufferHighWaterMark: 65536
+ssl: {
+ enabled: false
+}
+authentication: {
+ authenticator: com.baidu.hugegraph.auth.StandardAuthenticator,
+ authenticationHandler: com.baidu.hugegraph.auth.WsAndHttpBasicAuthHandler,
+ config: {tokens: conf/rest-server.properties}
+}
diff --git a/hugegraph-client/assembly/travis/conf/rest-server.properties b/hugegraph-client/assembly/travis/conf/rest-server.properties
new file mode 100644
index 0000000..d44ff6b
--- /dev/null
+++ b/hugegraph-client/assembly/travis/conf/rest-server.properties
@@ -0,0 +1,41 @@
+# bind url
+restserver.url=http://127.0.0.1:8080
+# gremlin server url, need to be consistent with host and port in gremlin-server.yaml
+#gremlinserver.url=http://127.0.0.1:8182
+
+graphs=./conf/graphs
+
+# The maximum thread ratio for batch writing, only take effect if the batch.max_write_threads is 0
+batch.max_write_ratio=80
+batch.max_write_threads=0
+
+# authentication configs
+# choose 'com.baidu.hugegraph.auth.StandardAuthenticator' or 'com.baidu.hugegraph.auth.ConfigAuthenticator'
+auth.authenticator=com.baidu.hugegraph.auth.StandardAuthenticator
+
+# for StandardAuthenticator mode
+#auth.graph_store=hugegraph
+# auth client config
+#auth.remote_url=127.0.0.1:8899,127.0.0.1:8898,127.0.0.1:8897
+
+# for ConfigAuthenticator mode
+#auth.admin_token=
+#auth.user_tokens=[]
+
+# rpc group configs of multi graph servers
+# rpc server configs
+rpc.server_host=127.0.0.1
+rpc.server_port=8090
+#rpc.server_timeout=30
+
+# rpc client configs (like enable to keep cache consistency)
+rpc.remote_url=127.0.0.1:8090
+#rpc.client_connect_timeout=20
+#rpc.client_reconnect_period=10
+#rpc.client_read_timeout=40
+#rpc.client_retries=3
+#rpc.client_load_balancer=consistentHash
+
+# lightweight load balancing (beta)
+server.id=server-1
+server.role=master
diff --git a/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh b/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh
new file mode 100755
index 0000000..c594320
--- /dev/null
+++ b/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+set -ev
+
+if [[ $# -ne 1 ]]; then
+ echo "Must pass commit id of hugegraph repo"
+ exit 1
+fi
+
+COMMIT_ID=$1
+HUGEGRAPH_GIT_URL="https://github.com/hugegraph/hugegraph.git"
+GIT_DIR=hugegraph
+
+# download code and compile
+git clone --depth 100 ${HUGEGRAPH_GIT_URL}
+cd "${GIT_DIR}"
+git checkout ${COMMIT_ID}
+mvn package -DskipTests
+
+TAR=$(echo hugegraph-*.tar.gz)
+tar -zxvf "${TAR}" -C ../
+cd ../
+rm -rf "${GIT_DIR}"
+
+HTTP_SERVER_DIR=$(echo hugegraph-*)
+HTTPS_SERVER_DIR="hugegraph_https"
+
+cp -r "${HTTP_SERVER_DIR}" "${HTTPS_SERVER_DIR}"
+
+# config auth options just for http server (must keep '/.')
+cp -rf "${TRAVIS_DIR}"/conf/. "${HTTP_SERVER_DIR}"/conf/
+
+# start HugeGraphServer with http protocol
+cd "${HTTP_SERVER_DIR}"
+echo -e "pa" | bin/init-store.sh || exit 1
+bin/start-hugegraph.sh || exit 1
+
+# config options for https server
+cd ../"${HTTPS_SERVER_DIR}"
+REST_SERVER_CONFIG="conf/rest-server.properties"
+GREMLIN_SERVER_CONFIG="conf/gremlin-server.yaml"
+sed -i "s?http://127.0.0.1:8080?https://127.0.0.1:8443?g" "$REST_SERVER_CONFIG"
+sed -i "s/#port: 8182/port: 8282/g" "$GREMLIN_SERVER_CONFIG"
+echo "gremlinserver.url=http://127.0.0.1:8282" >> ${REST_SERVER_CONFIG}
+
+# start HugeGraphServer with https protocol
+bin/init-store.sh
+bin/start-hugegraph.sh
+cd ../
diff --git a/hugegraph-client/build.sh b/hugegraph-client/build.sh
new file mode 100644
index 0000000..170bce5
--- /dev/null
+++ b/hugegraph-client/build.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+HUGEGRAPH_CLIENT_RELEASE_PATH="${PWD}/output/"
+
+export MAVEN_HOME="/home/scmtools/buildkit/maven/apache-maven-3.3.9/"
+export JAVA_HOME="/home/scmtools/buildkit/java/jdk1.8.0_25/"
+export PATH="$JAVA_HOME/bin:$MAVEN_HOME/bin:$PATH"
+
+mvn clean test -Dtest=UnitTestSuite
diff --git a/hugegraph-client/pom.xml b/hugegraph-client/pom.xml
new file mode 100644
index 0000000..25d2a90
--- /dev/null
+++ b/hugegraph-client/pom.xml
@@ -0,0 +1,209 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.baidu.hugegraph</groupId>
+ <artifactId>hugegraph-client</artifactId>
+ <version>2.1.0</version>
+ <packaging>jar</packaging>
+
+ <name>hugegraph-client</name>
+ <url>https://github.com/hugegraph/hugegraph-client</url>
+ <description>
+ hugegraph-client is a Java-written client of HugeGraph, providing
+ operations of graph, schema, gremlin, variables and traversals etc.
+ </description>
+
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>7</version>
+ </parent>
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <scm>
+ <url>https://github.com/hugegraph/hugegraph-client</url>
+ <connection>https://github.com/hugegraph/hugegraph-client</connection>
+ <developerConnection>https://github.com/hugegraph/hugegraph-client
+ </developerConnection>
+ </scm>
+ <developers>
+ <developer>
+ <name>lizhangmei</name>
+ <email>javaloveme@gmail.com</email>
+ </developer>
+ <developer>
+ <name>zhoney</name>
+ <email>zhangyi89817@126.com</email>
+ </developer>
+ <developer>
+ <name>liningrui</name>
+ <email>liningrui@vip.qq.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <compiler.source>1.8</compiler.source>
+ <compiler.target>1.8</compiler.target>
+ <hugegraph.common.version>2.1.0</hugegraph.common.version>
+ <jersey.version>3.0.3</jersey.version>
+ <mockito.version>2.8.47</mockito.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.baidu.hugegraph</groupId>
+ <artifactId>hugegraph-common</artifactId>
+ <version>${hugegraph.common.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.containers</groupId>
+ <artifactId>jersey-container-servlet</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.1</version>
+ <configuration>
+ <source>${compiler.source}</source>
+ <target>${compiler.target}</target>
+ <compilerArguments>
+ <Xmaxerrs>500</Xmaxerrs>
+ </compilerArguments>
+ <compilerArgs>
+ <arg>-Xlint:unchecked</arg>
+ </compilerArgs>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <archive>
+ <index>true</index>
+ <manifest>
+ <addDefaultImplementationEntries>
+ false
+ </addDefaultImplementationEntries>
+ <addDefaultSpecificationEntries>
+ true
+ </addDefaultSpecificationEntries>
+ </manifest>
+ <manifestEntries>
+ <!-- Must be on one line, otherwise the automatic
+ upgrade script cannot replace the version number -->
+ <Implementation-Version>2.0.1.0</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.jacoco</groupId>
+ <artifactId>jacoco-maven-plugin</artifactId>
+ <version>0.8.2</version>
+ <configuration>
+ <excludes>
+ <exclude>com/baidu/hugegraph/example/*.class</exclude>
+ </excludes>
+ </configuration>
+ <executions>
+ <execution>
+ <id>pre-unit-test</id>
+ <goals>
+ <goal>prepare-agent</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>post-unit-test</id>
+ <phase>test</phase>
+ <goals>
+ <goal>report</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.2.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9.1</version>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <!-- Prevent `gpg` from using pinentry programs -->
+ <gpgArguments>
+ <arg>--pinentry-mode</arg>
+ <arg>loopback</arg>
+ </gpgArguments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/annotation/UnimplementedFeature.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/annotation/UnimplementedFeature.java
new file mode 100644
index 0000000..a920beb
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/annotation/UnimplementedFeature.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a method is a feature to be implemented,
+ * and may cause an exception if it is used directly now.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface UnimplementedFeature {
+
+ String desc() default "";
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/API.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/API.java
new file mode 100644
index 0000000..c580a66
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/API.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.util.E;
+
+public abstract class API {
+
+ public static final String CHARSET = "UTF-8";
+ public static final String BATCH_ENCODING = "gzip";
+ public static final long NO_LIMIT = -1L;
+ public static final String PATH_SPLITOR = "/";
+
+ protected final RestClient client;
+
+ private String path;
+
+ public API(RestClient client) {
+ E.checkNotNull(client, "client");
+ this.client = client;
+ this.path = null;
+ }
+
+ public String path() {
+ E.checkState(this.path != null, "Path can't be null");
+ return this.path;
+ }
+
+ protected void path(String path) {
+ this.path = path;
+ }
+
+ protected void path(String pathTemplate, Object... args) {
+ this.path = String.format(pathTemplate, args);
+ }
+
+ protected abstract String type();
+
+ protected static void checkOffset(long value) {
+ E.checkArgument(value >= 0, "Offset must be >= 0, but got: %s", value);
+ }
+
+ protected static void checkLimit(long value, String name) {
+ E.checkArgument(value > 0 || value == NO_LIMIT,
+ "%s must be > 0 or == %s, but got: %s",
+ name, NO_LIMIT, value);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AccessAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AccessAPI.java
new file mode 100644
index 0000000..91dfb45
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AccessAPI.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Access;
+import com.baidu.hugegraph.structure.constant.HugeType;
+
+public class AccessAPI extends AuthAPI {
+
+ public AccessAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.ACCESS.string();
+ }
+
+ public Access create(Access access) {
+ RestResult result = this.client.post(this.path(), access);
+ return result.readObject(Access.class);
+ }
+
+ public Access get(Object id) {
+ RestResult result = this.client.get(this.path(), formatRelationId(id));
+ return result.readObject(Access.class);
+ }
+
+ public List<Access> list(Object group, Object target, int limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("limit", limit);
+ params.put("group", formatEntityId(group));
+ params.put("target", formatEntityId(target));
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Access.class);
+ }
+
+ public Access update(Access access) {
+ String id = formatRelationId(access.id());
+ RestResult result = this.client.put(this.path(), id, access);
+ return result.readObject(Access.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatRelationId(id));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AuthAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AuthAPI.java
new file mode 100644
index 0000000..646c6b8
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/AuthAPI.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.auth.AuthElement;
+
+public abstract class AuthAPI extends API {
+
+ private static final String PATH = "graphs/%s/auth/%s";
+
+ public AuthAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ }
+
+ public static String formatEntityId(Object id) {
+ if (id == null) {
+ return null;
+ } else if (id instanceof AuthElement) {
+ id = ((AuthElement) id).id();
+ }
+ return String.valueOf(id);
+ }
+
+ public static String formatRelationId(Object id) {
+ if (id == null) {
+ return null;
+ } else if (id instanceof AuthElement) {
+ id = ((AuthElement) id).id();
+ }
+ return String.valueOf(id);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/BelongAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/BelongAPI.java
new file mode 100644
index 0000000..41e5587
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/BelongAPI.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Belong;
+import com.baidu.hugegraph.structure.constant.HugeType;
+
+public class BelongAPI extends AuthAPI {
+
+ public BelongAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.BELONG.string();
+ }
+
+ public Belong create(Belong belong) {
+ RestResult result = this.client.post(this.path(), belong);
+ return result.readObject(Belong.class);
+ }
+
+ public Belong get(Object id) {
+ RestResult result = this.client.get(this.path(), formatRelationId(id));
+ return result.readObject(Belong.class);
+ }
+
+ public List<Belong> list(Object user, Object group, int limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("limit", limit);
+ params.put("user", formatEntityId(user));
+ params.put("group", formatEntityId(group));
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Belong.class);
+ }
+
+ public Belong update(Belong belong) {
+ String id = formatRelationId(belong.id());
+ RestResult result = this.client.put(this.path(), id, belong);
+ return result.readObject(Belong.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatRelationId(id));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/GroupAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/GroupAPI.java
new file mode 100644
index 0000000..5eb5ec9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/GroupAPI.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class GroupAPI extends AuthAPI {
+
+ public GroupAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.GROUP.string();
+ }
+
+ public Group create(Group group) {
+ RestResult result = this.client.post(this.path(), group);
+ return result.readObject(Group.class);
+ }
+
+ public Group get(Object id) {
+ RestResult result = this.client.get(this.path(), formatEntityId(id));
+ return result.readObject(Group.class);
+ }
+
+ public List<Group> list(int limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = ImmutableMap.of("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Group.class);
+ }
+
+ public Group update(Group group) {
+ String id = formatEntityId(group.id());
+ RestResult result = this.client.put(this.path(), id, group);
+ return result.readObject(Group.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatEntityId(id));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java
new file mode 100644
index 0000000..355a6ce
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LoginAPI.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+
+public class LoginAPI extends AuthAPI {
+
+ public LoginAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.LOGIN.string();
+ }
+
+ public LoginResult login(Login login) {
+ RestResult result = this.client.post(this.path(), login);
+ return result.readObject(LoginResult.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LogoutAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LogoutAPI.java
new file mode 100644
index 0000000..cd53f6b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/LogoutAPI.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class LogoutAPI extends AuthAPI {
+
+ public LogoutAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.LOGOUT.string();
+ }
+
+ public void logout() {
+ this.client.delete(this.path(), ImmutableMap.of());
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/ProjectAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/ProjectAPI.java
new file mode 100644
index 0000000..c6a3f07
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/ProjectAPI.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Project;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class ProjectAPI extends AuthAPI {
+
+ private static final String ACTION_ADD_GRAPH = "add_graph";
+ private static final String ACTION_REMOVE_GRAPH = "remove_graph";
+
+ public ProjectAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.PROJECT.string();
+ }
+
+ public Project create(Project project) {
+ RestResult result = this.client.post(this.path(), project);
+ return result.readObject(Project.class);
+ }
+
+ public Project get(Object id) {
+ RestResult result = this.client.get(this.path(), formatEntityId(id));
+ return result.readObject(Project.class);
+ }
+
+ public List<Project> list(long limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = ImmutableMap.of("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Project.class);
+ }
+
+ public Project update(Project project) {
+ String id = formatEntityId(project.id());
+ RestResult result = this.client.put(this.path(), id, project);
+ return result.readObject(Project.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatEntityId(id));
+ }
+
+ public Project addGraphs(Object projectId, Set<String> graphs) {
+ Project project = new Project();
+ project.graphs(graphs);
+ RestResult result = this.client.put(this.path(),
+ formatEntityId(projectId),
+ project,
+ ImmutableMap.of("action",
+ ACTION_ADD_GRAPH));
+ return result.readObject(Project.class);
+ }
+
+ public Project removeGraphs(Object projectId, Set<String> graphs) {
+ Project project = new Project();
+ project.graphs(graphs);
+ RestResult result = this.client.put(this.path(),
+ formatEntityId(projectId),
+ project,
+ ImmutableMap.of("action",
+ ACTION_REMOVE_GRAPH));
+ return result.readObject(Project.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TargetAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TargetAPI.java
new file mode 100644
index 0000000..31a80d2
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TargetAPI.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.Target;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class TargetAPI extends AuthAPI {
+
+ public TargetAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.TARGET.string();
+ }
+
+ public Target create(Target target) {
+ RestResult result = this.client.post(this.path(), target);
+ return result.readObject(Target.class);
+ }
+
+ public Target get(Object id) {
+ RestResult result = this.client.get(this.path(), formatEntityId(id));
+ return result.readObject(Target.class);
+ }
+
+ public List<Target> list(int limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = ImmutableMap.of("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Target.class);
+ }
+
+ public Target update(Target target) {
+ String id = formatEntityId(target.id());
+ RestResult result = this.client.put(this.path(), id, target);
+ return result.readObject(Target.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatEntityId(id));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TokenAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TokenAPI.java
new file mode 100644
index 0000000..8fb0bf5
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/TokenAPI.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.TokenPayload;
+import com.baidu.hugegraph.structure.constant.HugeType;
+
+public class TokenAPI extends AuthAPI {
+
+ public TokenAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.TOKEN_VERIFY.string();
+ }
+
+ public TokenPayload verifyToken() {
+ RestResult result = this.client.get(this.path());
+ return result.readObject(TokenPayload.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java
new file mode 100644
index 0000000..07855fc
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/auth/UserAPI.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.structure.auth.User.UserRole;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class UserAPI extends AuthAPI {
+
+ public UserAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.USER.string();
+ }
+
+ public User create(User user) {
+ RestResult result = this.client.post(this.path(), user);
+ return result.readObject(User.class);
+ }
+
+ public User get(Object id) {
+ RestResult result = this.client.get(this.path(), formatEntityId(id));
+ return result.readObject(User.class);
+ }
+
+ public UserRole getUserRole(Object id) {
+ String idEncoded = RestClient.encode(formatEntityId(id));
+ String path = String.join("/", this.path(), idEncoded, "role");
+ RestResult result = this.client.get(path);
+ return result.readObject(UserRole.class);
+ }
+
+ public List<User> list(int limit) {
+ checkLimit(limit, "Limit");
+ Map<String, Object> params = ImmutableMap.of("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), User.class);
+ }
+
+ public User update(User user) {
+ String id = formatEntityId(user.id());
+ RestResult result = this.client.put(this.path(), id, user);
+ return result.readObject(User.class);
+ }
+
+ public void delete(Object id) {
+ this.client.delete(this.path(), formatEntityId(id));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/EdgeAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/EdgeAPI.java
new file mode 100644
index 0000000..6e8e104
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/EdgeAPI.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.graph;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.NotAllCreatedException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.graph.BatchEdgeRequest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Edges;
+import com.google.common.collect.ImmutableMap;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+
+public class EdgeAPI extends GraphAPI {
+
+ public EdgeAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.EDGE.string();
+ }
+
+ public Edge create(Edge edge) {
+ RestResult result = this.client.post(this.path(), edge);
+ return result.readObject(Edge.class);
+ }
+
+ public List<String> create(List<Edge> edges, boolean checkVertex) {
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.putSingle("Content-Encoding", BATCH_ENCODING);
+ Map<String, Object> params = ImmutableMap.of("check_vertex",
+ checkVertex);
+ RestResult result = this.client.post(this.batchPath(), edges,
+ headers, params);
+ List<String> ids = result.readList(String.class);
+ if (edges.size() != ids.size()) {
+ throw new NotAllCreatedException(
+ "Not all edges are successfully created, " +
+ "expect '%s', the actual is '%s'",
+ ids, edges.size(), ids.size());
+ }
+ return ids;
+ }
+
+ public List<Edge> update(BatchEdgeRequest request) {
+ this.client.checkApiVersion("0.45", "batch property update");
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.putSingle("Content-Encoding", BATCH_ENCODING);
+ RestResult result = this.client.put(this.batchPath(), null,
+ request, headers);
+ return result.readList(this.type(), Edge.class);
+ }
+
+ public Edge append(Edge edge) {
+ String id = edge.id();
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ RestResult result = this.client.put(this.path(), id, edge, params);
+ return result.readObject(Edge.class);
+ }
+
+ public Edge eliminate(Edge edge) {
+ String id = edge.id();
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ RestResult result = this.client.put(this.path(), id, edge, params);
+ return result.readObject(Edge.class);
+ }
+
+ public Edge get(String id) {
+ RestResult result = this.client.get(this.path(), id);
+ return result.readObject(Edge.class);
+ }
+
+ public Edges list(int limit) {
+ return this.list(null, null, null, null, 0, null, limit);
+ }
+
+ public Edges list(Object vertexId, Direction direction,
+ String label, Map<String, Object> properties,
+ int offset, String page, int limit) {
+ return this.list(vertexId, direction, label, properties, false,
+ offset, page, limit);
+ }
+
+ public Edges list(Object vertexId, Direction direction, String label,
+ Map<String, Object> properties, boolean keepP,
+ int offset, String page, int limit) {
+ checkOffset(offset);
+ checkLimit(limit, "Limit");
+ String vid = GraphAPI.formatVertexId(vertexId, true);
+ String props = GraphAPI.formatProperties(properties);
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("vertex_id", vid);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("properties", props);
+ params.put("keep_start_p", keepP);
+ params.put("offset", offset);
+ params.put("limit", limit);
+ params.put("page", page);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readObject(Edges.class);
+ }
+
+ public void delete(String id) {
+ this.client.delete(this.path(), id);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/GraphAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/GraphAPI.java
new file mode 100644
index 0000000..9362238
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/GraphAPI.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.graph;
+
+import java.util.Map;
+import java.util.UUID;
+
+import org.glassfish.jersey.uri.UriComponent;
+import org.glassfish.jersey.uri.UriComponent.Type;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.util.E;
+import com.baidu.hugegraph.util.JsonUtil;
+
+public abstract class GraphAPI extends API {
+
+ private static final String PATH = "graphs/%s/graph/%s";
+
+ private final String batchPath;
+
+ public GraphAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ this.batchPath = String.join("/", this.path(), "batch");
+ }
+
+ public String batchPath() {
+ return this.batchPath;
+ }
+
+ public static String formatVertexId(Object id) {
+ return formatVertexId(id, false);
+ }
+
+ public static String formatVertexId(Object id, boolean allowNull) {
+ if (!allowNull) {
+ E.checkArgumentNotNull(id, "The vertex id can't be null");
+ } else {
+ if (id == null) {
+ return null;
+ }
+ }
+ boolean uuid = id instanceof UUID;
+ if (uuid) {
+ id = id.toString();
+ }
+ E.checkArgument(id instanceof String || id instanceof Number,
+ "The vertex id must be either String or " +
+ "Number, but got '%s'", id);
+ return (uuid ? "U" : "") + JsonUtil.toJson(id);
+ }
+
+ public static String formatProperties(Map<String, Object> properties) {
+ if (properties == null) {
+ return null;
+ }
+ String json = JsonUtil.toJson(properties);
+ /*
+ * Don't use UrlEncoder.encode, it encoded the space as `+`,
+ * which will invalidate the jersey's automatic decoding
+ * because it considers the space to be encoded as `%2F`
+ */
+ return encode(json);
+ }
+
+ public static String encode(String raw) {
+ return UriComponent.encode(raw, Type.QUERY_PARAM_SPACE_ENCODED);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/VertexAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/VertexAPI.java
new file mode 100644
index 0000000..13bb602
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graph/VertexAPI.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.graph;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.InvalidResponseException;
+import com.baidu.hugegraph.exception.NotAllCreatedException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.graph.BatchOlapPropertyRequest;
+import com.baidu.hugegraph.structure.graph.BatchVertexRequest;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.graph.Vertices;
+import com.google.common.collect.ImmutableMap;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+
+public class VertexAPI extends GraphAPI {
+
+ public VertexAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.VERTEX.string();
+ }
+
+ public Vertex create(Vertex vertex) {
+ RestResult result = this.client.post(this.path(), vertex);
+ return result.readObject(Vertex.class);
+ }
+
+ public List<Object> create(List<Vertex> vertices) {
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.putSingle("Content-Encoding", BATCH_ENCODING);
+ RestResult result = this.client.post(this.batchPath(), vertices,
+ headers);
+ List<Object> ids = result.readList(Object.class);
+ if (vertices.size() != ids.size()) {
+ throw new NotAllCreatedException(
+ "Not all vertices are successfully created, " +
+ "expect '%s', the actual is '%s'",
+ ids, vertices.size(), ids.size());
+ }
+ return ids;
+ }
+
+ public List<Vertex> update(BatchVertexRequest request) {
+ this.client.checkApiVersion("0.45", "batch property update");
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.putSingle("Content-Encoding", BATCH_ENCODING);
+ RestResult result = this.client.put(this.batchPath(), null,
+ request, headers);
+ return result.readList(this.type(), Vertex.class);
+ }
+
+ public int update(BatchOlapPropertyRequest request) {
+ this.client.checkApiVersion("0.59", "olap property batch update");
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.putSingle("Content-Encoding", BATCH_ENCODING);
+ String path = String.join("/", this.path(), "olap/batch");
+ RestResult result = this.client.put(path, null, request, headers);
+ Object size = result.readObject(Map.class).get("size");
+ if (!(size instanceof Integer)) {
+ throw new InvalidResponseException(
+ "The 'size' in response must be int, but got: %s(%s)",
+ size, size.getClass());
+ }
+ return (int) size;
+ }
+
+ public Vertex append(Vertex vertex) {
+ String id = GraphAPI.formatVertexId(vertex.id());
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ RestResult result = this.client.put(this.path(), id, vertex, params);
+ return result.readObject(Vertex.class);
+ }
+
+ public Vertex eliminate(Vertex vertex) {
+ String id = GraphAPI.formatVertexId(vertex.id());
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ RestResult result = this.client.put(this.path(), id, vertex, params);
+ return result.readObject(Vertex.class);
+ }
+
+ public Vertex get(Object id) {
+ String vertexId = GraphAPI.formatVertexId(id);
+ RestResult result = this.client.get(this.path(), vertexId);
+ return result.readObject(Vertex.class);
+ }
+
+ public Vertices list(int limit) {
+ return this.list(null, null, 0, null, limit);
+ }
+
+ public Vertices list(String label, Map<String, Object> properties,
+ int offset, String page, int limit) {
+ return this.list(label, properties, false, offset, page, limit);
+ }
+
+ public Vertices list(String label, Map<String, Object> properties,
+ boolean keepP, int offset, String page, int limit) {
+ checkOffset(offset);
+ checkLimit(limit, "Limit");
+ String props = GraphAPI.formatProperties(properties);
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("label", label);
+ params.put("properties", props);
+ params.put("keep_start_p", keepP);
+ params.put("offset", offset);
+ params.put("limit", limit);
+ params.put("page", page);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readObject(Vertices.class);
+ }
+
+ public void delete(Object id) {
+ String vertexId = GraphAPI.formatVertexId(id);
+ this.client.delete(this.path(), vertexId);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graphs/GraphsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graphs/GraphsAPI.java
new file mode 100644
index 0000000..8851699
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/graphs/GraphsAPI.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.graphs;
+
+import java.util.List;
+import java.util.Map;
+
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.MultivaluedHashMap;
+import org.apache.commons.lang3.StringUtils;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.InvalidResponseException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.GraphMode;
+import com.baidu.hugegraph.structure.constant.GraphReadMode;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class GraphsAPI extends API {
+
+ private static final String DELIMITER = "/";
+ private static final String MODE = "mode";
+ private static final String GRAPH_READ_MODE = "graph_read_mode";
+ private static final String CLEAR = "clear";
+
+ private static final String CONFIRM_MESSAGE = "confirm_message";
+
+ public GraphsAPI(RestClient client) {
+ super(client);
+ this.path(this.type());
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.GRAPHS.string();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> create(String name, String cloneGraphName,
+ String configText) {
+ this.client.checkApiVersion("0.67", "dynamic graph add");
+ MultivaluedHashMap<String, Object> headers = new MultivaluedHashMap<>();
+ headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
+ Map<String, Object> params = null;
+ if (StringUtils.isNotEmpty(cloneGraphName)) {
+ params = ImmutableMap.of("clone_graph_name", cloneGraphName);
+ }
+ RestResult result = this.client.post(joinPath(this.path(), name),
+ configText, headers, params);
+ return result.readObject(Map.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, String> get(String name) {
+ RestResult result = this.client.get(this.path(), name);
+ return result.readObject(Map.class);
+ }
+
+ public List<String> list() {
+ RestResult result = this.client.get(this.path());
+ return result.readList(this.type(), String.class);
+ }
+
+ public void clear(String graph, String message) {
+ this.client.delete(joinPath(this.path(), graph, CLEAR),
+ ImmutableMap.of(CONFIRM_MESSAGE, message));
+ }
+
+ public void drop(String graph, String message) {
+ this.client.checkApiVersion("0.67", "dynamic graph delete");
+ this.client.delete(joinPath(this.path(), graph),
+ ImmutableMap.of(CONFIRM_MESSAGE, message));
+ }
+
+ public void mode(String graph, GraphMode mode) {
+ // NOTE: Must provide id for PUT. If use "graph/mode", "/" will
+ // be encoded to "%2F". So use "mode" here although inaccurate.
+ this.client.put(joinPath(this.path(), graph, MODE), null, mode);
+ }
+
+ public GraphMode mode(String graph) {
+ RestResult result = this.client.get(joinPath(this.path(), graph), MODE);
+ @SuppressWarnings("unchecked")
+ Map<String, String> mode = result.readObject(Map.class);
+ String value = mode.get(MODE);
+ if (value == null) {
+ throw new InvalidResponseException(
+ "Invalid response, expect 'mode' in response");
+ }
+ try {
+ return GraphMode.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidResponseException(
+ "Invalid GraphMode value '%s'", value);
+ }
+ }
+
+ public void readMode(String graph, GraphReadMode readMode) {
+ this.client.checkApiVersion("0.59", "graph read mode");
+ // NOTE: Must provide id for PUT. If use "graph/graph_read_mode", "/"
+ // will be encoded to "%2F". So use "graph_read_mode" here although
+ // inaccurate.
+ this.client.put(joinPath(this.path(), graph, GRAPH_READ_MODE),
+ null, readMode);
+ }
+
+ public GraphReadMode readMode(String graph) {
+ this.client.checkApiVersion("0.59", "graph read mode");
+ RestResult result = this.client.get(joinPath(this.path(), graph),
+ GRAPH_READ_MODE);
+ @SuppressWarnings("unchecked")
+ Map<String, String> readMode = result.readObject(Map.class);
+ String value = readMode.get(GRAPH_READ_MODE);
+ if (value == null) {
+ throw new InvalidResponseException(
+ "Invalid response, expect 'graph_read_mode' in response");
+ }
+ try {
+ return GraphReadMode.valueOf(value);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidResponseException(
+ "Invalid GraphReadMode value '%s'", value);
+ }
+ }
+
+ private static String joinPath(String path, String graph) {
+ return String.join(DELIMITER, path, graph);
+ }
+
+ private static String joinPath(String path, String graph, String action) {
+ return String.join(DELIMITER, path, graph, action);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinAPI.java
new file mode 100644
index 0000000..ee89391
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinAPI.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.gremlin;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.gremlin.Response;
+
+public class GremlinAPI extends API {
+
+ public GremlinAPI(RestClient client) {
+ super(client);
+ this.path(type());
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.GREMLIN.string();
+ }
+
+ public Response post(GremlinRequest request) {
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(Response.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinRequest.java
new file mode 100644
index 0000000..8c6311b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/gremlin/GremlinRequest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.gremlin;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.baidu.hugegraph.driver.GremlinManager;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public class GremlinRequest {
+
+ // See org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer
+ public String gremlin;
+ public Map<String, Object> bindings;
+ public String language;
+ public Map<String, String> aliases;
+
+ public GremlinRequest(String gremlin) {
+ this.gremlin = gremlin;
+ this.bindings = new ConcurrentHashMap<>();
+ this.language = "gremlin-groovy";
+ this.aliases = new ConcurrentHashMap<>();
+ }
+
+ public static class Builder {
+ private GremlinRequest request;
+ private GremlinManager manager;
+
+ public Builder(String gremlin, GremlinManager executor) {
+ this.request = new GremlinRequest(gremlin);
+ this.manager = executor;
+ }
+
+ public ResultSet execute() {
+ return this.manager.execute(this.request);
+ }
+
+ public long executeAsTask() {
+ return this.manager.executeAsTask(this.request);
+ }
+
+ public Builder binding(String key, Object value) {
+ this.request.bindings.put(key, value);
+ return this;
+ }
+
+ public Builder language(String language) {
+ this.request.language = language;
+ return this;
+ }
+
+ public Builder alias(String key, String value) {
+ this.request.aliases.put(key, value);
+ return this;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/GremlinJobAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/GremlinJobAPI.java
new file mode 100644
index 0000000..80c7383
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/GremlinJobAPI.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.job;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.api.gremlin.GremlinRequest;
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+
+public class GremlinJobAPI extends JobAPI {
+
+ private static final String JOB_TYPE = "gremlin";
+
+ public GremlinJobAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String jobType() {
+ return JOB_TYPE;
+ }
+
+ public long execute(GremlinRequest request) {
+ RestResult result = this.client.post(this.path(), request);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/JobAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/JobAPI.java
new file mode 100644
index 0000000..6447759
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/JobAPI.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.job;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.constant.HugeType;
+
+public abstract class JobAPI extends API {
+
+ // For example: graphs/hugegraph/jobs/gremlin
+ private static final String PATH = "graphs/%s/%s/%s";
+
+ public JobAPI(RestClient client, String graph) {
+ super(client);
+ this.path(String.format(PATH, graph, this.type(), this.jobType()));
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.JOB.string();
+ }
+
+ protected abstract String jobType();
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/RebuildAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/RebuildAPI.java
new file mode 100644
index 0000000..4a077b9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/job/RebuildAPI.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.job;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.util.E;
+
+public class RebuildAPI extends JobAPI {
+
+ private static final String JOB_TYPE = "rebuild";
+
+ public RebuildAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String jobType() {
+ return JOB_TYPE;
+ }
+
+ public long rebuild(VertexLabel vertexLabel) {
+ return this.rebuildIndex(vertexLabel);
+ }
+
+ public long rebuild(EdgeLabel edgeLabel) {
+ return this.rebuildIndex(edgeLabel);
+ }
+
+ public long rebuild(IndexLabel indexLabel) {
+ return this.rebuildIndex(indexLabel);
+ }
+
+ private long rebuildIndex(SchemaElement element) {
+ E.checkArgument(element instanceof VertexLabel ||
+ element instanceof EdgeLabel ||
+ element instanceof IndexLabel,
+ "Only VertexLabel, EdgeLabel and IndexLabel support " +
+ "rebuild, but got '%s'", element);
+ String path = String.join(PATH_SPLITOR, this.path(), element.type());
+ RestResult result = this.client.put(path, element.name(), element);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/metrics/MetricsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/metrics/MetricsAPI.java
new file mode 100644
index 0000000..af8a2d2
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/metrics/MetricsAPI.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.metrics;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.util.CommonUtil;
+
+public class MetricsAPI extends API {
+
+ public MetricsAPI(RestClient client) {
+ super(client);
+ this.path(this.type());
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.METRICS.string();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Map<String, Object>> system() {
+ RestResult result = this.client.get(this.path(), "system");
+ Map<?, ?> map = result.readObject(Map.class);
+ CommonUtil.checkMapClass(map, String.class, Map.class);
+ for (Object mapValue : map.values()) {
+ CommonUtil.checkMapClass(mapValue, String.class, Object.class);
+ }
+ return (Map<String, Map<String, Object>>) map;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Map<String, Object>> backend() {
+ RestResult result = this.client.get(this.path(), "backend");
+ Map<?, ?> map = result.readObject(Map.class);
+ CommonUtil.checkMapClass(map, String.class, Map.class);
+ for (Object mapValue : map.values()) {
+ CommonUtil.checkMapClass(mapValue, String.class, Object.class);
+ }
+ return (Map<String, Map<String, Object>>) map;
+ }
+
+ public Map<String, Object> backend(String graph) {
+ return this.backend().get(graph);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Map<String, Object>> all() {
+ RestResult result = this.client.get(this.path());
+ Map<?, ?> map = result.readObject(Map.class);
+ CommonUtil.checkMapClass(map, String.class, Map.class);
+ for (Object mapValue : map.values()) {
+ CommonUtil.checkMapClass(mapValue, String.class, Object.class);
+ }
+ return (Map<String, Map<String, Object>>) map;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/EdgeLabelAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/EdgeLabelAPI.java
new file mode 100644
index 0000000..991887a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/EdgeLabelAPI.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class EdgeLabelAPI extends SchemaElementAPI {
+
+ public EdgeLabelAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.EDGE_LABEL.string();
+ }
+
+ public EdgeLabel create(EdgeLabel edgeLabel) {
+ Object el = this.checkCreateOrUpdate(edgeLabel);
+ RestResult result = this.client.post(this.path(), el);
+ return result.readObject(EdgeLabel.class);
+ }
+
+ public EdgeLabel append(EdgeLabel edgeLabel) {
+ String id = edgeLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ Object el = this.checkCreateOrUpdate(edgeLabel);
+ RestResult result = this.client.put(this.path(), id, el, params);
+ return result.readObject(EdgeLabel.class);
+ }
+
+ public EdgeLabel eliminate(EdgeLabel edgeLabel) {
+ String id = edgeLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ Object el = this.checkCreateOrUpdate(edgeLabel);
+ RestResult result = this.client.put(this.path(), id, el, params);
+ return result.readObject(EdgeLabel.class);
+ }
+
+ public EdgeLabel get(String name) {
+ RestResult result = this.client.get(this.path(), name);
+ return result.readObject(EdgeLabel.class);
+ }
+
+ public List<EdgeLabel> list() {
+ RestResult result = this.client.get(this.path());
+ return result.readList(this.type(), EdgeLabel.class);
+ }
+
+ public List<EdgeLabel> list(List<String> names) {
+ this.client.checkApiVersion("0.48", "getting schema by names");
+ E.checkArgument(names != null && !names.isEmpty(),
+ "The edge label names can't be null or empty");
+ Map<String, Object> params = ImmutableMap.of("names", names);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), EdgeLabel.class);
+ }
+
+ public long delete(String name) {
+ RestResult result = this.client.delete(this.path(), name);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+
+ @Override
+ protected Object checkCreateOrUpdate(SchemaElement schemaElement) {
+ EdgeLabel edgeLabel = (EdgeLabel) schemaElement;
+ Object el = edgeLabel;
+ if (this.client.apiVersionLt("0.54")) {
+ E.checkArgument(edgeLabel.ttl() == 0L &&
+ edgeLabel.ttlStartTime() == null,
+ "Not support ttl until api version 0.54");
+ el = edgeLabel.switchV53();
+ }
+ return el;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/IndexLabelAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/IndexLabelAPI.java
new file mode 100644
index 0000000..3e85543
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/IndexLabelAPI.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.IndexType;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class IndexLabelAPI extends SchemaElementAPI {
+
+ public IndexLabelAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.INDEX_LABEL.string();
+ }
+
+ public IndexLabel.IndexLabelWithTask create(IndexLabel indexLabel) {
+ Object il = this.checkCreateOrUpdate(indexLabel);
+ RestResult result = this.client.post(this.path(), il);
+ return result.readObject(IndexLabel.IndexLabelWithTask.class);
+ }
+
+ public IndexLabel append(IndexLabel indexLabel) {
+ if (this.client.apiVersionLt("0.50")) {
+ throw new NotSupportException("action append on index label");
+ }
+
+ String id = indexLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ Object il = this.checkCreateOrUpdate(indexLabel);
+ RestResult result = this.client.put(this.path(), id, il, params);
+ return result.readObject(IndexLabel.class);
+ }
+
+ public IndexLabel eliminate(IndexLabel indexLabel) {
+ if (this.client.apiVersionLt("0.50")) {
+ throw new NotSupportException("action eliminate on index label");
+ }
+
+ String id = indexLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ Object il = this.checkCreateOrUpdate(indexLabel);
+ RestResult result = this.client.put(this.path(), id, il, params);
+ return result.readObject(IndexLabel.class);
+ }
+
+ public IndexLabel get(String name) {
+ RestResult result = this.client.get(this.path(), name);
+ return result.readObject(IndexLabel.class);
+ }
+
+ public List<IndexLabel> list() {
+ RestResult result = this.client.get(this.path());
+ return result.readList(this.type(), IndexLabel.class);
+ }
+
+ public List<IndexLabel> list(List<String> names) {
+ this.client.checkApiVersion("0.48", "getting schema by names");
+ E.checkArgument(names != null && !names.isEmpty(),
+ "The index label names can't be null or empty");
+ Map<String, Object> params = ImmutableMap.of("names", names);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), IndexLabel.class);
+ }
+
+ public long delete(String name) {
+ RestResult result = this.client.delete(this.path(), name);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+
+ @Override
+ protected Object checkCreateOrUpdate(SchemaElement schemaElement) {
+ IndexLabel indexLabel = (IndexLabel) schemaElement;
+ if (indexLabel.indexType() == IndexType.SHARD) {
+ this.client.checkApiVersion("0.43", "shard index");
+ } else if (indexLabel.indexType() == IndexType.UNIQUE) {
+ this.client.checkApiVersion("0.44", "unique index");
+ }
+
+ IndexLabel il = indexLabel;
+ if (this.client.apiVersionLt("0.50")) {
+ E.checkArgument(indexLabel.userdata() == null ||
+ indexLabel.userdata().isEmpty(),
+ "Not support userdata of index label until api " +
+ "version 0.50");
+ E.checkArgument(indexLabel.rebuild(),
+ "Not support rebuild of index label until api " +
+ "version 0.57");
+ il = indexLabel.switchV49();
+ } else if (this.client.apiVersionLt("0.57")) {
+ E.checkArgument(indexLabel.rebuild(),
+ "Not support rebuild of index label until api " +
+ "version 0.57");
+ il = indexLabel.switchV56();
+ }
+ return il;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/PropertyKeyAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/PropertyKeyAPI.java
new file mode 100644
index 0000000..fcb9c51
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/PropertyKeyAPI.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.WriteType;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class PropertyKeyAPI extends SchemaElementAPI {
+
+ public PropertyKeyAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.PROPERTY_KEY.string();
+ }
+
+ public PropertyKey.PropertyKeyWithTask create(PropertyKey propertyKey) {
+ Object pkey = this.checkCreateOrUpdate(propertyKey);
+ RestResult result = this.client.post(this.path(), pkey);
+ if (this.client.apiVersionLt("0.65")) {
+ return new PropertyKey.PropertyKeyWithTask(
+ result.readObject(PropertyKey.class), 0L);
+ }
+ return result.readObject(PropertyKey.PropertyKeyWithTask.class);
+ }
+
+ public PropertyKey.PropertyKeyWithTask append(PropertyKey propertyKey) {
+ String id = propertyKey.name();
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ Object pkey = this.checkCreateOrUpdate(propertyKey);
+ RestResult result = this.client.put(this.path(), id, pkey, params);
+ return result.readObject(PropertyKey.PropertyKeyWithTask.class);
+ }
+
+ public PropertyKey.PropertyKeyWithTask eliminate(PropertyKey propertyKey) {
+ String id = propertyKey.name();
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ Object pkey = this.checkCreateOrUpdate(propertyKey);
+ RestResult result = this.client.put(this.path(), id, pkey, params);
+ return result.readObject(PropertyKey.PropertyKeyWithTask.class);
+ }
+
+ public PropertyKey.PropertyKeyWithTask clear(PropertyKey propertyKey) {
+ if (this.client.apiVersionLt("0.65")) {
+ throw new NotSupportException("action clear on property key");
+ }
+ String id = propertyKey.name();
+ Map<String, Object> params = ImmutableMap.of("action", "clear");
+ Object pkey = this.checkCreateOrUpdate(propertyKey);
+ RestResult result = this.client.put(this.path(), id, pkey, params);
+ return result.readObject(PropertyKey.PropertyKeyWithTask.class);
+ }
+
+ public PropertyKey get(String name) {
+ RestResult result = this.client.get(this.path(), name);
+ return result.readObject(PropertyKey.class);
+ }
+
+ public List<PropertyKey> list() {
+ RestResult result = this.client.get(this.path());
+ return result.readList(this.type(), PropertyKey.class);
+ }
+
+ public List<PropertyKey> list(List<String> names) {
+ this.client.checkApiVersion("0.48", "getting schema by names");
+ E.checkArgument(names != null && !names.isEmpty(),
+ "The property key names can't be null or empty");
+ Map<String, Object> params = ImmutableMap.of("names", names);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), PropertyKey.class);
+ }
+
+ public long delete(String name) {
+ if (this.client.apiVersionLt("0.65")) {
+ this.client.delete(this.path(), name);
+ return 0L;
+ }
+ RestResult result = this.client.delete(this.path(), name);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+
+ @Override
+ protected Object checkCreateOrUpdate(SchemaElement schemaElement) {
+ PropertyKey propertyKey = (PropertyKey) schemaElement;
+ Object pkey = propertyKey;
+ if (this.client.apiVersionLt("0.47")) {
+ E.checkArgument(propertyKey.aggregateType().isNone(),
+ "Not support aggregate property until " +
+ "api version 0.47");
+ pkey = propertyKey.switchV46();
+ } else if (this.client.apiVersionLt("0.59")) {
+ E.checkArgument(propertyKey.writeType() == WriteType.OLTP,
+ "Not support olap property key until " +
+ "api version 0.59");
+ pkey = propertyKey.switchV58();
+ }
+ return pkey;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaAPI.java
new file mode 100644
index 0000000..537b147
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaAPI.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+
+public class SchemaAPI extends API {
+
+ private static final String PATH = "graphs/%s/%s";
+
+ public SchemaAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, List<SchemaElement>> list() {
+ if (this.client.apiVersionLt("0.66")) {
+ throw new NotSupportException("schema get api");
+ }
+ RestResult result = this.client.get(this.path());
+ return result.readObject(Map.class);
+ }
+
+ @Override
+ protected String type() {
+ return "schema";
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaElementAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaElementAPI.java
new file mode 100644
index 0000000..2bc6135
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/SchemaElementAPI.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.SchemaElement;
+
+public abstract class SchemaElementAPI extends API {
+
+ private static final String PATH = "graphs/%s/schema/%s";
+
+ public SchemaElementAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ }
+
+ protected abstract Object checkCreateOrUpdate(SchemaElement schemaElement);
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/VertexLabelAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/VertexLabelAPI.java
new file mode 100644
index 0000000..a47f08c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/schema/VertexLabelAPI.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.schema;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class VertexLabelAPI extends SchemaElementAPI {
+
+ public VertexLabelAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.VERTEX_LABEL.string();
+ }
+
+ public VertexLabel create(VertexLabel vertexLabel) {
+ Object vl = this.checkCreateOrUpdate(vertexLabel);
+ RestResult result = this.client.post(this.path(), vl);
+ return result.readObject(VertexLabel.class);
+ }
+
+ public VertexLabel append(VertexLabel vertexLabel) {
+ String id = vertexLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "append");
+ Object vl = this.checkCreateOrUpdate(vertexLabel);
+ RestResult result = this.client.put(this.path(), id, vl, params);
+ return result.readObject(VertexLabel.class);
+ }
+
+ public VertexLabel eliminate(VertexLabel vertexLabel) {
+ String id = vertexLabel.name();
+ Map<String, Object> params = ImmutableMap.of("action", "eliminate");
+ Object vl = this.checkCreateOrUpdate(vertexLabel);
+ RestResult result = this.client.put(this.path(), id, vl, params);
+ return result.readObject(VertexLabel.class);
+ }
+
+ public VertexLabel get(String name) {
+ RestResult result = this.client.get(this.path(), name);
+ return result.readObject(VertexLabel.class);
+ }
+
+ public List<VertexLabel> list() {
+ RestResult result = this.client.get(this.path());
+ return result.readList(this.type(), VertexLabel.class);
+ }
+
+ public List<VertexLabel> list(List<String> names) {
+ this.client.checkApiVersion("0.48", "getting schema by names");
+ E.checkArgument(names != null && !names.isEmpty(),
+ "The vertex label names can't be null or empty");
+ Map<String, Object> params = ImmutableMap.of("names", names);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), VertexLabel.class);
+ }
+
+ public long delete(String name) {
+ RestResult result = this.client.delete(this.path(), name);
+ @SuppressWarnings("unchecked")
+ Map<String, Object> task = result.readObject(Map.class);
+ return TaskAPI.parseTaskId(task);
+ }
+
+ @Override
+ protected Object checkCreateOrUpdate(SchemaElement schemaElement) {
+ VertexLabel vertexLabel = (VertexLabel) schemaElement;
+ if (vertexLabel.idStrategy().isCustomizeUuid()) {
+ this.client.checkApiVersion("0.46", "customize UUID strategy");
+ }
+ Object vl = vertexLabel;
+ if (this.client.apiVersionLt("0.54")) {
+ E.checkArgument(vertexLabel.ttl() == 0L &&
+ vertexLabel.ttlStartTime() == null,
+ "Not support ttl until api version 0.54");
+ vl = vertexLabel.switchV53();
+ }
+ return vl;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TaskAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TaskAPI.java
new file mode 100644
index 0000000..51ffe05
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TaskAPI.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.task;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.ClientException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.util.E;
+import com.baidu.hugegraph.util.TaskCache;
+import com.google.common.collect.ImmutableMap;
+
+public class TaskAPI extends API {
+
+ private static final String PATH = "graphs/%s/tasks";
+ private String graph;
+ public static final String TASKS = "tasks";
+ public static final String TASK_ID = "task_id";
+ public static final long TASK_TIMEOUT = 60L;
+ private static final long QUERY_INTERVAL = 500L;
+
+ public TaskAPI(RestClient client, String graph) {
+ super(client);
+ this.path(String.format(PATH, graph));
+ this.graph = graph;
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.TASK.string();
+ }
+
+ public String graph() {
+ return this.graph;
+ }
+
+ public List<Task> list(String status, long limit) {
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("limit", limit);
+ if (status != null) {
+ params.put("status", status);
+ }
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(TASKS, Task.class);
+ }
+
+ public TasksWithPage list(String status, String page, long limit) {
+ E.checkArgument(page != null, "The page can not be null");
+ this.client.checkApiVersion("0.48", "getting tasks by paging");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("limit", limit);
+ params.put("page", page);
+ if (status != null) {
+ params.put("status", status);
+ }
+ RestResult result = this.client.get(this.path(), params);
+ return result.readObject(TasksWithPage.class);
+ }
+
+ public List<Task> list(List<Long> ids) {
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("ids", ids);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(TASKS, Task.class);
+ }
+
+ public Task get(long id) {
+ RestResult result = this.client.get(this.path(), String.valueOf(id));
+ return result.readObject(Task.class);
+ }
+
+ public void delete(long id) {
+ this.client.delete(path(), String.valueOf(id));
+ }
+
+ public Task cancel(long id) {
+ Map<String, Object> params = ImmutableMap.of("action", "cancel");
+ RestResult result = this.client.put(path(), String.valueOf(id),
+ ImmutableMap.of(), params);
+ return result.readObject(Task.class);
+ }
+
+ public Task waitUntilTaskSuccess(long taskId, long seconds) {
+ if (taskId == 0) {
+ return null;
+ }
+ long passes = seconds * 1000 / QUERY_INTERVAL;
+ try {
+ for (long pass = 0; ; pass++) {
+ Task task = this.getFromCache(taskId);
+ if (task.success()) {
+ return task;
+ } else if (task.completed()) {
+ throw new ClientException(
+ "Task '%s' is %s, result is '%s'",
+ taskId, task.status(), task.result());
+ }
+ if (pass >= passes) {
+ break;
+ }
+ try {
+ // Query every half second from cache to decrease waiting
+ // time because restful query is executed per second
+ Thread.sleep(QUERY_INTERVAL);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ throw new ClientException(
+ "Task '%s' not completed in %s seconds, " +
+ "it can still be queried by task-get API",
+ taskId, seconds);
+ } finally {
+ // Stop querying this task info whatever
+ this.removeFromCache(taskId);
+ }
+ }
+
+ private Task getFromCache(long taskId) {
+ return TaskCache.instance().get(this, taskId);
+ }
+
+ private void removeFromCache(long taskId) {
+ TaskCache.instance().remove(this, taskId);
+ }
+
+ public static long parseTaskId(Map<String, Object> task) {
+ E.checkState(task.size() == 1 && task.containsKey(TASK_ID),
+ "Task must be formatted to {\"%s\" : id}, but got %s",
+ TASK_ID, task);
+ Object taskId = task.get(TASK_ID);
+ E.checkState(taskId instanceof Number,
+ "Task id must be number, but got '%s'", taskId);
+ return ((Number) taskId).longValue();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TasksWithPage.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TasksWithPage.java
new file mode 100644
index 0000000..93f09b4
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/task/TasksWithPage.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.task;
+
+import java.util.List;
+
+import com.baidu.hugegraph.structure.Task;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TasksWithPage {
+
+ @JsonProperty
+ private String page;
+ @JsonProperty
+ private List<Task> tasks;
+
+ public String page() {
+ return this.page;
+ }
+
+ public List<Task> tasks() {
+ return this.tasks;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/AllShortestPathsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/AllShortestPathsAPI.java
new file mode 100644
index 0000000..26a2e7f
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/AllShortestPathsAPI.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+
+public class AllShortestPathsAPI extends TraversersAPI {
+
+ public AllShortestPathsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "allshortestpaths";
+ }
+
+ public List<Path> get(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth,
+ long degree, long skipDegree, long capacity) {
+ this.client.checkApiVersion("0.51", "all shortest path");
+ String source = GraphAPI.formatVertexId(sourceId, false);
+ String target = GraphAPI.formatVertexId(targetId, false);
+
+ checkPositive(maxDepth, "Max depth of shortest path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkSkipDegree(skipDegree, degree, capacity);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("target", target);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", maxDepth);
+ params.put("max_degree", degree);
+ params.put("skip_degree", skipDegree);
+ params.put("capacity", capacity);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList("paths", Path.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java
new file mode 100644
index 0000000..f6ec8cc
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.CountRequest;
+import com.baidu.hugegraph.util.E;
+
+public class CountAPI extends TraversersAPI {
+
+ private static final String COUNT = "count";
+
+ public CountAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "count";
+ }
+
+ public long post(CountRequest request) {
+ this.client.checkApiVersion("0.55", "count");
+ RestResult result = this.client.post(this.path(), request);
+ @SuppressWarnings("unchecked")
+ Map<String, Number> countMap = result.readObject(Map.class);
+ E.checkState(countMap.containsKey(COUNT),
+ "The result doesn't have key '%s'", COUNT);
+ return countMap.get(COUNT).longValue();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CrosspointsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CrosspointsAPI.java
new file mode 100644
index 0000000..9eb6d24
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CrosspointsAPI.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+
+public class CrosspointsAPI extends TraversersAPI {
+
+ public CrosspointsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "crosspoints";
+ }
+
+ public List<Path> get(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long degree,
+ long capacity, long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+ String target = GraphAPI.formatVertexId(targetId, false);
+
+ checkPositive(maxDepth, "Max depth of path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("target", target);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", maxDepth);
+ params.put("max_degree", degree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList("crosspoints", Path.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedCrosspointsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedCrosspointsAPI.java
new file mode 100644
index 0000000..8eca0f8
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedCrosspointsAPI.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.CrosspointsRequest;
+import com.baidu.hugegraph.structure.traverser.CustomizedCrosspoints;
+
+public class CustomizedCrosspointsAPI extends TraversersAPI {
+
+ public CustomizedCrosspointsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "customizedcrosspoints";
+ }
+
+ public CustomizedCrosspoints post(CrosspointsRequest request) {
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(CustomizedCrosspoints.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedPathsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedPathsAPI.java
new file mode 100644
index 0000000..ef1b032
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/CustomizedPathsAPI.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.structure.traverser.CustomizedPathsRequest;
+
+public class CustomizedPathsAPI extends TraversersAPI {
+
+ public CustomizedPathsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "customizedpaths";
+ }
+
+ public PathsWithVertices post(CustomizedPathsRequest request) {
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(PathsWithVertices.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/EdgesAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/EdgesAPI.java
new file mode 100644
index 0000000..680c335
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/EdgesAPI.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Edges;
+import com.baidu.hugegraph.structure.graph.Shard;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class EdgesAPI extends TraversersAPI {
+
+ public EdgesAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "edges";
+ }
+
+ public List<Edge> list(List<String> ids) {
+ E.checkArgument(ids != null && !ids.isEmpty(),
+ "Ids can't be null or empty");
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("ids", ids);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Edge.class);
+ }
+
+ public List<Shard> shards(long splitSize) {
+ String path = String.join(PATH_SPLITOR, this.path(), "shards");
+ Map<String, Object> params = ImmutableMap.of("split_size", splitSize);
+ RestResult result = this.client.get(path, params);
+ return result.readList("shards", Shard.class);
+ }
+
+ public Edges scan(Shard shard, String page, long pageLimit) {
+ E.checkArgument(shard != null, "Shard can't be null");
+ String path = String.join(PATH_SPLITOR, this.path(), "scan");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("start", shard.start());
+ params.put("end", shard.end());
+ params.put("page", page);
+ params.put("page_limit", pageLimit);
+ RestResult result = this.client.get(path, params);
+ return result.readObject(Edges.class);
+ }
+}
+
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityAPI.java
new file mode 100644
index 0000000..81053cd
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityAPI.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarity;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarityRequest;
+
+public class FusiformSimilarityAPI extends TraversersAPI {
+
+ public FusiformSimilarityAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "fusiformsimilarity";
+ }
+
+ public FusiformSimilarity post(FusiformSimilarityRequest request) {
+ this.client.checkApiVersion("0.49", "fusiform similarity");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(FusiformSimilarity.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityAPI.java
new file mode 100644
index 0000000..d39f236
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityAPI.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest;
+
+
+import com.baidu.hugegraph.util.E;
+
+public class JaccardSimilarityAPI extends TraversersAPI {
+
+ private static final String JACCARD_SIMILARITY = "jaccard_similarity";
+
+ public JaccardSimilarityAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "jaccardsimilarity";
+ }
+
+ public double get(Object vertexId, Object otherId, Direction direction,
+ String label, long degree) {
+ this.client.checkApiVersion("0.51", "jaccard similarity");
+ String vertex = GraphAPI.formatVertexId(vertexId, false);
+ String other = GraphAPI.formatVertexId(otherId, false);
+ checkDegree(degree);
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("vertex", vertex);
+ params.put("other", other);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_degree", degree);
+ RestResult result = this.client.get(this.path(), params);
+ @SuppressWarnings("unchecked")
+ Map<String, Double> jaccard = result.readObject(Map.class);
+ E.checkState(jaccard.containsKey(JACCARD_SIMILARITY),
+ "The result doesn't have key '%s'", JACCARD_SIMILARITY);
+ return jaccard.get(JACCARD_SIMILARITY);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<Object, Double> post(
+ SingleSourceJaccardSimilarityRequest request) {
+ this.client.checkApiVersion("0.58", "jaccard similar");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(Map.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KneighborAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KneighborAPI.java
new file mode 100644
index 0000000..493e1bf
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KneighborAPI.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.traverser.Kneighbor;
+import com.baidu.hugegraph.structure.traverser.KneighborRequest;
+
+public class KneighborAPI extends TraversersAPI {
+
+ public KneighborAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "kneighbor";
+ }
+
+ public List<Object> get(Object sourceId, Direction direction,
+ String label, int depth, long degree, long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+
+ checkPositive(depth, "Depth of k-neighbor");
+ checkDegree(degree);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", depth);
+ params.put("max_degree", degree);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList("vertices", Object.class);
+ }
+
+ public Kneighbor post(KneighborRequest request) {
+ this.client.checkApiVersion("0.58", "customized kneighbor");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(Kneighbor.class);
+ }
+}
+
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KoutAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KoutAPI.java
new file mode 100644
index 0000000..9e9ca01
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/KoutAPI.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.traverser.Kout;
+import com.baidu.hugegraph.structure.traverser.KoutRequest;
+
+public class KoutAPI extends TraversersAPI {
+
+ public KoutAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "kout";
+ }
+
+ public List<Object> get(Object sourceId, Direction direction,
+ String label, int depth, boolean nearest,
+ long degree, long capacity, long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+
+ checkPositive(depth, "Depth of k-out");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", depth);
+ params.put("nearest", nearest);
+ params.put("max_degree", degree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList("vertices", Object.class);
+ }
+
+ public Kout post(KoutRequest request) {
+ this.client.checkApiVersion("0.58", "customized kout");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(Kout.class);
+ }
+}
+
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathAPI.java
new file mode 100644
index 0000000..d81ac75
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathAPI.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.MultiNodeShortestPathRequest;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+
+public class MultiNodeShortestPathAPI extends TraversersAPI {
+
+ public MultiNodeShortestPathAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "multinodeshortestpath";
+ }
+
+ public PathsWithVertices post(MultiNodeShortestPathRequest request) {
+ this.client.checkApiVersion("0.58", "multi node shortest path");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(PathsWithVertices.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/NeighborRankAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/NeighborRankAPI.java
new file mode 100644
index 0000000..41a4a04
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/NeighborRankAPI.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.structure.traverser.Ranks;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class NeighborRankAPI extends TraversersAPI {
+
+ public NeighborRankAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "neighborrank";
+ }
+
+ public List<Ranks> post(Request request) {
+ RestResult result = this.client.post(this.path(), request);
+ return result.readList("ranks", Ranks.class);
+ }
+
+ public static class Request {
+
+ @JsonProperty("source")
+ private Object source;
+ @JsonProperty("steps")
+ private List<Step> steps;
+ @JsonProperty("alpha")
+ private double alpha;
+ @JsonProperty("capacity")
+ private long capacity;
+
+ private Request() {
+ this.source = null;
+ this.steps = new ArrayList<>();
+ this.alpha = Traverser.DEFAULT_ALPHA;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Request{source=%s,steps=%s,alpha=%s," +
+ "capacity=%s}", this.source, this.steps,
+ this.alpha, this.capacity);
+ }
+
+ public static class Builder {
+
+ private Request request;
+ private List<Step.Builder> stepBuilders;
+
+ private Builder() {
+ this.request = new Request();
+ this.stepBuilders = new ArrayList<>();
+ }
+
+ public Builder source(Object source) {
+ E.checkArgument(source != null, "The label of request " +
+ "for neighbor rank can't be null");
+ this.request.source = source;
+ return this;
+ }
+
+ public Step.Builder steps() {
+ Step.Builder builder = new Step.Builder();
+ this.stepBuilders.add(builder);
+ return builder;
+ }
+
+ public Builder alpha(double alpha) {
+ TraversersAPI.checkAlpha(alpha);
+ this.request.alpha = alpha;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Request build() {
+ for (Step.Builder builder : this.stepBuilders) {
+ this.request.steps.add(builder.build());
+ }
+ E.checkArgument(this.request.source != null,
+ "Source vertex can't be null");
+ E.checkArgument(this.request.steps != null &&
+ !this.request.steps.isEmpty(),
+ "Steps can't be null or empty");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkAlpha(this.request.alpha);
+ return this.request;
+ }
+ }
+
+ public static class Step {
+
+ @JsonProperty("direction")
+ private String direction;
+ @JsonProperty("labels")
+ private List<String> labels;
+ @JsonProperty("degree")
+ private long degree;
+ @JsonProperty("top")
+ private int top;
+
+ private Step() {
+ this.direction = null;
+ this.labels = new ArrayList<>();
+ this.degree = Traverser.DEFAULT_MAX_DEGREE;
+ this.top = (int) Traverser.DEFAULT_PATHS_LIMIT;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Step{direction=%s,labels=%s,degree=%s," +
+ "top=%s}", this.direction, this.labels,
+ this.degree, this.top);
+ }
+
+ public static class Builder {
+
+ private Step step;
+
+ private Builder() {
+ this.step = new Step();
+ }
+
+ public Step.Builder direction(Direction direction) {
+ this.step.direction = direction.toString();
+ return this;
+ }
+
+ public Step.Builder labels(List<String> labels) {
+ this.step.labels.addAll(labels);
+ return this;
+ }
+
+ public Step.Builder labels(String... labels) {
+ this.step.labels.addAll(Arrays.asList(labels));
+ return this;
+ }
+
+ public Step.Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.step.degree = degree;
+ return this;
+ }
+
+ public Step.Builder top(int top) {
+ E.checkArgument(top > 0 && top <= Traverser.DEFAULT_MAX_TOP,
+ "The top of each layer can't exceed %s",
+ Traverser.DEFAULT_MAX_TOP);
+ this.step.top = top;
+ return this;
+ }
+
+ private Step build() {
+ TraversersAPI.checkDegree(this.step.degree);
+ E.checkArgument(this.step.top > 0 &&
+ this.step.top <= Traverser.DEFAULT_MAX_TOP,
+ "The top of each layer can't exceed %s",
+ Traverser.DEFAULT_MAX_TOP);
+ return this.step;
+ }
+ }
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PathsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PathsAPI.java
new file mode 100644
index 0000000..5d35133
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PathsAPI.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.traverser.PathsRequest;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+
+public class PathsAPI extends TraversersAPI {
+
+ public PathsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "paths";
+ }
+
+ public List<Path> get(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long degree, long capacity,
+ long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+ String target = GraphAPI.formatVertexId(targetId, false);
+
+ checkPositive(maxDepth, "Max depth of path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("target", target);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", maxDepth);
+ params.put("max_degree", degree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList("paths", Path.class);
+ }
+
+ public PathsWithVertices post(PathsRequest request) {
+ this.client.checkApiVersion("0.58", "paths with property filter");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(PathsWithVertices.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PersonalRankAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PersonalRankAPI.java
new file mode 100644
index 0000000..43112b6
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/PersonalRankAPI.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.structure.traverser.Ranks;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PersonalRankAPI extends TraversersAPI {
+
+ public PersonalRankAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "personalrank";
+ }
+
+ public Ranks post(Request request) {
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(Ranks.class);
+ }
+
+ public static class Request {
+
+ @JsonProperty("source")
+ private Object source;
+ @JsonProperty("label")
+ private String label;
+ @JsonProperty("alpha")
+ private double alpha = Traverser.DEFAULT_ALPHA;
+ @JsonProperty("max_degree")
+ public long degree = Traverser.DEFAULT_MAX_DEGREE;
+ @JsonProperty("limit")
+ private long limit = Traverser.DEFAULT_LIMIT;
+ @JsonProperty("max_depth")
+ private int maxDepth = 5;
+ @JsonProperty("with_label")
+ private WithLabel withLabel = WithLabel.BOTH_LABEL;
+ @JsonProperty("sorted")
+ private boolean sorted = true;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Request{source=%s,label=%s,alpha=%s," +
+ "degree=%s,limit=%s,maxDepth=%s," +
+ "withLabel=%s,sorted=%s}",
+ this.source, this.label, this.alpha,
+ this.degree, this.limit, this.maxDepth,
+ this.withLabel, this.sorted);
+ }
+
+ public enum WithLabel {
+ SAME_LABEL,
+ OTHER_LABEL,
+ BOTH_LABEL
+ }
+
+ public static class Builder {
+
+ private Request request;
+
+ private Builder() {
+ this.request = new Request();
+ }
+
+ public Builder source(Object source) {
+ E.checkArgument(source != null, "The source of request " +
+ "for personal rank can't be null");
+ this.request.source = source;
+ return this;
+ }
+
+ public Builder label(String label) {
+ E.checkArgument(label != null, "The label of request " +
+ "for personal rank can't be null");
+ this.request.label = label;
+ return this;
+ }
+
+ public Builder alpha(double alpha) {
+ TraversersAPI.checkAlpha(alpha);
+ this.request.alpha = alpha;
+ return this;
+ }
+
+ public Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.request.degree = degree;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder maxDepth(int maxDepth) {
+ E.checkArgument(maxDepth > 0 &&
+ maxDepth <= Traverser.DEFAULT_MAX_DEPTH,
+ "The max depth must be in range (0, %s], " +
+ "but got: %s",
+ Traverser.DEFAULT_MAX_DEPTH, maxDepth);
+ this.request.maxDepth = maxDepth;
+ return this;
+ }
+
+ public Builder withLabel(WithLabel withLabel) {
+ this.request.withLabel = withLabel;
+ return this;
+ }
+
+ public Builder sorted(boolean sorted) {
+ this.request.sorted = sorted;
+ return this;
+ }
+
+ public Request build() {
+ E.checkArgument(this.request.source != null,
+ "Source vertex can't be null");
+ E.checkArgument(this.request.label != null,
+ "The label of rank request " +
+ "for personal rank can't be null");
+ TraversersAPI.checkAlpha(this.request.alpha);
+ TraversersAPI.checkDegree(this.request.degree);
+ TraversersAPI.checkLimit(this.request.limit);
+ E.checkArgument(this.request.maxDepth > 0 &&
+ this.request.maxDepth <=
+ Traverser.DEFAULT_MAX_DEPTH,
+ "The max depth must be in range (0, %s], " +
+ "but got: %s",
+ Traverser.DEFAULT_MAX_DEPTH,
+ this.request.maxDepth);
+ return this.request;
+ }
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RaysAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RaysAPI.java
new file mode 100644
index 0000000..a4fd9a4
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RaysAPI.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+
+public class RaysAPI extends TraversersAPI {
+
+ public RaysAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "rays";
+ }
+
+ public List<Path> get(Object sourceId, Direction direction, String label,
+ int depth, long degree, long capacity, long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+
+ checkPositive(depth, "Max depth of path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", depth);
+ params.put("max_degree", degree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Path.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RingsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RingsAPI.java
new file mode 100644
index 0000000..03bb1e9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/RingsAPI.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+
+public class RingsAPI extends TraversersAPI {
+
+ public RingsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "rings";
+ }
+
+ public List<Path> get(Object sourceId, Direction direction, String label,
+ int depth, boolean sourceInRing, long degree,
+ long capacity, long limit) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+
+ checkPositive(depth, "Max depth of path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkLimit(limit);
+
+ if (sourceInRing) {
+ this.client.checkApiVersion("0.40",
+ "source_in_ring arg of ring API");
+ }
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", depth);
+ params.put("source_in_ring", sourceInRing);
+ params.put("max_degree", degree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Path.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SameNeighborsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SameNeighborsAPI.java
new file mode 100644
index 0000000..928f16c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SameNeighborsAPI.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+
+public class SameNeighborsAPI extends TraversersAPI {
+
+ private static final String SAME_NEIGHBORS = "same_neighbors";
+
+ public SameNeighborsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "sameneighbors";
+ }
+
+ public List<Object> get(Object vertexId, Object otherId,
+ Direction direction, String label,
+ long degree, long limit) {
+ this.client.checkApiVersion("0.51", "same neighbors");
+ String vertex = GraphAPI.formatVertexId(vertexId, false);
+ String other = GraphAPI.formatVertexId(otherId, false);
+ checkDegree(degree);
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("vertex", vertex);
+ params.put("other", other);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_degree", degree);
+ params.put("limit", limit);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(SAME_NEIGHBORS, Object.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/ShortestPathAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/ShortestPathAPI.java
new file mode 100644
index 0000000..389b108
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/ShortestPathAPI.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+
+public class ShortestPathAPI extends TraversersAPI {
+
+ public ShortestPathAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "shortestpath";
+ }
+
+ public Path get(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth,
+ long degree, long skipDegree, long capacity) {
+ String source = GraphAPI.formatVertexId(sourceId, false);
+ String target = GraphAPI.formatVertexId(targetId, false);
+
+ checkPositive(maxDepth, "Max depth of shortest path");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkSkipDegree(skipDegree, degree, capacity);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("target", target);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("max_depth", maxDepth);
+ params.put("max_degree", degree);
+ params.put("skip_degree", skipDegree);
+ params.put("capacity", capacity);
+ RestResult result = this.client.get(this.path(), params);
+ List<Object> vertices = result.readList("path", Object.class);
+ return new Path(vertices);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathAPI.java
new file mode 100644
index 0000000..463ae34
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathAPI.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.traverser.WeightedPaths;
+import com.baidu.hugegraph.util.E;
+
+public class SingleSourceShortestPathAPI extends TraversersAPI {
+
+ public SingleSourceShortestPathAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "singlesourceshortestpath";
+ }
+
+ public WeightedPaths get(Object sourceId, Direction direction, String label,
+ String weight, long degree, long skipDegree,
+ long capacity, long limit, boolean withVertex) {
+ this.client.checkApiVersion("0.51", "single source shortest path");
+ String source = GraphAPI.formatVertexId(sourceId, false);
+
+ E.checkNotNull(weight, "weight");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkSkipDegree(skipDegree, degree, capacity);
+ checkLimit(limit);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("weight", weight);
+ params.put("max_degree", degree);
+ params.put("skip_degree", skipDegree);
+ params.put("capacity", capacity);
+ params.put("limit", limit);
+ params.put("with_vertex", withVertex);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readObject(WeightedPaths.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TemplatePathsAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TemplatePathsAPI.java
new file mode 100644
index 0000000..a52233a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TemplatePathsAPI.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.structure.traverser.TemplatePathsRequest;
+
+public class TemplatePathsAPI extends TraversersAPI {
+
+ public TemplatePathsAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "templatepaths";
+ }
+
+ public PathsWithVertices post(TemplatePathsRequest request) {
+ this.client.checkApiVersion("0.58", "template paths");
+ RestResult result = this.client.post(this.path(), request);
+ return result.readObject(PathsWithVertices.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TraversersAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TraversersAPI.java
new file mode 100644
index 0000000..93d5735
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/TraversersAPI.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.util.E;
+
+public class TraversersAPI extends API {
+
+ private static final String PATH = "graphs/%s/traversers/%s";
+
+ public TraversersAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ }
+
+ @Override
+ protected String type() {
+ return "traversers";
+ }
+
+ public static void checkPositive(int value, String name) {
+ E.checkArgument(value > 0,
+ "%s must be > 0, but got '%s'", name, value);
+ }
+
+ public static void checkDegree(long degree) {
+ checkLimit(degree, "Degree");
+ }
+
+ public static void checkCapacity(long capacity) {
+ checkLimit(capacity, "Capacity");
+ }
+
+ public static void checkLimit(long limit) {
+ checkLimit(limit, "Limit");
+ }
+
+ public static void checkAlpha(double alpha) {
+ E.checkArgument(alpha > 0 && alpha <= 1.0,
+ "The alpha of rank request must be in range (0, 1], " +
+ "but got '%s'", alpha);
+ }
+
+ public static void checkSkipDegree(long skipDegree, long degree,
+ long capacity) {
+ E.checkArgument(skipDegree >= 0L,
+ "The skipped degree must be >= 0, but got '%s'",
+ skipDegree);
+ if (capacity != NO_LIMIT) {
+ E.checkArgument(degree != NO_LIMIT && degree < capacity,
+ "The max degree must be < capacity");
+ E.checkArgument(skipDegree < capacity,
+ "The skipped degree must be < capacity");
+ }
+ if (skipDegree > 0L) {
+ E.checkArgument(degree != NO_LIMIT && skipDegree >= degree,
+ "The skipped degree must be >= max degree, " +
+ "but got skipped degree '%s' and max degree '%s'",
+ skipDegree, degree);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/VerticesAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/VerticesAPI.java
new file mode 100644
index 0000000..5a85865
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/VerticesAPI.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.graph.Shard;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.graph.Vertices;
+import com.baidu.hugegraph.util.E;
+import com.google.common.collect.ImmutableMap;
+
+public class VerticesAPI extends TraversersAPI {
+
+ public VerticesAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "vertices";
+ }
+
+ public List<Vertex> list(List<Object> ids) {
+ E.checkArgument(ids != null && !ids.isEmpty(),
+ "Ids can't be null or empty");
+
+ List<String> stringIds = new ArrayList<>(ids.size());
+ for (Object id : ids) {
+ stringIds.add(GraphAPI.formatVertexId(id, false));
+ }
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("ids", stringIds);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readList(this.type(), Vertex.class);
+ }
+
+ public List<Shard> shards(long splitSize) {
+ String path = String.join(PATH_SPLITOR, this.path(), "shards");
+ Map<String, Object> params = ImmutableMap.of("split_size", splitSize);
+ RestResult result = this.client.get(path, params);
+ return result.readList("shards", Shard.class);
+ }
+
+ public Vertices scan(Shard shard, String page, long pageLimit) {
+ E.checkArgument(shard != null, "Shard can't be null");
+ String path = String.join(PATH_SPLITOR, this.path(), "scan");
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("start", shard.start());
+ params.put("end", shard.end());
+ params.put("page", page);
+ params.put("page_limit", pageLimit);
+ RestResult result = this.client.get(path, params);
+ return result.readObject(Vertices.class);
+ }
+}
+
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathAPI.java
new file mode 100644
index 0000000..70de048
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathAPI.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graph.GraphAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.traverser.WeightedPath;
+import com.baidu.hugegraph.util.E;
+
+public class WeightedShortestPathAPI extends TraversersAPI {
+
+ public WeightedShortestPathAPI(RestClient client, String graph) {
+ super(client, graph);
+ }
+
+ @Override
+ protected String type() {
+ return "weightedshortestpath";
+ }
+
+ public WeightedPath get(Object sourceId, Object targetId,
+ Direction direction, String label,
+ String weight, long degree, long skipDegree,
+ long capacity, boolean withVertex) {
+ this.client.checkApiVersion("0.51", "weighted shortest path");
+ String source = GraphAPI.formatVertexId(sourceId, false);
+ String target = GraphAPI.formatVertexId(targetId, false);
+
+ E.checkNotNull(weight, "weight");
+ checkDegree(degree);
+ checkCapacity(capacity);
+ checkSkipDegree(skipDegree, degree, capacity);
+
+ Map<String, Object> params = new LinkedHashMap<>();
+ params.put("source", source);
+ params.put("target", target);
+ params.put("direction", direction);
+ params.put("label", label);
+ params.put("weight", weight);
+ params.put("max_degree", degree);
+ params.put("skip_degree", skipDegree);
+ params.put("capacity", capacity);
+ params.put("with_vertex", withVertex);
+ RestResult result = this.client.get(this.path(), params);
+ return result.readObject(WeightedPath.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/variables/VariablesAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/variables/VariablesAPI.java
new file mode 100644
index 0000000..521a93a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/variables/VariablesAPI.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.variables;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.google.common.collect.ImmutableMap;
+
+public class VariablesAPI extends API {
+
+ private static final String PATH = "graphs/%s/%s";
+
+ public VariablesAPI(RestClient client, String graph) {
+ super(client);
+ this.path(PATH, graph, this.type());
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.VARIABLES.string();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> get(String key) {
+ RestResult result = this.client.get(path(), key);
+ return result.readObject(Map.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> set(String key, Object value) {
+ value = ImmutableMap.of("data", value);
+ RestResult result = this.client.put(this.path(), key, value);
+ return result.readObject(Map.class);
+ }
+
+ public void remove(String key) {
+ this.client.delete(path(), key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, Object> all() {
+ RestResult result = this.client.get(path());
+ return result.readObject(Map.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/api/version/VersionAPI.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/version/VersionAPI.java
new file mode 100644
index 0000000..31fa5c3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/api/version/VersionAPI.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.version;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.version.Versions;
+
+public class VersionAPI extends API {
+
+ public VersionAPI(RestClient client) {
+ super(client);
+ this.path(this.type());
+ }
+
+ @Override
+ protected String type() {
+ return HugeType.VERSION.string();
+ }
+
+ public Versions get() {
+ RestResult result = this.client.get(this.path());
+ return result.readObject(Versions.class);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/client/RestClient.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/client/RestClient.java
new file mode 100644
index 0000000..f29c267
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/client/RestClient.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.client;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.rest.AbstractRestClient;
+import com.baidu.hugegraph.rest.ClientException;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.serializer.PathDeserializer;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.util.E;
+import com.baidu.hugegraph.util.VersionUtil;
+import com.baidu.hugegraph.util.VersionUtil.Version;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import jakarta.ws.rs.core.Response;
+
+public class RestClient extends AbstractRestClient {
+
+ private static final int SECOND = 1000;
+
+ private Version apiVersion = null;
+
+ static {
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(Path.class, new PathDeserializer());
+ RestResult.registerModule(module);
+ }
+
+ public RestClient(String url, String username, String password,
+ int timeout) {
+ super(url, username, password, timeout * SECOND);
+ }
+
+ public RestClient(String url, String username, String password, int timeout,
+ int maxConns, int maxConnsPerRoute,
+ String trustStoreFile, String trustStorePassword) {
+ super(url, username, password, timeout * SECOND, maxConns,
+ maxConnsPerRoute, trustStoreFile, trustStorePassword);
+ }
+
+ public void apiVersion(Version version) {
+ E.checkNotNull(version, "api version");
+ this.apiVersion = version;
+ }
+
+ public Version apiVersion() {
+ return this.apiVersion;
+ }
+
+ public void checkApiVersion(String minVersion, String message) {
+ if (this.apiVersionLt(minVersion)) {
+ throw new ClientException(
+ "HugeGraphServer API version must be >= %s to support " +
+ "%s, but current HugeGraphServer API version is: %s",
+ minVersion, message, this.apiVersion.get());
+ }
+ }
+
+ public boolean apiVersionLt(String minVersion) {
+ String apiVersion = this.apiVersion == null ?
+ null : this.apiVersion.get();
+ return apiVersion != null && !VersionUtil.gte(apiVersion, minVersion);
+ }
+
+ @Override
+ protected void checkStatus(Response response, Response.Status... statuses) {
+ boolean match = false;
+ for (Response.Status status : statuses) {
+ if (status.getStatusCode() == response.getStatus()) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ throw ServerException.fromResponse(response);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/AuthManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/AuthManager.java
new file mode 100644
index 0000000..356d1a4
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/AuthManager.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+
+import com.baidu.hugegraph.api.auth.AccessAPI;
+import com.baidu.hugegraph.api.auth.BelongAPI;
+import com.baidu.hugegraph.api.auth.GroupAPI;
+import com.baidu.hugegraph.api.auth.LoginAPI;
+import com.baidu.hugegraph.api.auth.LogoutAPI;
+import com.baidu.hugegraph.api.auth.ProjectAPI;
+import com.baidu.hugegraph.api.auth.TargetAPI;
+import com.baidu.hugegraph.api.auth.TokenAPI;
+import com.baidu.hugegraph.api.auth.UserAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.auth.Access;
+import com.baidu.hugegraph.structure.auth.Belong;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.auth.Project;
+import com.baidu.hugegraph.structure.auth.Target;
+import com.baidu.hugegraph.structure.auth.TokenPayload;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.structure.auth.User.UserRole;
+
+public class AuthManager {
+
+ private final TargetAPI targetAPI;
+ private final GroupAPI groupAPI;
+ private final UserAPI userAPI;
+ private final AccessAPI accessAPI;
+ private final BelongAPI belongAPI;
+ private final ProjectAPI projectAPI;
+ private final LoginAPI loginAPI;
+ private final LogoutAPI logoutAPI;
+ private final TokenAPI tokenAPI;
+
+ public AuthManager(RestClient client, String graph) {
+ this.targetAPI = new TargetAPI(client, graph);
+ this.groupAPI = new GroupAPI(client, graph);
+ this.userAPI = new UserAPI(client, graph);
+ this.accessAPI = new AccessAPI(client, graph);
+ this.belongAPI = new BelongAPI(client, graph);
+ this.projectAPI = new ProjectAPI(client, graph);
+ this.loginAPI = new LoginAPI(client, graph);
+ this.logoutAPI = new LogoutAPI(client, graph);
+ this.tokenAPI = new TokenAPI(client, graph);
+ }
+
+ public List<Target> listTargets() {
+ return this.listTargets(-1);
+ }
+
+ public List<Target> listTargets(int limit) {
+ return this.targetAPI.list(limit);
+ }
+
+ public Target getTarget(Object id) {
+ return this.targetAPI.get(id);
+ }
+
+ public Target createTarget(Target target) {
+ return this.targetAPI.create(target);
+ }
+
+ public Target updateTarget(Target target) {
+ return this.targetAPI.update(target);
+ }
+
+ public void deleteTarget(Object id) {
+ this.targetAPI.delete(id);
+ }
+
+ public List<Group> listGroups() {
+ return this.listGroups(-1);
+ }
+
+ public List<Group> listGroups(int limit) {
+ return this.groupAPI.list(limit);
+ }
+
+ public Group getGroup(Object id) {
+ return this.groupAPI.get(id);
+ }
+
+ public Group createGroup(Group group) {
+ return this.groupAPI.create(group);
+ }
+
+ public Group updateGroup(Group group) {
+ return this.groupAPI.update(group);
+ }
+
+ public void deleteGroup(Object id) {
+ this.groupAPI.delete(id);
+ }
+
+ public List<User> listUsers() {
+ return this.listUsers(-1);
+ }
+
+ public List<User> listUsers(int limit) {
+ return this.userAPI.list(limit);
+ }
+
+ public User getUser(Object id) {
+ return this.userAPI.get(id);
+ }
+
+ public UserRole getUserRole(Object id) {
+ return this.userAPI.getUserRole(id);
+ }
+
+ public User createUser(User user) {
+ return this.userAPI.create(user);
+ }
+
+ public User updateUser(User user) {
+ return this.userAPI.update(user);
+ }
+
+ public void deleteUser(Object id) {
+ this.userAPI.delete(id);
+ }
+
+ public List<Access> listAccesses() {
+ return this.listAccesses(-1);
+ }
+
+ public List<Access> listAccesses(int limit) {
+ return this.accessAPI.list(null, null, limit);
+ }
+
+ public List<Access> listAccessesByGroup(Object group, int limit) {
+ return this.accessAPI.list(group, null, limit);
+ }
+
+ public List<Access> listAccessesByTarget(Object target, int limit) {
+ return this.accessAPI.list(null, target, limit);
+ }
+
+ public Access getAccess(Object id) {
+ return this.accessAPI.get(id);
+ }
+
+ public Access createAccess(Access access) {
+ return this.accessAPI.create(access);
+ }
+
+ public Access updateAccess(Access access) {
+ return this.accessAPI.update(access);
+ }
+
+ public void deleteAccess(Object id) {
+ this.accessAPI.delete(id);
+ }
+
+ public List<Belong> listBelongs() {
+ return this.listBelongs(-1);
+ }
+
+ public List<Belong> listBelongs(int limit) {
+ return this.belongAPI.list(null, null, limit);
+ }
+
+ public List<Belong> listBelongsByUser(Object user, int limit) {
+ return this.belongAPI.list(user, null, limit);
+ }
+
+ public List<Belong> listBelongsByGroup(Object group, int limit) {
+ return this.belongAPI.list(null, group, limit);
+ }
+
+ public Belong getBelong(Object id) {
+ return this.belongAPI.get(id);
+ }
+
+ public Belong createBelong(Belong belong) {
+ return this.belongAPI.create(belong);
+ }
+
+ public Belong updateBelong(Belong belong) {
+ return this.belongAPI.update(belong);
+ }
+
+ public void deleteBelong(Object id) {
+ this.belongAPI.delete(id);
+ }
+
+ public void deleteAll() {
+ for (Belong belong : this.listBelongs()) {
+ this.deleteBelong(belong.id());
+ }
+ for (Access access : this.listAccesses()) {
+ this.deleteAccess(access.id());
+ }
+
+ for (User user : this.listUsers()) {
+ if (user.name().equals("admin")) {
+ continue;
+ }
+ this.deleteUser(user.id());
+ }
+ for (Group group : this.listGroups()) {
+ this.deleteGroup(group.id());
+ }
+ for (Target target : this.listTargets()) {
+ this.deleteTarget(target.id());
+ }
+ for (Project project : this.listProjects()) {
+ Set<String> graphs = project.graphs();
+ if (CollectionUtils.isNotEmpty(graphs)) {
+ this.projectRemoveGraphs(project.id(), graphs);
+ }
+ this.deleteProject(project.id());
+ }
+ }
+
+ public Project createProject(Project project) {
+ return this.projectAPI.create(project);
+ }
+
+ public Project getProject(Object id) {
+ return this.projectAPI.get(id);
+ }
+
+ public List<Project> listProjects() {
+ return this.listProject(-1);
+ }
+
+ public List<Project> listProject(int limit) {
+ return this.projectAPI.list(limit);
+ }
+
+ public Project updateProject(Project project) {
+ return this.projectAPI.update(project);
+ }
+
+ public Project projectAddGraphs(Object projectId, Set<String> graphs) {
+ return this.projectAPI.addGraphs(projectId, graphs);
+ }
+
+ public Project projectRemoveGraphs(Object projectId, Set<String> graphs) {
+ return this.projectAPI.removeGraphs(projectId, graphs);
+ }
+
+ public void deleteProject(Object id) {
+ this.projectAPI.delete(id);
+ }
+
+ public LoginResult login(Login login) {
+ return this.loginAPI.login(login);
+ }
+
+ public void logout() {
+ this.logoutAPI.logout();
+ }
+
+ public TokenPayload verifyToken() {
+ return this.tokenAPI.verifyToken();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphManager.java
new file mode 100644
index 0000000..48350b3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphManager.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.annotation.UnimplementedFeature;
+import com.baidu.hugegraph.api.graph.EdgeAPI;
+import com.baidu.hugegraph.api.graph.VertexAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.BatchEdgeRequest;
+import com.baidu.hugegraph.structure.graph.BatchOlapPropertyRequest;
+import com.baidu.hugegraph.structure.graph.BatchVertexRequest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.GraphIterator;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.util.E;
+
+public class GraphManager {
+
+ private final String graph;
+ private final VertexAPI vertexAPI;
+ private final EdgeAPI edgeAPI;
+
+ public GraphManager(RestClient client, String graph) {
+ this.graph = graph;
+ this.vertexAPI = new VertexAPI(client, graph);
+ this.edgeAPI = new EdgeAPI(client, graph);
+ }
+
+ public String graph() {
+ return this.graph;
+ }
+
+ public Vertex addVertex(Vertex vertex) {
+ vertex = this.vertexAPI.create(vertex);
+ this.attachManager(vertex);
+ return vertex;
+ }
+
+ public Vertex addVertex(Object... keyValues) {
+ Object label = this.getValue(T.label, keyValues);
+ if (!(label instanceof String)) {
+ throw new IllegalArgumentException(String.format(
+ "Expect a string value as the vertex label " +
+ "argument, but got: %s", label));
+ }
+ Vertex vertex = new Vertex(String.valueOf(label));
+ vertex.id(this.getValue(T.id, keyValues));
+ this.attachProperties(vertex, keyValues);
+ return this.addVertex(vertex);
+ }
+
+ public Vertex addVertex(String label, Map<String, Object> properties) {
+ return this.addVertex(label, null, properties);
+ }
+
+ public Vertex addVertex(String label, Object id,
+ Map<String, Object> properties) {
+ Vertex vertex = new Vertex(label);
+ vertex.id(id);
+ this.attachProperties(vertex, properties);
+ return this.addVertex(vertex);
+ }
+
+ public Vertex getVertex(Object vertexId) {
+ Vertex vertex = this.vertexAPI.get(vertexId);
+ this.attachManager(vertex);
+ return vertex;
+ }
+
+ public List<Vertex> addVertices(List<Vertex> vertices) {
+ List<Object> ids = this.vertexAPI.create(vertices);
+ for (int i = 0; i < vertices.size(); i++) {
+ Vertex vertex = vertices.get(i);
+ vertex.id(ids.get(i));
+ this.attachManager(vertex);
+ }
+ return vertices;
+ }
+
+ public List<Vertex> listVertices() {
+ return this.listVertices(-1);
+ }
+
+ public List<Vertex> listVertices(int limit) {
+ return this.listVertices(null, null, false, 0, limit);
+ }
+
+ public List<Vertex> listVertices(int offset, int limit) {
+ return this.listVertices(null, null, false, offset, limit);
+ }
+
+ public List<Vertex> listVertices(String label) {
+ return this.listVertices(label, null, false, 0, -1);
+ }
+
+ public List<Vertex> listVertices(String label, int limit) {
+ return this.listVertices(label, null, false, 0, limit);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties) {
+ return this.listVertices(label, properties, false, 0, -1);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties,
+ int limit) {
+ return this.listVertices(label, properties, false, 0, limit);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties,
+ boolean keepP) {
+ return this.listVertices(label, properties, keepP, 0, -1);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties,
+ boolean keepP,
+ int limit) {
+ return this.listVertices(label, properties, keepP, 0, limit);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties,
+ int offset,
+ int limit) {
+ return this.listVertices(label, properties, false, offset, limit);
+ }
+
+ public List<Vertex> listVertices(String label,
+ Map<String, Object> properties,
+ boolean keepP,
+ int offset,
+ int limit) {
+ List<Vertex> vertices = this.vertexAPI.list(label, properties, keepP,
+ offset, null, limit)
+ .results();
+ for (Vertex vertex : vertices) {
+ this.attachManager(vertex);
+ }
+ return vertices;
+ }
+
+ public Iterator<Vertex> iterateVertices(int sizePerPage) {
+ return this.iterateVertices(null, null, sizePerPage);
+ }
+
+ public Iterator<Vertex> iterateVertices(String label, int sizePerPage) {
+ return this.iterateVertices(label, null, sizePerPage);
+ }
+
+ public Iterator<Vertex> iterateVertices(String label,
+ Map<String, Object> properties,
+ int sizePerPage) {
+ return new GraphIterator<>(this, sizePerPage, (page) -> {
+ return this.vertexAPI.list(label, properties, 0, page, sizePerPage);
+ });
+ }
+
+ public void removeVertex(Object vertexId) {
+ this.vertexAPI.delete(vertexId);
+ }
+
+ public List<Vertex> updateVertices(BatchVertexRequest request) {
+ List<Vertex> newVertices = this.vertexAPI.update(request);
+ newVertices.forEach(vertex -> this.attachManager(vertex));
+ return newVertices;
+ }
+
+ public int updateVertices(BatchOlapPropertyRequest request) {
+ return this.vertexAPI.update(request);
+ }
+
+ public Vertex appendVertexProperty(Vertex vertex) {
+ vertex = this.vertexAPI.append(vertex);
+ this.attachManager(vertex);
+ return vertex;
+ }
+
+ public Vertex eliminateVertexProperty(Vertex vertex) {
+ vertex = this.vertexAPI.eliminate(vertex);
+ this.attachManager(vertex);
+ return vertex;
+ }
+
+ public Edge addEdge(Edge edge) {
+ if (edge.id() != null) {
+ throw new InvalidOperationException(
+ "Not allowed to custom id for edge: '%s'", edge);
+ }
+ edge = this.edgeAPI.create(edge);
+ this.attachManager(edge);
+ return edge;
+ }
+
+ public Edge addEdge(Vertex source, String label, Vertex target,
+ Object... properties) {
+ return this.addEdge(source.id(), label, target.id(), properties);
+ }
+
+ public Edge addEdge(Object sourceId, String label, Object targetId,
+ Object... properties) {
+ Edge edge = new Edge(label);
+ edge.sourceId(sourceId);
+ edge.targetId(targetId);
+ this.attachProperties(edge, properties);
+ return this.addEdge(edge);
+ }
+
+ public Edge addEdge(Vertex source, String label, Vertex target,
+ Map<String, Object> properties) {
+ return this.addEdge(source.id(), label, target.id(), properties);
+ }
+
+ public Edge addEdge(Object sourceId, String label, Object targetId,
+ Map<String, Object> properties) {
+ Edge edge = new Edge(label);
+ edge.sourceId(sourceId);
+ edge.targetId(targetId);
+ this.attachProperties(edge, properties);
+ return this.addEdge(edge);
+ }
+
+ public Edge getEdge(String edgeId) {
+ Edge edge = this.edgeAPI.get(edgeId);
+ this.attachManager(edge);
+ return edge;
+ }
+
+ public List<Edge> addEdges(List<Edge> edges) {
+ return this.addEdges(edges, true);
+ }
+
+ public List<Edge> addEdges(List<Edge> edges, boolean checkVertex) {
+ for (Edge edge : edges) {
+ edge.sourceId();
+ edge.targetId();
+ }
+ List<String> ids = this.edgeAPI.create(edges, checkVertex);
+ for (int i = 0; i < edges.size(); i++) {
+ Edge edge = edges.get(i);
+ edge.id(ids.get(i));
+ this.attachManager(edge);
+ }
+ return edges;
+ }
+
+ public List<Edge> listEdges() {
+ return this.listEdges(-1);
+ }
+
+ public List<Edge> listEdges(int limit) {
+ return this.getEdges(null, null, null, null, 0, limit);
+ }
+
+ public List<Edge> listEdges(int offset, int limit) {
+ return this.getEdges(null, null, null, null, offset, limit);
+ }
+
+ public List<Edge> listEdges(String label) {
+ return this.getEdges(null, null, label, null, 0, -1);
+ }
+
+ public List<Edge> listEdges(String label, int limit) {
+ return this.getEdges(null, null, label, null, 0, limit);
+ }
+
+ public List<Edge> listEdges(String label,
+ Map<String, Object> properties) {
+ return this.getEdges(null, null, label, properties, 0, -1);
+ }
+
+ public List<Edge> listEdges(String label,
+ Map<String, Object> properties,
+ int limit) {
+ return this.getEdges(null, null, label, properties, 0, limit);
+ }
+
+ public List<Edge> listEdges(String label,
+ Map<String, Object> properties,
+ boolean keepP) {
+ return this.getEdges(null, null, label, properties, keepP, 0, -1);
+ }
+
+ public List<Edge> listEdges(String label,
+ Map<String, Object> properties,
+ boolean keepP,
+ int limit) {
+ return this.getEdges(null, null, label, properties, keepP, 0, limit);
+ }
+
+ public List<Edge> getEdges(Object vertexId) {
+ return this.getEdges(vertexId, Direction.BOTH, null, null, 0, -1);
+ }
+
+ public List<Edge> getEdges(Object vertexId, int limit) {
+ return this.getEdges(vertexId, Direction.BOTH, null, null, 0, limit);
+ }
+
+ public List<Edge> getEdges(Object vertexId, Direction direction) {
+ return this.getEdges(vertexId, direction, null, null, 0, -1);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ int limit) {
+ return this.getEdges(vertexId, direction, null, null, 0, limit);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ String label) {
+ return this.getEdges(vertexId, direction, label, null, 0, -1);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ String label,
+ int limit) {
+ return this.getEdges(vertexId, direction, label, null, 0, limit);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ String label,
+ Map<String, Object> properties) {
+ return this.getEdges(vertexId, direction, label, properties, 0, -1);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ String label,
+ Map<String, Object> properties,
+ int offset,
+ int limit) {
+ return this.getEdges(vertexId, direction, label, properties, false,
+ offset, limit);
+ }
+
+ public List<Edge> getEdges(Object vertexId,
+ Direction direction,
+ String label,
+ Map<String, Object> properties,
+ boolean keepP,
+ int offset,
+ int limit) {
+ List<Edge> edges = this.edgeAPI.list(vertexId, direction, label,
+ properties, keepP,
+ offset, null, limit)
+ .results();
+ for (Edge edge : edges) {
+ this.attachManager(edge);
+ }
+ return edges;
+ }
+
+ public Iterator<Edge> iterateEdges(int sizePerPage) {
+ return this.iterateEdges(null, (Map<String, Object>) null, sizePerPage);
+ }
+
+ @UnimplementedFeature(desc = "Server doesn't support paging by label")
+ public Iterator<Edge> iterateEdges(String label, int sizePerPage) {
+ return this.iterateEdges(label, (Map<String, Object>) null, sizePerPage);
+ }
+
+ @UnimplementedFeature(desc = "Server doesn't support paging by label and properties")
+ public Iterator<Edge> iterateEdges(String label,
+ Map<String, Object> properties,
+ int sizePerPage) {
+ return new GraphIterator<>(this, sizePerPage, (page) -> {
+ return this.edgeAPI.list(null, null, label, properties,
+ 0, page, sizePerPage);
+ });
+ }
+
+ public Iterator<Edge> iterateEdges(Object vertexId, int sizePerPage) {
+ return this.iterateEdges(vertexId, Direction.BOTH, null, null,
+ sizePerPage);
+ }
+
+ public Iterator<Edge> iterateEdges(Object vertexId,
+ Direction direction,
+ int sizePerPage) {
+ return this.iterateEdges(vertexId, direction, null, null, sizePerPage);
+ }
+
+ public Iterator<Edge> iterateEdges(Object vertexId,
+ Direction direction,
+ String label,
+ int sizePerPage) {
+ return this.iterateEdges(vertexId, direction, label, null, sizePerPage);
+ }
+
+ public Iterator<Edge> iterateEdges(Object vertexId,
+ Direction direction,
+ String label,
+ Map<String, Object> properties,
+ int sizePerPage) {
+ return new GraphIterator<>(this, sizePerPage, (page) -> {
+ return this.edgeAPI.list(vertexId, direction, label, properties,
+ 0, page, sizePerPage);
+ });
+ }
+
+ public void removeEdge(String edgeId) {
+ this.edgeAPI.delete(edgeId);
+ }
+
+ public List<Edge> updateEdges(BatchEdgeRequest request) {
+ List<Edge> newEdges = this.edgeAPI.update(request);
+ newEdges.forEach(edge -> this.attachManager(edge));
+ return newEdges;
+ }
+
+ public Edge appendEdgeProperty(Edge edge) {
+ edge = this.edgeAPI.append(edge);
+ this.attachManager(edge);
+ return edge;
+ }
+
+ public Edge eliminateEdgeProperty(Edge edge) {
+ edge = this.edgeAPI.eliminate(edge);
+ this.attachManager(edge);
+ return edge;
+ }
+
+ private Object getValue(String key, Object... keyValues) {
+ E.checkArgument((keyValues.length & 0x01) == 0,
+ "The number of parameters must be even");
+ Object value = null;
+ for (int i = 0; i < keyValues.length; i = i + 2) {
+ if (keyValues[i].equals(key)) {
+ value = keyValues[i + 1];
+ break;
+ }
+ }
+ return value;
+ }
+
+ private void attachProperties(GraphElement element, Object... properties) {
+ E.checkArgument((properties.length & 0x01) == 0,
+ "The number of properties must be even");
+ for (int i = 0; i < properties.length; i = i + 2) {
+ if (!properties[i].equals(T.id) &&
+ !properties[i].equals(T.label)) {
+ element.property((String) properties[i], properties[i + 1]);
+ }
+ }
+ }
+
+ private void attachProperties(GraphElement element,
+ Map<String, Object> properties) {
+ if (properties != null) {
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ element.property(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ private void attachManager(GraphElement element) {
+ element.attachManager(this);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphsManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphsManager.java
new file mode 100644
index 0000000..bbb933a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GraphsManager.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.graphs.GraphsAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.constant.GraphMode;
+import com.baidu.hugegraph.structure.constant.GraphReadMode;
+
+public class GraphsManager {
+
+ private GraphsAPI graphsAPI;
+
+ public GraphsManager(RestClient client) {
+ this.graphsAPI = new GraphsAPI(client);
+ }
+
+ public Map<String, String> createGraph(String name, String configText) {
+ return this.graphsAPI.create(name, null, configText);
+ }
+
+ public Map<String, String> cloneGraph(String name, String cloneGraphName) {
+ return this.graphsAPI.create(name, cloneGraphName, null);
+ }
+
+ public Map<String, String> cloneGraph(String name, String cloneGraphName,
+ String configText) {
+ return this.graphsAPI.create(name, cloneGraphName, configText);
+ }
+
+ public Map<String, String> getGraph(String graph) {
+ return this.graphsAPI.get(graph);
+ }
+
+ public List<String> listGraph() {
+ return this.graphsAPI.list();
+ }
+
+ public void clearGraph(String graph, String message) {
+ this.graphsAPI.clear(graph, message);
+ }
+
+ public void dropGraph(String graph, String message) {
+ this.graphsAPI.drop(graph, message);
+ }
+
+ public void mode(String graph, GraphMode mode) {
+ this.graphsAPI.mode(graph, mode);
+ }
+
+ public GraphMode mode(String graph) {
+ return this.graphsAPI.mode(graph);
+ }
+
+ public void readMode(String graph, GraphReadMode readMode) {
+ this.graphsAPI.readMode(graph, readMode);
+ }
+
+ public GraphReadMode readMode(String graph) {
+ return this.graphsAPI.readMode(graph);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GremlinManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GremlinManager.java
new file mode 100644
index 0000000..e52d1ae
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/GremlinManager.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import com.baidu.hugegraph.api.gremlin.GremlinAPI;
+import com.baidu.hugegraph.api.gremlin.GremlinRequest;
+import com.baidu.hugegraph.api.job.GremlinJobAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.gremlin.Response;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+
+public class GremlinManager {
+
+ private final GraphManager graphManager;
+
+ private GremlinAPI gremlinAPI;
+ private GremlinJobAPI gremlinJobAPI;
+ private String graph;
+
+ public GremlinManager(RestClient client, String graph,
+ GraphManager graphManager) {
+ this.graphManager = graphManager;
+ this.gremlinAPI = new GremlinAPI(client);
+ this.gremlinJobAPI = new GremlinJobAPI(client, graph);
+ this.graph = graph;
+ }
+
+ public ResultSet execute(GremlinRequest request) {
+ // Bind "graph" to all graphs
+ request.aliases.put("graph", this.graph);
+ // Bind "g" to all graphs by custom rule which define in gremlin server.
+ request.aliases.put("g", "__g_" + this.graph);
+
+ Response response = this.gremlinAPI.post(request);
+ response.graphManager(this.graphManager);
+ // TODO: Can add some checks later
+ return response.result();
+ }
+
+ public long executeAsTask(GremlinRequest request) {
+ return this.gremlinJobAPI.execute(request);
+ }
+
+ public GremlinRequest.Builder gremlin(String gremlin) {
+ return new GremlinRequest.Builder(gremlin, this);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClient.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClient.java
new file mode 100644
index 0000000..43e5f67
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClient.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.io.Closeable;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.rest.ClientException;
+import com.baidu.hugegraph.util.VersionUtil;
+import com.baidu.hugegraph.version.ClientVersion;
+import jakarta.ws.rs.ProcessingException;
+
+public class HugeClient implements Closeable {
+
+ static {
+ ClientVersion.check();
+ }
+ private final RestClient client;
+ private final boolean borrowedClient;
+
+ private VersionManager version;
+ private GraphsManager graphs;
+ private SchemaManager schema;
+ private GraphManager graph;
+ private GremlinManager gremlin;
+ private TraverserManager traverser;
+ private VariablesManager variable;
+ private JobManager job;
+ private TaskManager task;
+ private AuthManager auth;
+ private MetricsManager metrics;
+
+ public HugeClient(HugeClientBuilder builder) {
+ this.borrowedClient = false;
+ try {
+ this.client = new RestClient(builder.url(),
+ builder.username(),
+ builder.password(),
+ builder.timeout(),
+ builder.maxConns(),
+ builder.maxConnsPerRoute(),
+ builder.trustStoreFile(),
+ builder.trustStorePassword());
+ } catch (ProcessingException e) {
+ throw new ClientException("Failed to connect url '%s'",
+ builder.url());
+ }
+ try {
+ this.initManagers(this.client, builder.graph());
+ } catch (Throwable e) {
+ this.client.close();
+ throw e;
+ }
+ }
+
+ public HugeClient(HugeClient client, String graph) {
+ this.borrowedClient = true;
+ this.client = client.client;
+ this.initManagers(this.client, graph);
+ }
+
+ public static HugeClientBuilder builder(String url, String graph) {
+ return new HugeClientBuilder(url, graph);
+ }
+
+ @Override
+ public void close() {
+ if (!this.borrowedClient) {
+ this.client.close();
+ }
+ }
+
+ private void initManagers(RestClient client, String graph) {
+ assert client != null;
+ // Check hugegraph-server api version
+ this.version = new VersionManager(client);
+ this.checkServerApiVersion();
+
+ this.graphs = new GraphsManager(client);
+ this.schema = new SchemaManager(client, graph);
+ this.graph = new GraphManager(client, graph);
+ this.gremlin = new GremlinManager(client, graph, this.graph);
+ this.traverser = new TraverserManager(client, this.graph);
+ this.variable = new VariablesManager(client, graph);
+ this.job = new JobManager(client, graph);
+ this.task = new TaskManager(client, graph);
+ this.auth = new AuthManager(client, graph);
+ this.metrics = new MetricsManager(client);
+ }
+
+ private void checkServerApiVersion() {
+ VersionUtil.Version apiVersion = VersionUtil.Version.of(
+ this.version.getApiVersion());
+ VersionUtil.check(apiVersion, "0.38", "0.68",
+ "hugegraph-api in server");
+ this.client.apiVersion(apiVersion);
+ }
+
+ public GraphsManager graphs() {
+ return this.graphs;
+ }
+
+ public SchemaManager schema() {
+ return this.schema;
+ }
+
+ public GraphManager graph() {
+ return this.graph;
+ }
+
+ public GremlinManager gremlin() {
+ return this.gremlin;
+ }
+
+ public TraverserManager traverser() {
+ return this.traverser;
+ }
+
+ public VariablesManager variables() {
+ return this.variable;
+ }
+
+ public JobManager job() {
+ return this.job;
+ }
+
+ public TaskManager task() {
+ return this.task;
+ }
+
+ public AuthManager auth() {
+ return this.auth;
+ }
+
+ public MetricsManager metrics() {
+ return this.metrics;
+ }
+
+ public void setAuthContext(String auth) {
+ this.client.setAuthContext(auth);
+ }
+
+ public String getAuthContext() {
+ return this.client.getAuthContext();
+ }
+
+ public void resetAuthContext() {
+ this.client.resetAuthContext();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClientBuilder.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClientBuilder.java
new file mode 100644
index 0000000..4eedeae
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/HugeClientBuilder.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import com.baidu.hugegraph.util.E;
+
+public class HugeClientBuilder {
+
+ private static final int CPUS = Runtime.getRuntime().availableProcessors();
+ private static final int DEFAULT_TIMEOUT = 20;
+ private static final int DEFAULT_MAX_CONNS = 4 * CPUS;
+ private static final int DEFAULT_MAX_CONNS_PER_ROUTE = 2 * CPUS;
+ private static final int DEFAULT_IDLE_TIME = 30;
+
+ private String url;
+ private String graph;
+ private String username;
+ private String password;
+ private int timeout;
+ private int maxConns;
+ private int maxConnsPerRoute;
+ private int idleTime;
+ private String trustStoreFile;
+ private String trustStorePassword;
+
+ public HugeClientBuilder(String url, String graph) {
+ E.checkArgument(url != null && !url.isEmpty(),
+ "Expect a string value as the url " +
+ "parameter argument, but got: %s", url);
+ E.checkArgument(graph != null && !graph.isEmpty(),
+ "Expect a string value as the graph name " +
+ "parameter argument, but got: %s", graph);
+ this.url = url;
+ this.graph = graph;
+ this.username = "";
+ this.password = "";
+ this.timeout = DEFAULT_TIMEOUT;
+ this.maxConns = DEFAULT_MAX_CONNS;
+ this.maxConnsPerRoute = DEFAULT_MAX_CONNS_PER_ROUTE;
+ this.trustStoreFile = "";
+ this.trustStorePassword = "";
+ this.idleTime = DEFAULT_IDLE_TIME;
+ }
+
+ public HugeClient build() {
+ E.checkArgument(this.url != null,
+ "The url parameter can't be null");
+ E.checkArgument(this.graph != null,
+ "The graph parameter can't be null");
+ return new HugeClient(this);
+ }
+
+ public HugeClientBuilder configGraph(String graph) {
+ this.graph = graph;
+ return this;
+ }
+
+ public HugeClientBuilder configIdleTime(int idleTime) {
+ E.checkArgument(idleTime > 0,
+ "The idleTime parameter must be > 0, " +
+ "but got %s", idleTime);
+ this.idleTime = idleTime;
+ return this;
+ }
+
+ public HugeClientBuilder configPool(int maxConns, int maxConnsPerRoute) {
+ if (maxConns == 0) {
+ maxConns = DEFAULT_MAX_CONNS;
+ }
+ if (maxConnsPerRoute == 0) {
+ maxConnsPerRoute = DEFAULT_MAX_CONNS_PER_ROUTE;
+ }
+ this.maxConns = maxConns;
+ this.maxConnsPerRoute = maxConnsPerRoute;
+ return this;
+ }
+
+ public HugeClientBuilder configSSL(String trustStoreFile,
+ String trustStorePassword) {
+ this.trustStoreFile = trustStoreFile;
+ this.trustStorePassword = trustStorePassword;
+ return this;
+ }
+
+ public HugeClientBuilder configTimeout(int timeout) {
+ if (timeout == 0) {
+ timeout = DEFAULT_TIMEOUT;
+ }
+ this.timeout = timeout;
+ return this;
+ }
+
+ public HugeClientBuilder configUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ public HugeClientBuilder configUser(String username, String password) {
+ if (username == null) {
+ username = "";
+ }
+ if (password == null) {
+ password = "";
+ }
+ this.username = username;
+ this.password = password;
+
+ return this;
+ }
+
+ public String url() {
+ return this.url;
+ }
+
+ public String graph() {
+ return this.graph;
+ }
+
+ public String username() {
+ return this.username;
+ }
+
+ public String password() {
+ return this.password;
+ }
+
+ public int timeout() {
+ return this.timeout;
+ }
+
+ public int maxConns() {
+ return maxConns;
+ }
+
+ public int maxConnsPerRoute() {
+ return this.maxConnsPerRoute;
+ }
+
+ public int idleTime() {
+ return this.idleTime;
+ }
+
+ public String trustStoreFile() {
+ return this.trustStoreFile;
+ }
+
+ public String trustStorePassword() {
+ return this.trustStorePassword;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/JobManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/JobManager.java
new file mode 100644
index 0000000..7fb440c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/JobManager.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import com.baidu.hugegraph.api.job.RebuildAPI;
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+
+import static com.baidu.hugegraph.api.task.TaskAPI.TASK_TIMEOUT;
+
+public class JobManager {
+
+ private RebuildAPI rebuildAPI;
+ private TaskAPI taskAPI;
+
+ public JobManager(RestClient client, String graph) {
+ this.rebuildAPI = new RebuildAPI(client, graph);
+ this.taskAPI = new TaskAPI(client, graph);
+ }
+
+ public void rebuild(VertexLabel vertexLabel) {
+ this.rebuild(vertexLabel, TASK_TIMEOUT);
+ }
+
+ public void rebuild(VertexLabel vertexLabel, long seconds) {
+ long task = this.rebuildAPI.rebuild(vertexLabel);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long rebuildAsync(VertexLabel vertexLabel) {
+ return this.rebuildAPI.rebuild(vertexLabel);
+ }
+
+ public void rebuild(EdgeLabel edgeLabel) {
+ this.rebuild(edgeLabel, TASK_TIMEOUT);
+ }
+
+ public void rebuild(EdgeLabel edgeLabel, long seconds) {
+ long task = this.rebuildAPI.rebuild(edgeLabel);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long rebuildAsync(EdgeLabel edgeLabel) {
+ return this.rebuildAPI.rebuild(edgeLabel);
+ }
+
+ public void rebuild(IndexLabel indexLabel) {
+ this.rebuild(indexLabel, TASK_TIMEOUT);
+ }
+
+ public void rebuild(IndexLabel indexLabel, long seconds) {
+ long task = this.rebuildAPI.rebuild(indexLabel);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long rebuildAsync(IndexLabel indexLabel) {
+ return this.rebuildAPI.rebuild(indexLabel);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/MetricsManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/MetricsManager.java
new file mode 100644
index 0000000..474fa2d
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/MetricsManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.api.metrics.MetricsAPI;
+import com.baidu.hugegraph.client.RestClient;
+
+public class MetricsManager {
+
+ private MetricsAPI metricsAPI;
+
+ public MetricsManager(RestClient client) {
+ this.metricsAPI = new MetricsAPI(client);
+ }
+
+ public Map<String, Map<String, Object>> backend() {
+ return this.metricsAPI.backend();
+ }
+
+ public Map<String, Object> backend(String graph) {
+ return this.metricsAPI.backend(graph);
+ }
+
+ public Map<String, Map<String, Object>> system() {
+ return this.metricsAPI.system();
+ }
+
+ /**
+ * The nesting level is too deep, may need to optimize the server first
+ */
+ public Map<String, Map<String, Object>> all() {
+ return this.metricsAPI.all();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/SchemaManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/SchemaManager.java
new file mode 100644
index 0000000..cee6f5d
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/SchemaManager.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import static com.baidu.hugegraph.api.task.TaskAPI.TASK_TIMEOUT;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.schema.EdgeLabelAPI;
+import com.baidu.hugegraph.api.schema.IndexLabelAPI;
+import com.baidu.hugegraph.api.schema.PropertyKeyAPI;
+import com.baidu.hugegraph.api.schema.SchemaAPI;
+import com.baidu.hugegraph.api.schema.VertexLabelAPI;
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.schema.BuilderProxy;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+
+public class SchemaManager {
+
+ private PropertyKeyAPI propertyKeyAPI;
+ private VertexLabelAPI vertexLabelAPI;
+ private EdgeLabelAPI edgeLabelAPI;
+ private IndexLabelAPI indexLabelAPI;
+ private SchemaAPI schemaAPI;
+ private TaskAPI taskAPI;
+
+ public SchemaManager(RestClient client, String graph) {
+ this.propertyKeyAPI = new PropertyKeyAPI(client, graph);
+ this.vertexLabelAPI = new VertexLabelAPI(client, graph);
+ this.edgeLabelAPI = new EdgeLabelAPI(client, graph);
+ this.indexLabelAPI = new IndexLabelAPI(client, graph);
+ this.schemaAPI = new SchemaAPI(client, graph);
+ this.taskAPI = new TaskAPI(client, graph);
+ }
+
+ public PropertyKey.Builder propertyKey(String name) {
+ PropertyKey.Builder builder = new PropertyKey.BuilderImpl(name, this);
+ BuilderProxy<PropertyKey.Builder> proxy = new BuilderProxy<>(builder);
+ return proxy.proxy();
+ }
+
+ public VertexLabel.Builder vertexLabel(String name) {
+ VertexLabel.Builder builder = new VertexLabel.BuilderImpl(name, this);
+ BuilderProxy<VertexLabel.Builder> proxy = new BuilderProxy<>(builder);
+ return proxy.proxy();
+ }
+
+ public EdgeLabel.Builder edgeLabel(String name) {
+ EdgeLabel.Builder builder = new EdgeLabel.BuilderImpl(name, this);
+ BuilderProxy<EdgeLabel.Builder> proxy = new BuilderProxy<>(builder);
+ return proxy.proxy();
+ }
+
+ public IndexLabel.Builder indexLabel(String name) {
+ IndexLabel.Builder builder = new IndexLabel.BuilderImpl(name, this);
+ BuilderProxy<IndexLabel.Builder> proxy = new BuilderProxy<>(builder);
+ return proxy.proxy();
+ }
+
+ public PropertyKey addPropertyKey(PropertyKey propertyKey) {
+ return this.addPropertyKey(propertyKey, TASK_TIMEOUT);
+ }
+
+ public PropertyKey addPropertyKey(PropertyKey propertyKey, long seconds) {
+ PropertyKey.PropertyKeyWithTask task = this.propertyKeyAPI
+ .create(propertyKey);
+ if (task.taskId() != 0L) {
+ this.taskAPI.waitUntilTaskSuccess(task.taskId(), seconds);
+ }
+ return task.propertyKey();
+ }
+
+ public long addPropertyKeyAsync(PropertyKey propertyKey) {
+ PropertyKey.PropertyKeyWithTask task = this.propertyKeyAPI
+ .create(propertyKey);
+ return task.taskId();
+ }
+
+ public PropertyKey appendPropertyKey(PropertyKey propertyKey) {
+ return this.propertyKeyAPI.append(propertyKey).propertyKey();
+ }
+
+ public PropertyKey eliminatePropertyKey(PropertyKey propertyKey) {
+ return this.propertyKeyAPI.eliminate(propertyKey).propertyKey();
+ }
+
+ public PropertyKey clearPropertyKey(PropertyKey propertyKey) {
+ return this.clearPropertyKey(propertyKey, TASK_TIMEOUT);
+ }
+
+ public PropertyKey clearPropertyKey(PropertyKey propertyKey, long seconds) {
+ PropertyKey.PropertyKeyWithTask task = this.propertyKeyAPI
+ .clear(propertyKey);
+ if (task.taskId() != 0L) {
+ this.taskAPI.waitUntilTaskSuccess(task.taskId(), seconds);
+ }
+ return task.propertyKey();
+ }
+
+ public long clearPropertyKeyAsync(PropertyKey propertyKey) {
+ PropertyKey.PropertyKeyWithTask task = this.propertyKeyAPI
+ .clear(propertyKey);
+ return task.taskId();
+ }
+
+ public void removePropertyKey(String name) {
+ this.removePropertyKey(name, TASK_TIMEOUT);
+ }
+
+ public void removePropertyKey(String name, long seconds) {
+ long task = this.propertyKeyAPI.delete(name);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long removePropertyKeyAsync(String name) {
+ return this.propertyKeyAPI.delete(name);
+ }
+
+ public PropertyKey getPropertyKey(String name) {
+ return this.propertyKeyAPI.get(name);
+ }
+
+ public List<PropertyKey> getPropertyKeys() {
+ return this.propertyKeyAPI.list();
+ }
+
+ public List<PropertyKey> getPropertyKeys(List<String> names) {
+ return this.propertyKeyAPI.list(names);
+ }
+
+ public VertexLabel addVertexLabel(VertexLabel vertexLabel) {
+ return this.vertexLabelAPI.create(vertexLabel);
+ }
+
+ public VertexLabel appendVertexLabel(VertexLabel vertexLabel) {
+ return this.vertexLabelAPI.append(vertexLabel);
+ }
+
+ public VertexLabel eliminateVertexLabel(VertexLabel vertexLabel) {
+ return this.vertexLabelAPI.eliminate(vertexLabel);
+ }
+
+ public void removeVertexLabel(String name) {
+ long task = this.vertexLabelAPI.delete(name);
+ this.taskAPI.waitUntilTaskSuccess(task, TASK_TIMEOUT);
+ }
+
+ public void removeVertexLabel(String name, long seconds) {
+ long task = this.vertexLabelAPI.delete(name);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long removeVertexLabelAsync(String name) {
+ return this.vertexLabelAPI.delete(name);
+ }
+
+ public VertexLabel getVertexLabel(String name) {
+ return this.vertexLabelAPI.get(name);
+ }
+
+ public List<VertexLabel> getVertexLabels() {
+ return this.vertexLabelAPI.list();
+ }
+
+ public List<VertexLabel> getVertexLabels(List<String> names) {
+ return this.vertexLabelAPI.list(names);
+ }
+
+ public EdgeLabel addEdgeLabel(EdgeLabel edgeLabel) {
+ return this.edgeLabelAPI.create(edgeLabel);
+ }
+
+ public EdgeLabel appendEdgeLabel(EdgeLabel edgeLabel) {
+ return this.edgeLabelAPI.append(edgeLabel);
+ }
+
+ public EdgeLabel eliminateEdgeLabel(EdgeLabel edgeLabel) {
+ return this.edgeLabelAPI.eliminate(edgeLabel);
+ }
+
+ public void removeEdgeLabel(String name) {
+ this.removeEdgeLabel(name, TASK_TIMEOUT);
+ }
+
+ public void removeEdgeLabel(String name, long seconds) {
+ long task = this.edgeLabelAPI.delete(name);
+ this.taskAPI.waitUntilTaskSuccess(task, seconds);
+ }
+
+ public long removeEdgeLabelAsync(String name) {
+ return this.edgeLabelAPI.delete(name);
+ }
+
+ public EdgeLabel getEdgeLabel(String name) {
+ return this.edgeLabelAPI.get(name);
+ }
+
+ public List<EdgeLabel> getEdgeLabels() {
+ return this.edgeLabelAPI.list();
+ }
+
+ public List<EdgeLabel> getEdgeLabels(List<String> names) {
+ return this.edgeLabelAPI.list(names);
+ }
+
+ public IndexLabel addIndexLabel(IndexLabel indexLabel) {
+ return this.addIndexLabel(indexLabel, TASK_TIMEOUT);
+ }
+
+ public IndexLabel addIndexLabel(IndexLabel indexLabel, long seconds) {
+ IndexLabel.IndexLabelWithTask cil = this.indexLabelAPI
+ .create(indexLabel);
+ if (cil.taskId() != 0L) {
+ this.taskAPI.waitUntilTaskSuccess(cil.taskId(), seconds);
+ }
+ return cil.indexLabel();
+ }
+
+ public long addIndexLabelAsync(IndexLabel indexLabel) {
+ IndexLabel.IndexLabelWithTask cil = this.indexLabelAPI
+ .create(indexLabel);
+ return cil.taskId();
+ }
+
+ public IndexLabel appendIndexLabel(IndexLabel indexLabel) {
+ return this.indexLabelAPI.append(indexLabel);
+ }
+
+ public IndexLabel eliminateIndexLabel(IndexLabel indexLabel) {
+ return this.indexLabelAPI.eliminate(indexLabel);
+ }
+
+ public void removeIndexLabel(String name) {
+ this.removeIndexLabel(name, TASK_TIMEOUT);
+ }
+
+ public void removeIndexLabel(String name, long secondss) {
+ long task = this.indexLabelAPI.delete(name);
+ this.taskAPI.waitUntilTaskSuccess(task, secondss);
+ }
+
+ public long removeIndexLabelAsync(String name) {
+ return this.indexLabelAPI.delete(name);
+ }
+
+ public IndexLabel getIndexLabel(String name) {
+ return this.indexLabelAPI.get(name);
+ }
+
+ public List<IndexLabel> getIndexLabels() {
+ return this.indexLabelAPI.list();
+ }
+
+ public List<IndexLabel> getIndexLabels(List<String> names) {
+ return this.indexLabelAPI.list(names);
+ }
+
+ public Map<String, List<SchemaElement>> getSchema() {
+ return this.schemaAPI.list();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TaskManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TaskManager.java
new file mode 100644
index 0000000..982bd77
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TaskManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.List;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.api.task.TasksWithPage;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.Task;
+
+public class TaskManager {
+
+ private TaskAPI taskAPI;
+
+ public TaskManager(RestClient client, String graph) {
+ this.taskAPI = new TaskAPI(client, graph);
+ }
+
+ public List<Task> list() {
+ return this.list(-1);
+ }
+
+ public List<Task> list(long limit) {
+ return this.taskAPI.list(null, limit);
+ }
+
+ public List<Task> list(List<Long> ids) {
+ return this.taskAPI.list(ids);
+ }
+
+ public List<Task> list(String status) {
+ return this.list(status, -1L);
+ }
+
+ public List<Task> list(String status, long limit) {
+ return this.taskAPI.list(status, limit);
+ }
+
+ public TasksWithPage list(String status, String page, long limit) {
+ return this.taskAPI.list(status, page, limit);
+ }
+
+ public Task get(long id) {
+ return this.taskAPI.get(id);
+ }
+
+ public void delete(long id) {
+ this.taskAPI.delete(id);
+ }
+
+ public Task cancel(long id) {
+ return this.taskAPI.cancel(id);
+ }
+
+ public Task waitUntilTaskCompleted(long taskId, long seconds) {
+ return this.taskAPI.waitUntilTaskSuccess(taskId, seconds);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java
new file mode 100644
index 0000000..f8ba4de
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java
@@ -0,0 +1,558 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.traverser.AllShortestPathsAPI;
+import com.baidu.hugegraph.api.traverser.CountAPI;
+import com.baidu.hugegraph.api.traverser.CrosspointsAPI;
+import com.baidu.hugegraph.api.traverser.CustomizedCrosspointsAPI;
+import com.baidu.hugegraph.api.traverser.CustomizedPathsAPI;
+import com.baidu.hugegraph.api.traverser.EdgesAPI;
+import com.baidu.hugegraph.api.traverser.FusiformSimilarityAPI;
+import com.baidu.hugegraph.api.traverser.JaccardSimilarityAPI;
+import com.baidu.hugegraph.api.traverser.KneighborAPI;
+import com.baidu.hugegraph.api.traverser.KoutAPI;
+import com.baidu.hugegraph.api.traverser.MultiNodeShortestPathAPI;
+import com.baidu.hugegraph.api.traverser.NeighborRankAPI;
+import com.baidu.hugegraph.api.traverser.PathsAPI;
+import com.baidu.hugegraph.api.traverser.PersonalRankAPI;
+import com.baidu.hugegraph.api.traverser.RaysAPI;
+import com.baidu.hugegraph.api.traverser.RingsAPI;
+import com.baidu.hugegraph.api.traverser.SameNeighborsAPI;
+import com.baidu.hugegraph.api.traverser.ShortestPathAPI;
+import com.baidu.hugegraph.api.traverser.SingleSourceShortestPathAPI;
+import com.baidu.hugegraph.api.traverser.TemplatePathsAPI;
+import com.baidu.hugegraph.api.traverser.VerticesAPI;
+import com.baidu.hugegraph.api.traverser.WeightedShortestPathAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Edges;
+import com.baidu.hugegraph.structure.graph.GraphIterator;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Shard;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.graph.Vertices;
+import com.baidu.hugegraph.structure.traverser.CountRequest;
+import com.baidu.hugegraph.structure.traverser.CrosspointsRequest;
+import com.baidu.hugegraph.structure.traverser.CustomizedCrosspoints;
+import com.baidu.hugegraph.structure.traverser.MultiNodeShortestPathRequest;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarity;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarityRequest;
+import com.baidu.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest;
+import com.baidu.hugegraph.structure.traverser.Kneighbor;
+import com.baidu.hugegraph.structure.traverser.KneighborRequest;
+import com.baidu.hugegraph.structure.traverser.Kout;
+import com.baidu.hugegraph.structure.traverser.KoutRequest;
+import com.baidu.hugegraph.structure.traverser.CustomizedPathsRequest;
+import com.baidu.hugegraph.structure.traverser.PathsRequest;
+import com.baidu.hugegraph.structure.traverser.Ranks;
+import com.baidu.hugegraph.structure.traverser.TemplatePathsRequest;
+import com.baidu.hugegraph.structure.traverser.WeightedPath;
+import com.baidu.hugegraph.structure.traverser.WeightedPaths;
+import com.baidu.hugegraph.util.E;
+
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_CAPACITY;
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_MAX_DEGREE;
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_ELEMENTS_LIMIT;
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_PAGE_LIMIT;
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_PATHS_LIMIT;
+
+public class TraverserManager {
+
+ private final GraphManager graphManager;
+ private JaccardSimilarityAPI jaccardSimilarityAPI;
+ private SameNeighborsAPI sameNeighborsAPI;
+ private ShortestPathAPI shortestPathAPI;
+ private AllShortestPathsAPI allShortestPathsAPI;
+ private SingleSourceShortestPathAPI singleSourceShortestPathAPI;
+ private WeightedShortestPathAPI weightedShortestPathAPI;
+ private MultiNodeShortestPathAPI multiNodeShortestPathAPI;
+ private PathsAPI pathsAPI;
+ private CrosspointsAPI crosspointsAPI;
+ private KoutAPI koutAPI;
+ private KneighborAPI kneighborAPI;
+ private CountAPI countAPI;
+ private RingsAPI ringsAPI;
+ private RaysAPI raysAPI;
+ private CustomizedPathsAPI customizedPathsAPI;
+ private CustomizedCrosspointsAPI customizedCrosspointsAPI;
+ private TemplatePathsAPI templatePathsAPI;
+ private FusiformSimilarityAPI fusiformSimilarityAPI;
+ private NeighborRankAPI neighborRankAPI;
+ private PersonalRankAPI personalRankAPI;
+ private VerticesAPI verticesAPI;
+ private EdgesAPI edgesAPI;
+
+ public TraverserManager(RestClient client, GraphManager graphManager) {
+ this.graphManager = graphManager;
+ String graph = graphManager.graph();
+ this.jaccardSimilarityAPI = new JaccardSimilarityAPI(client, graph);
+ this.sameNeighborsAPI = new SameNeighborsAPI(client, graph);
+ this.shortestPathAPI = new ShortestPathAPI(client, graph);
+ this.allShortestPathsAPI = new AllShortestPathsAPI(client, graph);
+ this.singleSourceShortestPathAPI = new SingleSourceShortestPathAPI(
+ client, graph);
+ this.weightedShortestPathAPI = new WeightedShortestPathAPI(
+ client, graph);
+ this.multiNodeShortestPathAPI = new MultiNodeShortestPathAPI(
+ client, graph);
+ this.pathsAPI = new PathsAPI(client, graph);
+ this.crosspointsAPI = new CrosspointsAPI(client, graph);
+ this.koutAPI = new KoutAPI(client, graph);
+ this.kneighborAPI = new KneighborAPI(client, graph);
+ this.countAPI = new CountAPI(client, graph);
+ this.ringsAPI = new RingsAPI(client, graph);
+ this.raysAPI = new RaysAPI(client, graph);
+ this.customizedPathsAPI = new CustomizedPathsAPI(client, graph);
+ this.customizedCrosspointsAPI = new CustomizedCrosspointsAPI(
+ client, graph);
+ this.templatePathsAPI = new TemplatePathsAPI(client, graph);
+ this.fusiformSimilarityAPI = new FusiformSimilarityAPI(client, graph);
+ this.neighborRankAPI = new NeighborRankAPI(client, graph);
+ this.personalRankAPI = new PersonalRankAPI(client, graph);
+ this.verticesAPI = new VerticesAPI(client, graph);
+ this.edgesAPI = new EdgesAPI(client, graph);
+ }
+
+ public double jaccardSimilarity(Object vertexId, Object otherId) {
+ return this.jaccardSimilarity(vertexId, otherId, DEFAULT_MAX_DEGREE);
+ }
+
+ public double jaccardSimilarity(Object vertexId, Object otherId,
+ long degree) {
+ return this.jaccardSimilarity(vertexId, otherId, Direction.BOTH,
+ null, degree);
+ }
+
+ public double jaccardSimilarity(Object vertexId, Object otherId,
+ Direction direction, String label,
+ long degree) {
+ return this.jaccardSimilarityAPI.get(vertexId, otherId, direction,
+ label, degree);
+ }
+
+ public Map<Object, Double> jaccardSimilarity(
+ SingleSourceJaccardSimilarityRequest request) {
+ return this.jaccardSimilarityAPI.post(request);
+ }
+
+ public List<Object> sameNeighbors(Object vertexId, Object otherId) {
+ return this.sameNeighbors(vertexId, otherId, DEFAULT_MAX_DEGREE);
+ }
+
+ public List<Object> sameNeighbors(Object vertexId, Object otherId,
+ long degree) {
+ return this.sameNeighbors(vertexId, otherId, Direction.BOTH,
+ null, degree);
+ }
+
+ public List<Object> sameNeighbors(Object vertexId, Object otherId,
+ Direction direction, String label,
+ long degree) {
+ return this.sameNeighbors(vertexId, otherId, direction, label,
+ degree, DEFAULT_PATHS_LIMIT);
+ }
+
+ public List<Object> sameNeighbors(Object vertexId, Object otherId,
+ Direction direction, String label,
+ long degree, long limit) {
+ return this.sameNeighborsAPI.get(vertexId, otherId, direction,
+ label, degree, limit);
+ }
+
+ public Path shortestPath(Object sourceId, Object targetId, int maxDepth) {
+ return this.shortestPath(sourceId, targetId, Direction.BOTH, null,
+ maxDepth);
+ }
+
+ public Path shortestPath(Object sourceId, Object targetId,
+ Direction direction, int maxDepth) {
+ return this.shortestPath(sourceId, targetId, direction, null,
+ maxDepth);
+ }
+
+ public Path shortestPath(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth) {
+ return this.shortestPath(sourceId, targetId, direction, label, maxDepth,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY);
+ }
+
+ public Path shortestPath(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth,
+ long degree, long capacity) {
+ return this.shortestPath(sourceId, targetId, direction, label,
+ maxDepth, degree, 0L, capacity);
+ }
+
+ public Path shortestPath(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth,
+ long degree, long skipDegree, long capacity) {
+ return this.shortestPathAPI.get(sourceId, targetId, direction, label,
+ maxDepth, degree, skipDegree, capacity);
+ }
+
+ public List<Path> allShortestPaths(Object sourceId, Object targetId,
+ int maxDepth) {
+ return this.allShortestPaths(sourceId, targetId, Direction.BOTH,
+ null, maxDepth);
+ }
+
+ public List<Path> allShortestPaths(Object sourceId, Object targetId,
+ Direction direction, int maxDepth) {
+ return this.allShortestPaths(sourceId, targetId, direction,
+ null, maxDepth);
+ }
+
+ public List<Path> allShortestPaths(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth) {
+ return this.allShortestPaths(sourceId, targetId, direction,
+ label, maxDepth, DEFAULT_MAX_DEGREE,
+ DEFAULT_CAPACITY);
+ }
+
+ public List<Path> allShortestPaths(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long degree,
+ long capacity) {
+ return this.allShortestPaths(sourceId, targetId, direction, label,
+ maxDepth, degree, 0L, capacity);
+ }
+
+ public List<Path> allShortestPaths(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long degree,
+ long skipDegree, long capacity) {
+ return this.allShortestPathsAPI.get(sourceId, targetId, direction,
+ label, maxDepth, degree,
+ skipDegree, capacity);
+ }
+
+ public WeightedPaths singleSourceShortestPath(Object sourceId,
+ String weight,
+ boolean withVertex) {
+ return this.singleSourceShortestPath(sourceId, Direction.BOTH, null,
+ weight, withVertex);
+ }
+
+ public WeightedPaths singleSourceShortestPath(Object sourceId,
+ Direction direction,
+ String label, String weight,
+ boolean withVertex) {
+ return this.singleSourceShortestPath(sourceId, direction, label, weight,
+ DEFAULT_MAX_DEGREE, 0L,
+ DEFAULT_CAPACITY,
+ DEFAULT_PATHS_LIMIT, withVertex);
+ }
+
+ public WeightedPaths singleSourceShortestPath(Object sourceId,
+ Direction direction,
+ String label, String weight,
+ long degree, long skipDegree,
+ long capacity, long limit,
+ boolean withVertex) {
+ return this.singleSourceShortestPathAPI.get(sourceId, direction, label,
+ weight, degree, skipDegree,
+ capacity, limit,
+ withVertex);
+ }
+
+ public WeightedPath weightedShortestPath(Object sourceId, Object targetId,
+ String weight, boolean withVertex) {
+ return this.weightedShortestPath(sourceId, targetId, Direction.BOTH,
+ null, weight, withVertex);
+ }
+
+ public WeightedPath weightedShortestPath(Object sourceId, Object targetId,
+ Direction direction, String label,
+ String weight,
+ boolean withVertex) {
+ return this.weightedShortestPath(sourceId, targetId, direction, label,
+ weight, DEFAULT_MAX_DEGREE, 0L,
+ DEFAULT_CAPACITY, withVertex);
+ }
+
+ public WeightedPath weightedShortestPath(Object sourceId, Object targetId,
+ Direction direction,
+ String label, String weight,
+ long degree, long skipDegree,
+ long capacity, boolean withVertex) {
+ return this.weightedShortestPathAPI.get(sourceId, targetId,direction,
+ label, weight, degree,
+ skipDegree, capacity,
+ withVertex);
+ }
+
+ public PathsWithVertices multiNodeShortestPath(
+ MultiNodeShortestPathRequest request) {
+ return this.multiNodeShortestPathAPI.post(request);
+ }
+
+ public List<Path> paths(Object sourceId, Object targetId, int maxDepth) {
+ return this.paths(sourceId, targetId, Direction.BOTH, null,
+ maxDepth, DEFAULT_PATHS_LIMIT);
+ }
+
+ public List<Path> paths(Object sourceId, Object targetId,
+ Direction direction, int maxDepth, long limit) {
+ return this.paths(sourceId, targetId, direction, null,
+ maxDepth, limit);
+ }
+
+ public List<Path> paths(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long limit) {
+ return this.paths(sourceId, targetId, direction, label, maxDepth,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY, limit);
+ }
+
+ public List<Path> paths(Object sourceId, Object targetId,
+ Direction direction, String label, int maxDepth,
+ long degree, long capacity, long limit) {
+ return this.pathsAPI.get(sourceId, targetId, direction, label,
+ maxDepth, degree, capacity, limit);
+ }
+
+ public PathsWithVertices paths(PathsRequest request) {
+ return this.pathsAPI.post(request);
+ }
+
+ public List<Path> crosspoint(Object sourceId, Object targetId,
+ int maxDepth) {
+ return this.crosspoint(sourceId, targetId, Direction.BOTH, null,
+ maxDepth, DEFAULT_PATHS_LIMIT);
+ }
+
+ public List<Path> crosspoint(Object sourceId, Object targetId,
+ Direction direction, int maxDepth,
+ long limit) {
+ return this.crosspoint(sourceId, targetId, direction, null,
+ maxDepth, limit);
+ }
+
+ public List<Path> crosspoint(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long limit) {
+ return this.crosspoint(sourceId, targetId, direction, label, maxDepth,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY, limit);
+ }
+
+ public List<Path> crosspoint(Object sourceId, Object targetId,
+ Direction direction, String label,
+ int maxDepth, long degree, long capacity,
+ long limit) {
+ return this.crosspointsAPI.get(sourceId, targetId, direction, label,
+ maxDepth, degree, capacity, limit);
+ }
+
+ public List<Object> kout(Object sourceId, int depth) {
+ return this.kout(sourceId, Direction.BOTH, depth);
+ }
+
+ public List<Object> kout(Object sourceId, Direction direction, int depth) {
+ return this.kout(sourceId, direction, null, depth, true);
+ }
+
+ public List<Object> kout(Object sourceId, Direction direction,
+ String label, int depth, boolean nearest) {
+ return this.kout(sourceId, direction, label, depth, nearest,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY,
+ DEFAULT_ELEMENTS_LIMIT);
+ }
+
+ public List<Object> kout(Object sourceId, Direction direction,
+ String label, int depth, boolean nearest,
+ long degree, long capacity, long limit) {
+ return this.koutAPI.get(sourceId, direction, label, depth, nearest,
+ degree, capacity, limit);
+ }
+
+ public Kout kout(KoutRequest request) {
+ return this.koutAPI.post(request);
+ }
+
+ public List<Object> kneighbor(Object sourceId, int depth) {
+ return this.kneighbor(sourceId, Direction.BOTH, null, depth);
+ }
+
+ public List<Object> kneighbor(Object sourceId, Direction direction,
+ int depth) {
+ return this.kneighbor(sourceId, direction, null, depth);
+ }
+
+ public List<Object> kneighbor(Object sourceId, Direction direction,
+ String label, int depth) {
+ return this.kneighbor(sourceId, direction, label, depth,
+ DEFAULT_MAX_DEGREE, DEFAULT_ELEMENTS_LIMIT);
+ }
+
+ public List<Object> kneighbor(Object sourceId, Direction direction,
+ String label, int depth,
+ long degree, long limit) {
+ return this.kneighborAPI.get(sourceId, direction, label, depth,
+ degree, limit);
+ }
+
+ public Kneighbor kneighbor(KneighborRequest request) {
+ return this.kneighborAPI.post(request);
+ }
+
+ public long count(CountRequest request) {
+ return this.countAPI.post(request);
+ }
+
+ public List<Path> rings(Object sourceId, int depth) {
+ return this.rings(sourceId, Direction.BOTH, null, depth, true,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY,
+ DEFAULT_ELEMENTS_LIMIT);
+ }
+
+ public List<Path> rings(Object sourceId, Direction direction, String label,
+ int depth, boolean sourceInRing, long degree,
+ long capacity, long limit) {
+ return this.ringsAPI.get(sourceId, direction, label, depth,
+ sourceInRing, degree, capacity, limit);
+ }
+
+ public List<Path> rays(Object sourceId, int depth) {
+ return this.rays(sourceId, Direction.BOTH, null, depth,
+ DEFAULT_MAX_DEGREE, DEFAULT_CAPACITY,
+ DEFAULT_ELEMENTS_LIMIT);
+ }
+
+ public List<Path> rays(Object sourceId, Direction direction, String label,
+ int depth, long degree, long capacity, long limit) {
+ return this.raysAPI.get(sourceId, direction, label, depth, degree,
+ capacity, limit);
+ }
+
+ public PathsWithVertices customizedPaths(CustomizedPathsRequest request) {
+ return this.customizedPathsAPI.post(request);
+ }
+
+ public CustomizedCrosspoints customizedCrosspointss(
+ CrosspointsRequest request) {
+ return this.customizedCrosspointsAPI.post(request);
+ }
+
+ public PathsWithVertices count(TemplatePathsRequest request) {
+ return this.templatePathsAPI.post(request);
+ }
+
+ public FusiformSimilarity fusiformSimilarity(
+ FusiformSimilarityRequest request) {
+ return this.fusiformSimilarityAPI.post(request);
+ }
+
+ public List<Ranks> neighborRank(NeighborRankAPI.Request request) {
+ return this.neighborRankAPI.post(request);
+ }
+
+ public Ranks personalRank(PersonalRankAPI.Request request) {
+ return this.personalRankAPI.post(request);
+ }
+
+ public List<Shard> vertexShards(long splitSize) {
+ return this.verticesAPI.shards(splitSize);
+ }
+
+ public List<Shard> edgeShards(long splitSize) {
+ return this.edgesAPI.shards(splitSize);
+ }
+
+ public List<Vertex> vertices(List<Object> ids) {
+ List<Vertex> vertices = this.verticesAPI.list(ids);
+ for (Vertex vertex : vertices) {
+ vertex.attachManager(this.graphManager);
+ }
+ return vertices;
+ }
+
+ public Vertices vertices(Shard shard) {
+ Vertices vertices = this.vertices(shard, null, 0L);
+ E.checkState(vertices.page() == null,
+ "Can't contains page when not in paging");
+ return vertices;
+ }
+
+ public Vertices vertices(Shard shard, String page) {
+ E.checkArgument(page != null, "Page can't be null");
+ return this.vertices(shard, page, DEFAULT_PAGE_LIMIT);
+ }
+
+ public Vertices vertices(Shard shard, String page, long pageLimit) {
+ E.checkArgument(page == null || pageLimit >= 0,
+ "Page limit must be >= 0 when page is not null");
+ Vertices vertices = this.verticesAPI.scan(shard, page, pageLimit);
+
+ for (Vertex vertex : vertices.results()) {
+ vertex.attachManager(this.graphManager);
+ }
+ return vertices;
+ }
+
+ public Iterator<Vertex> iteratorVertices(Shard shard, int sizePerPage) {
+ return new GraphIterator<>(this.graphManager, sizePerPage, (page) -> {
+ return this.vertices(shard, page, sizePerPage);
+ });
+ }
+
+ public List<Edge> edges(List<String> ids) {
+ List<Edge> edges = this.edgesAPI.list(ids);
+ for (Edge edge : edges) {
+ edge.attachManager(this.graphManager);
+ }
+ return edges;
+ }
+
+ public Edges edges(Shard shard) {
+ Edges edges = this.edges(shard, null, 0L);
+ E.checkState(edges.page() == null,
+ "Can't contains page when not in paging");
+ return edges;
+ }
+
+ public Edges edges(Shard shard, String page) {
+ E.checkArgument(page != null, "Page can't be null");
+ return this.edges(shard, page, DEFAULT_PAGE_LIMIT);
+ }
+
+ public Edges edges(Shard shard, String page, long pageLimit) {
+ E.checkArgument(page == null || pageLimit >= 0,
+ "Page limit must be >= 0 when page is not null");
+ Edges edges = this.edgesAPI.scan(shard, page, pageLimit);
+ for (Edge edge : edges.results()) {
+ edge.attachManager(this.graphManager);
+ }
+ return edges;
+ }
+
+ public Iterator<Edge> iteratorEdges(Shard shard, int sizePerPage) {
+ return new GraphIterator<>(this.graphManager, sizePerPage, (page) -> {
+ return this.edges(shard, page, sizePerPage);
+ });
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VariablesManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VariablesManager.java
new file mode 100644
index 0000000..ee6e1f6
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VariablesManager.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import com.baidu.hugegraph.api.variables.VariablesAPI;
+import com.baidu.hugegraph.client.RestClient;
+
+import java.util.Map;
+
+public class VariablesManager {
+
+ private VariablesAPI variablesAPI;
+
+ public VariablesManager(RestClient client, String graph) {
+ this.variablesAPI = new VariablesAPI(client, graph);
+ }
+
+ public Map<String, Object> get(String key) {
+ return this.variablesAPI.get(key);
+ }
+
+ public Map<String, Object> set(String key, Object value) {
+ return this.variablesAPI.set(key, value);
+ }
+
+ public void remove(String key) {
+ this.variablesAPI.remove(key);
+ }
+
+ public Map<String, Object> all() {
+ return this.variablesAPI.all();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VersionManager.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VersionManager.java
new file mode 100644
index 0000000..2ff9956
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/driver/VersionManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.driver;
+
+import com.baidu.hugegraph.api.version.VersionAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.structure.version.Versions;
+
+public class VersionManager {
+
+ private VersionAPI versionAPI;
+
+ public VersionManager(RestClient client) {
+ this.versionAPI = new VersionAPI(client);
+ }
+
+ public String getCoreVersion() {
+ Versions versions = this.versionAPI.get();
+ return versions.get("core");
+ }
+
+ public String getGremlinVersion() {
+ Versions versions = this.versionAPI.get();
+ return versions.get("gremlin");
+ }
+
+ public String getApiVersion() {
+ Versions versions = this.versionAPI.get();
+ return versions.get("api");
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/example/BatchExample.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/BatchExample.java
new file mode 100644
index 0000000..94f59e5
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/BatchExample.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.example;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+
+public class BatchExample {
+
+ public static void main(String[] args) {
+ // If connect failed will throw a exception.
+ HugeClient hugeClient = HugeClient.builder("http://localhost:8080",
+ "hugegraph").build();
+
+ SchemaManager schema = hugeClient.schema();
+
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("age").asInt().ifNotExist().create();
+ schema.propertyKey("lang").asText().ifNotExist().create();
+ schema.propertyKey("date").asDate().ifNotExist().create();
+ schema.propertyKey("price").asInt().ifNotExist().create();
+
+ schema.vertexLabel("person")
+ .properties("name", "age")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("person")
+ .properties("price")
+ .nullableKeys("price")
+ .append();
+
+ schema.vertexLabel("software")
+ .properties("name", "lang", "price")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("softwareByPrice")
+ .onV("software").by("price")
+ .range()
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("knows")
+ .link("person", "person")
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("created")
+ .link("person", "software")
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("createdByDate")
+ .onE("created").by("date")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ // get schema object by name
+ System.out.println(schema.getPropertyKey("name"));
+ System.out.println(schema.getVertexLabel("person"));
+ System.out.println(schema.getEdgeLabel("knows"));
+ System.out.println(schema.getIndexLabel("createdByDate"));
+
+ // list all schema objects
+ System.out.println(schema.getPropertyKeys());
+ System.out.println(schema.getVertexLabels());
+ System.out.println(schema.getEdgeLabels());
+ System.out.println(schema.getIndexLabels());
+
+ GraphManager graph = hugeClient.graph();
+
+ Vertex marko = new Vertex("person").property("name", "marko")
+ .property("age", 29);
+ Vertex vadas = new Vertex("person").property("name", "vadas")
+ .property("age", 27);
+ Vertex lop = new Vertex("software").property("name", "lop")
+ .property("lang", "java")
+ .property("price", 328);
+ Vertex josh = new Vertex("person").property("name", "josh")
+ .property("age", 32);
+ Vertex ripple = new Vertex("software").property("name", "ripple")
+ .property("lang", "java")
+ .property("price", 199);
+ Vertex peter = new Vertex("person").property("name", "peter")
+ .property("age", 35);
+
+ Edge markoKnowsVadas = new Edge("knows").source(marko).target(vadas)
+ .property("date", "2016-01-10");
+ Edge markoKnowsJosh = new Edge("knows").source(marko).target(josh)
+ .property("date", "2013-02-20");
+ Edge markoCreateLop = new Edge("created").source(marko).target(lop)
+ .property("date",
+ "2017-12-10");
+ Edge joshCreateRipple = new Edge("created").source(josh).target(ripple)
+ .property("date",
+ "2017-12-10");
+ Edge joshCreateLop = new Edge("created").source(josh).target(lop)
+ .property("date", "2009-11-11");
+ Edge peterCreateLop = new Edge("created").source(peter).target(lop)
+ .property("date",
+ "2017-03-24");
+
+ List<Vertex> vertices = new ArrayList<>();
+ vertices.add(marko);
+ vertices.add(vadas);
+ vertices.add(lop);
+ vertices.add(josh);
+ vertices.add(ripple);
+ vertices.add(peter);
+
+ List<Edge> edges = new ArrayList<>();
+ edges.add(markoKnowsVadas);
+ edges.add(markoKnowsJosh);
+ edges.add(markoCreateLop);
+ edges.add(joshCreateRipple);
+ edges.add(joshCreateLop);
+ edges.add(peterCreateLop);
+
+ vertices = graph.addVertices(vertices);
+ vertices.forEach(vertex -> System.out.println(vertex));
+
+ edges = graph.addEdges(edges, false);
+ edges.forEach(edge -> System.out.println(edge));
+
+ hugeClient.close();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/example/MovieExample.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/MovieExample.java
new file mode 100644
index 0000000..88bf5ca
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/MovieExample.java
@@ -0,0 +1,582 @@
+package com.baidu.hugegraph.example;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+
+public class MovieExample {
+
+ public static void main(String[] args) {
+ // If connect failed will throw a exception.
+ HugeClient hugeClient = HugeClient.builder("http://localhost:8080",
+ "hugegraph").build();
+
+ SchemaManager schema = hugeClient.schema();
+
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("born").asInt().ifNotExist().create();
+ schema.propertyKey("title").asText().ifNotExist().create();
+ schema.propertyKey("released").asInt().ifNotExist().create();
+ schema.propertyKey("score").asInt().ifNotExist().create();
+ schema.propertyKey("roles").asText().ifNotExist().create();
+
+ schema.vertexLabel("person")
+ .properties("name", "born")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+ schema.vertexLabel("movie")
+ .properties("title", "released")
+ .primaryKeys("title")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("ACTED_IN").multiTimes().properties("roles")
+ .sourceLabel("person").targetLabel("movie")
+ .sortKeys("roles")
+ .ifNotExist()
+ .create();
+ schema.edgeLabel("DIRECTED").properties("score")
+ .sourceLabel("person").targetLabel("movie")
+ .ifNotExist()
+ .create();
+ schema.edgeLabel("PRODUCED").properties("score")
+ .sourceLabel("person").targetLabel("movie")
+ .ifNotExist()
+ .create();
+ schema.edgeLabel("WROTE").properties("score")
+ .sourceLabel("person").targetLabel("movie")
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("personByBorn").onV("person").by("born")
+ .range().ifNotExist().create();
+
+ GraphManager graph = hugeClient.graph();
+
+ Vertex theMatrix = graph.addVertex(T.label, "movie", "title", "The Matrix", "released", 1999);
+ Vertex keanu = graph.addVertex(T.label, "person", "name", "keanu Reeves", "born", 1964);
+ Vertex carrie = graph.addVertex(T.label, "person", "name", "carrie-anne Moss", "born", 1967);
+ Vertex laurence = graph.addVertex(T.label, "person", "name", "laurence Fishburne", "born", 1961);
+ Vertex hugo = graph.addVertex(T.label, "person", "name", "hugo Weaving", "born", 1960);
+ Vertex lillyW = graph.addVertex(T.label, "person", "name", "Lilly Wachowski", "born", 1967);
+ Vertex lanaW = graph.addVertex(T.label, "person", "name", "Lana Wachowski", "born", 1965);
+ Vertex joelS = graph.addVertex(T.label, "person", "name", "Joel Silver", "born", 1952);
+
+ keanu.addEdge("ACTED_IN", theMatrix, "roles", "Neo");
+ carrie.addEdge("ACTED_IN", theMatrix, "roles", "Trinity");
+ laurence.addEdge("ACTED_IN", theMatrix, "roles", "Morpheus");
+ hugo.addEdge("ACTED_IN", theMatrix, "roles", "agent Smith");
+ lillyW.addEdge("DIRECTED", theMatrix, "score", 10);
+ lanaW.addEdge("DIRECTED", theMatrix, "score", 10);
+ joelS.addEdge("PRODUCED", theMatrix, "score", 10);
+
+ Vertex emil = graph.addVertex(T.label, "person", "name", "emil Eifrem", "born", 1978);
+ emil.addEdge("ACTED_IN", theMatrix, "roles", "emil");
+
+ Vertex theMatrixReloaded = graph.addVertex(T.label, "movie", "title", "The Matrix Reloaded", "released", 2003);
+
+ keanu.addEdge("ACTED_IN", theMatrixReloaded, "roles", "Neo");
+ carrie.addEdge("ACTED_IN", theMatrixReloaded, "roles", "Trinity");
+ laurence.addEdge("ACTED_IN", theMatrixReloaded, "roles", "Morpheus");
+ hugo.addEdge("ACTED_IN", theMatrixReloaded, "roles", "agent Smith");
+ lillyW.addEdge("DIRECTED", theMatrixReloaded, "score", 10);
+ lanaW.addEdge("DIRECTED", theMatrix, "score", 10);
+ joelS.addEdge("PRODUCED", theMatrixReloaded, "score", 10);
+
+ Vertex theMatrixRevolutions = graph.addVertex(T.label, "movie", "title", "The Matrix Revolutions", "released", 2003);
+
+ keanu.addEdge("ACTED_IN", theMatrixRevolutions, "roles", "Neo");
+ carrie.addEdge("ACTED_IN", theMatrixRevolutions, "roles", "Trinity");
+ laurence.addEdge("ACTED_IN", theMatrixRevolutions, "roles", "Morpheus");
+ hugo.addEdge("ACTED_IN", theMatrixRevolutions, "roles", "agent Smith");
+ lillyW.addEdge("DIRECTED", theMatrixRevolutions, "score", 10);
+ lanaW.addEdge("DIRECTED", theMatrixRevolutions, "score", 10);
+ joelS.addEdge("PRODUCED", theMatrixRevolutions, "score", 10);
+
+ Vertex theDevilsadvocate = graph.addVertex(T.label, "movie", "title", "The Devil's advocate", "released", 1997);
+
+ Vertex charlize = graph.addVertex(T.label, "person", "name", "charlize Theron", "born", 1975);
+ Vertex al = graph.addVertex(T.label, "person", "name", "al Pacino", "born", 1940);
+ Vertex taylor = graph.addVertex(T.label, "person", "name", "taylor Hackford", "born", 1944);
+
+ keanu.addEdge("ACTED_IN", theDevilsadvocate, "roles", "Kevin Lomax");
+ charlize.addEdge("ACTED_IN", theDevilsadvocate, "roles", "Mary ann Lomax");
+ al.addEdge("ACTED_IN", theDevilsadvocate, "roles", "John Milton");
+ taylor.addEdge("DIRECTED", theDevilsadvocate, "score", 10);
+
+ Vertex aFewGoodMen = graph.addVertex(T.label, "movie", "title", "a Few Good Men", "released", 1992);
+
+ Vertex tomC = graph.addVertex(T.label, "person", "name", "Tom Cruise", "born", 1962);
+ Vertex jackN = graph.addVertex(T.label, "person", "name", "Jack Nicholson", "born", 1937);
+ Vertex demiM = graph.addVertex(T.label, "person", "name", "Demi Moore", "born", 1962);
+ Vertex kevinB = graph.addVertex(T.label, "person", "name", "Kevin Bacon", "born", 1958);
+ Vertex kieferS = graph.addVertex(T.label, "person", "name", "Kiefer Sutherland", "born", 1966);
+ Vertex noahW = graph.addVertex(T.label, "person", "name", "Noah Wyle", "born", 1971);
+ Vertex cubaG = graph.addVertex(T.label, "person", "name", "Cuba Gooding Jr.", "born", 1968);
+ Vertex kevinP = graph.addVertex(T.label, "person", "name", "Kevin Pollak", "born", 1957);
+ Vertex jtw = graph.addVertex(T.label, "person", "name", "J.T. Walsh", "born", 1943);
+ Vertex jamesM = graph.addVertex(T.label, "person", "name", "James Marshall", "born", 1967);
+ Vertex christopherG = graph.addVertex(T.label, "person", "name", "Christopher Guest", "born", 1948);
+ Vertex robR = graph.addVertex(T.label, "person", "name", "Rob Reiner", "born", 1947);
+ Vertex aaronS = graph.addVertex(T.label, "person", "name", "aaron Sorkin", "born", 1961);
+
+ tomC.addEdge("ACTED_IN", aFewGoodMen, "roles", "Lt. Daniel Kaffee");
+ jackN.addEdge("ACTED_IN", aFewGoodMen, "roles", "Col. nathan R. Jessup");
+ demiM.addEdge("ACTED_IN", aFewGoodMen, "roles", "Lt. Cdr. Joanne Galloway");
+ kevinB.addEdge("ACTED_IN", aFewGoodMen, "roles", "Capt. Jack Ross");
+ kieferS.addEdge("ACTED_IN", aFewGoodMen, "roles", "Lt. Jonathan Kendrick");
+ noahW.addEdge("ACTED_IN", aFewGoodMen, "roles", "Cpl. Jeffrey Barnes");
+ cubaG.addEdge("ACTED_IN", aFewGoodMen, "roles", "Cpl. Carl Hammaker");
+ kevinP.addEdge("ACTED_IN", aFewGoodMen, "roles", "Lt. Sam Weinberg");
+ jtw.addEdge("ACTED_IN", aFewGoodMen, "roles", "Lt. Col. Matthew andrew Markinson");
+ jamesM.addEdge("ACTED_IN", aFewGoodMen, "roles", "Pfc. Louden Downey");
+ christopherG.addEdge("ACTED_IN", aFewGoodMen, "roles", "Dr. Stone");
+ aaronS.addEdge("ACTED_IN", aFewGoodMen, "roles", "Man in Bar");
+ robR.addEdge("DIRECTED", aFewGoodMen, "score", 10);
+ aaronS.addEdge("WROTE", aFewGoodMen, "score", 10);
+
+ Vertex topGun = graph.addVertex(T.label, "movie", "title", "Top Gun", "released", 1986);
+
+ Vertex kellyM = graph.addVertex(T.label, "person", "name", "Kelly McGillis", "born", 1957);
+ Vertex valK = graph.addVertex(T.label, "person", "name", "Val Kilmer", "born", 1959);
+ Vertex anthonyE = graph.addVertex(T.label, "person", "name", "anthony Edwards", "born", 1962);
+ Vertex tomS = graph.addVertex(T.label, "person", "name", "Tom Skerritt", "born", 1933);
+ Vertex megR = graph.addVertex(T.label, "person", "name", "Meg Ryan", "born", 1961);
+ Vertex tonyS = graph.addVertex(T.label, "person", "name", "Tony Scott", "born", 1944);
+ Vertex jimC = graph.addVertex(T.label, "person", "name", "Jim Cash", "born", 1941);
+
+ tomC.addEdge("ACTED_IN", topGun, "roles", "Maverick");
+ kellyM.addEdge("ACTED_IN", topGun, "roles", "Charlie");
+ valK.addEdge("ACTED_IN", topGun, "roles", "Iceman");
+ anthonyE.addEdge("ACTED_IN", topGun, "roles", "Goose");
+ tomS.addEdge("ACTED_IN", topGun, "roles", "Viper");
+ megR.addEdge("ACTED_IN", topGun, "roles", "Carole");
+ tonyS.addEdge("DIRECTED", topGun, "score", 10);
+ jimC.addEdge("WROTE", topGun, "score", 10);
+
+ Vertex jerryMaguire = graph.addVertex(T.label, "movie", "title", "Jerry Maguire", "released", 2000);
+
+ Vertex reneeZ = graph.addVertex(T.label, "person", "name", "Renee Zellweger", "born", 1969);
+ Vertex kellyP = graph.addVertex(T.label, "person", "name", "Kelly Preston", "born", 1962);
+ Vertex jerryO = graph.addVertex(T.label, "person", "name", "Jerry O'Connell", "born", 1974);
+ Vertex jayM = graph.addVertex(T.label, "person", "name", "Jay Mohr", "born", 1970);
+ Vertex bonnieH = graph.addVertex(T.label, "person", "name", "Bonnie Hunt", "born", 1970);
+ Vertex reginaK = graph.addVertex(T.label, "person", "name", "Regina King", "born", 1961);
+ Vertex jonathanL = graph.addVertex(T.label, "person", "name", "Jonathan Lipnicki", "born", 1996);
+ Vertex cameronC = graph.addVertex(T.label, "person", "name", "Cameron Crowe", "born", 1957);
+
+ tomC.addEdge("ACTED_IN", jerryMaguire, "roles", "Jerry Maguire");
+ cubaG.addEdge("ACTED_IN", jerryMaguire, "roles", "Rod Tidwell");
+ reneeZ.addEdge("ACTED_IN", jerryMaguire, "roles", "Dorothy Boyd");
+ kellyP.addEdge("ACTED_IN", jerryMaguire, "roles", "avery Bishop");
+ jerryO.addEdge("ACTED_IN", jerryMaguire, "roles", "Frank Cushman");
+ jayM.addEdge("ACTED_IN", jerryMaguire, "roles", "Bob Sugar");
+ bonnieH.addEdge("ACTED_IN", jerryMaguire, "roles", "Laurel Boyd");
+ reginaK.addEdge("ACTED_IN", jerryMaguire, "roles", "Marcee Tidwell");
+ jonathanL.addEdge("ACTED_IN", jerryMaguire, "roles", "Ray Boyd");
+ cameronC.addEdge("DIRECTED", jerryMaguire, "score", 10);
+ cameronC.addEdge("PRODUCED", jerryMaguire, "score", 10);
+ cameronC.addEdge("WROTE", jerryMaguire, "score", 10);
+
+ Vertex standByMe = graph.addVertex(T.label, "movie", "title", "Stand By Me", "released", 1986);
+
+ Vertex riverP = graph.addVertex(T.label, "person", "name", "River Phoenix", "born", 1970);
+ Vertex coreyF = graph.addVertex(T.label, "person", "name", "Corey Feldman", "born", 1971);
+ Vertex wilW = graph.addVertex(T.label, "person", "name", "Wil Wheaton", "born", 1972);
+ Vertex johnC = graph.addVertex(T.label, "person", "name", "John Cusack", "born", 1966);
+ Vertex marshallB = graph.addVertex(T.label, "person", "name", "Marshall Bell", "born", 1942);
+
+ wilW.addEdge("ACTED_IN", standByMe, "roles", "Gordie Lachance");
+ riverP.addEdge("ACTED_IN", standByMe, "roles", "Chris Chambers");
+ jerryO.addEdge("ACTED_IN", standByMe, "roles", "Vern Tessio");
+ coreyF.addEdge("ACTED_IN", standByMe, "roles", "Teddy Duchamp");
+ johnC.addEdge("ACTED_IN", standByMe, "roles", "Denny Lachance");
+ kieferS.addEdge("ACTED_IN", standByMe, "roles", "ace Merrill");
+ marshallB.addEdge("ACTED_IN", standByMe, "roles", "Mr. Lachance");
+ robR.addEdge("DIRECTED", standByMe, "score", 10);
+
+ Vertex asGoodasItGets = graph.addVertex(T.label, "movie", "title", "as Good as It Gets", "released", 1997);
+
+ Vertex helenH = graph.addVertex(T.label, "person", "name", "Helen Hunt", "born", 1963);
+ Vertex gregK = graph.addVertex(T.label, "person", "name", "Greg Kinnear", "born", 1963);
+ Vertex jamesB = graph.addVertex(T.label, "person", "name", "James L. Brooks", "born", 1940);
+
+ jackN.addEdge("ACTED_IN", asGoodasItGets, "roles", "Melvin Udall");
+ helenH.addEdge("ACTED_IN", asGoodasItGets, "roles", "Carol Connelly");
+ gregK.addEdge("ACTED_IN", asGoodasItGets, "roles", "Simon Bishop");
+ cubaG.addEdge("ACTED_IN", asGoodasItGets, "roles", "Frank Sachs");
+ jamesB.addEdge("DIRECTED", asGoodasItGets, "score", 10);
+
+ Vertex whatDreamsMayCome = graph.addVertex(T.label, "movie", "title", "What Dreams May Come", "released", 1998);
+
+ Vertex annabellaS = graph.addVertex(T.label, "person", "name", "annabella Sciorra", "born", 1960);
+ Vertex maxS = graph.addVertex(T.label, "person", "name", "Max von Sydow", "born", 1929);
+ Vertex wernerH = graph.addVertex(T.label, "person", "name", "Werner Herzog", "born", 1942);
+ Vertex robin = graph.addVertex(T.label, "person", "name", "robin Williams", "born", 1951);
+ Vertex vincentW = graph.addVertex(T.label, "person", "name", "Vincent Ward", "born", 1956);
+
+ robin.addEdge("ACTED_IN", whatDreamsMayCome, "roles", "Chris Nielsen");
+ cubaG.addEdge("ACTED_IN", whatDreamsMayCome, "roles", "albert Lewis");
+ annabellaS.addEdge("ACTED_IN", whatDreamsMayCome, "roles", "annie Collins-Nielsen");
+ maxS.addEdge("ACTED_IN", whatDreamsMayCome, "roles", "The Tracker");
+ wernerH.addEdge("ACTED_IN", whatDreamsMayCome, "roles", "The Face");
+ vincentW.addEdge("DIRECTED", whatDreamsMayCome, "score", 10);
+
+ Vertex snowFallingonCedars = graph.addVertex(T.label, "movie", "title", "Snow Falling on Cedars", "released", 1999);
+
+ Vertex ethanH = graph.addVertex(T.label, "person", "name", "Ethan Hawke", "born", 1970);
+ Vertex rickY = graph.addVertex(T.label, "person", "name", "Rick Yune", "born", 1971);
+ Vertex jamesC = graph.addVertex(T.label, "person", "name", "James Cromwell", "born", 1940);
+ Vertex scottH = graph.addVertex(T.label, "person", "name", "Scott Hicks", "born", 1953);
+
+ ethanH.addEdge("ACTED_IN", snowFallingonCedars, "roles", "Ishmael Chambers");
+ rickY.addEdge("ACTED_IN", snowFallingonCedars, "roles", "Kazuo Miyamoto");
+ maxS.addEdge("ACTED_IN", snowFallingonCedars, "roles", "Nels Gudmundsson");
+ jamesC.addEdge("ACTED_IN", snowFallingonCedars, "roles", "Judge Fielding");
+ scottH.addEdge("DIRECTED", snowFallingonCedars, "score", 10);
+
+ Vertex youveGotMail = graph.addVertex(T.label, "movie", "title", "You've Got Mail", "released", 1998);
+
+ Vertex parkerP = graph.addVertex(T.label, "person", "name", "Parker Posey", "born", 1968);
+ Vertex daveC = graph.addVertex(T.label, "person", "name", "Dave Chappelle", "born", 1973);
+ Vertex steveZ = graph.addVertex(T.label, "person", "name", "Steve Zahn", "born", 1967);
+ Vertex tomH = graph.addVertex(T.label, "person", "name", "Tom Hanks", "born", 1956);
+ Vertex noraE = graph.addVertex(T.label, "person", "name", "Nora Ephron", "born", 1941);
+
+ tomH.addEdge("ACTED_IN", youveGotMail, "roles", "Joe Fox");
+ megR.addEdge("ACTED_IN", youveGotMail, "roles", "Kathleen Kelly");
+ gregK.addEdge("ACTED_IN", youveGotMail, "roles", "Frank Navasky");
+ parkerP.addEdge("ACTED_IN", youveGotMail, "roles", "Patricia Eden");
+ daveC.addEdge("ACTED_IN", youveGotMail, "roles", "Kevin Jackson");
+ steveZ.addEdge("ACTED_IN", youveGotMail, "roles", "George Pappas");
+ noraE.addEdge("DIRECTED", youveGotMail, "score", 10);
+
+ Vertex sleeplessInSeattle = graph.addVertex(T.label, "movie", "title", "Sleepless in Seattle", "released", 1993);
+
+ Vertex ritaW = graph.addVertex(T.label, "person", "name", "Rita Wilson", "born", 1956);
+ Vertex billPull = graph.addVertex(T.label, "person", "name", "Bill Pullman", "born", 1953);
+ Vertex victorG = graph.addVertex(T.label, "person", "name", "Victor Garber", "born", 1949);
+ Vertex rosieO = graph.addVertex(T.label, "person", "name", "Rosie O'Donnell", "born", 1962);
+
+ tomH.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "Sam Baldwin");
+ megR.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "annie Reed");
+ ritaW.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "Suzy");
+ billPull.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "Walter");
+ victorG.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "Greg");
+ rosieO.addEdge("ACTED_IN", sleeplessInSeattle, "roles", "Becky");
+ noraE.addEdge("DIRECTED", sleeplessInSeattle, "score", 10);
+
+ Vertex joeVersustheVolcano = graph.addVertex(T.label, "movie", "title", "Joe Versus the Volcano", "released", 1990);
+
+ Vertex johnS = graph.addVertex(T.label, "person", "name", "John Patrick Stanley", "born", 1950);
+ Vertex nathan = graph.addVertex(T.label, "person", "name", "nathan Lane", "born", 1956);
+
+ tomH.addEdge("ACTED_IN", joeVersustheVolcano, "roles", "Joe Banks");
+ megR.addEdge("ACTED_IN", joeVersustheVolcano, "roles", "DeDe, angelica Graynamore, Patricia Graynamore");
+ nathan.addEdge("ACTED_IN", joeVersustheVolcano, "roles", "Baw");
+ johnS.addEdge("DIRECTED", joeVersustheVolcano, "score", 10);
+
+ Vertex whenHarryMetSally = graph.addVertex(T.label, "movie", "title", "When Harry Met Sally", "released", 1998);
+
+ Vertex billyC = graph.addVertex(T.label, "person", "name", "Billy Crystal", "born", 1948);
+ Vertex carrieF = graph.addVertex(T.label, "person", "name", "carrie Fisher", "born", 1956);
+ Vertex brunoK = graph.addVertex(T.label, "person", "name", "Bruno Kirby", "born", 1949);
+
+ billyC.addEdge("ACTED_IN", whenHarryMetSally, "roles", "Harry Burns");
+ megR.addEdge("ACTED_IN", whenHarryMetSally, "roles", "Sally albright");
+ carrieF.addEdge("ACTED_IN", whenHarryMetSally, "roles", "Marie");
+ brunoK.addEdge("ACTED_IN", whenHarryMetSally, "roles", "Jess");
+ robR.addEdge("DIRECTED", whenHarryMetSally, "score", 10);
+ robR.addEdge("PRODUCED", whenHarryMetSally, "score", 10);
+ noraE.addEdge("PRODUCED", whenHarryMetSally, "score", 10);
+ noraE.addEdge("WROTE", whenHarryMetSally, "score", 10);
+
+ Vertex thatThingYouDo = graph.addVertex(T.label, "movie", "title", "That Thing You Do", "released", 1996);
+
+ Vertex livT = graph.addVertex(T.label, "person", "name", "Liv Tyler", "born", 1977);
+
+ tomH.addEdge("ACTED_IN", thatThingYouDo, "roles", "Mr. White");
+ livT.addEdge("ACTED_IN", thatThingYouDo, "roles", "Faye Dolan");
+ charlize.addEdge("ACTED_IN", thatThingYouDo, "roles", "Tina");
+ tomH.addEdge("DIRECTED", thatThingYouDo, "score", 10);
+
+ Vertex theReplacements = graph.addVertex(T.label, "movie", "title", "The Replacements", "released", 2000);
+
+ Vertex brooke = graph.addVertex(T.label, "person", "name", "brooke Langton", "born", 1970);
+ Vertex gene = graph.addVertex(T.label, "person", "name", "gene Hackman", "born", 1930);
+ Vertex orlando = graph.addVertex(T.label, "person", "name", "orlando Jones", "born", 1968);
+ Vertex howard = graph.addVertex(T.label, "person", "name", "howard Deutch", "born", 1950);
+
+ keanu.addEdge("ACTED_IN", theReplacements, "roles", "Shane Falco");
+ brooke.addEdge("ACTED_IN", theReplacements, "roles", "annabelle Farrell");
+ gene.addEdge("ACTED_IN", theReplacements, "roles", "Jimmy McGinty");
+ orlando.addEdge("ACTED_IN", theReplacements, "roles", "Clifford Franklin");
+ howard.addEdge("DIRECTED", theReplacements, "score", 10);
+
+ Vertex rescueDawn = graph.addVertex(T.label, "movie", "title", "rescueDawn", "released", 2006);
+
+ Vertex christianB = graph.addVertex(T.label, "person", "name", "Christian Bale", "born", 1974);
+ Vertex zachG = graph.addVertex(T.label, "person", "name", "Zach Grenier", "born", 1954);
+
+ marshallB.addEdge("ACTED_IN", rescueDawn, "roles", "admiral");
+ christianB.addEdge("ACTED_IN", rescueDawn, "roles", "Dieter Dengler");
+ zachG.addEdge("ACTED_IN", rescueDawn, "roles", "Squad Leader");
+ steveZ.addEdge("ACTED_IN", rescueDawn, "roles", "Duane");
+ wernerH.addEdge("DIRECTED", rescueDawn, "score", 10);
+
+ Vertex theBirdcage = graph.addVertex(T.label, "movie", "title", "The Birdcage", "released", 1996);
+
+ Vertex mikeN = graph.addVertex(T.label, "person", "name", "Mike Nichols", "born", 1931);
+
+ robin.addEdge("ACTED_IN", theBirdcage, "roles", "armand Goldman");
+ nathan.addEdge("ACTED_IN", theBirdcage, "roles", "albert Goldman");
+ gene.addEdge("ACTED_IN", theBirdcage, "roles", "Sen. Kevin Keeley");
+ mikeN.addEdge("DIRECTED", theBirdcage, "score", 10);
+
+ Vertex unforgiven = graph.addVertex(T.label, "movie", "title", "unforgiven", "released", 1992);
+
+ Vertex richardH = graph.addVertex(T.label, "person", "name", "Richard Harris", "born", 1930);
+ Vertex clintE = graph.addVertex(T.label, "person", "name", "Richard Harris", "born", 1930);
+
+ richardH.addEdge("ACTED_IN", unforgiven, "roles", "English Bob");
+ clintE.addEdge("ACTED_IN", unforgiven, "roles", "Bill Munny");
+ gene.addEdge("ACTED_IN", unforgiven, "roles", "Little Bill Daggett");
+ clintE.addEdge("DIRECTED", unforgiven, "score", 10);
+
+ Vertex johnnyMnemonic = graph.addVertex(T.label, "movie", "title", "Johnny Mnemonic", "released", 1995);
+
+ Vertex takeshi = graph.addVertex(T.label, "person", "name", "takeshi Kitano", "born", 1947);
+ Vertex dina = graph.addVertex(T.label, "person", "name", "dina Meyer", "born", 1968);
+ Vertex iceT = graph.addVertex(T.label, "person", "name", "Ice-T", "born", 1958);
+ Vertex robertL = graph.addVertex(T.label, "person", "name", "Robert Longo", "born", 1953);
+
+ keanu.addEdge("ACTED_IN", johnnyMnemonic, "roles", "Johnny Mnemonic");
+ takeshi.addEdge("ACTED_IN", johnnyMnemonic, "roles", "Takahashi");
+ dina.addEdge("ACTED_IN", johnnyMnemonic, "roles", "Jane");
+ iceT.addEdge("ACTED_IN", johnnyMnemonic, "roles", "J-Bone");
+ robertL.addEdge("DIRECTED", johnnyMnemonic, "score", 10);
+
+ Vertex cloudatlas = graph.addVertex(T.label, "movie", "title", "Cloud atlas", "released", 2012);
+
+ Vertex halleB = graph.addVertex(T.label, "person", "name", "Halle Berry", "born", 1966);
+ Vertex jimB = graph.addVertex(T.label, "person", "name", "Jim Broadbent", "born", 1949);
+ Vertex tomT = graph.addVertex(T.label, "person", "name", "Tom Tykwer", "born", 1965);
+ Vertex davidMitchell = graph.addVertex(T.label, "person", "name", "David Mitchell", "born", 1969);
+ Vertex stefanarndt = graph.addVertex(T.label, "person", "name", "Stefan arndt", "born", 1961);
+
+ tomH.addEdge("ACTED_IN", cloudatlas, "roles", "Zachry, Dr. Henry Goose, Isaac Sachs, Dermot Hoggins");
+ hugo.addEdge("ACTED_IN", cloudatlas, "roles", "Bill Smoke, Haskell Moore, Tadeusz Kesselring, Nurse Noakes,"
+ + " Boardman Mephi, Old Georgie");
+ halleB.addEdge("ACTED_IN", cloudatlas, "roles", "Luisa Rey, Jocasta ayrs, Ovid, Meronym");
+ jimB.addEdge("ACTED_IN", cloudatlas, "roles", "Vyvyan ayrs, Captain Molyneux, Timothy Cavendish");
+ tomT.addEdge("DIRECTED", cloudatlas, "score", 10);
+ lillyW.addEdge("DIRECTED", cloudatlas, "score", 10);
+ lanaW.addEdge("DIRECTED", cloudatlas, "score", 10);
+ davidMitchell.addEdge("WROTE", cloudatlas, "score", 10);
+ stefanarndt.addEdge("PRODUCED", cloudatlas, "score", 10);
+
+ Vertex theDaVinciCode = graph.addVertex(T.label, "movie", "title", "The Da Vinci Code", "released", 2006);
+
+ Vertex ianM = graph.addVertex(T.label, "person", "name", "Ian McKellen", "born", 1939);
+ Vertex audreyT = graph.addVertex(T.label, "person", "name", "audrey Tautou", "born", 1976);
+ Vertex paulB = graph.addVertex(T.label, "person", "name", "Paul Bettany", "born", 1971);
+ Vertex ronH = graph.addVertex(T.label, "person", "name", "Ron howard", "born", 1954);
+
+ tomH.addEdge("ACTED_IN", theDaVinciCode, "roles", "Dr. Robert Langdon");
+ ianM.addEdge("ACTED_IN", theDaVinciCode, "roles", "Sir Leight Teabing");
+ audreyT.addEdge("ACTED_IN", theDaVinciCode, "roles", "Sophie Neveu");
+ paulB.addEdge("ACTED_IN", theDaVinciCode, "roles", "Silas");
+ ronH.addEdge("DIRECTED", theDaVinciCode, "score", 10);
+
+ Vertex vforVendetta = graph.addVertex(T.label, "movie", "title", "The Da Vinci Code", "released", 2006);
+
+ Vertex natalieP = graph.addVertex(T.label, "person", "name", "Natalie Portman", "born", 1981);
+ Vertex stephenR = graph.addVertex(T.label, "person", "name", "Stephen Rea", "born", 1946);
+ Vertex johnH = graph.addVertex(T.label, "person", "name", "John Hurt", "born", 1940);
+ Vertex benM = graph.addVertex(T.label, "person", "name", "Ben Miles", "born", 1967);
+
+ hugo.addEdge("ACTED_IN", vforVendetta, "roles", "V");
+ natalieP.addEdge("ACTED_IN", vforVendetta, "roles", "Evey Hammond");
+ stephenR.addEdge("ACTED_IN", vforVendetta, "roles", "Eric Finch");
+ johnH.addEdge("ACTED_IN", vforVendetta, "roles", "High Chancellor adam Sutler");
+ benM.addEdge("ACTED_IN", vforVendetta, "roles", "Dascomb");
+ jamesM.addEdge("DIRECTED", vforVendetta, "score", 10);
+ lillyW.addEdge("PRODUCED", vforVendetta, "score", 10);
+ lanaW.addEdge("PRODUCED", vforVendetta, "score", 10);
+ joelS.addEdge("PRODUCED", vforVendetta, "score", 10);
+ lillyW.addEdge("WROTE", vforVendetta, "score", 10);
+ lanaW.addEdge("WROTE", vforVendetta, "score", 10);
+
+ Vertex speedRacer = graph.addVertex(T.label, "movie", "title", "Speed Racer", "released", 2008);
+
+ Vertex matthewF = graph.addVertex(T.label, "person", "name", "Matthew Fox", "born", 1966);
+ Vertex emileH = graph.addVertex(T.label, "person", "name", "Emile Hirsch", "born", 1985);
+ Vertex johnG = graph.addVertex(T.label, "person", "name", "John Goodman", "born", 1940);
+ Vertex susanS = graph.addVertex(T.label, "person", "name", "Susan Sarandon", "born", 1966);
+ Vertex christinaR = graph.addVertex(T.label, "person", "name", "Christina Ricci", "born", 1980);
+ Vertex rain = graph.addVertex(T.label, "person", "name", "Rain", "born", 1982);
+
+ emileH.addEdge("ACTED_IN", speedRacer, "roles", "Speed Racer");
+ johnG.addEdge("ACTED_IN", speedRacer, "roles", "Pops");
+ susanS.addEdge("ACTED_IN", speedRacer, "roles", "Mom");
+ matthewF.addEdge("ACTED_IN", speedRacer, "roles", "Racer X");
+ christinaR.addEdge("ACTED_IN", speedRacer, "roles", "Trixie");
+ rain.addEdge("ACTED_IN", speedRacer, "roles", "Taejo Togokahn");
+ benM.addEdge("ACTED_IN", speedRacer, "roles", "Kass Jones");
+ lillyW.addEdge("DIRECTED", speedRacer, "score", 10);
+ lanaW.addEdge("DIRECTED", speedRacer, "score", 10);
+ lillyW.addEdge("WROTE", speedRacer, "score", 10);
+ lanaW.addEdge("WROTE", speedRacer, "score", 10);
+ joelS.addEdge("PRODUCED", speedRacer, "score", 10);
+
+ Vertex ninjaassassin = graph.addVertex(T.label, "movie", "title", "Speed Racer", "released", 2009);
+
+ Vertex naomieH = graph.addVertex(T.label, "person", "name", "Naomie Harris", "born", 1982);
+
+ rain.addEdge("ACTED_IN", ninjaassassin, "roles", "Raizo");
+ naomieH.addEdge("ACTED_IN", ninjaassassin, "roles", "Mika Coretti");
+ rickY.addEdge("ACTED_IN", ninjaassassin, "roles", "takeshi");
+ benM.addEdge("ACTED_IN", ninjaassassin, "roles", "Ryan Maslow");
+ jamesM.addEdge("DIRECTED", ninjaassassin, "score", 10);
+ lillyW.addEdge("PRODUCED", ninjaassassin, "score", 10);
+ lanaW.addEdge("PRODUCED", ninjaassassin, "score", 10);
+ joelS.addEdge("PRODUCED", ninjaassassin, "score", 10);
+
+ Vertex theGreenMile = graph.addVertex(T.label, "movie", "title", "The Green Mile", "released", 1999);
+
+ Vertex michaelD = graph.addVertex(T.label, "person", "name", "Michael Clarke Duncan", "born", 1957);
+ Vertex davidM = graph.addVertex(T.label, "person", "name", "David Morse", "born", 1953);
+ Vertex samR = graph.addVertex(T.label, "person", "name", "Sam Rockwell", "born", 1968);
+ Vertex garyS = graph.addVertex(T.label, "person", "name", "Gary Sinise", "born", 1955);
+ Vertex patriciaC = graph.addVertex(T.label, "person", "name", "Patricia Clarkson", "born", 1959);
+ Vertex frankD = graph.addVertex(T.label, "person", "name", "Frank Darabont", "born", 1959);
+
+ tomH.addEdge("ACTED_IN", theGreenMile, "roles", "Paul Edgecomb");
+ michaelD.addEdge("ACTED_IN", theGreenMile, "roles", "John Coffey");
+ davidM.addEdge("ACTED_IN", theGreenMile, "roles", "Brutus");
+ bonnieH.addEdge("ACTED_IN", theGreenMile, "roles", "Jan Edgecomb");
+ jamesC.addEdge("ACTED_IN", theGreenMile, "roles", "Warden Hal Moores");
+ samR.addEdge("ACTED_IN", theGreenMile, "roles", "Wild Bill' Wharton");
+ garyS.addEdge("ACTED_IN", theGreenMile, "roles", "Burt Hammersmith");
+ patriciaC.addEdge("ACTED_IN", theGreenMile, "roles", "Melinda Moores");
+ frankD.addEdge("DIRECTED", theGreenMile, "score", 10);
+
+ Vertex frostNixon = graph.addVertex(T.label, "movie", "title", "Frost/Nixon", "released", 2008);
+
+ Vertex frankL = graph.addVertex(T.label, "person", "name", "Frank Langella", "born", 1938);
+ Vertex michaelS = graph.addVertex(T.label, "person", "name", "Michael Sheen", "born", 1969);
+ Vertex oliverP = graph.addVertex(T.label, "person", "name", "Oliver Platt", "born", 1960);
+
+ frankL.addEdge("ACTED_IN", frostNixon, "roles", "Richard Nixon");
+ michaelS.addEdge("ACTED_IN", frostNixon, "roles", "David Frost");
+ kevinB.addEdge("ACTED_IN", frostNixon, "roles", "Jack Brennan");
+ oliverP.addEdge("ACTED_IN", frostNixon, "roles", "Bob Zelnick");
+ samR.addEdge("ACTED_IN", frostNixon, "roles", "James Reston, Jr.");
+ ronH.addEdge("DIRECTED", frostNixon, "score", 10);
+
+ Vertex hoffa = graph.addVertex(T.label, "movie", "title", "hoffa", "released", 1992);
+
+ Vertex dannyD = graph.addVertex(T.label, "person", "name", "Danny DeVito", "born", 1944);
+ Vertex johnR = graph.addVertex(T.label, "person", "name", "John C. Reilly", "born", 1965);
+
+ jackN.addEdge("ACTED_IN", hoffa, "roles", "hoffa");
+ dannyD.addEdge("ACTED_IN", hoffa, "roles", "Robert Ciaro");
+ jtw.addEdge("ACTED_IN", hoffa, "roles", "Frank Fitzsimmons");
+ johnR.addEdge("ACTED_IN", hoffa, "roles", "Peter Connelly");
+ dannyD.addEdge("DIRECTED", hoffa, "score", 10);
+
+ Vertex apollo13 = graph.addVertex(T.label, "movie", "title", "apollo 13", "released", 1995);
+
+ Vertex edH = graph.addVertex(T.label, "person", "name", "Ed Harris", "born", 1950);
+ Vertex billPax = graph.addVertex(T.label, "person", "name", "Bill Paxton", "born", 1955);
+
+ tomH.addEdge("ACTED_IN", apollo13, "roles", "Jim Lovell");
+ kevinB.addEdge("ACTED_IN", apollo13, "roles", "Jack Swigert");
+ edH.addEdge("ACTED_IN", apollo13, "roles", "gene Kranz");
+ billPax.addEdge("ACTED_IN", apollo13, "roles", "Fred Haise");
+ garyS.addEdge("ACTED_IN", apollo13, "roles", "Ken Mattingly");
+ ronH.addEdge("DIRECTED", apollo13, "score", 10);
+
+ Vertex twister = graph.addVertex(T.label, "movie", "title", "twister", "released", 1996);
+
+ Vertex philipH = graph.addVertex(T.label, "person", "name", "Philip Seymour Hoffman", "born", 1967);
+ Vertex janB = graph.addVertex(T.label, "person", "name", "Jan de Bont", "born", 1943);
+
+ billPax.addEdge("ACTED_IN", twister, "roles", "Bill Harding");
+ helenH.addEdge("ACTED_IN", twister, "roles", "Dr. Jo Harding");
+ zachG.addEdge("ACTED_IN", twister, "roles", "Eddie");
+ philipH.addEdge("ACTED_IN", twister, "roles", "Dustin 'Dusty' Davis");
+ janB.addEdge("DIRECTED", twister, "score", 10);
+
+ Vertex castaway = graph.addVertex(T.label, "movie", "title", "Cast away", "released", 2000);
+
+ Vertex robertZ = graph.addVertex(T.label, "person", "name", "Robert Zemeckis", "born", 1951);
+
+ tomH.addEdge("ACTED_IN", castaway, "roles", "Chuck Noland");
+ helenH.addEdge("ACTED_IN", castaway, "roles", "Kelly Frears");
+ robertZ.addEdge("DIRECTED", castaway, "score", 10);
+
+ Vertex oneFlewOvertheCuckoosNest = graph.addVertex(T.label, "movie", "title", "One Flew Over the Cuckoo's Nest", "released", 1975);
+
+ Vertex milosF = graph.addVertex(T.label, "person", "name", "Milos Forman", "born", 1932);
+
+ jackN.addEdge("ACTED_IN", oneFlewOvertheCuckoosNest, "roles", "Randle McMurphy");
+ dannyD.addEdge("ACTED_IN", oneFlewOvertheCuckoosNest, "roles", "Martini");
+ milosF.addEdge("DIRECTED", oneFlewOvertheCuckoosNest, "score", 10);
+
+ Vertex somethingsGottaGive = graph.addVertex(T.label, "movie", "title", "Something's Gotta Give", "released", 2003);
+
+ Vertex dianeK = graph.addVertex(T.label, "person", "name", "Diane Keaton", "born", 1946);
+ Vertex nancyM = graph.addVertex(T.label, "person", "name", "Nancy Meyers", "born", 1949);
+
+ jackN.addEdge("ACTED_IN", somethingsGottaGive, "roles", "Harry Sanborn");
+ dianeK.addEdge("ACTED_IN", somethingsGottaGive, "roles", "Erica Barry");
+ keanu.addEdge("ACTED_IN", somethingsGottaGive, "roles", "Julian Mercer");
+ nancyM.addEdge("DIRECTED", somethingsGottaGive, "score", 10);
+ nancyM.addEdge("PRODUCED", somethingsGottaGive, "score", 10);
+ nancyM.addEdge("WROTE", somethingsGottaGive, "score", 10);
+
+ Vertex bicentennialMan = graph.addVertex(T.label, "movie", "title", "Bicentennial Man", "released", 2000);
+
+ Vertex chrisC = graph.addVertex(T.label, "person", "name", "Chris Columbus", "born", 1958);
+
+ robin.addEdge("ACTED_IN", bicentennialMan, "roles", "andrew Marin");
+ oliverP.addEdge("ACTED_IN", bicentennialMan, "roles", "Rupert Burns");
+ chrisC.addEdge("DIRECTED", bicentennialMan, "score", 10);
+
+ Vertex charlieWilsonsWar = graph.addVertex(T.label, "movie", "title", "Charlie Wilson's War", "released", 2007);
+
+ Vertex juliaR = graph.addVertex(T.label, "person", "name", "Julia Roberts", "born", 1967);
+
+ tomH.addEdge("ACTED_IN", charlieWilsonsWar, "roles", "Rep. Charlie Wilson");
+ juliaR.addEdge("ACTED_IN", charlieWilsonsWar, "roles", "Joanne Herring");
+ philipH.addEdge("ACTED_IN", charlieWilsonsWar, "roles", "Gust avrakotos");
+ mikeN.addEdge("DIRECTED", charlieWilsonsWar, "score", 10);
+
+ Vertex thePolarExpress = graph.addVertex(T.label, "movie", "title", "The Polar Express", "released", 2004);
+
+ tomH.addEdge("ACTED_IN", thePolarExpress, "roles", "Hero Boy");
+
+ robertZ.addEdge("DIRECTED", thePolarExpress, "score", 10);
+
+ Vertex aLeagueofTheirOwn = graph.addVertex(T.label, "movie", "title", "a League of Their Own", "released", 1992);
+
+ Vertex madonna = graph.addVertex(T.label, "person", "name", "madonna", "born", 1954);
+ Vertex geenaD = graph.addVertex(T.label, "person", "name", "Geena Davis", "born", 1956);
+ Vertex loriP = graph.addVertex(T.label, "person", "name", "Lori Petty", "born", 1963);
+ Vertex pennyM = graph.addVertex(T.label, "person", "name", "Penny Marshall", "born", 1943);
+
+ tomH.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "Jimmy Dugan");
+ geenaD.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "Dottie Hinson");
+ loriP.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "Kit Keller");
+ rosieO.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "Doris Murphy");
+ madonna.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "all the Way' Mae Mordabito");
+ billPax.addEdge("ACTED_IN", aLeagueofTheirOwn, "roles", "Bob Hinson");
+ pennyM.addEdge("DIRECTED", aLeagueofTheirOwn, "score", 10);
+
+ hugeClient.close();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/example/SingleExample.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/SingleExample.java
new file mode 100644
index 0000000..d7e0523
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/example/SingleExample.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.example;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.GremlinManager;
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.Result;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+
+public class SingleExample {
+
+ public static void main(String[] args) throws IOException {
+ // If connect failed will throw a exception.
+ HugeClient hugeClient = HugeClient.builder("http://localhost:8080",
+ "hugegraph").build();
+
+ SchemaManager schema = hugeClient.schema();
+
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("age").asInt().ifNotExist().create();
+ schema.propertyKey("city").asText().ifNotExist().create();
+ schema.propertyKey("weight").asDouble().ifNotExist().create();
+ schema.propertyKey("lang").asText().ifNotExist().create();
+ schema.propertyKey("date").asDate().ifNotExist().create();
+ schema.propertyKey("price").asInt().ifNotExist().create();
+
+ schema.vertexLabel("person")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("software")
+ .properties("name", "lang", "price")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("personByAgeAndCity")
+ .onV("person")
+ .by("age", "city")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("softwareByPrice")
+ .onV("software")
+ .by("price")
+ .range()
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("knows")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .properties("date", "weight")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("created")
+ .sourceLabel("person").targetLabel("software")
+ .properties("date", "weight")
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("createdByDate")
+ .onE("created")
+ .by("date")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("createdByWeight")
+ .onE("created")
+ .by("weight")
+ .range()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("knowsByWeight")
+ .onE("knows")
+ .by("weight")
+ .range()
+ .ifNotExist()
+ .create();
+
+ GraphManager graph = hugeClient.graph();
+ Vertex marko = graph.addVertex(T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ Vertex vadas = graph.addVertex(T.label, "person", "name", "vadas",
+ "age", 27, "city", "Hongkong");
+ Vertex lop = graph.addVertex(T.label, "software", "name", "lop",
+ "lang", "java", "price", 328);
+ Vertex josh = graph.addVertex(T.label, "person", "name", "josh",
+ "age", 32, "city", "Beijing");
+ Vertex ripple = graph.addVertex(T.label, "software", "name", "ripple",
+ "lang", "java", "price", 199);
+ Vertex peter = graph.addVertex(T.label, "person", "name", "peter",
+ "age", 35, "city", "Shanghai");
+
+ marko.addEdge("knows", vadas, "date", "2016-01-10", "weight", 0.5);
+ marko.addEdge("knows", josh, "date", "2013-02-20", "weight", 1.0);
+ marko.addEdge("created", lop, "date", "2017-12-10", "weight", 0.4);
+ josh.addEdge("created", lop, "date", "2009-11-11", "weight", 0.4);
+ josh.addEdge("created", ripple, "date", "2017-12-10", "weight", 1.0);
+ peter.addEdge("created", lop, "date", "2017-03-24", "weight", 0.2);
+
+ GremlinManager gremlin = hugeClient.gremlin();
+ System.out.println("==== Path ====");
+ ResultSet resultSet = gremlin.gremlin("g.V().outE().path()").execute();
+ Iterator<Result> results = resultSet.iterator();
+ results.forEachRemaining(result -> {
+ System.out.println(result.getObject().getClass());
+ Object object = result.getObject();
+ if (object instanceof Vertex) {
+ System.out.println(((Vertex) object).id());
+ } else if (object instanceof Edge) {
+ System.out.println(((Edge) object).id());
+ } else if (object instanceof Path) {
+ List<Object> elements = ((Path) object).objects();
+ elements.forEach(element -> {
+ System.out.println(element.getClass());
+ System.out.println(element);
+ });
+ } else {
+ System.out.println(object);
+ }
+ });
+
+ hugeClient.close();
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidOperationException.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidOperationException.java
new file mode 100644
index 0000000..4a09898
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidOperationException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.exception;
+
+import com.baidu.hugegraph.rest.ClientException;
+
+public class InvalidOperationException extends ClientException {
+
+ private static final long serialVersionUID = -7618213317796656644L;
+
+ public InvalidOperationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidOperationException(String message, Object... args) {
+ super(message, args);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidResponseException.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidResponseException.java
new file mode 100644
index 0000000..7c95042
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/InvalidResponseException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.exception;
+
+import com.baidu.hugegraph.rest.ClientException;
+
+public class InvalidResponseException extends ClientException {
+
+ private static final long serialVersionUID = -6837901607110262081L;
+
+ public InvalidResponseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InvalidResponseException(String message, Object... args) {
+ super(message, args);
+ }
+
+ public static InvalidResponseException expectField(String expectField,
+ Object parentField) {
+ return new InvalidResponseException(
+ "Invalid response, expect '%s' in '%s'",
+ expectField, parentField);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotAllCreatedException.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotAllCreatedException.java
new file mode 100644
index 0000000..80e59a8
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotAllCreatedException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.exception;
+
+import java.util.Collection;
+
+public class NotAllCreatedException extends ServerException {
+
+ private static final long serialVersionUID = -8795820552805040556L;
+
+ private Collection<?> ids;
+
+ public NotAllCreatedException(String message, Collection<?> ids,
+ Object... args) {
+ super(message, args);
+ this.ids = ids;
+ }
+
+ public Collection<?> ids() {
+ return this.ids;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotSupportException.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotSupportException.java
new file mode 100644
index 0000000..f91ba0f
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/NotSupportException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.exception;
+
+import com.baidu.hugegraph.rest.ClientException;
+
+public class NotSupportException extends ClientException {
+
+ private static final long serialVersionUID = -8711375282196157056L;
+
+ private static final String PREFIX = "Not support ";
+
+ public NotSupportException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public NotSupportException(String message, Object... args) {
+ super(PREFIX + message, args);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/ServerException.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/ServerException.java
new file mode 100644
index 0000000..ea77229
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/exception/ServerException.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.exception;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.rest.RestResult;
+import jakarta.ws.rs.core.Response;
+
+public class ServerException extends RuntimeException {
+
+ private static final long serialVersionUID = 6335623004322652358L;
+
+ private static final String[] EXCEPTION_KEYS = {"exception",
+ "Exception-Class"};
+ private static final String[] MESSAGE_KEYS = {"message"};
+ private static final String[] CAUSE_KEYS = {"cause", "exceptions"};
+ private static final String[] TRACE_KEYS = {"trace", "stackTrace"};
+
+
+ private int status = 0;
+ private String exception;
+ private String message;
+ private String cause;
+ private Object trace;
+
+ public static ServerException fromResponse(Response response) {
+ RestResult rs = new RestResult(response);
+ ServerException exception = new ServerException(rs.content());
+ exception.status(response.getStatus());
+ try {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> json = rs.readObject(Map.class);
+ exception.exception = (String) getByKeys(json, EXCEPTION_KEYS);
+ exception.message = (String) getByKeys(json, MESSAGE_KEYS);
+ exception.cause = (String) getByKeys(json, CAUSE_KEYS);
+ exception.trace = getByKeys(json, TRACE_KEYS);
+ } catch (Exception ignored) {}
+
+ return exception;
+ }
+
+ public ServerException(String message) {
+ this.message = message;
+ }
+
+ public ServerException(String message, Object... args) {
+ this(String.format(message, args));
+ }
+
+ public String exception() {
+ return this.exception;
+ }
+
+ public String message() {
+ return this.message;
+ }
+
+ public String cause() {
+ return this.cause;
+ }
+
+ public Object trace() {
+ return this.trace;
+ }
+
+ @Override
+ public String getMessage() {
+ return this.message;
+ }
+
+ @Override
+ public Throwable getCause() {
+ if (this.cause() == null || this.cause().isEmpty()) {
+ return null;
+ }
+ return new ServerCause(this.cause());
+ }
+
+ public void status(int status) {
+ this.status = status;
+ }
+
+ public int status() {
+ return this.status;
+ }
+
+ @Override
+ public String toString() {
+ String s = this.exception;
+ String message = getLocalizedMessage();
+ return (message != null) ? (s + ": " + message) : s;
+ }
+
+ private static Object getByKeys(Map<String, Object> json, String[] keys) {
+ for (String key : keys) {
+ if (json.containsKey(key)) {
+ return json.get(key);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The stack trace of server exception
+ */
+ private static class ServerCause extends RuntimeException {
+
+ private static final long serialVersionUID = 8755660573085501031L;
+
+ public ServerCause(String cause) {
+ super(cause, null, true, false);
+ }
+
+ @Override
+ public String toString() {
+ return super.getMessage();
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/serializer/PathDeserializer.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/serializer/PathDeserializer.java
new file mode 100644
index 0000000..264aab1
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/serializer/PathDeserializer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.serializer;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import com.baidu.hugegraph.exception.InvalidResponseException;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.util.JsonUtil;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+
+public class PathDeserializer extends JsonDeserializer<Path> {
+
+ @Override
+ public Path deserialize(JsonParser parser, DeserializationContext ctxt)
+ throws IOException {
+
+ JsonNode node = parser.getCodec().readTree(parser);
+ Path path = new Path();
+
+ // Parse node 'labels'
+ JsonNode labelsNode = node.get("labels");
+ if (labelsNode != null) {
+ if (labelsNode.getNodeType() != JsonNodeType.ARRAY) {
+ throw InvalidResponseException.expectField("labels", node);
+ }
+ Object labels = JsonUtil.convertValue(labelsNode, Object.class);
+ ((List<?>) labels).forEach(path::labels);
+ }
+
+ // Parse node 'objects'
+ JsonNode objectsNode = node.get("objects");
+ if (objectsNode == null ||
+ objectsNode.getNodeType() != JsonNodeType.ARRAY) {
+ throw InvalidResponseException.expectField("objects", node);
+ }
+
+ Iterator<JsonNode> objects = objectsNode.elements();
+ while (objects.hasNext()) {
+ JsonNode objectNode = objects.next();
+ JsonNode typeNode = objectNode.get("type");
+ Object object;
+ if (typeNode != null) {
+ object = parseTypedNode(objectNode, typeNode);
+ } else {
+ object = JsonUtil.convertValue(objectNode, Object.class);
+ }
+ path.objects(object);
+ }
+
+ // Parse node 'crosspoint'
+ JsonNode crosspointNode = node.get("crosspoint");
+ if (crosspointNode != null) {
+ Object object = JsonUtil.convertValue(crosspointNode, Object.class);
+ path.crosspoint(object);
+ }
+ return path;
+ }
+
+ private Object parseTypedNode(JsonNode objectNode, JsonNode typeNode) {
+ String type = typeNode.asText();
+ if ("vertex".equals(type)) {
+ return JsonUtil.convertValue(objectNode, Vertex.class);
+ } else if ("edge".equals(type)) {
+ return JsonUtil.convertValue(objectNode, Edge.class);
+ } else {
+ throw InvalidResponseException.expectField("vertex/edge", type);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Element.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Element.java
new file mode 100644
index 0000000..4ad8a6c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Element.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure;
+
+import java.util.Objects;
+
+public abstract class Element {
+
+ public abstract String type();
+
+ public abstract Object id();
+
+ @Override
+ public int hashCode() {
+ return this.id().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || this.getClass() != other.getClass()) {
+ return false;
+ }
+ return Objects.equals(this.id(), ((Element) other).id());
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s(type %s)", this.id(), this.type());
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/GraphElement.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/GraphElement.java
new file mode 100644
index 0000000..042882c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/GraphElement.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.structure.constant.GraphAttachable;
+import com.baidu.hugegraph.util.E;
+import com.baidu.hugegraph.util.ReflectionUtil;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public abstract class GraphElement extends Element implements GraphAttachable {
+
+ // Hold a graphManager object to call graphApi
+ protected GraphManager manager;
+
+ @JsonProperty("label")
+ protected String label;
+ @JsonProperty("type")
+ protected String type;
+ @JsonProperty("properties")
+ protected Map<String, Object> properties;
+
+ public GraphElement() {
+ this.properties = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public void attachManager(GraphManager manager) {
+ this.manager = manager;
+ }
+
+ public String label() {
+ return this.label;
+ }
+
+ @Override
+ public String type() {
+ return this.type;
+ }
+
+ protected boolean fresh() {
+ return this.manager == null;
+ }
+
+ public Object property(String key) {
+ return this.properties.get(key);
+ }
+
+ public GraphElement property(String name, Object value) {
+ E.checkArgumentNotNull(name, "property name");
+ E.checkArgumentNotNull(value, "property value");
+
+ Class<?> clazz = value.getClass();
+ E.checkArgument(ReflectionUtil.isSimpleType(clazz) ||
+ clazz.equals(UUID.class) ||
+ clazz.equals(Date.class) ||
+ value instanceof List ||
+ value instanceof Set,
+ "Invalid property value type: '%s'", clazz);
+
+ this.properties.put(name, value);
+ return this;
+ }
+
+ public Map<String, Object> properties() {
+ return this.properties;
+ }
+
+ protected abstract GraphElement setProperty(String key, Object value);
+
+ public abstract GraphElement removeProperty(String key);
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/SchemaElement.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/SchemaElement.java
new file mode 100644
index 0000000..fe1131b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/SchemaElement.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public abstract class SchemaElement extends Element {
+
+ @JsonProperty("id")
+ protected long id;
+ @JsonProperty("name")
+ protected String name;
+ @JsonProperty("properties")
+ protected Set<String> properties;
+ @JsonProperty("check_exist")
+ protected boolean checkExist;
+ @JsonProperty("user_data")
+ protected Map<String, Object> userdata;
+ @JsonProperty("status")
+ protected String status;
+
+ public SchemaElement(String name) {
+ this.name = name;
+ this.properties = new ConcurrentSkipListSet<>();
+ this.userdata = new ConcurrentHashMap<>();
+ this.checkExist = true;
+ this.status = null;
+ }
+
+ @Override
+ public Long id() {
+ return this.id;
+ }
+
+ public void resetId() {
+ this.id = 0L;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public Set<String> properties() {
+ return this.properties;
+ }
+
+ public Map<String, Object> userdata() {
+ return this.userdata;
+ }
+
+ public String status() {
+ return this.status;
+ }
+
+ public boolean checkExist() {
+ return this.checkExist;
+ }
+
+ public void checkExist(boolean checkExist) {
+ this.checkExist = checkExist;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Task.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Task.java
new file mode 100644
index 0000000..bffc1a1
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/Task.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableSet;
+
+public class Task {
+
+ public static final long TASK_ID_NULL = 0L;
+
+ @JsonProperty
+ private long id;
+
+ @JsonProperty(P.TYPE)
+ private String type;
+
+ @JsonProperty(P.NAME)
+ private String name;
+
+ @JsonProperty(P.STATUS)
+ private String status;
+
+ @JsonProperty(P.CALLABLE)
+ private String callable;
+
+ @JsonProperty(P.CREATE)
+ private long create;
+
+ @JsonProperty(P.UPDATE)
+ private long update;
+
+ @JsonProperty(P.PROGRESS)
+ private long progress;
+
+ @JsonProperty(P.RETRIES)
+ private long retries;
+
+ @JsonProperty(P.INPUT)
+ private String input;
+
+ @JsonProperty(P.RESULT)
+ private Object result;
+
+ @JsonProperty(P.DESCRIPTION)
+ private String description;
+
+ @JsonProperty(P.DEPENDENCIES)
+ private Set<Long> dependencies;
+
+ @JsonProperty(P.SERVER)
+ private String server;
+
+ public long id() {
+ return this.id;
+ }
+
+ public String type() {
+ return this.type;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public String status() {
+ return this.status;
+ }
+
+ public String callable() {
+ return this.callable;
+ }
+
+ public long createTime() {
+ return this.create;
+ }
+
+ public long updateTime() {
+ return this.update;
+ }
+
+ public long progress() {
+ return this.progress;
+ }
+
+ public long retries() {
+ return this.retries;
+ }
+
+ public String input() {
+ return this.input;
+ }
+
+ public Object result() {
+ return this.result;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public Set<Long> dependencies() {
+ return this.dependencies;
+ }
+
+ public String server() {
+ return this.server;
+ }
+
+ public boolean completed() {
+ return ImmutableSet.of("success", "failed", "cancelled")
+ .contains(this.status);
+ }
+
+ public boolean cancelled() {
+ return "cancelled".equals(this.status);
+ }
+
+ public boolean cancelling() {
+ return "cancelling".equals(this.status);
+ }
+
+ public boolean success() {
+ return "success".equals(this.status);
+ }
+
+ public Map<String, Object> asMap() {
+ E.checkState(this.name != null, "Task name can't be null");
+
+ Map<String, Object> map = new HashMap<>();
+
+ map.put(P.ID, this.id);
+ map.put(P.TYPE, this.type);
+ map.put(P.NAME, this.name);
+ map.put(P.CALLABLE, this.callable);
+ map.put(P.STATUS, this.status);
+ map.put(P.PROGRESS, this.progress);
+ map.put(P.CREATE, this.create);
+ map.put(P.RETRIES, this.retries);
+ if (this.description != null) {
+ map.put(P.DESCRIPTION, this.description);
+ }
+ if (this.update != 0) {
+ map.put(P.UPDATE, this.update);
+ }
+ if (this.input != null) {
+ map.put(P.INPUT, this.input);
+ }
+ if (this.result != null) {
+ map.put(P.RESULT, this.result);
+ }
+ if (this.dependencies != null) {
+ map.put(P.DEPENDENCIES, this.dependencies);
+ }
+ if (this.server != null) {
+ map.put(P.SERVER, this.server);
+ }
+
+ return map;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Task(id=%s)", this.id);
+ }
+
+ public static final class P {
+
+ public static final String ID = "id";
+ public static final String TYPE = "task_type";
+ public static final String NAME = "task_name";
+ public static final String CALLABLE = "task_callable";
+ public static final String DESCRIPTION = "task_description";
+ public static final String STATUS = "task_status";
+ public static final String PROGRESS = "task_progress";
+ public static final String CREATE = "task_create";
+ public static final String UPDATE = "task_update";
+ public static final String RETRIES = "task_retries";
+ public static final String INPUT = "task_input";
+ public static final String RESULT = "task_result";
+ public static final String DEPENDENCIES = "task_dependencies";
+ public static final String SERVER = "task_server";
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Access.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Access.java
new file mode 100644
index 0000000..770ccb4
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Access.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Date;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Access extends AuthElement {
+
+ @JsonProperty("group")
+ private Object group;
+ @JsonProperty("target")
+ private Object target;
+ @JsonProperty("access_permission")
+ private HugePermission permission;
+ @JsonProperty("access_description")
+ private String description;
+
+ @JsonProperty("access_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date create;
+ @JsonProperty("access_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date update;
+ @JsonProperty("access_creator")
+ protected String creator;
+
+ @Override
+ public String type() {
+ return HugeType.ACCESS.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+
+ public Object group() {
+ return this.group;
+ }
+
+ public void group(Object group) {
+ if (group instanceof Group) {
+ group = ((Group) group).id();
+ }
+ this.group = group;
+ }
+
+ public Object target() {
+ return this.target;
+ }
+
+ public void target(Object target) {
+ if (target instanceof Target) {
+ target = ((Target) target).id();
+ }
+ this.target = target;
+ }
+
+ public HugePermission permission() {
+ return this.permission;
+ }
+
+ public void permission(HugePermission permission) {
+ this.permission = permission;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public void description(String description) {
+ this.description = description;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/AuthElement.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/AuthElement.java
new file mode 100644
index 0000000..840aac3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/AuthElement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Date;
+
+import com.baidu.hugegraph.structure.Element;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public abstract class AuthElement extends Element {
+
+ public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
+
+ @JsonProperty("id")
+ protected Object id;
+
+ @Override
+ public Object id() {
+ return this.id;
+ }
+
+ public abstract Date createTime();
+
+ public abstract Date updateTime();
+
+ public abstract String creator();
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Belong.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Belong.java
new file mode 100644
index 0000000..de0b072
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Belong.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Date;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Belong extends AuthElement {
+
+ @JsonProperty("user")
+ private Object user;
+ @JsonProperty("group")
+ private Object group;
+ @JsonProperty("belong_description")
+ private String description;
+
+ @JsonProperty("belong_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date create;
+ @JsonProperty("belong_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date update;
+ @JsonProperty("belong_creator")
+ protected String creator;
+
+ @Override
+ public String type() {
+ return HugeType.BELONG.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+
+ public Object user() {
+ return this.user;
+ }
+
+ public void user(Object user) {
+ if (user instanceof User) {
+ user = ((User) user).id();
+ }
+ this.user = user;
+ }
+
+ public Object group() {
+ return this.group;
+ }
+
+ public void group(Object group) {
+ if (group instanceof Group) {
+ group = ((Group) group).id();
+ }
+ this.group = group;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public void description(String description) {
+ this.description = description;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Group.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Group.java
new file mode 100644
index 0000000..ab53c9b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Group.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Date;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Group extends AuthElement {
+
+ @JsonProperty("group_name")
+ private String name;
+ @JsonProperty("group_description")
+ private String description;
+
+ @JsonProperty("group_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date create;
+ @JsonProperty("group_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date update;
+ @JsonProperty("group_creator")
+ protected String creator;
+
+ @Override
+ public String type() {
+ return HugeType.GROUP.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public void name(String name) {
+ this.name = name;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public void description(String description) {
+ this.description = description;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugePermission.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugePermission.java
new file mode 100644
index 0000000..0bd4782
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugePermission.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+public enum HugePermission {
+
+ NONE(0x00),
+
+ READ(0x01),
+ WRITE(0x02),
+ DELETE(0x04),
+ EXECUTE(0x08),
+
+ ANY(0x7f);
+
+ private final byte code;
+
+ HugePermission(int code) {
+ assert code < 256;
+ this.code = (byte) code;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name().toLowerCase();
+ }
+
+ public boolean match(HugePermission other) {
+ if (other == ANY) {
+ return this == ANY;
+ }
+ return (this.code & other.code) != 0;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResource.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResource.java
new file mode 100644
index 0000000..846d056
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResource.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Map;
+import java.util.Objects;
+
+import com.baidu.hugegraph.util.JsonUtil;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class HugeResource {
+
+ public static final String ANY = "*";
+
+ @JsonProperty("type")
+ private HugeResourceType type = HugeResourceType.NONE;
+
+ @JsonProperty("label")
+ private String label = ANY;
+
+ @JsonProperty("properties")
+ private Map<String, Object> properties; // value can be predicate
+
+ public HugeResource() {
+ // pass
+ }
+
+ public HugeResource(HugeResourceType type) {
+ this(type, ANY);
+ }
+
+ public HugeResource(HugeResourceType type, String label) {
+ this(type, label, null);
+ }
+
+ public HugeResource(HugeResourceType type, String label,
+ Map<String, Object> properties) {
+ this.type = type;
+ this.label = label;
+ this.properties = properties;
+ }
+
+
+ public HugeResourceType resourceType() {
+ return this.type;
+ }
+
+ public String label() {
+ return this.label;
+ }
+
+ public Map<String, Object> properties() {
+ return this.properties;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof HugeResource)) {
+ return false;
+ }
+ HugeResource other = (HugeResource) object;
+ return this.type == other.type &&
+ Objects.equals(this.label, other.label) &&
+ Objects.equals(this.properties, other.properties);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.type, this.label, this.properties);
+ }
+
+ @Override
+ public String toString() {
+ return JsonUtil.toJson(this);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResourceType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResourceType.java
new file mode 100644
index 0000000..5c2de4b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/HugeResourceType.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+public enum HugeResourceType {
+
+ NONE,
+
+ STATUS,
+
+ VERTEX,
+
+ EDGE,
+
+ VERTEX_AGGR,
+
+ EDGE_AGGR,
+
+ VAR,
+
+ GREMLIN,
+
+ TASK,
+
+ PROPERTY_KEY,
+
+ VERTEX_LABEL,
+
+ EDGE_LABEL,
+
+ INDEX_LABEL, // include create/rebuild/delete index
+
+ SCHEMA,
+
+ META,
+
+ ALL,
+
+ GRANT,
+
+ USER_GROUP,
+
+ PROJECT,
+
+ TARGET,
+
+ METRICS,
+
+ ROOT;
+
+ public boolean isGraph() {
+ int ord = this.ordinal();
+ return VERTEX.ordinal() <= ord && ord <= EDGE.ordinal();
+ }
+
+ public boolean isSchema() {
+ int ord = this.ordinal();
+ return PROPERTY_KEY.ordinal() <= ord && ord <= SCHEMA.ordinal();
+ }
+
+ public boolean isAuth() {
+ int ord = this.ordinal();
+ return GRANT.ordinal() <= ord && ord <= TARGET.ordinal();
+ }
+
+ public boolean isGrantOrUser() {
+ return this == GRANT || this == USER_GROUP;
+ }
+
+ public boolean isRepresentative() {
+ return this == ROOT || this == ALL || this == SCHEMA;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Login.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Login.java
new file mode 100644
index 0000000..f98cd2e
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Login.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Login {
+
+ @JsonProperty("user_name")
+ private String name;
+
+ @JsonProperty("user_password")
+ private String password;
+
+ public void name(String name) {
+ this.name = name;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public void password(String password) {
+ this.password = password;
+ }
+
+ public String password() {
+ return this.password;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/LoginResult.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/LoginResult.java
new file mode 100644
index 0000000..7f92df0
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/LoginResult.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class LoginResult {
+
+ @JsonProperty("token")
+ private String token;
+
+ public void token(String token) {
+ this.token = token;
+ }
+
+ public String token() {
+ return this.token;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Project.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Project.java
new file mode 100644
index 0000000..754dcc9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Project.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Project extends AuthElement {
+
+ @JsonProperty("project_name")
+ private String name;
+ @JsonProperty("project_admin_group")
+ private String adminGroup;
+ @JsonProperty("project_op_group")
+ private String opGroup;
+ @JsonProperty("project_graphs")
+ private Set<String> graphs;
+ @JsonProperty("project_target")
+ private String target;
+ @JsonProperty("project_description")
+ private String description;
+
+ @JsonProperty("project_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ private Date create;
+ @JsonProperty("project_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ private Date update;
+ @JsonProperty("project_creator")
+ private String creator;
+
+ public Project() {
+ }
+
+ public Project(Object id) {
+ this(id, null, null);
+ }
+
+ public Project(String name) {
+ this(name, null);
+ }
+
+ public Project(String name, String description) {
+ this(null, name, description);
+ }
+
+ public Project(Object id, String name, String description) {
+ this.id = id;
+ this.name = name;
+ this.description = description;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public void name(String name) {
+ this.name = name;
+ }
+
+ public String adminGroup() {
+ return this.adminGroup;
+ }
+
+ public String opGroup() {
+ return this.opGroup;
+ }
+
+ public Set<String> graphs() {
+ return this.graphs;
+ }
+
+ public void graphs(Set<String> graphs) {
+ if (graphs != null) {
+ this.graphs = new HashSet<>(graphs);
+ } else {
+ this.graphs = null;
+ }
+ }
+
+ public String target() {
+ return this.target;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public void description(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String type() {
+ return HugeType.PROJECT.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Target.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Target.java
new file mode 100644
index 0000000..e2a8721
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/Target.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Target extends AuthElement {
+
+ @JsonProperty("target_name")
+ private String name;
+ @JsonProperty("target_graph")
+ private String graph;
+ @JsonProperty("target_url")
+ private String url;
+ @JsonProperty("target_resources")
+ private List<HugeResource> resources;
+
+ @JsonProperty("target_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date create;
+ @JsonProperty("target_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date update;
+ @JsonProperty("target_creator")
+ protected String creator;
+
+ @Override
+ public String type() {
+ return HugeType.TARGET.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public void name(String name) {
+ this.name = name;
+ }
+
+ public String graph() {
+ return this.graph;
+ }
+
+ public void graph(String graph) {
+ this.graph = graph;
+ }
+
+ public String url() {
+ return this.url;
+ }
+
+ public void url(String url) {
+ this.url = url;
+ }
+
+ public HugeResource resource() {
+ if (this.resources == null || this.resources.size() != 1) {
+ return null;
+ }
+ return this.resources.get(0);
+ }
+
+ public List<HugeResource> resources() {
+ if (this.resources == null) {
+ return null;
+ }
+ return Collections.unmodifiableList(this.resources);
+ }
+
+ public void resources(List<HugeResource> resources) {
+ this.resources = resources;
+ }
+
+ public void resources(HugeResource... resources) {
+ this.resources = Arrays.asList(resources);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/TokenPayload.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/TokenPayload.java
new file mode 100644
index 0000000..925fa63
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/TokenPayload.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TokenPayload {
+
+ @JsonProperty("user_id")
+ private String userId;
+
+ @JsonProperty("user_name")
+ private String username;
+
+ private TokenPayload() {
+ }
+
+ public String userId() {
+ return this.userId;
+ }
+
+ public void userId(String userId) {
+ this.userId = userId;
+ }
+
+ public String username() {
+ return this.username;
+ }
+
+ public void username(String username) {
+ this.username = username;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/User.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/User.java
new file mode 100644
index 0000000..c06ebbd
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/auth/User.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.auth;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.util.JsonUtil;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class User extends AuthElement {
+
+ @JsonProperty("user_name")
+ private String name;
+ @JsonProperty("user_password")
+ private String password;
+ @JsonProperty("user_phone")
+ private String phone;
+ @JsonProperty("user_email")
+ private String email;
+ @JsonProperty("user_avatar")
+ private String avatar;
+ @JsonProperty("user_description")
+ private String description;
+
+ @JsonProperty("user_create")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date create;
+ @JsonProperty("user_update")
+ @JsonFormat(pattern = DATE_FORMAT)
+ protected Date update;
+ @JsonProperty("user_creator")
+ protected String creator;
+
+ @Override
+ public String type() {
+ return HugeType.USER.string();
+ }
+
+ @Override
+ public Date createTime() {
+ return this.create;
+ }
+
+ @Override
+ public Date updateTime() {
+ return this.update;
+ }
+
+ @Override
+ public String creator() {
+ return this.creator;
+ }
+
+ public String name() {
+ return this.name;
+ }
+
+ public void name(String name) {
+ this.name = name;
+ }
+
+ public String password() {
+ return this.password;
+ }
+
+ public void password(String password) {
+ this.password = password;
+ }
+
+ public String phone() {
+ return this.phone;
+ }
+
+ public void phone(String phone) {
+ this.phone = phone;
+ }
+
+ public String email() {
+ return this.email;
+ }
+
+ public void email(String email) {
+ this.email = email;
+ }
+
+ public String avatar() {
+ return this.avatar;
+ }
+
+ public void avatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public String description() {
+ return this.description;
+ }
+
+ public void description(String description) {
+ this.description = description;
+ }
+
+ public static class UserRole {
+
+ @JsonProperty("roles")
+ private Map<String, Map<HugePermission, List<HugeResource>>> roles;
+
+ public Map<String, Map<HugePermission, List<HugeResource>>> roles() {
+ return Collections.unmodifiableMap(this.roles);
+ }
+
+ @Override
+ public String toString() {
+ return JsonUtil.toJson(this);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/AggregateType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/AggregateType.java
new file mode 100644
index 0000000..7b8bc5f
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/AggregateType.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum AggregateType {
+
+ NONE(0, "none"),
+ MAX(1, "max"),
+ MIN(2, "min"),
+ SUM(3, "sum"),
+ OLD(4, "old");
+
+ private final byte code;
+ private final String name;
+
+ AggregateType(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public boolean isNone() {
+ return this == NONE;
+ }
+
+ public boolean isMax() {
+ return this == MAX;
+ }
+
+ public boolean isMin() {
+ return this == MIN;
+ }
+
+ public boolean isSum() {
+ return this == SUM;
+ }
+
+ public boolean isNumber() {
+ return this.isMax() || this.isMin() || this.isSum();
+ }
+
+ public boolean isOld() {
+ return this == OLD;
+ }
+
+ public boolean isIndexable() {
+ return this == NONE || this == MAX || this == MIN || this == OLD;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Cardinality.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Cardinality.java
new file mode 100644
index 0000000..879b0af
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Cardinality.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum Cardinality {
+
+ SINGLE(1, "single"),
+
+ LIST(2, "list"),
+
+ SET(3, "set");
+
+ // HugeKeys define
+ private byte code = 0;
+ private String name = null;
+
+ Cardinality(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/DataType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/DataType.java
new file mode 100644
index 0000000..380ea27
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/DataType.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.UUID;
+
+public enum DataType {
+
+ OBJECT(1, "object", Serializable.class),
+ BOOLEAN(2, "boolean", Boolean.class),
+ BYTE(3, "byte", Byte.class),
+ INT(4, "int", Integer.class),
+ LONG(5, "long", Long.class),
+ FLOAT(6, "float", Float.class),
+ DOUBLE(7, "double", Double.class),
+ TEXT(8, "text", String.class),
+ BLOB(9, "blob", byte[].class),
+ DATE(10, "date", Date.class),
+ UUID(11, "uuid", UUID.class);
+
+ private byte code = 0;
+ private String name = null;
+ private Class<?> clazz = null;
+
+ DataType(int code, String name, Class<?> clazz) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ this.clazz = clazz;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public Class<?> clazz() {
+ return this.clazz;
+ }
+
+ public boolean isNumber() {
+ return this == BYTE || this == INT || this == LONG ||
+ this == FLOAT || this == DOUBLE;
+ }
+
+ public boolean isDate() {
+ return this == DataType.DATE;
+ }
+
+ public boolean isUUID() {
+ return this == DataType.UUID;
+ }
+
+ public boolean isBoolean() {
+ return this == BOOLEAN;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Direction.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Direction.java
new file mode 100644
index 0000000..7d93c21
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Direction.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum Direction {
+
+ OUT(1, "out"),
+
+ IN(2, "in"),
+
+ BOTH(3, "both");
+
+ private byte code = 0;
+ private String name = null;
+
+ Direction(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Frequency.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Frequency.java
new file mode 100644
index 0000000..6345143
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Frequency.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum Frequency {
+
+ DEFAULT(0, "default"),
+
+ SINGLE(1, "single"),
+
+ MULTIPLE(2, "multiple");
+
+ private byte code = 0;
+ private String name = null;
+
+ Frequency(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphAttachable.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphAttachable.java
new file mode 100644
index 0000000..f87c105
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphAttachable.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+import com.baidu.hugegraph.driver.GraphManager;
+
+public interface GraphAttachable {
+
+ public void attachManager(GraphManager manager);
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphMode.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphMode.java
new file mode 100644
index 0000000..594e738
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphMode.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum GraphMode {
+
+ /*
+ * None mode is regular mode
+ * 1. Not allowed create schema with specified id
+ * 2. Not support create vertex with id for AUTOMATIC id strategy
+ */
+ NONE(1, "none"),
+
+ /*
+ * Restoring mode is used to restore schema and graph data to an new graph.
+ * 1. Support create schema with specified id
+ * 2. Support create vertex with id for AUTOMATIC id strategy
+ */
+ RESTORING(2, "restoring"),
+
+ /*
+ * MERGING mode is used to merge schema and graph data to an existing graph.
+ * 1. Not allowed create schema with specified id
+ * 2. Support create vertex with id for AUTOMATIC id strategy
+ */
+ MERGING(3, "merging"),
+
+ /*
+ * LOADING mode used to load data via hugegraph-loader.
+ */
+ LOADING(4, "loading");
+
+ private final byte code;
+ private final String name;
+
+ GraphMode(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public boolean maintaining() {
+ return this == RESTORING || this == MERGING;
+ }
+
+ public boolean loading() {
+ return this == LOADING;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphReadMode.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphReadMode.java
new file mode 100644
index 0000000..73fec49
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/GraphReadMode.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum GraphReadMode {
+
+ ALL(1, "all"),
+
+ OLTP_ONLY(2, "oltp_only"),
+
+ OLAP_ONLY(3, "olap_only");
+
+ private final byte code;
+ private final String name;
+
+ private GraphReadMode(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public boolean showOlap() {
+ return this == ALL || this == OLAP_ONLY;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/HugeType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/HugeType.java
new file mode 100644
index 0000000..f505dc9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/HugeType.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum HugeType {
+
+ // Schema
+ VERTEX_LABEL(1, "vertexlabels"),
+ EDGE_LABEL(2, "edgelabels"),
+ PROPERTY_KEY(3, "propertykeys"),
+ INDEX_LABEL(4, "indexlabels"),
+
+ // Auth
+ TARGET(50, "targets"),
+ GROUP(51, "groups"),
+ USER(52, "users"),
+ ACCESS(53, "accesses"),
+ BELONG(54, "belongs"),
+ PROJECT(55, "projects"),
+ LOGIN(56, "login"),
+ LOGOUT(57, "logout"),
+ TOKEN_VERIFY(58, "verify"),
+
+ // Data
+ VERTEX(101, "vertices"),
+ EDGE(120, "edges"),
+
+ // Variables
+ VARIABLES(130, "variables"),
+
+ // Task
+ TASK(140, "tasks"),
+
+ // Job
+ JOB(150, "jobs"),
+
+ // Gremlin
+ GREMLIN(201, "gremlin"),
+
+ GRAPHS(220, "graphs"),
+
+ // Version
+ VERSION(230, "versions"),
+
+ // Metrics
+ METRICS(240, "metrics");
+
+ private int code;
+ private String name = null;
+
+ HugeType(int code, String name) {
+ assert code < 256;
+ this.code = code;
+ this.name = name;
+ }
+
+ public int code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IdStrategy.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IdStrategy.java
new file mode 100644
index 0000000..4d4280a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IdStrategy.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum IdStrategy {
+
+ DEFAULT(0, "default"),
+
+ AUTOMATIC(1, "automatic"),
+
+ PRIMARY_KEY(2, "primary_key"),
+
+ CUSTOMIZE_STRING(3, "customize_string"),
+
+ CUSTOMIZE_NUMBER(4, "customize_number"),
+
+ CUSTOMIZE_UUID(5, "customize_uuid");
+
+ private byte code = 0;
+ private String name = null;
+
+ IdStrategy(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public boolean isAutomatic() {
+ return this == IdStrategy.AUTOMATIC;
+ }
+
+ public boolean isCustomize() {
+ return this == IdStrategy.CUSTOMIZE_STRING ||
+ this == IdStrategy.CUSTOMIZE_NUMBER ||
+ this == IdStrategy.CUSTOMIZE_UUID;
+ }
+
+ public boolean isCustomizeString() {
+ return this == IdStrategy.CUSTOMIZE_STRING;
+ }
+
+ public boolean isCustomizeNumber() {
+ return this == IdStrategy.CUSTOMIZE_NUMBER;
+ }
+
+ public boolean isCustomizeUuid() {
+ return this == IdStrategy.CUSTOMIZE_UUID;
+ }
+
+ public boolean isPrimaryKey() {
+ return this == IdStrategy.PRIMARY_KEY;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IndexType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IndexType.java
new file mode 100644
index 0000000..9b9bbe8
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/IndexType.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum IndexType {
+
+ // For secondary query
+ SECONDARY(1, "secondary"),
+
+ // For range query
+ RANGE(2, "range"),
+
+ // For full-text query (not supported now)
+ SEARCH(3, "search"),
+
+ // For prefix + range query
+ SHARD(4, "shard"),
+
+ // For unique properties
+ UNIQUE(5, "unique");
+
+ private byte code = 0;
+ private String name = null;
+
+ IndexType(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/T.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/T.java
new file mode 100644
index 0000000..f121662
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/T.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public class T {
+
+ public static final String id = "id";
+ public static final String label = "label";
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java
new file mode 100644
index 0000000..791e5ee
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public class Traverser {
+
+ public static final long DEFAULT_CAPACITY = 10_000_000L;
+ public static final long DEFAULT_LIMIT = 100L;
+ public static final long DEFAULT_ELEMENTS_LIMIT = 10_000_000L;
+ public static final long DEFAULT_MAX_DEGREE = 10_000L;
+ public static final long DEFAULT_CROSSPOINT_LIMIT = 10_000L;
+ public static final long DEFAULT_PATHS_LIMIT = 10L;
+ public static final long DEFAULT_DEDUP_SIZE = 1_000_000L;
+ public static final long DEFAULT_SKIP_DEGREE = 100_000L;
+ public static final long DEFAULT_SAMPLE = 100L;
+ public static final double DEFAULT_WEIGHT = 0.0D;
+ public static final long DEFAULT_PAGE_LIMIT = 100_000L;
+ public static final double DEFAULT_ALPHA = 0.9;
+ public static final int DEFAULT_MAX_TOP = 1000;
+ public static final int DEFAULT_MAX_DEPTH = 5000;
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/WriteType.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/WriteType.java
new file mode 100644
index 0000000..5052033
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/constant/WriteType.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.constant;
+
+public enum WriteType {
+
+ // OLTP property key
+ OLTP(1, "oltp"),
+
+ // OLAP property key without index
+ OLAP_COMMON(2, "olap_common"),
+
+ // OLAP property key with secondary index
+ OLAP_SECONDARY(3, "olap_secondary"),
+
+ // OLAP property key with range index
+ OLAP_RANGE(4, "olap_range");
+
+ private byte code = 0;
+ private String name = null;
+
+ WriteType(int code, String name) {
+ assert code < 256;
+ this.code = (byte) code;
+ this.name = name;
+ }
+
+ public byte code() {
+ return this.code;
+ }
+
+ public String string() {
+ return this.name;
+ }
+
+ public boolean oltp() {
+ return this == OLTP;
+ }
+
+ public boolean olap() {
+ return this == OLAP_COMMON ||
+ this == OLAP_RANGE ||
+ this == OLAP_SECONDARY;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchEdgeRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchEdgeRequest.java
new file mode 100644
index 0000000..b06703c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchEdgeRequest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BatchEdgeRequest {
+
+ @JsonProperty("edges")
+ private List<Edge> edges;
+ @JsonProperty("update_strategies")
+ private Map<String, UpdateStrategy> updateStrategies;
+ @JsonProperty("check_vertex")
+ private boolean checkVertex;
+ @JsonProperty("create_if_not_exist")
+ private boolean createIfNotExist;
+
+ public BatchEdgeRequest() {
+ this.edges = null;
+ this.updateStrategies = null;
+ this.checkVertex = false;
+ this.createIfNotExist = true;
+ }
+
+ public static Builder createBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BatchEdgeRequest{edges=%s," +
+ "updateStrategies=%s," +
+ "checkVertex=%s,createIfNotExist=%s}",
+ this.edges, this.updateStrategies,
+ this.checkVertex, this.createIfNotExist);
+ }
+
+ public static class Builder {
+
+ private BatchEdgeRequest req;
+
+ public Builder() {
+ this.req = new BatchEdgeRequest();
+ }
+
+ public Builder edges(List<Edge> edges) {
+ this.req.edges = edges;
+ return this;
+ }
+
+ public Builder updatingStrategies(Map<String, UpdateStrategy> map) {
+ this.req.updateStrategies = new HashMap<>(map);
+ return this;
+ }
+
+ public Builder updatingStrategy(String property,
+ UpdateStrategy strategy) {
+ this.req.updateStrategies.put(property, strategy);
+ return this;
+ }
+
+ public Builder checkVertex(boolean checkVertex) {
+ this.req.checkVertex = checkVertex;
+ return this;
+ }
+
+ public Builder createIfNotExist(boolean createIfNotExist) {
+ this.req.createIfNotExist = createIfNotExist;
+ return this;
+ }
+
+ public BatchEdgeRequest build() {
+ E.checkArgumentNotNull(req, "BatchEdgeRequest can't be null");
+ E.checkArgumentNotNull(req.edges,
+ "Parameter 'edges' can't be null");
+ E.checkArgument(req.updateStrategies != null &&
+ !req.updateStrategies.isEmpty(),
+ "Parameter 'update_strategies' can't be empty");
+ E.checkArgument(req.createIfNotExist == true,
+ "Parameter 'create_if_not_exist' " +
+ "dose not support false now");
+ return this.req;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchOlapPropertyRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchOlapPropertyRequest.java
new file mode 100644
index 0000000..2eda5be
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchOlapPropertyRequest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.List;
+
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BatchOlapPropertyRequest {
+
+ @JsonProperty("vertices")
+ private List<OlapVertex> vertices;
+ @JsonProperty("property_key")
+ private String propertyKey;
+
+ public BatchOlapPropertyRequest() {
+ this.vertices = null;
+ this.propertyKey = null;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BatchOlapPropertyRequest{vertices=%s," +
+ "propertyKey=%s}",
+ this.vertices, this.propertyKey);
+ }
+
+ public static class Builder {
+
+ private BatchOlapPropertyRequest req;
+
+ private Builder() {
+ this.req = new BatchOlapPropertyRequest();
+ }
+
+ public Builder vertices(List<OlapVertex> vertices) {
+ E.checkArgument(vertices != null && !vertices.isEmpty(),
+ "Parameter 'vertices' can't be null or empty");
+ this.req.vertices = vertices;
+ return this;
+ }
+
+ public Builder propertyKey(String propertyKey) {
+ E.checkArgumentNotNull(propertyKey,
+ "The property key can't be null");
+ this.req.propertyKey = propertyKey;
+ return this;
+ }
+
+ public BatchOlapPropertyRequest build() {
+ E.checkArgumentNotNull(req,
+ "BatchOlapPropertyRequest can't be null");
+ E.checkArgumentNotNull(req.vertices,
+ "Parameter 'vertices' can't be null");
+ E.checkArgument(req.propertyKey != null,
+ "Parameter 'property_key' can't be empty");
+ return this.req;
+ }
+ }
+
+ public static class OlapVertex {
+
+ @JsonProperty("id")
+ public Object id;
+ @JsonProperty("label")
+ public String label;
+ @JsonProperty("value")
+ public Object value;
+
+ @Override
+ public String toString() {
+ return String.format("OlapVertex{id=%s,label=%s,value=%s}",
+ this.id, this.label, this.value);
+ }
+
+ public static class Builder {
+
+ private OlapVertex vertex;
+
+ public Builder() {
+ this.vertex = new OlapVertex();
+ }
+
+ public Builder id(Object id) {
+ E.checkArgumentNotNull(id, "The id of vertex can't be null");
+ this.vertex.id = id;
+ return this;
+ }
+
+ public Builder label(String label) {
+ E.checkArgumentNotNull(label,
+ "The label of vertex can't be null");
+ this.vertex.label = label;
+ return this;
+ }
+
+ public Builder value(Object value) {
+ E.checkArgumentNotNull(value,
+ "The value of vertex can't be null");
+ this.vertex.value = value;
+ return this;
+ }
+
+ public OlapVertex build() {
+ E.checkArgumentNotNull(this.vertex.id,
+ "The id of vertex can't be null");
+ E.checkArgumentNotNull(this.vertex.label,
+ "The label of vertex can't be null");
+ E.checkArgumentNotNull(this.vertex.value,
+ "The value of vertex can't be null");
+ return this.vertex;
+ }
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchVertexRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchVertexRequest.java
new file mode 100644
index 0000000..ca57d62
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/BatchVertexRequest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class BatchVertexRequest {
+
+ @JsonProperty("vertices")
+ private List<Vertex> vertices;
+ @JsonProperty("update_strategies")
+ private Map<String, UpdateStrategy> updateStrategies;
+ @JsonProperty("create_if_not_exist")
+ private boolean createIfNotExist;
+
+ public BatchVertexRequest() {
+ this.vertices = null;
+ this.updateStrategies = null;
+ this.createIfNotExist = true;
+ }
+
+ public static Builder createBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BatchVertexRequest{vertices=%s," +
+ "updateStrategies=%s,createIfNotExist=%s}",
+ this.vertices, this.updateStrategies,
+ this.createIfNotExist);
+ }
+
+ public static class Builder {
+
+ private BatchVertexRequest req;
+
+ public Builder() {
+ this.req = new BatchVertexRequest();
+ }
+
+ public Builder vertices(List<Vertex> vertices) {
+ this.req.vertices = vertices;
+ return this;
+ }
+
+ public Builder updatingStrategies(Map<String, UpdateStrategy> map) {
+ this.req.updateStrategies = new HashMap<>(map);
+ return this;
+ }
+
+ public Builder updatingStrategy(String property,
+ UpdateStrategy strategy) {
+ this.req.updateStrategies.put(property, strategy);
+ return this;
+ }
+
+ public Builder createIfNotExist(boolean createIfNotExist) {
+ this.req.createIfNotExist = createIfNotExist;
+ return this;
+ }
+
+ public BatchVertexRequest build() {
+ E.checkArgumentNotNull(req, "BatchVertexRequest can't be null");
+ E.checkArgumentNotNull(req.vertices,
+ "Parameter 'vertices' can't be null");
+ E.checkArgument(req.updateStrategies != null &&
+ !req.updateStrategies.isEmpty(),
+ "Parameter 'update_strategies' can't be empty");
+ E.checkArgument(req.createIfNotExist == true,
+ "Parameter 'create_if_not_exist' " +
+ "dose not support false now");
+ return this.req;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edge.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edge.java
new file mode 100644
index 0000000..6d6c0df
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edge.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.util.E;
+import com.baidu.hugegraph.util.SplicingIdGenerator;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Edge extends GraphElement {
+
+ @JsonProperty("id")
+ private String id;
+ @JsonProperty("outV")
+ private Object sourceId;
+ @JsonProperty("inV")
+ private Object targetId;
+ @JsonProperty("outVLabel")
+ private String sourceLabel;
+ @JsonProperty("inVLabel")
+ private String targetLabel;
+
+ private Vertex source;
+ private Vertex target;
+ private String name;
+
+ @JsonCreator
+ public Edge(@JsonProperty("label") String label) {
+ this.label = label;
+ this.type = "edge";
+ this.sourceId = null;
+ this.targetId = null;
+ this.sourceLabel = null;
+ this.targetLabel = null;
+ this.source = null;
+ this.target = null;
+ this.name = null;
+ }
+
+ public String id() {
+ return this.id;
+ }
+
+ public void id(String id) {
+ this.id = id;
+ }
+
+ public Object sourceId() {
+ if (this.sourceId == null && this.source != null) {
+ this.sourceId = this.source.id();
+ }
+ if (this.sourceId == null) {
+ throw new InvalidOperationException(
+ "Must set source vertex id or add vertices " +
+ "before add edges");
+ }
+ return this.sourceId;
+ }
+
+ public void sourceId(Object sourceId) {
+ E.checkArgumentNotNull(sourceId, "The source vertex id can't be null");
+ this.sourceId = sourceId;
+ }
+
+ public Edge source(Vertex source) {
+ if (source.id() == null) {
+ this.source = source;
+ }
+ this.sourceId = source.id();
+ this.sourceLabel = source.label();
+ return this;
+ }
+
+ public Object targetId() {
+ if (this.targetId == null && this.target != null) {
+ this.targetId = this.target.id();
+ }
+ if (this.targetId == null) {
+ throw new InvalidOperationException(
+ "Must set target vertex id or add vertices " +
+ "before add edges");
+ }
+ return this.targetId;
+ }
+
+ public void targetId(Object targetId) {
+ E.checkArgumentNotNull(targetId, "The target vertex id can't be null");
+ this.targetId = targetId;
+ }
+
+ public Edge target(Vertex target) {
+ if (target.id() == null) {
+ this.target = target;
+ }
+ this.targetId = target.id();
+ this.targetLabel = target.label();
+ return this;
+ }
+
+ public boolean linkedVertex(Object vertexId) {
+ return this.sourceId.equals(vertexId) ||
+ this.targetId.equals(vertexId);
+ }
+
+ public boolean linkedVertex(Vertex vertex) {
+ return this.linkedVertex(vertex.id());
+ }
+
+ public String sourceLabel() {
+ return this.sourceLabel;
+ }
+
+ public void sourceLabel(String sourceLabel) {
+ this.sourceLabel = sourceLabel;
+ }
+
+ public String targetLabel() {
+ return this.targetLabel;
+ }
+
+ public void targetLabel(String targetLabel) {
+ this.targetLabel = targetLabel;
+ }
+
+ public String name() {
+ if (this.name == null) {
+ String[] idParts = SplicingIdGenerator.split(this.id);
+ E.checkState(idParts.length == 4,
+ "The edge id must be formatted by 4 parts, " +
+ "actual is %s", idParts.length);
+ this.name = idParts[2];
+ }
+ return this.name;
+ }
+
+ @Override
+ public Edge property(String key, Object value) {
+ E.checkNotNull(key, "The property name can not be null");
+ E.checkNotNull(value, "The property value can not be null");
+ if (this.fresh()) {
+ return (Edge) super.property(key, value);
+ } else {
+ return this.setProperty(key, value);
+ }
+ }
+
+ @Override
+ protected Edge setProperty(String key, Object value) {
+ Edge edge = new Edge(this.label);
+ edge.id(this.id);
+ edge.sourceId(this.sourceId);
+ edge.targetId(this.targetId);
+ edge.property(key, value);
+ // NOTE: append can also be used to update property
+ edge = this.manager.appendEdgeProperty(edge);
+
+ super.property(key, edge.property(key));
+ return this;
+ }
+
+ @Override
+ public Edge removeProperty(String key) {
+ E.checkNotNull(key, "The property name can not be null");
+ if (!this.properties.containsKey(key)) {
+ throw new InvalidOperationException(
+ "The edge '%s' doesn't have the property '%s'",
+ this.id, key);
+ }
+ Edge edge = new Edge(this.label);
+ edge.id(this.id);
+ edge.sourceId(this.sourceId);
+ edge.targetId(this.targetId);
+
+ Object value = this.properties.get(key);
+ edge.property(key, value);
+ this.manager.eliminateEdgeProperty(edge);
+
+ this.properties().remove(key);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{id=%s, sourceId=%s, sourceLabel=%s, " +
+ "targetId=%s, targetLabel=%s, " +
+ "label=%s, properties=%s}",
+ this.id, this.sourceId, this.sourceLabel,
+ this.targetId, this.targetLabel,
+ this.label, this.properties);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edges.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edges.java
new file mode 100644
index 0000000..fe824a7
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Edges.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Edges extends Pageable<Edge> {
+
+ @JsonProperty
+ private List<Edge> edges;
+
+ @JsonCreator
+ public Edges(@JsonProperty("edges") List<Edge> edges,
+ @JsonProperty("page") String page) {
+ super(page);
+ this.edges = edges;
+ }
+
+ @Override
+ public List<Edge> results() {
+ return this.edges;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Graph.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Graph.java
new file mode 100644
index 0000000..c682a0c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Graph.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.util.Log;
+
+/**
+ * HugeGraph is a mirror of server-side data(vertex/edge), it used to speed up
+ * data access. Note, however, the memory can't hold large amounts of data.
+ */
+public class Graph {
+
+ private static final Logger LOG = Log.logger(Graph.class);
+
+ private Map<Object, HugeVertex> hugeVerticesMap;
+ private List<HugeEdge> hugeEdges;
+
+ public Graph(GraphManager graph) {
+ LOG.debug("Loading Graph...");
+
+ List<Vertex> vertices = graph.listVertices();
+ LOG.debug("Loaded vertices: {}", vertices.size());
+
+ List<Edge> edges = graph.listEdges();
+ LOG.debug("Loaded edges: {}", edges.size());
+
+ this.mergeEdges2Vertices(vertices, edges);
+ LOG.debug("Loaded Graph");
+ }
+
+ public Graph(List<Vertex> vertices, List<Edge> edges) {
+ this.mergeEdges2Vertices(vertices, edges);
+ }
+
+ public Iterator<HugeVertex> vertices() {
+ return this.hugeVerticesMap.values().iterator();
+ }
+
+ public HugeVertex vertex(Object id) {
+ return this.hugeVerticesMap.get(id);
+ }
+
+ public Iterator<HugeEdge> edges() {
+ return this.hugeEdges.iterator();
+ }
+
+ private void mergeEdges2Vertices(List<Vertex> vertices,
+ List<Edge> edges) {
+ this.hugeVerticesMap = new HashMap<>(vertices.size());
+ for (Vertex v : vertices) {
+ this.hugeVerticesMap.put(v.id(), new HugeVertex(v));
+ }
+
+ this.hugeEdges = new ArrayList<>(edges.size());
+ for (Edge e : edges) {
+ HugeVertex src = this.hugeVerticesMap.get(e.sourceId());
+ HugeVertex tgt = this.hugeVerticesMap.get(e.targetId());
+
+ HugeEdge edge = new HugeEdge(e);
+ edge.source(src);
+ edge.target(tgt);
+
+ src.addEdge(edge);
+ tgt.addEdge(edge);
+
+ this.hugeEdges.add(edge);
+ }
+ }
+
+ public static class HugeVertex {
+
+ private Vertex vertex;
+ private List<HugeEdge> edges;
+
+ public HugeVertex(Vertex v) {
+ this.vertex = v;
+ this.edges = new ArrayList<>();
+ }
+
+ public Vertex vertex() {
+ return this.vertex;
+ }
+
+ public void vertex(Vertex vertex) {
+ this.vertex = vertex;
+ }
+
+ public void addEdge(HugeEdge e) {
+ this.edges.add(e);
+ }
+
+ public List<HugeEdge> getEdges() {
+ return this.edges;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("HugeVertex{vertex=%s, edges=%s}",
+ this.vertex, this.edges);
+ }
+ }
+
+ public static class HugeEdge {
+
+ private Edge edge;
+ private HugeVertex source;
+ private HugeVertex target;
+
+ public HugeEdge(Edge e) {
+ this.edge = e;
+ }
+
+ public Edge edge() {
+ return this.edge;
+ }
+
+ public HugeVertex source() {
+ return this.source;
+ }
+
+ public void source(HugeVertex source) {
+ this.source = source;
+ }
+
+ public HugeVertex target() {
+ return this.target;
+ }
+
+ public void target(HugeVertex target) {
+ this.target = target;
+ }
+
+ public HugeVertex other(HugeVertex vertex) {
+ return vertex == this.source ? this.target : this.source;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("HugeEdge{edge=%s, sourceId=%s, targetId=%s}",
+ this.edge,
+ this.source.vertex(),
+ this.target.vertex());
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/GraphIterator.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/GraphIterator.java
new file mode 100644
index 0000000..7394bb1
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/GraphIterator.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.function.Function;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.util.E;
+
+public class GraphIterator<T extends GraphElement> implements Iterator<T> {
+
+ private final GraphManager graphManager;
+ private final int sizePerPage;
+ private final Function<String, Pageable<T>> pageFetcher;
+ private List<T> results;
+ private String page;
+ private int cursor;
+ private boolean finished;
+
+ public GraphIterator(final GraphManager graphManager, final int sizePerPage,
+ final Function<String, Pageable<T>> pageFetcher) {
+ E.checkNotNull(graphManager, "Graph manager");
+ E.checkNotNull(pageFetcher, "Page fetcher");
+ this.graphManager = graphManager;
+ this.sizePerPage = sizePerPage;
+ this.pageFetcher = pageFetcher;
+ this.results = null;
+ this.page = "";
+ this.cursor = 0;
+ this.finished = false;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (this.results == null || this.cursor >= this.results.size()) {
+ this.fetch();
+ }
+ assert this.results != null;
+ return this.cursor < this.results.size();
+ }
+
+ private void fetch() {
+ if (this.finished) {
+ return;
+ }
+ Pageable<T> pageable = this.pageFetcher.apply(this.page);
+ this.results = pageable.results();
+ this.page = pageable.page();
+ this.cursor = 0;
+ E.checkState(this.results.size() <= this.sizePerPage,
+ "Server returned unexpected results: %s > %s",
+ this.results.size(), this.sizePerPage);
+ if (this.results.size() < this.sizePerPage || this.page == null) {
+ this.finished = true;
+ }
+ }
+
+ @Override
+ public T next() {
+ if (!this.hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ T elem = this.results.get(this.cursor++);
+ E.checkState(elem != null,
+ "The server data is invalid, some records are null");
+ elem.attachManager(this.graphManager);
+ return elem;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Pageable.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Pageable.java
new file mode 100644
index 0000000..83ba273
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Pageable.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.List;
+
+import com.baidu.hugegraph.structure.GraphElement;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public abstract class Pageable<T extends GraphElement> {
+
+ @JsonProperty
+ private String page;
+
+ @JsonCreator
+ public Pageable(@JsonProperty("page") String page) {
+ this.page = page;
+ }
+
+ public abstract List<T> results();
+
+ public String page() {
+ return this.page;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Path.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Path.java
new file mode 100644
index 0000000..546bbfc
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Path.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.structure.constant.GraphAttachable;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableList;
+
+public class Path implements GraphAttachable {
+
+ @JsonProperty
+ private List<Object> labels;
+ @JsonProperty
+ private List<Object> objects;
+ @JsonProperty
+ private Object crosspoint;
+
+ public Path() {
+ this(ImmutableList.of());
+ }
+
+ public Path(List<Object> objects) {
+ this(null, objects);
+ }
+
+ public Path(Object crosspoint, List<Object> objects) {
+ this.crosspoint = crosspoint;
+ this.labels = new CopyOnWriteArrayList<>();
+ this.objects = new CopyOnWriteArrayList<>(objects);
+ }
+
+ public List<Object> labels() {
+ return Collections.unmodifiableList(this.labels);
+ }
+
+ public void labels(Object... labels) {
+ this.labels.addAll(Arrays.asList(labels));
+ }
+
+ public List<Object> objects() {
+ return Collections.unmodifiableList(this.objects);
+ }
+
+ public void objects(Object... objects) {
+ this.objects.addAll(Arrays.asList(objects));
+ }
+
+ public Object crosspoint() {
+ return this.crosspoint;
+ }
+
+ public void crosspoint(Object crosspoint) {
+ this.crosspoint = crosspoint;
+ }
+
+ public int size() {
+ return this.objects.size();
+ }
+
+ @Override
+ public void attachManager(GraphManager manager) {
+ for (Object object : this.objects) {
+ if (object instanceof GraphAttachable) {
+ ((GraphAttachable) object).attachManager(manager);
+ }
+ }
+ if (this.crosspoint instanceof GraphAttachable) {
+ ((GraphAttachable) this.crosspoint).attachManager(manager);
+ }
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof Path)) {
+ return false;
+ }
+ Path other = (Path) object;
+ return Objects.equals(this.labels, other.labels) &&
+ Objects.equals(this.objects, other.objects) &&
+ Objects.equals(this.crosspoint, other.crosspoint);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{labels=%s, objects=%s, crosspoint=%s}",
+ this.labels, this.objects, this.crosspoint);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Shard.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Shard.java
new file mode 100644
index 0000000..295d6e6
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Shard.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Shard is used for backend storage (like cassandra, hbase) scanning
+ * operations. Each shard represents a range of tokens for a node.
+ * Reading data from a given shard does not cross multiple nodes.
+ */
+public class Shard {
+
+ @JsonProperty("start")
+ private String start;
+ @JsonProperty("end")
+ private String end;
+ @JsonProperty("length")
+ private long length;
+
+ public Shard() {
+ }
+
+ public Shard(String start, String end, long length) {
+ this.start = start;
+ this.end = end;
+ this.length = length;
+ }
+
+ public String start() {
+ return this.start;
+ }
+
+ public void start(String start) {
+ this.start = start;
+ }
+
+ public String end() {
+ return this.end;
+ }
+
+ public void end(String end) {
+ this.end = end;
+ }
+
+ public long length() {
+ return this.length;
+ }
+
+ public void length(long length) {
+ this.length = length;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Shard{start=%s, end=%s, length=%s}",
+ this.start, this.end, this.length);
+ }
+}
\ No newline at end of file
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/UpdateStrategy.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/UpdateStrategy.java
new file mode 100644
index 0000000..55a8001
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/UpdateStrategy.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+public enum UpdateStrategy {
+
+ SUM,
+
+ BIGGER,
+
+ SMALLER,
+
+ UNION,
+
+ INTERSECTION,
+
+ APPEND,
+
+ ELIMINATE,
+
+ OVERRIDE
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertex.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertex.java
new file mode 100644
index 0000000..0322410
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertex.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Vertex extends GraphElement {
+
+ @JsonProperty("id")
+ private Object id;
+
+ @JsonCreator
+ public Vertex(@JsonProperty("label") String label) {
+ this.label = label;
+ this.type = "vertex";
+ }
+
+ public Object id() {
+ return this.id;
+ }
+
+ public void id(Object id) {
+ this.id = id;
+ }
+
+ public Edge addEdge(String label, Vertex vertex, Object... properties) {
+ E.checkNotNull(label, "The edge label can not be null.");
+ E.checkNotNull(vertex, "The target vertex can not be null.");
+ return this.manager.addEdge(this, label, vertex, properties);
+ }
+
+ public Edge addEdge(String label, Vertex vertex,
+ Map<String, Object> properties) {
+ E.checkNotNull(label, "The edge label can not be null.");
+ E.checkNotNull(vertex, "The target vertex can not be null.");
+ return this.manager.addEdge(this, label, vertex, properties);
+ }
+
+ @Override
+ public Vertex property(String key, Object value) {
+ E.checkNotNull(key, "The property name can not be null");
+ E.checkNotNull(value, "The property value can not be null");
+ if (this.fresh()) {
+ return (Vertex) super.property(key, value);
+ } else {
+ return this.setProperty(key, value);
+ }
+ }
+
+ @Override
+ protected Vertex setProperty(String key, Object value) {
+ Vertex vertex = new Vertex(this.label);
+ vertex.id(this.id);
+ vertex.property(key, value);
+ // NOTE: append can also be used to update property
+ vertex = this.manager.appendVertexProperty(vertex);
+
+ super.property(key, vertex.property(key));
+ return this;
+ }
+
+ @Override
+ public Vertex removeProperty(String key) {
+ E.checkNotNull(key, "The property name can not be null");
+ if (!this.properties.containsKey(key)) {
+ throw new InvalidOperationException(
+ "The vertex '%s' doesn't have the property '%s'",
+ this.id, key);
+ }
+ Vertex vertex = new Vertex(this.label);
+ vertex.id(this.id);
+ Object value = this.properties.get(key);
+ vertex.property(key, value);
+ this.manager.eliminateVertexProperty(vertex);
+
+ this.properties().remove(key);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{id=%s, label=%s, properties=%s}",
+ this.id, this.label, this.properties);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertices.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertices.java
new file mode 100644
index 0000000..10ae842
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/graph/Vertices.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.graph;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Vertices extends Pageable<Vertex> {
+
+ @JsonProperty
+ private List<Vertex> vertices;
+
+ @JsonCreator
+ public Vertices(@JsonProperty("vertices") List<Vertex> vertices,
+ @JsonProperty("page") String page) {
+ super(page);
+ this.vertices = vertices;
+ }
+
+ @Override
+ public List<Vertex> results() {
+ return this.vertices;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Response.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Response.java
new file mode 100644
index 0000000..901b687
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Response.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.gremlin;
+
+import java.util.Map;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Response {
+
+ public static class Status {
+ @JsonProperty
+ private String message;
+ @JsonProperty
+ private int code;
+ @JsonProperty
+ private Map<String, ?> attributes;
+
+ public String message() {
+ return message;
+ }
+
+ public int code() {
+ return code;
+ }
+
+ public Map<String, ?> attributes() {
+ return attributes;
+ }
+ }
+
+ @JsonProperty
+ private String requestId;
+ @JsonProperty
+ private Status status;
+ @JsonProperty
+ private ResultSet result;
+
+ public void graphManager(GraphManager graphManager) {
+ this.result.graphManager(graphManager);
+ }
+
+ public String requestId() {
+ return this.requestId;
+ }
+
+ public Status status() {
+ return this.status;
+ }
+
+ public ResultSet result() {
+ return this.result;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Result.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Result.java
new file mode 100644
index 0000000..2d5a13e
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/Result.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.gremlin;
+
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+
+public class Result {
+
+ private Object object;
+
+ public Result(Object object) {
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return this.object;
+ }
+
+ public String getString() {
+ return this.object.toString();
+ }
+
+ public int getInt() {
+ return Integer.parseInt(this.object.toString());
+ }
+
+ public byte getByte() {
+ return Byte.parseByte(this.object.toString());
+ }
+
+ public short getShort() {
+ return Short.parseShort(this.object.toString());
+ }
+
+ public long getLong() {
+ return Long.parseLong(this.object.toString());
+ }
+
+ public float getFloat() {
+ return Float.parseFloat(this.object.toString());
+ }
+
+ public double getDouble() {
+ return Double.parseDouble(this.object.toString());
+ }
+
+ public boolean getBoolean() {
+ return Boolean.parseBoolean(this.object.toString());
+ }
+
+ public boolean isNull() {
+ return null == this.object;
+ }
+
+ public Vertex getVertex() {
+ return (Vertex) this.object;
+ }
+
+ public Edge getEdge() {
+ return (Edge) this.object;
+ }
+
+ public Path getPath() {
+ return (Path) this.object;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/ResultSet.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/ResultSet.java
new file mode 100644
index 0000000..3b93fa3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/gremlin/ResultSet.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.gremlin;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.rest.SerializeException;
+import com.baidu.hugegraph.serializer.PathDeserializer;
+import com.baidu.hugegraph.structure.constant.GraphAttachable;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class ResultSet {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private GraphManager graphManager = null;
+
+ @JsonProperty
+ private List<Object> data;
+ @JsonProperty
+ private Map<String, ?> meta;
+
+ static {
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(Path.class, new PathDeserializer());
+ MAPPER.registerModule(module);
+ }
+
+ public void graphManager(GraphManager graphManager) {
+ this.graphManager = graphManager;
+ }
+
+ public List<Object> data() {
+ return this.data;
+ }
+
+ public int size() {
+ return this.data.size();
+ }
+
+ public Result get(int index) {
+ if (index >= this.data.size()) {
+ return null;
+ }
+
+ Object object = this.data().get(index);
+ if (object == null) {
+ return null;
+ }
+
+ Class<?> clazz = this.parseResultClass(object);
+ // Primitive type
+ if (clazz.equals(object.getClass())) {
+ return new Result(object);
+ }
+
+ try {
+ String rawValue = MAPPER.writeValueAsString(object);
+ object = MAPPER.readValue(rawValue, clazz);
+ if (object instanceof GraphAttachable) {
+ ((GraphAttachable) object).attachManager(graphManager);
+ }
+ return new Result(object);
+ } catch (Exception e) {
+ throw new SerializeException(
+ "Failed to deserialize: %s", e, object);
+ }
+ }
+
+ /**
+ * TODO: Still need to constantly add and optimize
+ */
+ private Class<?> parseResultClass(Object object) {
+ if (object.getClass().equals(LinkedHashMap.class)) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> map = (Map<String, Object>) object;
+ String type = (String) map.get("type");
+ if (type != null) {
+ if ("vertex".equals(type)) {
+ return Vertex.class;
+ } else if ("edge".equals(type)) {
+ return Edge.class;
+ }
+ } else {
+ if (map.get("labels") != null) {
+ return Path.class;
+ }
+ }
+ }
+
+ return object.getClass();
+ }
+
+ public Iterator<Result> iterator() {
+ E.checkState(this.data != null, "Invalid response from server");
+ E.checkState(this.graphManager != null, "Must hold a graph manager");
+
+ return new Iterator<Result>() {
+
+ private int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return this.index < ResultSet.this.data.size();
+ }
+
+ @Override
+ public Result next() {
+ if (!this.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return get(this.index++);
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/BuilderProxy.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/BuilderProxy.java
new file mode 100644
index 0000000..d0460b0
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/BuilderProxy.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import com.baidu.hugegraph.exception.InvalidOperationException;
+
+public class BuilderProxy<T> implements InvocationHandler {
+
+ private boolean finished;
+ private T builder;
+ private T proxy;
+
+ @SuppressWarnings("unchecked")
+ public BuilderProxy(T builder) {
+ this.finished = false;
+ this.builder = builder;
+ this.proxy = (T) Proxy.newProxyInstance(
+ builder.getClass().getClassLoader(),
+ builder.getClass().getInterfaces(),
+ this);
+ }
+
+ public T proxy() {
+ return this.proxy;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ if (this.finished) {
+ throw new InvalidOperationException(
+ "Can't access builder which is completed");
+ }
+ // The result may be equal this.builder, like method `asText`
+ Object result = method.invoke(this.builder, args);
+ if (result == this.builder) {
+ result = this.proxy;
+ } else {
+ this.finished = true;
+ }
+ return result;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/EdgeLabel.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/EdgeLabel.java
new file mode 100644
index 0000000..7453784
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/EdgeLabel.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.Frequency;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.util.CollectionUtil;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class EdgeLabel extends SchemaLabel {
+
+ @JsonProperty("frequency")
+ private Frequency frequency;
+ @JsonProperty("source_label")
+ private String sourceLabel;
+ @JsonProperty("target_label")
+ private String targetLabel;
+ @JsonProperty("sort_keys")
+ private List<String> sortKeys;
+ @JsonProperty("ttl")
+ private long ttl;
+ @JsonProperty("ttl_start_time")
+ private String ttlStartTime;
+
+ @JsonCreator
+ public EdgeLabel(@JsonProperty("name") String name) {
+ super(name);
+ this.frequency = Frequency.DEFAULT;
+ this.sortKeys = new CopyOnWriteArrayList<>();
+ this.ttl = 0L;
+ this.ttlStartTime = null;
+ }
+
+ @Override
+ public String type() {
+ return HugeType.EDGE_LABEL.string();
+ }
+
+ public Frequency frequency() {
+ return this.frequency;
+ }
+
+ public String sourceLabel() {
+ return this.sourceLabel;
+ }
+
+ public String targetLabel() {
+ return this.targetLabel;
+ }
+
+ public boolean linkedVertexLabel(String vertexLabel) {
+ return this.sourceLabel.equals(vertexLabel) ||
+ this.targetLabel.equals(vertexLabel);
+ }
+
+ public List<String> sortKeys() {
+ return this.sortKeys;
+ }
+
+ public long ttl() {
+ return this.ttl;
+ }
+
+ public String ttlStartTime() {
+ return this.ttlStartTime;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, sourceLabel=%s, targetLabel=%s, " +
+ "sortKeys=%s, indexLabels=%s, nullableKeys=%s, " +
+ "properties=%s, ttl=%s, ttlStartTime=%s, " +
+ "status=%s}",
+ this.name, this.sourceLabel, this.targetLabel,
+ this.sortKeys, this.indexLabels,
+ this.nullableKeys, this.properties, this.ttl,
+ this.ttlStartTime, this.status);
+ }
+
+ public EdgeLabelV53 switchV53() {
+ return new EdgeLabelV53(this);
+ }
+
+ public interface Builder extends SchemaBuilder<EdgeLabel> {
+
+ Builder properties(String... properties);
+
+ Builder sortKeys(String... keys);
+
+ Builder nullableKeys(String... keys);
+
+ Builder link(String sourceLabel, String targetLabel);
+
+ Builder sourceLabel(String label);
+
+ Builder targetLabel(String label);
+
+ Builder frequency(Frequency frequency);
+
+ Builder singleTime();
+
+ Builder multiTimes();
+
+ Builder ttl(long ttl);
+
+ Builder ttlStartTime(String ttlStartTime);
+
+ Builder enableLabelIndex(boolean enable);
+
+ Builder userdata(String key, Object val);
+
+ Builder ifNotExist();
+ }
+
+ public static class BuilderImpl implements Builder {
+
+ private EdgeLabel edgeLabel;
+ private SchemaManager manager;
+
+ public BuilderImpl(String name, SchemaManager manager) {
+ this.edgeLabel = new EdgeLabel(name);
+ this.manager = manager;
+ }
+
+ @Override
+ public EdgeLabel build() {
+ return this.edgeLabel;
+ }
+
+ @Override
+ public EdgeLabel create() {
+ return this.manager.addEdgeLabel(this.edgeLabel);
+ }
+
+ @Override
+ public EdgeLabel append() {
+ return this.manager.appendEdgeLabel(this.edgeLabel);
+ }
+
+ @Override
+ public EdgeLabel eliminate() {
+ return this.manager.eliminateEdgeLabel(this.edgeLabel);
+ }
+
+ @Override
+ public void remove() {
+ this.manager.removeEdgeLabel(this.edgeLabel.name);
+ }
+
+ @Override
+ public Builder properties(String... properties) {
+ this.edgeLabel.properties.addAll(Arrays.asList(properties));
+ return this;
+ }
+
+ @Override
+ public Builder sortKeys(String... keys) {
+ E.checkArgument(this.edgeLabel.sortKeys.isEmpty(),
+ "Not allowed to assign sort keys multi times");
+ List<String> sortKeys = Arrays.asList(keys);
+ E.checkArgument(CollectionUtil.allUnique(sortKeys),
+ "Invalid sort keys %s, which contains some " +
+ "duplicate properties", sortKeys);
+ this.edgeLabel.sortKeys.addAll(sortKeys);
+ return this;
+ }
+
+ @Override
+ public Builder nullableKeys(String... keys) {
+ this.edgeLabel.nullableKeys.addAll(Arrays.asList(keys));
+ return this;
+ }
+
+ @Override
+ public Builder link(String sourceLabel, String targetLabel) {
+ this.edgeLabel.sourceLabel = sourceLabel;
+ this.edgeLabel.targetLabel = targetLabel;
+ return this;
+ }
+
+ @Override
+ public Builder sourceLabel(String label) {
+ this.edgeLabel.sourceLabel = label;
+ return this;
+ }
+
+ @Override
+ public Builder targetLabel(String label) {
+ this.edgeLabel.targetLabel = label;
+ return this;
+ }
+
+ @Override
+ public Builder frequency(Frequency frequency) {
+ this.checkFrequency();
+ this.edgeLabel.frequency = frequency;
+ return this;
+ }
+
+ @Override
+ public Builder singleTime() {
+ this.checkFrequency();
+ this.edgeLabel.frequency = Frequency.SINGLE;
+ return this;
+ }
+
+ @Override
+ public Builder multiTimes() {
+ this.checkFrequency();
+ this.edgeLabel.frequency = Frequency.MULTIPLE;
+ return this;
+ }
+
+
+ @Override
+ public Builder ttl(long ttl) {
+ E.checkArgument(ttl >= 0L, "The ttl must >= 0, but got: %s", ttl);
+ this.edgeLabel.ttl = ttl;
+ return this;
+ }
+
+ @Override
+ public Builder ttlStartTime(String ttlStartTime) {
+ this.edgeLabel.ttlStartTime = ttlStartTime;
+ return this;
+ }
+
+ @Override
+ public Builder enableLabelIndex(boolean enable) {
+ this.edgeLabel.enableLabelIndex = enable;
+ return this;
+ }
+
+ @Override
+ public Builder userdata(String key, Object val) {
+ E.checkArgumentNotNull(key, "The user data key can't be null");
+ E.checkArgumentNotNull(val, "The user data value can't be null");
+ this.edgeLabel.userdata.put(key, val);
+ return this;
+ }
+
+ @Override
+ public Builder ifNotExist() {
+ this.edgeLabel.checkExist = false;
+ return this;
+ }
+
+ private void checkFrequency() {
+ E.checkArgument(this.edgeLabel.frequency == Frequency.DEFAULT,
+ "Not allowed to change frequency for " +
+ "edge label '%s'", this.edgeLabel.name);
+ }
+ }
+
+ public static class EdgeLabelV53 extends SchemaLabel {
+
+ @JsonProperty("frequency")
+ private Frequency frequency;
+ @JsonProperty("source_label")
+ private String sourceLabel;
+ @JsonProperty("target_label")
+ private String targetLabel;
+ @JsonProperty("sort_keys")
+ private List<String> sortKeys;
+
+ @JsonCreator
+ public EdgeLabelV53(@JsonProperty("name") String name) {
+ super(name);
+ this.frequency = Frequency.DEFAULT;
+ this.sortKeys = new CopyOnWriteArrayList<>();
+ }
+
+ private EdgeLabelV53(EdgeLabel edgeLabel) {
+ super(edgeLabel.name);
+ this.frequency = edgeLabel.frequency;
+ this.sortKeys = edgeLabel.sortKeys;
+ this.sourceLabel = edgeLabel.sourceLabel;
+ this.targetLabel = edgeLabel.targetLabel;
+ this.id = edgeLabel.id();
+ this.properties = edgeLabel.properties();
+ this.userdata = edgeLabel.userdata();
+ this.checkExist = edgeLabel.checkExist();
+ this.nullableKeys = edgeLabel.nullableKeys;
+ this.enableLabelIndex = edgeLabel.enableLabelIndex;
+ }
+
+ public Frequency frequency() {
+ return this.frequency;
+ }
+
+ public List<String> sortKeys() {
+ return this.sortKeys;
+ }
+
+ public String sourceLabel() {
+ return this.sourceLabel;
+ }
+
+ public String targetLabel() {
+ return this.targetLabel;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, sourceLabel=%s, targetLabel=%s, " +
+ "sortKeys=%s, nullableKeys=%s, properties=%s}",
+ this.name, this.sourceLabel, this.targetLabel,
+ this.sortKeys, this.nullableKeys,
+ this.properties);
+ }
+
+ @Override
+ public String type() {
+ return HugeType.EDGE_LABEL.string();
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/IndexLabel.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/IndexLabel.java
new file mode 100644
index 0000000..b995140
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/IndexLabel.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.IndexType;
+import com.baidu.hugegraph.util.CollectionUtil;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties({"properties"})
+public class IndexLabel extends SchemaElement {
+
+ @JsonProperty("base_type")
+ protected HugeType baseType;
+ @JsonProperty("base_value")
+ protected String baseValue;
+ @JsonProperty("index_type")
+ protected IndexType indexType;
+ @JsonProperty("fields")
+ protected List<String> fields;
+ @JsonProperty("rebuild")
+ protected boolean rebuild = true;
+
+ @JsonCreator
+ public IndexLabel(@JsonProperty("name") String name) {
+ super(name);
+ this.indexType = null;
+ this.fields = new CopyOnWriteArrayList<>();
+ }
+
+ @Override
+ public String type() {
+ return HugeType.INDEX_LABEL.string();
+ }
+
+ public HugeType baseType() {
+ return this.baseType;
+ }
+
+ public String baseValue() {
+ return this.baseValue;
+ }
+
+ public IndexType indexType() {
+ return this.indexType;
+ }
+
+ public List<String> indexFields() {
+ return this.fields;
+ }
+
+ public boolean rebuild() {
+ return this.rebuild;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, baseType=%s, baseValue=%s, " +
+ "indexType=%s, fields=%s, status=%s}",
+ this.name, this.baseType, this.baseValue,
+ this.indexType, this.fields, this.status);
+ }
+
+ public IndexLabelV49 switchV49() {
+ return new IndexLabelV49(this);
+ }
+
+ public IndexLabelV56 switchV56() {
+ return new IndexLabelV56(this);
+ }
+
+ public interface Builder extends SchemaBuilder<IndexLabel> {
+
+ Builder on(boolean isVertex, String baseValue);
+
+ Builder onV(String baseValue);
+
+ Builder onE(String baseValue);
+
+ Builder by(String... fields);
+
+ Builder indexType(IndexType indexType);
+
+ Builder secondary();
+
+ Builder range();
+
+ Builder search();
+
+ Builder shard();
+
+ Builder unique();
+
+ Builder userdata(String key, Object val);
+
+ Builder ifNotExist();
+
+ Builder rebuild(boolean rebuild);
+ }
+
+ public static class BuilderImpl implements Builder {
+
+ private IndexLabel indexLabel;
+ private SchemaManager manager;
+
+ public BuilderImpl(String name, SchemaManager manager) {
+ this.indexLabel = new IndexLabel(name);
+ this.manager = manager;
+ }
+
+ @Override
+ public IndexLabel build() {
+ return this.indexLabel;
+ }
+
+ @Override
+ public IndexLabel create() {
+ return this.manager.addIndexLabel(this.indexLabel);
+ }
+
+ @Override
+ public IndexLabel append() {
+ return this.manager.appendIndexLabel(this.indexLabel);
+ }
+
+ @Override
+ public IndexLabel eliminate() {
+ return this.manager.eliminateIndexLabel(this.indexLabel);
+ }
+
+ @Override
+ public void remove() {
+ this.manager.removeIndexLabel(this.indexLabel.name);
+ }
+
+ @Override
+ public Builder on(boolean isVertex, String baseValue) {
+ return isVertex ? this.onV(baseValue) : this.onE(baseValue);
+ }
+
+ @Override
+ public Builder onV(String baseValue) {
+ this.indexLabel.baseType = HugeType.VERTEX_LABEL;
+ this.indexLabel.baseValue = baseValue;
+ return this;
+ }
+
+ @Override
+ public Builder onE(String baseValue) {
+ this.indexLabel.baseType = HugeType.EDGE_LABEL;
+ this.indexLabel.baseValue = baseValue;
+ return this;
+ }
+
+ @Override
+ public Builder by(String... fields) {
+ E.checkArgument(this.indexLabel.fields.isEmpty(),
+ "Not allowed to assign index fields multi times");
+ List<String> indexFields = Arrays.asList(fields);
+ E.checkArgument(CollectionUtil.allUnique(indexFields),
+ "Invalid index fields %s, which contains some " +
+ "duplicate properties", indexFields);
+ this.indexLabel.fields.addAll(indexFields);
+ return this;
+ }
+
+ @Override
+ public Builder indexType(IndexType indexType) {
+ this.indexLabel.indexType = indexType;
+ return this;
+ }
+
+ @Override
+ public Builder secondary() {
+ this.indexLabel.indexType = IndexType.SECONDARY;
+ return this;
+ }
+
+ @Override
+ public Builder range() {
+ this.indexLabel.indexType = IndexType.RANGE;
+ return this;
+ }
+
+ @Override
+ public Builder search() {
+ this.indexLabel.indexType = IndexType.SEARCH;
+ return this;
+ }
+
+ @Override
+ public Builder shard() {
+ this.indexLabel.indexType = IndexType.SHARD;
+ return this;
+ }
+
+ @Override
+ public Builder unique() {
+ this.indexLabel.indexType = IndexType.UNIQUE;
+ return this;
+ }
+
+ @Override
+ public Builder userdata(String key, Object val) {
+ E.checkArgumentNotNull(key, "The user data key can't be null");
+ E.checkArgumentNotNull(val, "The user data value can't be null");
+ this.indexLabel.userdata.put(key, val);
+ return this;
+ }
+
+ @Override
+ public Builder ifNotExist() {
+ this.indexLabel.checkExist = false;
+ return this;
+ }
+
+ @Override
+ public Builder rebuild(boolean rebuild) {
+ this.indexLabel.rebuild = rebuild;
+ return this;
+ }
+ }
+
+ public static class IndexLabelWithTask {
+
+ @JsonProperty("index_label")
+ private IndexLabel indexLabel;
+
+ @JsonProperty("task_id")
+ private long taskId;
+
+ public IndexLabel indexLabel() {
+ return this.indexLabel;
+ }
+
+ public long taskId() {
+ return this.taskId;
+ }
+ }
+
+ @JsonIgnoreProperties({"properties", "rebuild"})
+ public static class IndexLabelV56 extends IndexLabel {
+
+ public IndexLabelV56(IndexLabel indexLabel) {
+ super(indexLabel.name);
+ E.checkArgument(indexLabel.rebuild,
+ "The rebuild of indexlabel must be true");
+ this.id = indexLabel.id();
+ this.baseType = indexLabel.baseType;
+ this.baseValue = indexLabel.baseValue;
+ this.indexType = indexLabel.indexType;
+ this.fields = indexLabel.fields;
+ }
+
+ @Override
+ public boolean rebuild() {
+ throw new NotSupportException("rebuild for index label");
+ }
+ }
+
+ @JsonIgnoreProperties({"properties", "user_data", "rebuild"})
+ public static class IndexLabelV49 extends IndexLabel {
+
+ public IndexLabelV49(IndexLabel indexLabel) {
+ super(indexLabel.name);
+ E.checkArgument(indexLabel.userdata.isEmpty(),
+ "The userdata of indexlabel must be empty");
+ E.checkArgument(indexLabel.rebuild,
+ "The rebuild of indexlabel must be true");
+ this.id = indexLabel.id();
+ this.baseType = indexLabel.baseType;
+ this.baseValue = indexLabel.baseValue;
+ this.indexType = indexLabel.indexType;
+ this.fields = indexLabel.fields;
+ }
+
+ @Override
+ public Map<String, Object> userdata() {
+ throw new NotSupportException("user data for index label");
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/PropertyKey.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/PropertyKey.java
new file mode 100644
index 0000000..1be7d39
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/PropertyKey.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.structure.constant.AggregateType;
+import com.baidu.hugegraph.structure.constant.Cardinality;
+import com.baidu.hugegraph.structure.constant.DataType;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.WriteType;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PropertyKey extends SchemaElement {
+
+ @JsonProperty("data_type")
+ private DataType dataType;
+ @JsonProperty("cardinality")
+ private Cardinality cardinality;
+ @JsonProperty("aggregate_type")
+ private AggregateType aggregateType;
+ @JsonProperty("write_type")
+ private WriteType writeType;
+
+ @JsonCreator
+ public PropertyKey(@JsonProperty("name") String name) {
+ super(name);
+ this.dataType = DataType.TEXT;
+ this.cardinality = Cardinality.SINGLE;
+ this.aggregateType = AggregateType.NONE;
+ this.writeType = WriteType.OLTP;
+ }
+
+ @Override
+ public String type() {
+ return HugeType.PROPERTY_KEY.string();
+ }
+
+ public DataType dataType() {
+ return this.dataType;
+ }
+
+ public Cardinality cardinality() {
+ return this.cardinality;
+ }
+
+ public AggregateType aggregateType() {
+ return this.aggregateType;
+ }
+
+ public WriteType writeType() {
+ return this.writeType;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, cardinality=%s, dataType=%s, " +
+ "aggregateType=%s, properties=%s, " +
+ "writeType=%s}",
+ this.name, this.cardinality, this.dataType,
+ this.aggregateType, this.properties,
+ this.writeType);
+ }
+
+ public PropertyKeyV46 switchV46() {
+ return new PropertyKeyV46(this);
+ }
+
+ public PropertyKeyV58 switchV58() {
+ return new PropertyKeyV58(this);
+ }
+
+ public interface Builder extends SchemaBuilder<PropertyKey> {
+
+ Builder dataType(DataType dataType);
+
+ Builder asText();
+
+ Builder asInt();
+
+ Builder asDate();
+
+ Builder asUUID();
+
+ Builder asBoolean();
+
+ Builder asByte();
+
+ Builder asBlob();
+
+ Builder asDouble();
+
+ Builder asFloat();
+
+ Builder asLong();
+
+ Builder cardinality(Cardinality cardinality);
+
+ Builder valueSingle();
+
+ Builder valueList();
+
+ Builder valueSet();
+
+ Builder aggregateType(AggregateType aggregateType);
+
+ Builder writeType(WriteType writeType);
+
+ Builder calcSum();
+
+ Builder calcMax();
+
+ Builder calcMin();
+
+ Builder calcOld();
+
+ Builder userdata(String key, Object val);
+
+ Builder ifNotExist();
+ }
+
+ public static class BuilderImpl implements Builder {
+
+ private PropertyKey propertyKey;
+ private SchemaManager manager;
+
+ public BuilderImpl(String name, SchemaManager manager) {
+ this.propertyKey = new PropertyKey(name);
+ this.manager = manager;
+ }
+
+ @Override
+ public PropertyKey build() {
+ return this.propertyKey;
+ }
+
+ @Override
+ public PropertyKey create() {
+ return this.manager.addPropertyKey(this.propertyKey);
+ }
+
+ @Override
+ public PropertyKey append() {
+ return this.manager.appendPropertyKey(this.propertyKey);
+ }
+
+ @Override
+ public PropertyKey eliminate() {
+ return this.manager.eliminatePropertyKey(this.propertyKey);
+ }
+
+ @Override
+ public void remove() {
+ this.manager.removePropertyKey(this.propertyKey.name);
+ }
+
+ @Override
+ public Builder dataType(DataType dataType) {
+ this.propertyKey.dataType = dataType;
+ return this;
+ }
+
+ @Override
+ public Builder asText() {
+ this.propertyKey.dataType = DataType.TEXT;
+ return this;
+ }
+
+ @Override
+ public Builder asInt() {
+ this.propertyKey.dataType = DataType.INT;
+ return this;
+ }
+
+ @Override
+ public Builder asDate() {
+ this.propertyKey.dataType = DataType.DATE;
+ return this;
+ }
+
+ @Override
+ public Builder asUUID() {
+ this.propertyKey.dataType = DataType.UUID;
+ return this;
+ }
+
+ @Override
+ public Builder asBoolean() {
+ this.propertyKey.dataType = DataType.BOOLEAN;
+ return this;
+ }
+
+ @Override
+ public Builder asByte() {
+ this.propertyKey.dataType = DataType.BYTE;
+ return this;
+ }
+
+ @Override
+ public Builder asBlob() {
+ this.propertyKey.dataType = DataType.BLOB;
+ return this;
+ }
+
+ @Override
+ public Builder asDouble() {
+ this.propertyKey.dataType = DataType.DOUBLE;
+ return this;
+ }
+
+ @Override
+ public Builder asFloat() {
+ this.propertyKey.dataType = DataType.FLOAT;
+ return this;
+ }
+
+ @Override
+ public Builder asLong() {
+ this.propertyKey.dataType = DataType.LONG;
+ return this;
+ }
+
+ @Override
+ public Builder cardinality(Cardinality cardinality) {
+ this.propertyKey.cardinality = cardinality;
+ return this;
+ }
+
+ @Override
+ public Builder valueSingle() {
+ this.propertyKey.cardinality = Cardinality.SINGLE;
+ return this;
+ }
+
+ @Override
+ public Builder valueList() {
+ this.propertyKey.cardinality = Cardinality.LIST;
+ return this;
+ }
+
+ @Override
+ public Builder valueSet() {
+ this.propertyKey.cardinality = Cardinality.SET;
+ return this;
+ }
+
+ @Override
+ public Builder aggregateType(AggregateType aggregateType) {
+ this.propertyKey.aggregateType = aggregateType;
+ return this;
+ }
+
+ @Override
+ public Builder writeType(WriteType writeType) {
+ this.propertyKey.writeType = writeType;
+ return this;
+ }
+
+ @Override
+ public Builder calcSum() {
+ this.propertyKey.aggregateType = AggregateType.SUM;
+ return this;
+ }
+
+ @Override
+ public Builder calcMax() {
+ this.propertyKey.aggregateType = AggregateType.MAX;
+ return this;
+ }
+
+ @Override
+ public Builder calcMin() {
+ this.propertyKey.aggregateType = AggregateType.MIN;
+ return this;
+ }
+
+ @Override
+ public Builder calcOld() {
+ this.propertyKey.aggregateType = AggregateType.OLD;
+ return this;
+ }
+
+ @Override
+ public Builder userdata(String key, Object val) {
+ E.checkArgumentNotNull(key, "The user data key can't be null");
+ E.checkArgumentNotNull(val, "The user data value can't be null");
+ this.propertyKey.userdata.put(key, val);
+ return this;
+ }
+
+ @Override
+ public Builder ifNotExist() {
+ this.propertyKey.checkExist = false;
+ return this;
+ }
+ }
+
+ public static class PropertyKeyWithTask {
+
+ @JsonProperty("property_key")
+ private PropertyKey propertyKey;
+
+ @JsonProperty("task_id")
+ private long taskId;
+
+ @JsonCreator
+ public PropertyKeyWithTask(@JsonProperty("property_key")
+ PropertyKey propertyKey,
+ @JsonProperty("task_id")
+ long taskId) {
+ this.propertyKey = propertyKey;
+ this.taskId = taskId;
+ }
+
+ public PropertyKey propertyKey() {
+ return this.propertyKey;
+ }
+
+ public long taskId() {
+ return this.taskId;
+ }
+ }
+
+ public static class PropertyKeyV46 extends SchemaElement {
+
+ @JsonProperty("data_type")
+ private DataType dataType;
+ @JsonProperty("cardinality")
+ private Cardinality cardinality;
+
+ @JsonCreator
+ public PropertyKeyV46(@JsonProperty("name") String name) {
+ super(name);
+ this.dataType = DataType.TEXT;
+ this.cardinality = Cardinality.SINGLE;
+ }
+
+ private PropertyKeyV46(PropertyKey propertyKey) {
+ super(propertyKey.name);
+ this.dataType = propertyKey.dataType;
+ this.cardinality = propertyKey.cardinality;
+ this.id = propertyKey.id();
+ this.properties = propertyKey.properties();
+ this.userdata = propertyKey.userdata();
+ this.checkExist = propertyKey.checkExist();
+ }
+
+ public DataType dataType() {
+ return this.dataType;
+ }
+
+ public Cardinality cardinality() {
+ return this.cardinality;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, cardinality=%s, dataType=%s, " +
+ "properties=%s}", this.name, this.cardinality,
+ this.dataType, this.properties);
+ }
+
+ @Override
+ public String type() {
+ return HugeType.PROPERTY_KEY.string();
+ }
+ }
+
+ public static class PropertyKeyV58 extends PropertyKeyV46 {
+
+ @JsonProperty("aggregate_type")
+ private AggregateType aggregateType;
+
+ @JsonCreator
+ public PropertyKeyV58(@JsonProperty("name") String name) {
+ super(name);
+ this.aggregateType = AggregateType.NONE;
+ }
+
+ private PropertyKeyV58(PropertyKey propertyKey) {
+ super(propertyKey);
+ this.aggregateType = propertyKey.aggregateType;
+ }
+
+ public AggregateType aggregateType() {
+ return this.aggregateType;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, cardinality=%s, dataType=%s, " +
+ "aggregateType=%s, properties=%s}", this.name,
+ this.cardinality(), this.dataType(),
+ this.aggregateType, this.properties);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaBuilder.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaBuilder.java
new file mode 100644
index 0000000..b728275
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaBuilder.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import com.baidu.hugegraph.structure.SchemaElement;
+
+public interface SchemaBuilder<T extends SchemaElement> {
+
+ T build();
+
+ T create();
+
+ T append();
+
+ T eliminate();
+
+ void remove();
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaLabel.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaLabel.java
new file mode 100644
index 0000000..89dd4c3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/SchemaLabel.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
+
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableList;
+
+public abstract class SchemaLabel extends SchemaElement {
+
+ @JsonProperty("nullable_keys")
+ protected Set<String> nullableKeys;
+ @JsonProperty("enable_label_index")
+ protected Boolean enableLabelIndex;
+ @JsonProperty("index_labels")
+ protected List<String> indexLabels;
+
+ public SchemaLabel(String name) {
+ super(name);
+ this.nullableKeys = new ConcurrentSkipListSet<>();
+ this.indexLabels = null;
+ this.enableLabelIndex = null;
+ }
+
+ public Set<String> nullableKeys() {
+ return this.nullableKeys;
+ }
+
+ public List<String> indexLabels() {
+ if (this.indexLabels == null) {
+ return ImmutableList.of();
+ }
+ return Collections.unmodifiableList(this.indexLabels);
+ }
+
+ public boolean enableLabelIndex() {
+ return this.enableLabelIndex;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/VertexLabel.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/VertexLabel.java
new file mode 100644
index 0000000..c859f92
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/schema/VertexLabel.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.schema;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.IdStrategy;
+import com.baidu.hugegraph.util.CollectionUtil;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class VertexLabel extends SchemaLabel {
+
+ @JsonProperty("id_strategy")
+ private IdStrategy idStrategy;
+ @JsonProperty("primary_keys")
+ private List<String> primaryKeys;
+ @JsonProperty("ttl")
+ private long ttl;
+ @JsonProperty("ttl_start_time")
+ private String ttlStartTime;
+
+ @JsonCreator
+ public VertexLabel(@JsonProperty("name") String name) {
+ super(name);
+ this.idStrategy = IdStrategy.DEFAULT;
+ this.primaryKeys = new CopyOnWriteArrayList<>();
+ this.ttl = 0L;
+ this.ttlStartTime = null;
+ }
+
+ @Override
+ public String type() {
+ return HugeType.VERTEX_LABEL.string();
+ }
+
+ public IdStrategy idStrategy() {
+ return this.idStrategy;
+ }
+
+ public List<String> primaryKeys() {
+ return this.primaryKeys;
+ }
+
+ public long ttl() {
+ return this.ttl;
+ }
+
+ public String ttlStartTime() {
+ return this.ttlStartTime;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, idStrategy=%s, primaryKeys=%s, " +
+ "indexLabels=%s, nullableKeys=%s, properties=%s," +
+ " ttl=%s, ttlStartTime=%s, status=%s}",
+ this.name, this.idStrategy, this.primaryKeys,
+ this.indexLabels, this.nullableKeys,
+ this.properties, this.ttl, this.ttlStartTime,
+ this.status);
+ }
+
+ public VertexLabelV53 switchV53() {
+ return new VertexLabelV53(this);
+ }
+
+ public interface Builder extends SchemaBuilder<VertexLabel> {
+
+ Builder idStrategy(IdStrategy idStrategy);
+
+ Builder useAutomaticId();
+
+ Builder usePrimaryKeyId();
+
+ Builder useCustomizeStringId();
+
+ Builder useCustomizeNumberId();
+
+ Builder useCustomizeUuidId();
+
+ Builder properties(String... properties);
+
+ Builder primaryKeys(String... keys);
+
+ Builder nullableKeys(String... keys);
+
+ Builder ttl(long ttl);
+
+ Builder ttlStartTime(String ttlStartTime);
+
+ Builder enableLabelIndex(boolean enable);
+
+ Builder userdata(String key, Object val);
+
+ Builder ifNotExist();
+ }
+
+ public static class BuilderImpl implements Builder {
+
+ private VertexLabel vertexLabel;
+ private SchemaManager manager;
+
+ public BuilderImpl(String name, SchemaManager manager) {
+ this.vertexLabel = new VertexLabel(name);
+ this.manager = manager;
+ }
+
+ @Override
+ public VertexLabel build() {
+ return this.vertexLabel;
+ }
+
+ @Override
+ public VertexLabel create() {
+ return this.manager.addVertexLabel(this.vertexLabel);
+ }
+
+ @Override
+ public VertexLabel append() {
+ return this.manager.appendVertexLabel(this.vertexLabel);
+ }
+
+ @Override
+ public VertexLabel eliminate() {
+ return this.manager.eliminateVertexLabel(this.vertexLabel);
+ }
+
+ @Override
+ public void remove() {
+ this.manager.removeVertexLabel(this.vertexLabel.name);
+ }
+
+ @Override
+ public Builder idStrategy(IdStrategy idStrategy) {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = idStrategy;
+ return this;
+ }
+
+ @Override
+ public Builder useAutomaticId() {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = IdStrategy.AUTOMATIC;
+ return this;
+ }
+
+ @Override
+ public Builder usePrimaryKeyId() {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = IdStrategy.PRIMARY_KEY;
+ return this;
+ }
+
+ @Override
+ public Builder useCustomizeStringId() {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = IdStrategy.CUSTOMIZE_STRING;
+ return this;
+ }
+
+ @Override
+ public Builder useCustomizeNumberId() {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = IdStrategy.CUSTOMIZE_NUMBER;
+ return this;
+ }
+
+ @Override
+ public Builder useCustomizeUuidId() {
+ this.checkIdStrategy();
+ this.vertexLabel.idStrategy = IdStrategy.CUSTOMIZE_UUID;
+ return this;
+ }
+
+ @Override
+ public Builder properties(String... properties) {
+ this.vertexLabel.properties.addAll(Arrays.asList(properties));
+ return this;
+ }
+
+ @Override
+ public Builder primaryKeys(String... keys) {
+ E.checkArgument(this.vertexLabel.primaryKeys.isEmpty(),
+ "Not allowed to assign primary keys multi times");
+ List<String> primaryKeys = Arrays.asList(keys);
+ E.checkArgument(CollectionUtil.allUnique(primaryKeys),
+ "Invalid primary keys %s, which contains some " +
+ "duplicate properties", primaryKeys);
+ this.vertexLabel.primaryKeys.addAll(primaryKeys);
+ return this;
+ }
+
+ @Override
+ public Builder nullableKeys(String... keys) {
+ this.vertexLabel.nullableKeys.addAll(Arrays.asList(keys));
+ return this;
+ }
+
+ @Override
+ public Builder ttl(long ttl) {
+ E.checkArgument(ttl >= 0L, "The ttl must >= 0, but got: %s", ttl);
+ this.vertexLabel.ttl = ttl;
+ return this;
+ }
+
+ @Override
+ public Builder ttlStartTime(String ttlStartTime) {
+ this.vertexLabel.ttlStartTime = ttlStartTime;
+ return this;
+ }
+
+ @Override
+ public Builder enableLabelIndex(boolean enable) {
+ this.vertexLabel.enableLabelIndex = enable;
+ return this;
+ }
+
+ @Override
+ public Builder userdata(String key, Object val) {
+ E.checkArgumentNotNull(key, "The user data key can't be null");
+ E.checkArgumentNotNull(val, "The user data value can't be null");
+ this.vertexLabel.userdata.put(key, val);
+ return this;
+ }
+
+ @Override
+ public Builder ifNotExist() {
+ this.vertexLabel.checkExist = false;
+ return this;
+ }
+
+ private void checkIdStrategy() {
+ E.checkArgument(this.vertexLabel.idStrategy == IdStrategy.DEFAULT,
+ "Not allowed to change id strategy for " +
+ "vertex label '%s'", this.vertexLabel.name);
+ }
+ }
+
+ public static class VertexLabelV53 extends SchemaLabel {
+
+ @JsonProperty("id_strategy")
+ private IdStrategy idStrategy;
+ @JsonProperty("primary_keys")
+ private List<String> primaryKeys;
+
+ @JsonCreator
+ public VertexLabelV53(@JsonProperty("name") String name) {
+ super(name);
+ this.idStrategy = IdStrategy.DEFAULT;
+ this.primaryKeys = new CopyOnWriteArrayList<>();
+ }
+
+ private VertexLabelV53(VertexLabel vertexLabel) {
+ super(vertexLabel.name);
+ this.idStrategy = vertexLabel.idStrategy;
+ this.primaryKeys = vertexLabel.primaryKeys;
+ this.id = vertexLabel.id();
+ this.properties = vertexLabel.properties();
+ this.userdata = vertexLabel.userdata();
+ this.checkExist = vertexLabel.checkExist();
+ this.nullableKeys = vertexLabel.nullableKeys;
+ this.enableLabelIndex = vertexLabel.enableLabelIndex;
+ }
+
+ public IdStrategy idStrategy() {
+ return this.idStrategy;
+ }
+
+ public List<String> primaryKeys() {
+ return this.primaryKeys;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{name=%s, idStrategy=%s, primaryKeys=%s, " +
+ "nullableKeys=%s, properties=%s}",
+ this.name, this.idStrategy, this.primaryKeys,
+ this.nullableKeys, this.properties);
+ }
+
+ @Override
+ public String type() {
+ return HugeType.VERTEX_LABEL.string();
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CountRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CountRequest.java
new file mode 100644
index 0000000..3d20aab
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CountRequest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CountRequest {
+
+ @JsonProperty("source")
+ private Object source;
+ @JsonProperty("steps")
+ private List<EdgeStep> steps;
+ @JsonProperty("contains_traversed")
+ public boolean containsTraversed;
+ @JsonProperty("dedup_size")
+ public long dedupSize;
+
+ private CountRequest() {
+ this.source = null;
+ this.steps = new ArrayList<>();
+ this.containsTraversed = false;
+ this.dedupSize = Traverser.DEFAULT_DEDUP_SIZE;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CountRequest{source=%s,steps=%s," +
+ "containsTraversed=%s,dedupSize=%s",
+ this.source, this.steps, this.containsTraversed,
+ this.dedupSize);
+ }
+
+ public static class Builder {
+
+ private CountRequest request;
+ private List<EdgeStep.Builder> stepBuilders;
+
+ private Builder() {
+ this.request = new CountRequest();
+ this.stepBuilders = new ArrayList<>();
+ }
+
+ public Builder source(Object source) {
+ E.checkArgumentNotNull(source, "The source can't be null");
+ this.request.source = source;
+ return this;
+ }
+
+ public EdgeStep.Builder steps() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilders.add(builder);
+ return builder;
+ }
+
+ public Builder containsTraversed(boolean containsTraversed) {
+ this.request.containsTraversed = containsTraversed;
+ return this;
+ }
+
+ public Builder dedupSize(long dedupSize) {
+ checkDedupSize(dedupSize);
+ this.request.dedupSize = dedupSize;
+ return this;
+ }
+
+ public CountRequest build() {
+ E.checkArgumentNotNull(this.request.source,
+ "The source can't be null");
+ for (EdgeStep.Builder builder : this.stepBuilders) {
+ this.request.steps.add(builder.build());
+ }
+ E.checkArgument(this.request.steps != null &&
+ !this.request.steps.isEmpty(),
+ "The steps can't be null or empty");
+ checkDedupSize(this.request.dedupSize);
+ return this.request;
+ }
+ }
+
+ private static void checkDedupSize(long dedupSize) {
+ E.checkArgument(dedupSize >= 0L || dedupSize == API.NO_LIMIT,
+ "The dedup size must be >= 0 or == %s, but got: %s",
+ API.NO_LIMIT, dedupSize);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CrosspointsRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CrosspointsRequest.java
new file mode 100644
index 0000000..6e222d4
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CrosspointsRequest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CrosspointsRequest {
+
+ @JsonProperty("sources")
+ private VerticesArgs sources;
+ @JsonProperty("path_patterns")
+ private List<PathPattern> pathPatterns;
+ @JsonProperty("capacity")
+ private long capacity;
+ @JsonProperty("limit")
+ private long limit;
+ @JsonProperty("with_path")
+ private boolean withPath;
+ @JsonProperty("with_vertex")
+ private boolean withVertex;
+
+ private CrosspointsRequest() {
+ this.sources = null;
+ this.pathPatterns = new ArrayList<>();
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.limit = Traverser.DEFAULT_CROSSPOINT_LIMIT;
+ this.withPath = false;
+ this.withVertex = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CrosspointsRequest{sourceVertex=%s," +
+ "pathPatterns=%s,capacity=%s,limit=%s," +
+ "withPath=%s,withVertex=%s}",
+ this.sources, this.pathPatterns, this.capacity,
+ this.limit, this.withPath, this.withVertex);
+ }
+
+ public static class Builder {
+
+ private CrosspointsRequest request;
+ private VerticesArgs.Builder sourcesBuilder;
+ private List<PathPattern.Builder> pathPatternBuilders;
+
+ private Builder() {
+ this.request = new CrosspointsRequest();
+ this.sourcesBuilder = VerticesArgs.builder();
+ this.pathPatternBuilders = new ArrayList<>();
+ }
+
+ public VerticesArgs.Builder sources() {
+ return this.sourcesBuilder;
+ }
+
+ public PathPattern.Builder pathPatterns() {
+ PathPattern.Builder builder = new PathPattern.Builder();
+ this.pathPatternBuilders.add(builder);
+ return builder;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withPath(boolean withPath) {
+ this.request.withPath = withPath;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public CrosspointsRequest build() {
+ this.request.sources = this.sourcesBuilder.build();
+ for (PathPattern.Builder builder : this.pathPatternBuilders) {
+ this.request.pathPatterns.add(builder.build());
+ }
+ E.checkArgument(this.request.sources != null,
+ "Source vertices can't be null");
+ E.checkArgument(this.request.pathPatterns != null &&
+ !this.request.pathPatterns.isEmpty(),
+ "Steps can't be null or empty");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ return this.request;
+ }
+ }
+
+ public static class PathPattern {
+
+ @JsonProperty("steps")
+ private List<Step> steps;
+
+ private PathPattern() {
+ this.steps = new ArrayList<>();
+ }
+
+ public static class Builder {
+
+ private PathPattern pathPattern;
+ private List<Step.Builder> stepBuilders;
+
+ private Builder() {
+ this.pathPattern = new PathPattern();
+ this.stepBuilders = new ArrayList<>();
+ }
+
+ public Step.Builder steps() {
+ Step.Builder builder = new Step.Builder();
+ this.stepBuilders.add(builder);
+ return builder;
+ }
+
+ private PathPattern build() {
+ for (Step.Builder builder : this.stepBuilders) {
+ this.pathPattern.steps.add(builder.build());
+ }
+ E.checkArgument(this.pathPattern.steps != null &&
+ !this.pathPattern.steps.isEmpty(),
+ "Steps of path pattern can't be empty");
+ return this.pathPattern;
+ }
+ }
+ }
+
+ public static class Step {
+
+ @JsonProperty("direction")
+ private String direction;
+ @JsonProperty("labels")
+ private List<String> labels;
+ @JsonProperty("properties")
+ private Map<String, Object> properties;
+ @JsonProperty("degree")
+ private long degree;
+
+ private Step() {
+ this.direction = null;
+ this.labels = new ArrayList<>();
+ this.properties = new HashMap<>();
+ this.degree = Traverser.DEFAULT_MAX_DEGREE;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Step{direction=%s,labels=%s,properties=%s," +
+ "degree=%s}", this.direction, this.labels,
+ this.properties, this.degree);
+ }
+
+ public static class Builder {
+
+ private Step step;
+
+ private Builder() {
+ this.step = new Step();
+ }
+
+ public Builder direction(Direction direction) {
+ this.step.direction = direction.toString();
+ return this;
+ }
+
+ public Builder labels(List<String> labels) {
+ this.step.labels = labels;
+ return this;
+ }
+
+ public Builder labels(String label) {
+ this.step.labels.add(label);
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ this.step.properties = properties;
+ return this;
+ }
+
+ public Builder properties(String key, Object value) {
+ this.step.properties.put(key, value);
+ return this;
+ }
+
+ public Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.step.degree = degree;
+ return this;
+ }
+
+ private Step build() {
+ TraversersAPI.checkDegree(this.step.degree);
+ return this.step;
+ }
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedCrosspoints.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedCrosspoints.java
new file mode 100644
index 0000000..2d519b3
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedCrosspoints.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CustomizedCrosspoints {
+
+ @JsonProperty
+ private List<Object> crosspoints;
+ @JsonProperty
+ private List<Path> paths;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public List<Object> crosspoints() {
+ return this.crosspoints;
+ }
+
+ public List<Path> paths() {
+ return this.paths;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedPathsRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedPathsRequest.java
new file mode 100644
index 0000000..a466753
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/CustomizedPathsRequest.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import static com.baidu.hugegraph.api.API.NO_LIMIT;
+
+public class CustomizedPathsRequest {
+
+ @JsonProperty("sources")
+ private VerticesArgs sources;
+ @JsonProperty("steps")
+ private List<Step> steps;
+ @JsonProperty("sort_by")
+ private SortBy sortBy;
+ @JsonProperty("capacity")
+ private long capacity;
+ @JsonProperty("limit")
+ private long limit;
+ @JsonProperty("with_vertex")
+ private boolean withVertex;
+
+ private CustomizedPathsRequest() {
+ this.sources = null;
+ this.steps = new ArrayList<>();
+ this.sortBy = SortBy.NONE;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.limit = Traverser.DEFAULT_PATHS_LIMIT;
+ this.withVertex = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("CustomizedPathsRequest{sourceVertex=%s,steps=%s," +
+ "sortBy=%s,capacity=%s,limit=%s," +
+ "withVertex=%s}", this.sources, this.steps,
+ this.sortBy, this.capacity, this.limit,
+ this.withVertex);
+ }
+
+ public static class Builder {
+
+ private CustomizedPathsRequest request;
+ private VerticesArgs.Builder sourcesBuilder;
+ private List<Step.Builder> stepBuilders;
+
+ private Builder() {
+ this.request = new CustomizedPathsRequest();
+ this.sourcesBuilder = VerticesArgs.builder();
+ this.stepBuilders = new ArrayList<>();
+ }
+
+ public VerticesArgs.Builder sources() {
+ return this.sourcesBuilder;
+ }
+
+ public Step.Builder steps() {
+ Step.Builder builder = new Step.Builder();
+ this.stepBuilders.add(builder);
+ return builder;
+ }
+
+ public Builder sortBy(SortBy sortBy) {
+ this.request.sortBy = sortBy;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public CustomizedPathsRequest build() {
+ this.request.sources = this.sourcesBuilder.build();
+ for (Step.Builder builder : this.stepBuilders) {
+ this.request.steps.add(builder.build());
+ }
+ E.checkArgument(this.request.sources != null,
+ "Source vertices can't be null");
+ E.checkArgument(this.request.steps != null &&
+ !this.request.steps.isEmpty(),
+ "Steps can't be null or empty");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ return this.request;
+ }
+ }
+
+ public static class Step {
+
+ @JsonProperty("direction")
+ private String direction;
+ @JsonProperty("labels")
+ private List<String> labels;
+ @JsonProperty("properties")
+ private Map<String, Object> properties;
+ @JsonProperty("weight_by")
+ private String weightBy;
+ @JsonProperty("default_weight")
+ private double defaultWeight;
+ @JsonProperty("degree")
+ private long degree;
+ @JsonProperty("sample")
+ private long sample;
+
+ private Step() {
+ this.direction = null;
+ this.labels = new ArrayList<>();
+ this.properties = new HashMap<>();
+ this.weightBy = null;
+ this.defaultWeight = Traverser.DEFAULT_WEIGHT;
+ this.degree = Traverser.DEFAULT_MAX_DEGREE;
+ this.sample = Traverser.DEFAULT_SAMPLE;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Step{direction=%s,labels=%s,properties=%s," +
+ "weightBy=%s,defaultWeight=%s,degree=%s," +
+ "sample=%s}", this.direction, this.labels,
+ this.properties, this.weightBy,
+ this.defaultWeight, this.degree, this.sample);
+ }
+
+ public static class Builder {
+
+ private Step step;
+
+ private Builder() {
+ this.step = new Step();
+ }
+
+ public Builder direction(Direction direction) {
+ this.step.direction = direction.toString();
+ return this;
+ }
+
+ public Builder labels(List<String> labels) {
+ this.step.labels.addAll(labels);
+ return this;
+ }
+
+ public Builder labels(String... labels) {
+ this.step.labels.addAll(Arrays.asList(labels));
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ this.step.properties = properties;
+ return this;
+ }
+
+ public Builder properties(String key, Object value) {
+ this.step.properties.put(key, value);
+ return this;
+ }
+
+ public Builder weightBy(String property) {
+ this.step.weightBy = property;
+ return this;
+ }
+
+ public Builder defaultWeight(double weight) {
+ this.step.defaultWeight = weight;
+ return this;
+ }
+
+ public Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.step.degree = degree;
+ return this;
+ }
+
+ public Builder sample(int sample) {
+ E.checkArgument(sample == NO_LIMIT || sample > 0,
+ "The sample must be > 0 or == -1, but got: %s",
+ sample);
+ this.step.sample = sample;
+ return this;
+ }
+
+ private Step build() {
+ TraversersAPI.checkDegree(this.step.degree);
+ E.checkArgument(this.step.sample > 0 ||
+ this.step.sample == NO_LIMIT,
+ "The sample must be > 0 or == -1, but got: %s",
+ this.step.sample);
+ E.checkArgument(this.step.degree == NO_LIMIT ||
+ this.step.degree >= this.step.sample,
+ "Degree must be greater than or equal to " +
+ "sample, but got degree %s and sample %s",
+ this.step.degree, this.step.sample);
+ return this.step;
+ }
+ }
+ }
+
+ public enum SortBy {
+ INCR,
+ DECR,
+ NONE
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/EdgeStep.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/EdgeStep.java
new file mode 100644
index 0000000..2be861a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/EdgeStep.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class EdgeStep {
+
+ @JsonProperty("direction")
+ protected Direction direction;
+ @JsonProperty("labels")
+ protected List<String> labels;
+ @JsonProperty("properties")
+ protected Map<String, Object> properties;
+ @JsonProperty("degree")
+ protected long degree;
+ @JsonProperty("skip_degree")
+ protected long skipDegree;
+
+ protected EdgeStep() {
+ this.direction = Direction.BOTH;
+ this.labels = new ArrayList<>();
+ this.properties = new HashMap<>();
+ this.degree = Traverser.DEFAULT_MAX_DEGREE;
+ this.skipDegree = Traverser.DEFAULT_SKIP_DEGREE;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("EdgeStep{direction=%s,labels=%s,properties=%s," +
+ "degree=%s,skipDegree=%s}",
+ this.direction, this.labels, this.properties,
+ this.degree, this.skipDegree);
+ }
+
+ public static class Builder {
+
+ protected EdgeStep step;
+
+ private Builder() {
+ this.step = new EdgeStep();
+ }
+
+ public EdgeStep.Builder direction(Direction direction) {
+ this.step.direction = direction;
+ return this;
+ }
+
+ public EdgeStep.Builder labels(List<String> labels) {
+ this.step.labels = labels;
+ return this;
+ }
+
+ public EdgeStep.Builder labels(String label) {
+ this.step.labels.add(label);
+ return this;
+ }
+
+ public EdgeStep.Builder properties(Map<String, Object> properties) {
+ this.step.properties = properties;
+ return this;
+ }
+
+ public EdgeStep.Builder properties(String key, Object value) {
+ this.step.properties.put(key, value);
+ return this;
+ }
+
+ public EdgeStep.Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.step.degree = degree;
+ return this;
+ }
+
+ public EdgeStep.Builder skipDegree(long skipDegree) {
+ TraversersAPI.checkSkipDegree(skipDegree, this.step.degree,
+ API.NO_LIMIT);
+ this.step.skipDegree = skipDegree;
+ return this;
+ }
+
+ public EdgeStep build() {
+ TraversersAPI.checkDegree(this.step.degree);
+ TraversersAPI.checkSkipDegree(this.step.skipDegree,
+ this.step.degree, API.NO_LIMIT);
+ return this.step;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarity.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarity.java
new file mode 100644
index 0000000..b5ce09c
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class FusiformSimilarity {
+
+ @JsonProperty("similars")
+ private Map<Object, Set<Similar>> similarsMap;
+ @JsonProperty("vertices")
+ private Set<Vertex> vertices;
+
+ public Map<Object, Set<Similar>> similarsMap() {
+ return this.similarsMap;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+
+ public int size() {
+ return this.similarsMap.size();
+ }
+
+ public Map.Entry<Object, Set<Similar>> first() {
+ return this.similarsMap.entrySet().iterator().next();
+ }
+
+ public static class Similar {
+
+ @JsonProperty
+ private Object id;
+ @JsonProperty
+ private double score;
+ @JsonProperty
+ private Set<Object> intermediaries;
+
+ public Object id() {
+ return this.id;
+ }
+
+ public double score() {
+ return this.score;
+ }
+
+ public Set<Object> intermediaries() {
+ return this.intermediaries;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarityRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarityRequest.java
new file mode 100644
index 0000000..34981a9
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/FusiformSimilarityRequest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class FusiformSimilarityRequest {
+
+ @JsonProperty("sources")
+ private VerticesArgs sources;
+ @JsonProperty("label")
+ public String label;
+ @JsonProperty("direction")
+ public String direction;
+ @JsonProperty("min_neighbors")
+ public int minNeighbors;
+ @JsonProperty("alpha")
+ public double alpha;
+ @JsonProperty("min_similars")
+ public int minSimilars;
+ @JsonProperty("top")
+ public int top;
+ @JsonProperty("group_property")
+ public String groupProperty;
+ @JsonProperty("min_groups")
+ public int minGroups;
+ @JsonProperty("max_degree")
+ public long degree;
+ @JsonProperty("capacity")
+ public long capacity;
+ @JsonProperty("limit")
+ public long limit;
+ @JsonProperty("with_intermediary")
+ public boolean withIntermediary;
+ @JsonProperty("with_vertex")
+ public boolean withVertex;
+
+ private FusiformSimilarityRequest() {
+ this.sources = null;
+ this.label = null;
+ this.direction = null;
+ this.minNeighbors = 0;
+ this.degree = Traverser.DEFAULT_MAX_DEGREE;
+ this.alpha = 1.0f;
+ this.minSimilars = 1;
+ this.top = 0;
+ this.groupProperty = null;
+ this.minGroups = 0;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.limit = Traverser.DEFAULT_PATHS_LIMIT;
+ this.withIntermediary = false;
+ this.withVertex = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("FusiformSimilarityRequest{sourceVertex=%s," +
+ "label=%s,direction=%s,minNeighbors=%s," +
+ "alpha=%s,minSimilars=%s,top=%s," +
+ "groupProperty=%s,minGroups=%s," +
+ "degree=%s,capacity=%s,limit=%s," +
+ "withIntermediary=%s,withVertex=%s}",
+ this.sources, this.label, this.direction,
+ this.minNeighbors, this.alpha, this.minSimilars,
+ this.top, this.groupProperty, this.minGroups,
+ this.degree, this.capacity, this.limit,
+ this.withIntermediary, this.withVertex);
+ }
+
+ public static class Builder {
+
+ private FusiformSimilarityRequest request;
+ private VerticesArgs.Builder sourcesBuilder;
+
+ private Builder() {
+ this.request = new FusiformSimilarityRequest();
+ this.sourcesBuilder = VerticesArgs.builder();
+ }
+
+ public VerticesArgs.Builder sources() {
+ return this.sourcesBuilder;
+ }
+
+ public Builder label(String label) {
+ this.request.label = label;
+ return this;
+ }
+
+ public Builder direction(Direction direction) {
+ this.request.direction = direction.toString();
+ return this;
+ }
+
+ public Builder minNeighbors(int minNeighbors) {
+ TraversersAPI.checkPositive(minNeighbors, "min neighbor count");
+ this.request.minNeighbors = minNeighbors;
+ return this;
+ }
+
+ public Builder alpha(double alpha) {
+ TraversersAPI.checkAlpha(alpha);
+ this.request.alpha = alpha;
+ return this;
+ }
+
+ public Builder minSimilars(int minSimilars) {
+ TraversersAPI.checkPositive(minSimilars, "min similar count");
+ this.request.minSimilars = minSimilars;
+ return this;
+ }
+
+ public Builder top(int top) {
+ TraversersAPI.checkPositive(top, "top");
+ this.request.top = top;
+ return this;
+ }
+
+ public Builder groupProperty(String groupProperty) {
+ E.checkArgumentNotNull(groupProperty,
+ "The group property can't be null");
+ this.request.groupProperty = groupProperty;
+ return this;
+ }
+
+ public Builder minGroups(int minGroups) {
+ TraversersAPI.checkPositive(minGroups, "min group count");
+ this.request.minGroups = minGroups;
+ return this;
+ }
+
+ public Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.request.degree = degree;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withIntermediary(boolean withIntermediary) {
+ this.request.withIntermediary = withIntermediary;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public FusiformSimilarityRequest build() {
+ this.request.sources = this.sourcesBuilder.build();
+ E.checkArgument(this.request.sources != null,
+ "Source vertices can't be null");
+ TraversersAPI.checkPositive(request.minNeighbors,
+ "min neighbor count");
+ TraversersAPI.checkPositive(request.minSimilars,
+ "min similar count");
+ if (request.groupProperty != null) {
+ TraversersAPI.checkPositive(request.minGroups,
+ "min group count");
+ }
+ TraversersAPI.checkAlpha(request.alpha);
+ TraversersAPI.checkDegree(request.degree);
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kneighbor.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kneighbor.java
new file mode 100644
index 0000000..512700f
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kneighbor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Kneighbor {
+
+ @JsonProperty
+ private int size;
+ @JsonProperty("kneighbor")
+ private Set<Object> ids;
+ @JsonProperty
+ private List<Path> paths;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public int size() {
+ return this.size;
+ }
+
+ public Set<Object> ids() {
+ return this.ids;
+ }
+
+ public List<Path> paths() {
+ return this.paths;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KneighborRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KneighborRequest.java
new file mode 100644
index 0000000..7907cc5
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KneighborRequest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class KneighborRequest {
+
+ @JsonProperty("source")
+ private Object source;
+ @JsonProperty("step")
+ public EdgeStep step;
+ @JsonProperty("max_depth")
+ public int maxDepth;
+ @JsonProperty("count_only")
+ public boolean countOnly = false;
+ @JsonProperty("limit")
+ public long limit = Traverser.DEFAULT_LIMIT;
+ @JsonProperty("with_vertex")
+ public boolean withVertex = false;
+ @JsonProperty("with_path")
+ public boolean withPath = false;
+
+ private KneighborRequest() {
+ this.source = null;
+ this.step = null;
+ this.maxDepth = Traverser.DEFAULT_MAX_DEPTH;
+ this.countOnly = false;
+ this.limit = Traverser.DEFAULT_PATHS_LIMIT;
+ this.withVertex = false;
+ this.withPath = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("KneighborRequest{source=%s,step=%s,maxDepth=%s" +
+ "countOnly=%s,limit=%s,withVertex=%s,withPath=%s}",
+ this.source, this.step, this.maxDepth,
+ this.countOnly, this.limit,
+ this.withVertex, this.withPath);
+ }
+
+ public static class Builder {
+
+ private KneighborRequest request;
+ private EdgeStep.Builder stepBuilder;
+
+ private Builder() {
+ this.request = new KneighborRequest();
+ this.stepBuilder = EdgeStep.builder();
+ }
+
+ public Builder source(Object source) {
+ E.checkNotNull(source, "source");
+ this.request.source = source;
+ return this;
+ }
+
+ public EdgeStep.Builder step() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilder = builder;
+ return builder;
+ }
+
+ public Builder maxDepth(int maxDepth) {
+ TraversersAPI.checkPositive(maxDepth, "max depth");
+ this.request.maxDepth = maxDepth;
+ return this;
+ }
+
+ public Builder countOnly(boolean countOnly) {
+ this.request.countOnly = countOnly;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public Builder withPath(boolean withPath) {
+ this.request.withPath = withPath;
+ return this;
+ }
+
+ public KneighborRequest build() {
+ E.checkNotNull(this.request.source, "The source can't be null");
+ this.request.step = this.stepBuilder.build();
+ E.checkNotNull(this.request.step, "step");
+ TraversersAPI.checkPositive(this.request.maxDepth, "max depth");
+ TraversersAPI.checkLimit(this.request.limit);
+ if (this.request.countOnly) {
+ E.checkArgument(!this.request.withVertex &&
+ !this.request.withPath,
+ "Can't return vertex or path " +
+ "when count only is true");
+ }
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kout.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kout.java
new file mode 100644
index 0000000..ade813f
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Kout.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Kout {
+
+ @JsonProperty
+ private int size;
+ @JsonProperty("kout")
+ private Set<Object> ids;
+ @JsonProperty
+ private List<Path> paths;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public int size() {
+ return this.size;
+ }
+
+ public Set<Object> ids() {
+ return this.ids;
+ }
+
+ public List<Path> paths() {
+ return this.paths;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KoutRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KoutRequest.java
new file mode 100644
index 0000000..0217a03
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/KoutRequest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class KoutRequest {
+
+ @JsonProperty("source")
+ private Object source;
+ @JsonProperty("step")
+ public EdgeStep step;
+ @JsonProperty("max_depth")
+ public int maxDepth;
+ @JsonProperty("nearest")
+ public boolean nearest = true;
+ @JsonProperty("count_only")
+ public boolean countOnly = false;
+ @JsonProperty("capacity")
+ public long capacity = Traverser.DEFAULT_CAPACITY;
+ @JsonProperty("limit")
+ public long limit = Traverser.DEFAULT_LIMIT;
+ @JsonProperty("with_vertex")
+ public boolean withVertex = false;
+ @JsonProperty("with_path")
+ public boolean withPath = false;
+
+ private KoutRequest() {
+ this.source = null;
+ this.step = null;
+ this.maxDepth = Traverser.DEFAULT_MAX_DEPTH;
+ this.nearest = true;
+ this.countOnly = false;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.limit = Traverser.DEFAULT_PATHS_LIMIT;
+ this.withVertex = false;
+ this.withPath = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("KoutRequest{source=%s,step=%s,maxDepth=%s" +
+ "nearest=%s,countOnly=%s,capacity=%s," +
+ "limit=%s,withVertex=%s,withPath=%s}",
+ this.source, this.step, this.maxDepth,
+ this.nearest, this.countOnly, this.capacity,
+ this.limit, this.withVertex, this.withPath);
+ }
+
+ public static class Builder {
+
+ private KoutRequest request;
+ private EdgeStep.Builder stepBuilder;
+
+ private Builder() {
+ this.request = new KoutRequest();
+ this.stepBuilder = EdgeStep.builder();
+ }
+
+ public Builder source(Object source) {
+ E.checkNotNull(source, "source");
+ this.request.source = source;
+ return this;
+ }
+
+ public EdgeStep.Builder step() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilder = builder;
+ return builder;
+ }
+
+ public Builder maxDepth(int maxDepth) {
+ TraversersAPI.checkPositive(maxDepth, "max depth");
+ this.request.maxDepth = maxDepth;
+ return this;
+ }
+
+ public Builder nearest(boolean nearest) {
+ this.request.nearest = nearest;
+ return this;
+ }
+
+ public Builder countOnly(boolean countOnly) {
+ this.request.countOnly = countOnly;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public Builder withPath(boolean withPath) {
+ this.request.withPath = withPath;
+ return this;
+ }
+
+ public KoutRequest build() {
+ E.checkNotNull(this.request.source, "The source can't be null");
+ this.request.step = this.stepBuilder.build();
+ E.checkNotNull(this.request.step, "step");
+ TraversersAPI.checkPositive(this.request.maxDepth, "max depth");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ if (this.request.countOnly) {
+ E.checkArgument(!this.request.withVertex &&
+ !this.request.withPath,
+ "Can't return vertex or path " +
+ "when count only is true");
+ }
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java
new file mode 100644
index 0000000..9bd67d7
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MultiNodeShortestPathRequest {
+
+ @JsonProperty("vertices")
+ public VerticesArgs vertices;
+ @JsonProperty("step")
+ public EdgeStep step;
+ @JsonProperty("max_depth")
+ public int maxDepth;
+ @JsonProperty("capacity")
+ public long capacity = Traverser.DEFAULT_CAPACITY;
+ @JsonProperty("with_vertex")
+ public boolean withVertex = false;
+
+ private MultiNodeShortestPathRequest() {
+ this.vertices = null;
+ this.step = null;
+ this.maxDepth = Traverser.DEFAULT_MAX_DEPTH;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.withVertex = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("MultiNodeShortestPathRequest{vertices=%s," +
+ "step=%s,maxDepth=%s,capacity=%s,withVertex=%s}",
+ this.vertices, this.step, this.maxDepth,
+ this.capacity, this.withVertex);
+ }
+
+ public static class Builder {
+
+ private MultiNodeShortestPathRequest request;
+ private VerticesArgs.Builder verticesBuilder;
+ private EdgeStep.Builder stepBuilder;
+
+ private Builder() {
+ this.request = new MultiNodeShortestPathRequest();
+ this.verticesBuilder = VerticesArgs.builder();
+ this.stepBuilder = EdgeStep.builder();
+ }
+
+ public VerticesArgs.Builder vertices() {
+ return this.verticesBuilder;
+ }
+
+ public EdgeStep.Builder step() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilder = builder;
+ return builder;
+ }
+
+ public Builder maxDepth(int maxDepth) {
+ TraversersAPI.checkPositive(maxDepth, "max depth");
+ this.request.maxDepth = maxDepth;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public MultiNodeShortestPathRequest build() {
+ this.request.vertices = this.verticesBuilder.build();
+ E.checkArgument(this.request.vertices != null,
+ "The vertices can't be null");
+ this.request.step = this.stepBuilder.build();
+ E.checkArgument(this.request.step != null,
+ "The step can't be null");
+ TraversersAPI.checkPositive(this.request.maxDepth, "max depth");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsRequest.java
new file mode 100644
index 0000000..5b77287
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsRequest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PathsRequest {
+
+ @JsonProperty("sources")
+ private VerticesArgs sources;
+ @JsonProperty("targets")
+ private VerticesArgs targets;
+ @JsonProperty("step")
+ public EdgeStep step;
+ @JsonProperty("max_depth")
+ public int depth;
+ @JsonProperty("nearest")
+ public boolean nearest = false;
+ @JsonProperty("capacity")
+ public long capacity = Traverser.DEFAULT_CAPACITY;
+ @JsonProperty("limit")
+ public long limit = Traverser.DEFAULT_LIMIT;
+ @JsonProperty("with_vertex")
+ public boolean withVertex = false;
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PathRequest{sources=%s,targets=%s,step=%s," +
+ "maxDepth=%s,nearest=%s,capacity=%s," +
+ "limit=%s,withVertex=%s}",
+ this.sources, this.targets, this.step, this.depth,
+ this.nearest, this.capacity,
+ this.limit, this.withVertex);
+ }
+
+ public static class Builder {
+
+ private PathsRequest request;
+ private EdgeStep.Builder stepBuilder;
+ private VerticesArgs.Builder sourcesBuilder;
+ private VerticesArgs.Builder targetsBuilder;
+
+ private Builder() {
+ this.request = new PathsRequest();
+ this.stepBuilder = EdgeStep.builder();
+ this.sourcesBuilder = VerticesArgs.builder();
+ this.targetsBuilder = VerticesArgs.builder();
+ }
+
+ public VerticesArgs.Builder sources() {
+ return this.sourcesBuilder;
+ }
+
+ public VerticesArgs.Builder targets() {
+ return this.targetsBuilder;
+ }
+
+ public EdgeStep.Builder step() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilder = builder;
+ return builder;
+ }
+
+ public PathsRequest.Builder maxDepth(int maxDepth) {
+ TraversersAPI.checkPositive(maxDepth, "max depth");
+ this.request.depth = maxDepth;
+ return this;
+ }
+
+ public PathsRequest.Builder nearest(boolean nearest) {
+ this.request.nearest = nearest;
+ return this;
+ }
+
+ public PathsRequest.Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public PathsRequest.Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public PathsRequest.Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public PathsRequest build() {
+ this.request.sources = this.sourcesBuilder.build();
+ E.checkArgument(this.request.sources != null,
+ "Source vertices can't be null");
+ this.request.targets = this.targetsBuilder.build();
+ E.checkArgument(this.request.targets != null,
+ "Target vertices can't be null");
+ this.request.step = this.stepBuilder.build();
+ E.checkNotNull(this.request.step, "The steps can't be null");
+ TraversersAPI.checkPositive(this.request.depth, "max depth");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsWithVertices.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsWithVertices.java
new file mode 100644
index 0000000..6668816
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/PathsWithVertices.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class PathsWithVertices {
+
+ @JsonProperty
+ private List<Paths> paths;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public List<Paths> paths() {
+ return this.paths;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+
+ public static class Paths {
+
+ @JsonProperty
+ private List<Object> objects;
+ @JsonProperty
+ private List<Double> weights;
+
+ public List<Object> objects() {
+ return this.objects;
+ }
+
+ public List<Double> weights() {
+ return this.weights;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Ranks.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Ranks.java
new file mode 100644
index 0000000..a8bb315
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/Ranks.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.LinkedHashMap;
+
+public class Ranks extends LinkedHashMap<Object, Double> {
+
+ private static final long serialVersionUID = -517025731560925613L;
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/RepeatEdgeStep.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/RepeatEdgeStep.java
new file mode 100644
index 0000000..1783b7a
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/RepeatEdgeStep.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Map;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RepeatEdgeStep extends EdgeStep {
+
+ @JsonProperty("max_times")
+ public int maxTimes = 1;
+
+ private RepeatEdgeStep() {
+ super();
+ this.maxTimes = 1;
+ }
+
+ public static Builder repeatStepBuilder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("RepeatEdgeStep{direction=%s,labels=%s," +
+ "properties=%s,degree=%s,skipDegree=%s," +
+ "maxTimes=%s}",
+ this.direction, this.labels, this.properties,
+ this.degree, this.skipDegree, this.maxTimes);
+ }
+
+ public static class Builder {
+
+ protected RepeatEdgeStep step;
+
+ private Builder() {
+ this.step = new RepeatEdgeStep();
+ }
+
+ public Builder direction(Direction direction) {
+ this.step.direction = direction;
+ return this;
+ }
+
+ public Builder labels(List<String> labels) {
+ this.step.labels = labels;
+ return this;
+ }
+
+ public Builder labels(String label) {
+ this.step.labels.add(label);
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ this.step.properties = properties;
+ return this;
+ }
+
+ public Builder properties(String key, Object value) {
+ this.step.properties.put(key, value);
+ return this;
+ }
+
+ public Builder maxTimes(int maxTimes) {
+ this.step.maxTimes = maxTimes;
+ return this;
+ }
+
+ public Builder degree(long degree) {
+ TraversersAPI.checkDegree(degree);
+ this.step.degree = degree;
+ return this;
+ }
+
+ public Builder skipDegree(long skipDegree) {
+ TraversersAPI.checkSkipDegree(skipDegree, this.step.degree,
+ API.NO_LIMIT);
+ this.step.skipDegree = skipDegree;
+ return this;
+ }
+
+ public RepeatEdgeStep build() {
+ TraversersAPI.checkDegree(this.step.degree);
+ TraversersAPI.checkSkipDegree(this.step.skipDegree,
+ this.step.degree, API.NO_LIMIT);
+ TraversersAPI.checkPositive(this.step.maxTimes, "max times");
+ return this.step;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/SingleSourceJaccardSimilarityRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/SingleSourceJaccardSimilarityRequest.java
new file mode 100644
index 0000000..f5207b0
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/SingleSourceJaccardSimilarityRequest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class SingleSourceJaccardSimilarityRequest {
+
+ @JsonProperty("vertex")
+ private Object vertex;
+ @JsonProperty("step")
+ public EdgeStep step;
+ @JsonProperty("top")
+ public int top = 10;
+ @JsonProperty("capacity")
+ public long capacity = Traverser.DEFAULT_CAPACITY;
+
+ private SingleSourceJaccardSimilarityRequest() {
+ this.vertex = null;
+ this.step = null;
+ this.top = 10;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("SingleSourceJaccardSimilarityRequest{vertex=%s," +
+ "step=%s,top=%s,capacity=%s}",
+ this.vertex, this.step, this.top, this.capacity);
+ }
+
+ public static class Builder {
+
+ private SingleSourceJaccardSimilarityRequest request;
+ private EdgeStep.Builder stepBuilder;
+
+ private Builder() {
+ this.request = new SingleSourceJaccardSimilarityRequest();
+ this.stepBuilder = EdgeStep.builder();
+ }
+
+ public Builder vertex(Object vertex) {
+ E.checkNotNull(vertex, "vertex");
+ this.request.vertex = vertex;
+ return this;
+ }
+
+ public EdgeStep.Builder step() {
+ EdgeStep.Builder builder = EdgeStep.builder();
+ this.stepBuilder = builder;
+ return builder;
+ }
+
+ public Builder top(int top) {
+ TraversersAPI.checkPositive(top, "top");
+ this.request.top = top;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public SingleSourceJaccardSimilarityRequest build() {
+ E.checkArgument(this.request.vertex != null,
+ "The vertex can't be null");
+ this.request.step = this.stepBuilder.build();
+ E.checkNotNull(this.request.step, "step");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ E.checkArgument(this.request.top >= 0,
+ "The top must be >= 0, but got: %s",
+ this.request.top);
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/TemplatePathsRequest.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/TemplatePathsRequest.java
new file mode 100644
index 0000000..ee24d1b
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/TemplatePathsRequest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.baidu.hugegraph.api.traverser.TraversersAPI;
+import com.baidu.hugegraph.structure.constant.Traverser;
+import com.baidu.hugegraph.util.E;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class TemplatePathsRequest {
+
+ @JsonProperty("sources")
+ private VerticesArgs sources;
+ @JsonProperty("targets")
+ private VerticesArgs targets;
+ @JsonProperty("steps")
+ public List<RepeatEdgeStep> steps;
+ @JsonProperty("with_ring")
+ public boolean withRing = false;
+ @JsonProperty("capacity")
+ public long capacity = Traverser.DEFAULT_CAPACITY;
+ @JsonProperty("limit")
+ public long limit = Traverser.DEFAULT_PATHS_LIMIT;
+ @JsonProperty("with_vertex")
+ public boolean withVertex = false;
+
+ private TemplatePathsRequest() {
+ this.sources = null;
+ this.targets = null;
+ this.steps = new ArrayList<>();
+ this.withRing = false;
+ this.capacity = Traverser.DEFAULT_CAPACITY;
+ this.limit = Traverser.DEFAULT_PATHS_LIMIT;
+ this.withVertex = false;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("TemplatePathsRequest{sources=%s,targets=%s," +
+ "steps=%s,withRing=%s,capacity=%s,limit=%s," +
+ "withVertex=%s}", this.sources, this.targets,
+ this.steps, this.withRing, this.capacity,
+ this.limit, this.withVertex);
+ }
+
+ public static class Builder {
+
+ private TemplatePathsRequest request;
+ private VerticesArgs.Builder sourcesBuilder;
+ private VerticesArgs.Builder targetsBuilder;
+ private List<RepeatEdgeStep.Builder> stepBuilders;
+
+ private Builder() {
+ this.request = new TemplatePathsRequest();
+ this.sourcesBuilder = VerticesArgs.builder();
+ this.targetsBuilder = VerticesArgs.builder();
+ this.stepBuilders = new ArrayList<>();
+ }
+
+ public VerticesArgs.Builder sources() {
+ return this.sourcesBuilder;
+ }
+
+ public VerticesArgs.Builder targets() {
+ return this.targetsBuilder;
+ }
+
+ public RepeatEdgeStep.Builder steps() {
+ RepeatEdgeStep.Builder builder = RepeatEdgeStep.repeatStepBuilder();
+ this.stepBuilders.add(builder);
+ return builder;
+ }
+
+ public Builder withRing(boolean withRing) {
+ this.request.withRing = withRing;
+ return this;
+ }
+
+ public Builder capacity(long capacity) {
+ TraversersAPI.checkCapacity(capacity);
+ this.request.capacity = capacity;
+ return this;
+ }
+
+ public Builder limit(long limit) {
+ TraversersAPI.checkLimit(limit);
+ this.request.limit = limit;
+ return this;
+ }
+
+ public Builder withVertex(boolean withVertex) {
+ this.request.withVertex = withVertex;
+ return this;
+ }
+
+ public TemplatePathsRequest build() {
+ this.request.sources = this.sourcesBuilder.build();
+ E.checkArgument(this.request.sources != null,
+ "Source vertices can't be null");
+ this.request.targets = this.targetsBuilder.build();
+ E.checkArgument(this.request.targets != null,
+ "Target vertices can't be null");
+ for (RepeatEdgeStep.Builder builder : this.stepBuilders) {
+ this.request.steps.add(builder.build());
+ }
+ E.checkArgument(this.request.steps != null &&
+ !this.request.steps.isEmpty(),
+ "The steps can't be null or empty");
+ TraversersAPI.checkCapacity(this.request.capacity);
+ TraversersAPI.checkLimit(this.request.limit);
+ return this.request;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/VerticesArgs.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/VerticesArgs.java
new file mode 100644
index 0000000..4a6a193
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/VerticesArgs.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.baidu.hugegraph.util.E;
+
+public class VerticesArgs {
+
+ public Set<Object> ids;
+ public String label;
+ public Map<String, Object> properties;
+
+ private VerticesArgs() {
+ this.ids = new HashSet<>();
+ this.label = null;
+ this.properties = new HashMap<>();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("VerticesArgs{ids=%s,label=%s,properties=%s}",
+ this.ids, this.label, this.properties);
+ }
+
+ public static class Builder {
+
+ private VerticesArgs vertices;
+
+ private Builder() {
+ this.vertices = new VerticesArgs();
+ }
+
+ public Builder ids(Set<Object> ids) {
+ this.vertices.ids.addAll(ids);
+ return this;
+ }
+
+ public Builder ids(Object... ids) {
+ this.vertices.ids.addAll(Arrays.asList(ids));
+ return this;
+ }
+
+ public Builder label(String label) {
+ this.vertices.label = label;
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ this.vertices.properties = properties;
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ this.vertices.properties.put(key, value);
+ return this;
+ }
+
+ protected VerticesArgs build() {
+ E.checkArgument(!((this.vertices.ids == null ||
+ this.vertices.ids.isEmpty()) &&
+ (this.vertices.properties == null ||
+ this.vertices.properties.isEmpty()) &&
+ (this.vertices.label == null ||
+ this.vertices.label.isEmpty())),
+ "No vertices provided");
+ return this.vertices;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPath.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPath.java
new file mode 100644
index 0000000..2a50e62
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPath.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class WeightedPath {
+
+ @JsonProperty
+ private Path path;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public Path path() {
+ return this.path;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+
+ public static class Path {
+
+ @JsonProperty
+ private double weight;
+ @JsonProperty
+ private List<Object> vertices;
+
+ public double weight() {
+ return this.weight;
+ }
+
+ public List<Object> vertices() {
+ return this.vertices;
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPaths.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPaths.java
new file mode 100644
index 0000000..c7ce572
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/traverser/WeightedPaths.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.traverser;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class WeightedPaths {
+
+ @JsonProperty
+ private Map<Object, WeightedPath.Path> paths;
+ @JsonProperty
+ private Set<Vertex> vertices;
+
+ public Map<Object, WeightedPath.Path> paths() {
+ return this.paths;
+ }
+
+ public Set<Vertex> vertices() {
+ return this.vertices;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/version/Versions.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/version/Versions.java
new file mode 100644
index 0000000..6324422
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/structure/version/Versions.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.structure.version;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Versions {
+
+ @JsonProperty
+ private Map<String, String> versions;
+
+ public String get(String name) {
+ return this.versions.get(name);
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/util/CommonUtil.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/CommonUtil.java
new file mode 100644
index 0000000..4506a33
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/CommonUtil.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.util;
+
+import java.util.Map;
+
+public final class CommonUtil {
+
+ public static void checkMapClass(Object object, Class<?> kClass,
+ Class<?> vClass) {
+ E.checkArgumentNotNull(object, "The object can't be null");
+ E.checkArgument(object instanceof Map,
+ "The object must be instance of Map, but got '%s'(%s)",
+ object, object.getClass());
+ E.checkArgumentNotNull(kClass, "The key class can't be null");
+ E.checkArgumentNotNull(vClass, "The value class can't be null");
+ Map<?, ?> map = (Map<?, ?>) object;
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ E.checkState(kClass.isAssignableFrom(key.getClass()),
+ "The map key must be instance of %s, " +
+ "but got '%s'(%s)", kClass, key, key.getClass());
+ E.checkState(vClass.isAssignableFrom(value.getClass()),
+ "The map value must be instance of %s, " +
+ "but got '%s'(%s)", vClass, value, value.getClass());
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/util/IdUtil.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/IdUtil.java
new file mode 100644
index 0000000..9ebe91d
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/IdUtil.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Copied from HugeGraph(https://github.com/hugegraph/hugegraph)
+ */
+public final class IdUtil {
+
+ public static String escape(char splitor, char escape, String... values) {
+ int length = values.length + 4;
+ for (String value : values) {
+ length += value.length();
+ }
+ StringBuilder escaped = new StringBuilder(length);
+ // Do escape for every item in values
+ for (String value : values) {
+ if (escaped.length() > 0) {
+ escaped.append(splitor);
+ }
+
+ if (value.indexOf(splitor) == -1) {
+ escaped.append(value);
+ continue;
+ }
+
+ // Do escape for current item
+ for (int i = 0, n = value.length(); i < n; i++) {
+ char ch = value.charAt(i);
+ if (ch == splitor) {
+ escaped.append(escape);
+ }
+ escaped.append(ch);
+ }
+ }
+ return escaped.toString();
+ }
+
+ public static String[] unescape(String id, String splitor, String escape) {
+ /*
+ * Note that the `splitor`/`escape` maybe special characters in regular
+ * expressions, but this is a frequently called method, for faster
+ * execution, we forbid the use of special characters as delimiter
+ * or escape sign.
+ * The `limit` param -1 in split method can ensure empty string be
+ * splited to a part.
+ */
+ String[] parts = id.split("(?<!" + escape + ")" + splitor, -1);
+ for (int i = 0; i < parts.length; i++) {
+ parts[i] = StringUtils.replace(parts[i], escape + splitor,
+ splitor);
+ }
+ return parts;
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/util/JsonUtil.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/JsonUtil.java
new file mode 100644
index 0000000..d596402
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/JsonUtil.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.util;
+
+import java.io.IOException;
+
+import com.baidu.hugegraph.rest.SerializeException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.Module;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public final class JsonUtil {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ public static void registerModule(Module module) {
+ MAPPER.registerModule(module);
+ }
+
+ public static String toJson(Object object) {
+ try {
+ return MAPPER.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ throw new SerializeException("Failed to serialize object '%s'",
+ e, object);
+ }
+ }
+
+ public static <T> T fromJson(String json, Class<T> clazz) {
+ try {
+ return MAPPER.readValue(json, clazz);
+ } catch (IOException e) {
+ throw new SerializeException("Failed to deserialize json '%s'",
+ e, json);
+ }
+ }
+
+ public static <T> T convertValue(JsonNode node, Class<T> clazz) {
+ try {
+ return MAPPER.convertValue(node, clazz);
+ } catch (IllegalArgumentException e) {
+ throw new SerializeException("Failed to deserialize json node '%s'",
+ e, node);
+ }
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/util/SplicingIdGenerator.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/SplicingIdGenerator.java
new file mode 100644
index 0000000..732258d
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/SplicingIdGenerator.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.util;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Copied from HugeGraph(https://github.com/hugegraph/hugegraph)
+ */
+public class SplicingIdGenerator {
+
+ private static volatile SplicingIdGenerator instance;
+
+ public static SplicingIdGenerator instance() {
+ if (instance == null) {
+ synchronized (SplicingIdGenerator.class) {
+ if (instance == null) {
+ instance = new SplicingIdGenerator();
+ }
+ }
+ }
+ return instance;
+ }
+
+ /*
+ * The following defines can't be java regex special characters:
+ * "\^$.|?*+()[{"
+ * See: http://www.regular-expressions.info/characters.html
+ */
+ private static final char ESCAPE = '`';
+ private static final char IDS_SPLITOR = '>';
+ private static final char ID_SPLITOR = ':';
+ private static final char NAME_SPLITOR = '!';
+
+ public static final String ESCAPE_STR = String.valueOf(ESCAPE);
+ public static final String IDS_SPLITOR_STR = String.valueOf(IDS_SPLITOR);
+ public static final String ID_SPLITOR_STR = String.valueOf(ID_SPLITOR);
+
+ /****************************** id generate ******************************/
+
+ /**
+ * Concat multiple ids into one composite id with IDS_SPLITOR
+ * @param ids the string id values to be concatted
+ * @return concatted string value
+ */
+ public static String concat(String... ids) {
+ // NOTE: must support string id when using this method
+ return IdUtil.escape(IDS_SPLITOR, ESCAPE, ids);
+ }
+
+ /**
+ * Split a composite id into multiple ids with IDS_SPLITOR
+ * @param ids the string id value to be splitted
+ * @return splitted string values
+ */
+ public static String[] split(String ids) {
+ return IdUtil.unescape(ids, IDS_SPLITOR_STR, ESCAPE_STR);
+ }
+
+ /**
+ * Concat property values with NAME_SPLITOR
+ * @param values the property values to be concatted
+ * @return concatted string value
+ */
+ public static String concatValues(List<?> values) {
+ // Convert the object list to string array
+ int valuesSize = values.size();
+ String[] parts = new String[valuesSize];
+ for (int i = 0; i < valuesSize; i++) {
+ parts[i] = values.get(i).toString();
+ }
+ return IdUtil.escape(NAME_SPLITOR, ESCAPE, parts);
+ }
+
+ /**
+ * Concat property values with NAME_SPLITOR
+ * @param values the property values to be concatted
+ * @return concatted string value
+ */
+ public static String concatValues(Object... values) {
+ return concatValues(Arrays.asList(values));
+ }
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/util/TaskCache.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/TaskCache.java
new file mode 100644
index 0000000..c278ac7
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/util/TaskCache.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.structure.Task;
+
+public class TaskCache {
+
+ private static final Task FAKE_TASK = new Task();
+
+ private Map<TaskAPI, Map<Long, Task>> taskTable;
+ private ScheduledExecutorService service;
+
+ private static TaskCache INSTANCE = new TaskCache();
+
+ private TaskCache() {
+ this.taskTable = new ConcurrentHashMap<>();
+ this.service = null;
+ }
+
+ public static TaskCache instance() {
+ return INSTANCE;
+ }
+
+ public Task get(TaskAPI api, long task) {
+ this.add(api, task);
+ return this.taskTable.get(api).get(task);
+ }
+
+ public void remove(TaskAPI api, long task) {
+ Map<Long, Task> tasks = this.tasks(api);
+ tasks.remove(task);
+ if (tasks.isEmpty()) {
+ this.taskTable.remove(api);
+ }
+ if (this.taskTable.isEmpty()) {
+ this.stop();
+ }
+ }
+
+ private void add(TaskAPI api, long task) {
+ Map<Long, Task> tasks = this.tasks(api);
+ if (!tasks.containsKey(task)) {
+ tasks.putIfAbsent(task, FAKE_TASK);
+ }
+ if (this.service == null || this.service.isShutdown()) {
+ this.start();
+ }
+ }
+
+ private Map<Long, Task> tasks(TaskAPI api) {
+ if (!this.taskTable.containsKey(api)) {
+ this.taskTable.putIfAbsent(api, new ConcurrentHashMap<>());
+ }
+ return this.taskTable.get(api);
+ }
+
+ private synchronized void start() {
+ if (this.service == null || this.service.isShutdown()) {
+ this.service = ExecutorUtil.newScheduledThreadPool("task-worker");
+ // Schedule a query task to query task status every 1 second,
+ this.service.scheduleAtFixedRate(this::asyncQueryTask, 0L, 1L,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ private synchronized void stop() {
+ if (this.taskTable.isEmpty() && this.service != null) {
+ this.service.shutdown();
+ }
+ }
+
+ private void asyncQueryTask() {
+ for (Map.Entry<TaskAPI, Map<Long, Task>> query :
+ this.taskTable.entrySet()) {
+ TaskAPI api = query.getKey();
+ Map<Long, Task> targets = query.getValue();
+ if (targets == null || targets.isEmpty()) {
+ this.taskTable.remove(api);
+ continue;
+ }
+ List<Long> taskIds = new ArrayList<>(targets.keySet());
+ List<Task> results = api.list(taskIds);
+ for (Task task : results) {
+ targets.put(task.id(), task);
+ }
+ }
+ };
+}
diff --git a/hugegraph-client/src/main/java/com/baidu/hugegraph/version/ClientVersion.java b/hugegraph-client/src/main/java/com/baidu/hugegraph/version/ClientVersion.java
new file mode 100644
index 0000000..1ffbccb
--- /dev/null
+++ b/hugegraph-client/src/main/java/com/baidu/hugegraph/version/ClientVersion.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.version;
+
+import com.baidu.hugegraph.util.VersionUtil;
+import com.baidu.hugegraph.util.VersionUtil.Version;
+
+public class ClientVersion {
+
+ static {
+ // Check versions of the dependency packages
+ ClientVersion.check();
+ }
+
+ public static final String NAME = "hugegraph-client";
+
+ public static final Version VERSION = Version.of(ClientVersion.class);
+
+ public static void check() {
+ // Check version of hugegraph-common
+ VersionUtil.check(CommonVersion.VERSION, "2.1", "2.2",
+ CommonVersion.NAME);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/BaseClientTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/BaseClientTest.java
new file mode 100644
index 0000000..94d5c4d
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/BaseClientTest.java
@@ -0,0 +1,340 @@
+package com.baidu.hugegraph;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import com.baidu.hugegraph.driver.AuthManager;
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.GraphsManager;
+import com.baidu.hugegraph.driver.GremlinManager;
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.MetricsManager;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.driver.TaskManager;
+import com.baidu.hugegraph.driver.TraverserManager;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.google.common.collect.ImmutableMap;
+
+public class BaseClientTest {
+
+ protected static final String BASE_URL = "http://127.0.0.1:8080";
+ protected static final String GRAPH = "hugegraph";
+ protected static final String USERNAME = "admin";
+ protected static final String PASSWORD = "pa";
+ protected static final int TIMEOUT = 10;
+
+ private static HugeClient client;
+
+ protected static HugeClient open() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .build();
+ return client;
+ }
+
+ @BeforeClass
+ public static void init() {
+ if (client == null) {
+ open();
+ }
+ }
+
+ @AfterClass
+ public static void clear() throws Exception {
+ // client.close();
+ }
+
+ public static HugeClient baseClient() {
+ return client;
+ }
+
+ public static SchemaManager schema() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.schema();
+ }
+
+ public static GraphManager graph() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.graph();
+ }
+
+ public static GremlinManager gremlin() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.gremlin();
+ }
+
+ public static TraverserManager traverser() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.traverser();
+ }
+
+ public static TaskManager task() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.task();
+ }
+
+ public static AuthManager auth() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.auth();
+ }
+
+ public static GraphsManager graphs() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.graphs();
+ }
+
+ public static MetricsManager metrics() {
+ Assert.assertNotNull("Not opened client", client);
+ return client.metrics();
+ }
+
+ @Before
+ public void setup() {
+ // this.clearData();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ // pass
+ }
+
+ protected static Object getVertexId(String label, String key,
+ String value) {
+ return getVertex(label, key, value).id();
+ }
+
+ protected static Vertex getVertex(String label, String key, String value) {
+ Map<String, Object> params = ImmutableMap.of(key, value);
+ List<Vertex> vertices = graph().listVertices(label, params);
+ Assert.assertEquals(1, vertices.size());
+ return vertices.get(0);
+ }
+
+ protected static String getEdgeId(String label, String key, String value) {
+ return getEdge(label, key, value).id();
+ }
+
+ protected static Edge getEdge(String label, String key, String value) {
+ Map<String, Object> params = ImmutableMap.of(key, value);
+ List<Edge> edges = graph().listEdges(label, params);
+ Assert.assertEquals(1, edges.size());
+ return edges.get(0);
+ }
+
+ protected static void assertContains(List<PropertyKey> propertyKeys,
+ PropertyKey propertyKey) {
+ Assert.assertTrue(Utils.contains(propertyKeys, propertyKey));
+ }
+
+ protected static void assertContains(List<VertexLabel> vertexLabels,
+ VertexLabel vertexLabel) {
+ Assert.assertTrue(Utils.contains(vertexLabels, vertexLabel));
+ }
+
+ protected static void assertContains(List<EdgeLabel> edgeLabels,
+ EdgeLabel edgeLabel) {
+ Assert.assertTrue(Utils.contains(edgeLabels, edgeLabel));
+ }
+
+ protected static void assertContains(List<IndexLabel> indexLabels,
+ IndexLabel indexLabel) {
+ Assert.assertTrue(Utils.contains(indexLabels, indexLabel));
+ }
+
+ protected static void initPropertyKey() {
+ SchemaManager schema = schema();
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("age").asInt().ifNotExist().create();
+ schema.propertyKey("city").asText().ifNotExist().create();
+ schema.propertyKey("lang").asText().ifNotExist().create();
+ schema.propertyKey("date").asDate().ifNotExist().create();
+ schema.propertyKey("price").asInt().ifNotExist().create();
+ schema.propertyKey("weight").asDouble().ifNotExist().create();
+ }
+
+ protected static void initVertexLabel() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("person")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("software")
+ .properties("name", "lang", "price")
+ .primaryKeys("name")
+ .nullableKeys("price")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("book")
+ .useCustomizeStringId()
+ .properties("name", "price")
+ .nullableKeys("price")
+ .ifNotExist()
+ .create();
+ }
+
+ protected static void initEdgeLabel() {
+ SchemaManager schema = schema();
+
+ schema.edgeLabel("knows")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .multiTimes()
+ .properties("date", "city")
+ .sortKeys("date")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .properties("date", "city")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+ }
+
+ protected static void initIndexLabel() {
+ SchemaManager schema = schema();
+
+ schema.indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .range()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("knowsByDate")
+ .onE("knows")
+ .by("date")
+ .secondary()
+ .ifNotExist()
+ .create();
+
+ schema.indexLabel("createdByDate")
+ .onE("created")
+ .by("date")
+ .secondary()
+ .ifNotExist()
+ .create();
+ }
+
+ protected static void initVertex() {
+ graph().addVertex(T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 27, "city", "Hongkong");
+ graph().addVertex(T.label, "software", "name", "lop",
+ "lang", "java", "price", 328);
+ graph().addVertex(T.label, "person", "name", "josh",
+ "age", 32, "city", "Beijing");
+ graph().addVertex(T.label, "software", "name", "ripple",
+ "lang", "java", "price", 199);
+ graph().addVertex(T.label, "person", "name", "peter",
+ "age", 29, "city", "Shanghai");
+ }
+
+ protected static void initEdge() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ graph().addEdge(markoId, "knows", vadasId, "date", "2012-01-10");
+ graph().addEdge(markoId, "knows", joshId, "date", "2013-01-10");
+ graph().addEdge(markoId, "created", lopId,
+ "date", "2014-01-10", "city", "Shanghai");
+ graph().addEdge(joshId, "created", rippleId,
+ "date", "2015-01-10", "city", "Beijing");
+ graph().addEdge(joshId, "created", lopId,
+ "date", "2016-01-10", "city", "Beijing");
+ graph().addEdge(peterId, "created", lopId,
+ "date", "2017-01-10", "city", "Hongkong");
+ }
+
+ protected List<Vertex> create100PersonBatch() {
+ List<Vertex> vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "Person" + "-" + i);
+ vertex.property("city", "Beijing");
+ vertex.property("age", 30);
+ vertices.add(vertex);
+ }
+ return vertices;
+ }
+
+ protected List<Vertex> create50SoftwareBatch() {
+ List<Vertex> vertices = new ArrayList<>(50);
+ for (int i = 0; i < 50; i++) {
+ Vertex vertex = new Vertex("software");
+ vertex.property("name", "Software" + "-" + i);
+ vertex.property("lang", "java");
+ vertex.property("price", 328);
+ vertices.add(vertex);
+ }
+ return vertices;
+ }
+
+ protected List<Edge> create50CreatedBatch() {
+ VertexLabel person = schema().getVertexLabel("person");
+ VertexLabel software = schema().getVertexLabel("software");
+
+ List<Edge> edges = new ArrayList<>(50);
+ for (int i = 0; i < 50; i++) {
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(person.id() + ":Person-" + i);
+ edge.targetId(software.id() + ":Software-" + i);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+ edges.add(edge);
+ }
+ return edges;
+ }
+
+ protected List<Edge> create50KnowsBatch() {
+ VertexLabel person = schema().getVertexLabel("person");
+
+ List<Edge> edges = new ArrayList<>(50);
+ for (int i = 0; i < 50; i++) {
+ Edge edge = new Edge("knows");
+ edge.sourceLabel("person");
+ edge.targetLabel("person");
+ edge.sourceId(person.id() + ":Person-" + i);
+ edge.targetId(person.id() + ":Person-" + (i + 50));
+ edge.property("date", "2017-03-24");
+ edges.add(edge);
+ }
+ return edges;
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java
new file mode 100644
index 0000000..62329db
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import com.baidu.hugegraph.api.auth.AccessApiTest;
+import com.baidu.hugegraph.api.auth.BelongApiTest;
+import com.baidu.hugegraph.api.auth.GroupApiTest;
+import com.baidu.hugegraph.api.auth.LoginApiTest;
+import com.baidu.hugegraph.api.auth.LogoutApiTest;
+import com.baidu.hugegraph.api.auth.ProjectApiTest;
+import com.baidu.hugegraph.api.auth.TargetApiTest;
+import com.baidu.hugegraph.api.auth.TokenApiTest;
+import com.baidu.hugegraph.api.auth.UserApiTest;
+import com.baidu.hugegraph.api.traverser.AllShortestPathsApiTest;
+import com.baidu.hugegraph.api.traverser.CommonTraverserApiTest;
+import com.baidu.hugegraph.api.traverser.CountApiTest;
+import com.baidu.hugegraph.api.traverser.CustomizedPathsApiTest;
+import com.baidu.hugegraph.api.traverser.FusiformSimilarityApiTest;
+import com.baidu.hugegraph.api.traverser.JaccardSimilarityApiTest;
+import com.baidu.hugegraph.api.traverser.KneighborApiTest;
+import com.baidu.hugegraph.api.traverser.KoutApiTest;
+import com.baidu.hugegraph.api.traverser.MultiNodeShortestPathApiTest;
+import com.baidu.hugegraph.api.traverser.NeighborRankApiTest;
+import com.baidu.hugegraph.api.traverser.PathsApiTest;
+import com.baidu.hugegraph.api.traverser.PersonalRankApiTest;
+import com.baidu.hugegraph.api.traverser.RingsRaysApiTest;
+import com.baidu.hugegraph.api.traverser.SameNeighborsApiTest;
+import com.baidu.hugegraph.api.traverser.ShortestPathApiTest;
+import com.baidu.hugegraph.api.traverser.SingleSourceShortestPathApiTest;
+import com.baidu.hugegraph.api.traverser.TemplatePathsApiTest;
+import com.baidu.hugegraph.api.traverser.WeightedShortestPathApiTest;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ PropertyKeyApiTest.class,
+ VertexLabelApiTest.class,
+ EdgeLabelApiTest.class,
+ IndexLabelApiTest.class,
+ SchemaApiTest.class,
+
+ VertexApiTest.class,
+ EdgeApiTest.class,
+ BatchUpdateElementApiTest.class,
+
+ GremlinApiTest.class,
+ VariablesApiTest.class,
+ TaskApiTest.class,
+ JobApiTest.class,
+ RestoreApiTest.class,
+ GraphsApiTest.class,
+
+ CommonTraverserApiTest.class,
+ KoutApiTest.class,
+ KneighborApiTest.class,
+ PathsApiTest.class,
+ CountApiTest.class,
+ RingsRaysApiTest.class,
+ SameNeighborsApiTest.class,
+ JaccardSimilarityApiTest.class,
+ ShortestPathApiTest.class,
+ AllShortestPathsApiTest.class,
+ SingleSourceShortestPathApiTest.class,
+ WeightedShortestPathApiTest.class,
+ MultiNodeShortestPathApiTest.class,
+ CustomizedPathsApiTest.class,
+ TemplatePathsApiTest.class,
+ FusiformSimilarityApiTest.class,
+ NeighborRankApiTest.class,
+ PersonalRankApiTest.class,
+
+ TargetApiTest.class,
+ GroupApiTest.class,
+ UserApiTest.class,
+ AccessApiTest.class,
+ BelongApiTest.class,
+ ProjectApiTest.class,
+ LoginApiTest.class,
+ LogoutApiTest.class,
+ TokenApiTest.class
+})
+public class ApiTestSuite {
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java
new file mode 100644
index 0000000..d8a5d1f
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.api.graph.EdgeAPI;
+import com.baidu.hugegraph.api.graph.VertexAPI;
+import com.baidu.hugegraph.api.graphs.GraphsAPI;
+import com.baidu.hugegraph.api.job.RebuildAPI;
+import com.baidu.hugegraph.api.schema.EdgeLabelAPI;
+import com.baidu.hugegraph.api.schema.IndexLabelAPI;
+import com.baidu.hugegraph.api.schema.PropertyKeyAPI;
+import com.baidu.hugegraph.api.schema.SchemaAPI;
+import com.baidu.hugegraph.api.schema.VertexLabelAPI;
+import com.baidu.hugegraph.api.task.TaskAPI;
+import com.baidu.hugegraph.api.variables.VariablesAPI;
+import com.baidu.hugegraph.api.version.VersionAPI;
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.VersionUtil;
+
+public class BaseApiTest extends BaseClientTest {
+
+ protected static RestClient client;
+
+ protected static VersionAPI versionAPI;
+ protected static GraphsAPI graphsAPI;
+
+ protected static PropertyKeyAPI propertyKeyAPI;
+ protected static VertexLabelAPI vertexLabelAPI;
+ protected static EdgeLabelAPI edgeLabelAPI;
+ protected static IndexLabelAPI indexLabelAPI;
+ protected static SchemaAPI schemaAPI;
+
+ protected static VertexAPI vertexAPI;
+ protected static EdgeAPI edgeAPI;
+
+ protected static VariablesAPI variablesAPI;
+ protected static TaskAPI taskAPI;
+ protected static RebuildAPI rebuildAPI;
+
+ protected static RestClient initClient() {
+ client = new RestClient(BASE_URL, USERNAME, PASSWORD, TIMEOUT);
+ return client;
+ }
+
+ @BeforeClass
+ public static void init() {
+ BaseClientTest.init();
+ if (client == null) {
+ initClient();
+ }
+
+ versionAPI = new VersionAPI(client);
+ client.apiVersion(VersionUtil.Version.of(versionAPI.get().get("api")));
+
+ graphsAPI = new GraphsAPI(client);
+
+ propertyKeyAPI = new PropertyKeyAPI(client, GRAPH);
+ vertexLabelAPI = new VertexLabelAPI(client, GRAPH);
+ edgeLabelAPI = new EdgeLabelAPI(client, GRAPH);
+ indexLabelAPI = new IndexLabelAPI(client, GRAPH);
+ schemaAPI = new SchemaAPI(client, GRAPH);
+
+ vertexAPI = new VertexAPI(client, GRAPH);
+ edgeAPI = new EdgeAPI(client, GRAPH);
+
+ variablesAPI = new VariablesAPI(client, GRAPH);
+ taskAPI = new TaskAPI(client, GRAPH);
+ rebuildAPI = new RebuildAPI(client, GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() throws Exception {
+ Assert.assertNotNull("Not opened client", client);
+
+ clearData();
+ client.close();
+ client = null;
+
+ BaseClientTest.clear();
+ }
+
+ protected RestClient client() {
+ return client;
+ }
+
+ protected static void clearData() {
+ // Clear edge
+ edgeAPI.list(-1).results().forEach(edge -> {
+ edgeAPI.delete(edge.id());
+ });
+ // Clear vertex
+ vertexAPI.list(-1).results().forEach(vertex -> {
+ vertexAPI.delete(vertex.id());
+ });
+
+ // Clear schema
+ List<Long> ilTaskIds = new ArrayList<>();
+ indexLabelAPI.list().forEach(indexLabel -> {
+ ilTaskIds.add(indexLabelAPI.delete(indexLabel.name()));
+ });
+ ilTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+
+ List<Long> elTaskIds = new ArrayList<>();
+ edgeLabelAPI.list().forEach(edgeLabel -> {
+ elTaskIds.add(edgeLabelAPI.delete(edgeLabel.name()));
+ });
+ elTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+
+ List<Long> vlTaskIds = new ArrayList<>();
+ vertexLabelAPI.list().forEach(vertexLabel -> {
+ vlTaskIds.add(vertexLabelAPI.delete(vertexLabel.name()));
+ });
+ vlTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+
+ List<Long> pkTaskIds = new ArrayList<>();
+ propertyKeyAPI.list().forEach(propertyKey -> {
+ pkTaskIds.add(propertyKeyAPI.delete(propertyKey.name()));
+ });
+ pkTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+
+ // Clear system
+ taskAPI.list(null, -1).forEach(task -> {
+ taskAPI.delete(task.id());
+ });
+ }
+
+ protected static void waitUntilTaskCompleted(long taskId) {
+ if (taskId == 0L) {
+ return;
+ }
+ taskAPI.waitUntilTaskSuccess(taskId, TIMEOUT);
+ }
+
+ protected static void waitUntilTaskCompleted(long taskId, long timeout) {
+ if (taskId == 0L) {
+ return;
+ }
+ taskAPI.waitUntilTaskSuccess(taskId, timeout);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BatchUpdateElementApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BatchUpdateElementApiTest.java
new file mode 100644
index 0000000..d4ec65f
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/BatchUpdateElementApiTest.java
@@ -0,0 +1,760 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import static com.baidu.hugegraph.structure.graph.UpdateStrategy.INTERSECTION;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.structure.graph.BatchEdgeRequest;
+import com.baidu.hugegraph.structure.graph.BatchVertexRequest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.UpdateStrategy;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class BatchUpdateElementApiTest extends BaseApiTest {
+
+ private static final int BATCH_SIZE = 5;
+
+ @BeforeClass
+ public static void prepareSchema() {
+ SchemaManager schema = schema();
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("price").asInt().ifNotExist().create();
+ schema.propertyKey("date").asDate().ifNotExist().create();
+ schema.propertyKey("set").asText().valueSet().ifNotExist().create();
+ schema.propertyKey("list").asText().valueList().ifNotExist().create();
+
+ schema.vertexLabel("object")
+ .properties("name", "price", "date", "set", "list")
+ .primaryKeys("name")
+ .nullableKeys("price", "date", "set", "list")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("updates")
+ .sourceLabel("object")
+ .targetLabel("object")
+ .properties("name", "price", "date", "set", "list")
+ .nullableKeys("name", "price", "date", "set", "list")
+ .ifNotExist()
+ .create();
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ vertexAPI.list(-1).results().forEach(v -> vertexAPI.delete(v.id()));
+ edgeAPI.list(-1).results().forEach(e -> edgeAPI.delete(e.id()));
+ }
+
+ /* Vertex Test */
+ @Test
+ public void testVertexBatchUpdateStrategySum() {
+ BatchVertexRequest req = batchVertexRequest("price", 1, -1,
+ UpdateStrategy.SUM);
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 0);
+
+ req = batchVertexRequest("price", 2, 3, UpdateStrategy.SUM);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 5);
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyBigger() {
+ // TODO: Add date comparison after fixing the date serialization bug
+ BatchVertexRequest req = batchVertexRequest("price", -3, 1,
+ UpdateStrategy.BIGGER);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 1);
+
+ req = batchVertexRequest("price", 7, 3, UpdateStrategy.BIGGER);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 7);
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategySmaller() {
+ BatchVertexRequest req = batchVertexRequest("price", -3, 1,
+ UpdateStrategy.SMALLER);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", -3);
+
+ req = batchVertexRequest("price", 7, 3, UpdateStrategy.SMALLER);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 3);
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyUnion() {
+ BatchVertexRequest req = batchVertexRequest("set", "old", "new",
+ UpdateStrategy.UNION);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "set", "new", "old");
+
+ req = batchVertexRequest("set", "old", "old", UpdateStrategy.UNION);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "set", "old");
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyIntersection() {
+ BatchVertexRequest req = batchVertexRequest("set", "old", "new",
+ INTERSECTION);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "set");
+
+ req = batchVertexRequest("set", "old", "old", INTERSECTION);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "set", "old");
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyAppend() {
+ BatchVertexRequest req = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.APPEND);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "list", "old", "old");
+
+ req = batchVertexRequest("list", "old", "new", UpdateStrategy.APPEND);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "list", "old", "new");
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyEliminate() {
+ BatchVertexRequest req = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "list");
+
+ req = batchVertexRequest("list", "old", "x", UpdateStrategy.ELIMINATE);
+ vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "list", "old");
+ }
+
+ @Test
+ public void testVertexBatchUpdateStrategyOverride() {
+ BatchVertexRequest req = batchVertexRequest("price", -1, 1,
+ UpdateStrategy.OVERRIDE);
+ assertBatchResponse(vertexAPI.update(req), "price", 1);
+
+ // Construct a specialized test case
+ graph().addVertices(this.createNVertexBatch("object", -1, 2));
+ List<String> list = ImmutableList.of("newStr1", "newStr2");
+
+ Vertex v1 = new Vertex("object");
+ v1.property("name", "tom");
+ v1.property("price", 1);
+ v1.property("list", list);
+
+ Vertex v2 = new Vertex("object");
+ v2.property("name", "tom");
+
+ Map<String, UpdateStrategy> strategies;
+ strategies = ImmutableMap.of("price", UpdateStrategy.OVERRIDE,
+ "list", UpdateStrategy.OVERRIDE);
+ req = BatchVertexRequest.createBuilder()
+ .vertices(ImmutableList.of(v1, v2))
+ .updatingStrategies(strategies)
+ .createIfNotExist(true)
+ .build();
+
+ List<Vertex> vertices = vertexAPI.update(req);
+ Assert.assertEquals(1, vertices.size());
+ Map<String, Object> expectProperties = ImmutableMap.of("name", "tom",
+ "price", 1,
+ "list", list);
+ Assert.assertEquals(vertices.get(0).properties(), expectProperties);
+ }
+
+ @Test
+ public void testVertexBatchUpdateWithNullValues() {
+ BatchVertexRequest req = batchVertexRequest("price", 1, null,
+ UpdateStrategy.OVERRIDE);
+ List<Vertex> vertices = vertexAPI.update(req);
+ assertBatchResponse(vertices, "price", 1);
+ }
+
+ @Test
+ public void testVertexBatchUpdateWithInvalidArgs() {
+ BatchVertexRequest req1 = batchVertexRequest("set", "old", "old",
+ UpdateStrategy.UNION);
+
+ Assert.assertThrows(ServerException.class, () -> {
+ List<Vertex> vertices = Whitebox.getInternalState(req1, "vertices");
+ vertices.set(1, null);
+ Whitebox.setInternalState(req1, "vertices", vertices);
+ vertexAPI.update(req1);
+ }, e -> {
+ Assert.assertContains("The batch body can't contain null record",
+ e.toString());
+ });
+
+ BatchVertexRequest req2 = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req2, "vertices", null);
+ vertexAPI.update(req2);
+ }, e -> {
+ String expect = "Parameter 'vertices' can't be null";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req3 = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req3, "vertices", ImmutableList.of());
+ vertexAPI.update(req3);
+ }, e -> {
+ String expect = "The number of vertices can't be 0";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req4 = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req4, "createIfNotExist", false);
+ vertexAPI.update(req4);
+ }, e -> {
+ String expect = "Parameter 'create_if_not_exist' " +
+ "dose not support false now";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req5 = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req5, "updateStrategies", null);
+ vertexAPI.update(req5);
+ }, e -> {
+ String expect = "Parameter 'update_strategies' can't be empty";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req6 = batchVertexRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req6, "updateStrategies",
+ ImmutableMap.of());
+ vertexAPI.update(req6);
+ }, e -> {
+ String expect = "Parameter 'update_strategies' can't be empty";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testVertexInvalidUpdateStrategy() {
+ BatchVertexRequest req1 = batchVertexRequest("name", "old", "new",
+ UpdateStrategy.SUM);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req1);
+ }, e -> {
+ String expect = "Property type must be Number for strategy SUM, " +
+ "but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req2 = batchVertexRequest("name", "old", "new",
+ UpdateStrategy.BIGGER);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req2);
+ }, e -> {
+ String expect = "Property type must be Date or Number " +
+ "for strategy BIGGER, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req3 = batchVertexRequest("name", "old", "new",
+ UpdateStrategy.SMALLER);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req3);
+ }, e -> {
+ String expect = "Property type must be Date or Number " +
+ "for strategy SMALLER, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req4 = batchVertexRequest("price", 1, -1,
+ UpdateStrategy.UNION);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req4);
+ }, e -> {
+ String expect = "Property type must be Set or List " +
+ "for strategy UNION, but got type Integer, Integer";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req5 = batchVertexRequest("date", "old", "new",
+ INTERSECTION);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req5);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy INTERSECTION, but got type Date, Long";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req6 = batchVertexRequest("price", 1, -1,
+ UpdateStrategy.APPEND);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req6);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy APPEND, but got type Integer, Integer";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchVertexRequest req7 = batchVertexRequest("name", "old", "new",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ vertexAPI.update(req7);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy ELIMINATE, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ /* Edge Test */
+ @Test
+ public void testEdgeBatchUpdateStrategySum() {
+ BatchEdgeRequest req = batchEdgeRequest("price", -1, 1,
+ UpdateStrategy.SUM);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 0);
+
+ req = batchEdgeRequest("price", 2, 3, UpdateStrategy.SUM);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 5);
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyBigger() {
+ // TODO: Add date comparison after fixing the date serialization bug
+ BatchEdgeRequest req = batchEdgeRequest("price", -3, 1,
+ UpdateStrategy.BIGGER);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 1);
+
+ req = batchEdgeRequest("price", 7, 3, UpdateStrategy.BIGGER);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 7);
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategySmaller() {
+ BatchEdgeRequest req = batchEdgeRequest("price", -3, 1,
+ UpdateStrategy.SMALLER);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", -3);
+
+ req = batchEdgeRequest("price", 7, 3, UpdateStrategy.SMALLER);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 3);
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyUnion() {
+ BatchEdgeRequest req = batchEdgeRequest("set", "old", "new",
+ UpdateStrategy.UNION);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "set", "new", "old");
+
+ req = batchEdgeRequest("set", "old", "old", UpdateStrategy.UNION);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "set", "old");
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyIntersection() {
+ BatchEdgeRequest req = batchEdgeRequest("set", "old", "new",
+ INTERSECTION);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "set");
+
+ req = batchEdgeRequest("set", "old", "old", INTERSECTION);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "set", "old");
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyAppend() {
+ BatchEdgeRequest req = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.APPEND);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "list", "old", "old");
+
+ req = batchEdgeRequest("list", "old", "new", UpdateStrategy.APPEND);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "list", "old", "new");
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyEliminate() {
+ BatchEdgeRequest req = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "list");
+
+ req = batchEdgeRequest("list", "old", "new", UpdateStrategy.ELIMINATE);
+ edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "list", "old");
+ }
+
+ @Test
+ public void testEdgeBatchUpdateStrategyOverride() {
+ BatchEdgeRequest req = batchEdgeRequest("price", -1, 1,
+ UpdateStrategy.OVERRIDE);
+ assertBatchResponse(edgeAPI.update(req), "price", 1);
+
+ // Construct a specialized test case
+ graph().addEdges(this.createNEdgesBatch("object", "updates", -1, 2));
+ List<String> list = ImmutableList.of("newStr1", "newStr2");
+ String vid = "1:a";
+
+ Edge e1 = new Edge("updates");
+ e1.sourceLabel("object");
+ e1.targetLabel("object");
+ e1.sourceId(vid);
+ e1.targetId(vid);
+ e1.property("name", "tom");
+ e1.property("price", 1);
+ e1.property("list", list);
+
+ Edge e2 = new Edge("updates");
+ e2.sourceLabel("object");
+ e2.targetLabel("object");
+ e2.sourceId(vid);
+ e2.targetId(vid);
+ e2.property("name", "tom");
+
+ Map<String, UpdateStrategy> strategies;
+ strategies = ImmutableMap.of("price", UpdateStrategy.OVERRIDE,
+ "list", UpdateStrategy.OVERRIDE);
+ req = BatchEdgeRequest.createBuilder()
+ .edges(ImmutableList.of(e1, e2))
+ .updatingStrategies(strategies)
+ .checkVertex(false)
+ .createIfNotExist(true)
+ .build();
+
+ List<Edge> edges = edgeAPI.update(req);
+ Assert.assertEquals(1, edges.size());
+ Map<String, Object> expectProperties = ImmutableMap.of("name", "tom",
+ "price", 1,
+ "list", list);
+ Assert.assertEquals(edges.get(0).properties(), expectProperties);
+ }
+
+ @Test
+ public void testEdgeBatchUpdateWithNullValues() {
+ BatchEdgeRequest req = batchEdgeRequest("price", 1, null,
+ UpdateStrategy.OVERRIDE);
+
+ List<Edge> edges = edgeAPI.update(req);
+ assertBatchResponse(edges, "price", 1);
+ }
+
+ @Test
+ public void testEdgeBatchUpdateWithInvalidArgs() {
+ BatchEdgeRequest req1 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ List<Edge> edges = Whitebox.getInternalState(req1, "edges");
+ edges.set(1, null);
+ Whitebox.setInternalState(req1, "edges", edges);
+ edgeAPI.update(req1);
+ }, e -> {
+ String expect = "The batch body can't contain null record";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req2 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req2, "edges", null);
+ edgeAPI.update(req2);
+ }, e -> {
+ String expect = "Parameter 'edges' can't be null";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req3 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req3, "edges", ImmutableList.of());
+ edgeAPI.update(req3);
+ }, e -> {
+ String expect = "The number of edges can't be 0";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req4 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req4, "createIfNotExist", false);
+ edgeAPI.update(req4);
+ }, e -> {
+ String expect = "Parameter 'create_if_not_exist' " +
+ "dose not support false now";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req5 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req5, "updateStrategies", null);
+ edgeAPI.update(req5);
+ }, e -> {
+ String expect = "Parameter 'update_strategies' can't be empty";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req6 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(req6, "updateStrategies",
+ ImmutableMap.of());
+ edgeAPI.update(req6);
+ }, e -> {
+ String expect = "Parameter 'update_strategies' can't be empty";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req7 = batchEdgeRequest("list", "old", "old",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ List<Edge> edges = this.createNEdgesBatch("object", "updates",
+ "old", 501);
+ Whitebox.setInternalState(req7, "edges", edges);
+ edgeAPI.update(req7);
+ }, e -> {
+ String expect = "Too many edges for one time post";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testEdgeInvalidUpdateStrategy() {
+ BatchEdgeRequest req1 = batchEdgeRequest("name", "old", "new",
+ UpdateStrategy.SUM);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req1);
+ }, e -> {
+ String expect = "Property type must be Number for strategy SUM, " +
+ "but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req2 = batchEdgeRequest("name", "old", "new",
+ UpdateStrategy.BIGGER);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req2);
+ }, e -> {
+ String expect = "Property type must be Date or Number " +
+ "for strategy BIGGER, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req3 = batchEdgeRequest("name", "old", "new",
+ UpdateStrategy.SMALLER);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req3);
+ }, e -> {
+ String expect = "Property type must be Date or Number " +
+ "for strategy SMALLER, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req4 = batchEdgeRequest("price", 1, -1,
+ UpdateStrategy.UNION);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req4);
+ }, e -> {
+ String expect = "Property type must be Set or List " +
+ "for strategy UNION, but got type Integer, Integer";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req5 = batchEdgeRequest("date", "old", "new",
+ INTERSECTION);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req5);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy INTERSECTION, but got type Date, Long";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req6 = batchEdgeRequest("price", 1, -1,
+ UpdateStrategy.APPEND);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req6);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy APPEND, but got type Integer, Integer";
+ Assert.assertContains(expect, e.getMessage());
+ });
+
+ BatchEdgeRequest req7 = batchEdgeRequest("name", "old", "new",
+ UpdateStrategy.ELIMINATE);
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.update(req7);
+ }, e -> {
+ String expect = "Property type must be Set or List for " +
+ "strategy ELIMINATE, but got type String, String";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ private BatchVertexRequest batchVertexRequest(String key, Object oldData,
+ Object newData,
+ UpdateStrategy strategy) {
+ // Init old & new vertices
+ graph().addVertices(this.createNVertexBatch("object", oldData,
+ BATCH_SIZE));
+ List<Vertex> vertices = this.createNVertexBatch("object", newData,
+ BATCH_SIZE);
+
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of(key, strategy);
+ BatchVertexRequest req;
+ req = new BatchVertexRequest.Builder().vertices(vertices)
+ .updatingStrategies(strategies)
+ .createIfNotExist(true)
+ .build();
+ return req;
+ }
+
+ private BatchEdgeRequest batchEdgeRequest(String key, Object oldData,
+ Object newData,
+ UpdateStrategy strategy) {
+ // Init old vertices & edges
+ graph().addVertices(this.createNVertexBatch("object", oldData,
+ BATCH_SIZE * 2));
+ graph().addEdges(this.createNEdgesBatch("object", "updates",
+ oldData, BATCH_SIZE));
+ List<Edge> edges = this.createNEdgesBatch("object", "updates",
+ newData, BATCH_SIZE);
+
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of(key, strategy);
+ BatchEdgeRequest req;
+ req = new BatchEdgeRequest.Builder().edges(edges)
+ .updatingStrategies(strategies)
+ .checkVertex(false)
+ .createIfNotExist(true)
+ .build();
+ return req;
+ }
+
+ private List<Vertex> createNVertexBatch(String vertexLabel,
+ Object symbol, int num) {
+ List<Vertex> vertices = new ArrayList<>(num);
+ for (int i = 1; i <= num; i++) {
+ Vertex vertex = new Vertex(vertexLabel);
+ vertex.property("name", String.valueOf(i));
+ if (symbol instanceof Number) {
+ vertex.property("price", (int) symbol * i);
+ }
+ vertex.property("date", new Date(System.currentTimeMillis() + i));
+ vertex.property("set", ImmutableSet.of(String.valueOf(symbol) + i));
+ vertex.property("list",
+ ImmutableList.of(String.valueOf(symbol) + i));
+ vertices.add(vertex);
+ }
+ return vertices;
+ }
+
+ private List<Edge> createNEdgesBatch(String vertexLabel, String edgeLabel,
+ Object symbol, int num) {
+ VertexLabel vLabel = schema().getVertexLabel(vertexLabel);
+
+ List<Edge> edges = new ArrayList<>(num);
+ for (int i = 1; i <= num; i++) {
+ Edge edge = new Edge(edgeLabel);
+ edge.sourceLabel(vertexLabel);
+ edge.targetLabel(vertexLabel);
+ edge.sourceId(vLabel.id() + ":" + i);
+ edge.targetId(vLabel.id() + ":" + i * 2);
+ edge.property("name", String.valueOf(i));
+ if (symbol instanceof Number) {
+ edge.property("price", (int) symbol * i);
+ }
+ edge.property("date", new Date(System.currentTimeMillis() + i));
+ edge.property("set", ImmutableSet.of(String.valueOf(symbol) + i));
+ edge.property("list", ImmutableList.of(String.valueOf(symbol) + i));
+ edges.add(edge);
+ }
+ return edges;
+ }
+
+ private static void assertBatchResponse(List<? extends GraphElement> list,
+ String property, int result) {
+ Assert.assertEquals(BATCH_SIZE, list.size());
+ list.forEach(element -> {
+ String index = String.valueOf(element.property("name"));
+ Object value = element.property(property);
+ Assert.assertTrue(value instanceof Number);
+ Assert.assertEquals(result * Integer.parseInt(index), value);
+ });
+ }
+
+ private static void assertBatchResponse(List<? extends GraphElement> list,
+ String property, String... data) {
+ Assert.assertEquals(BATCH_SIZE, list.size());
+ list.forEach(element -> {
+ String index = String.valueOf(element.property("name"));
+ Object value = element.property(property);
+ Assert.assertTrue(value instanceof List);
+ if (data.length == 0) {
+ Assert.assertTrue(((List<?>) value).isEmpty());
+ } else if (data.length == 1) {
+ Assert.assertEquals(ImmutableList.of(data[0] + index), value);
+ } else {
+ Assert.assertEquals(ImmutableList.of(data[0] + index,
+ data[1] + index), value);
+ }
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeApiTest.java
new file mode 100644
index 0000000..9ab8dbb
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeApiTest.java
@@ -0,0 +1,761 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableMap;
+
+public class EdgeApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initVertex();
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ edgeAPI.list(-1).results().forEach(e -> edgeAPI.delete(e.id()));
+ }
+
+ @Test
+ public void testCreate() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ edge = edgeAPI.create(edge);
+
+ Assert.assertEquals("created", edge.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("software", edge.targetLabel());
+ Assert.assertEquals(outVId, edge.sourceId());
+ Assert.assertEquals(inVId, edge.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date,
+ "city", "Hongkong");
+ Assert.assertEquals(props, edge.properties());
+ }
+
+ @Test
+ public void testCreateWithUndefinedLabel() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("undefined");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedPropertyKey() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("not-exist-key", "not-exist-value");
+ edge.property("city", "Hongkong");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testCreateWithoutSourceOrTargetLabel() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ edge = edgeAPI.create(edge);
+
+ Assert.assertEquals("created", edge.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("software", edge.targetLabel());
+ Assert.assertEquals(outVId, edge.sourceId());
+ Assert.assertEquals(inVId, edge.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date,
+ "city", "Hongkong");
+ Assert.assertEquals(props, edge.properties());
+ }
+
+ @Test
+ public void testCreateWithUndefinedSourceOrTargetLabel() {
+ Edge edge = new Edge("created");
+ edge.sourceLabel("undefined");
+ edge.targetLabel("undefined");
+ edge.sourceId("person:peter");
+ edge.targetId("software:lop");
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testCreateWithoutSourceOrTargetId() {
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testCreateWithNotExistSourceOrTargetId() {
+ Edge edge = new Edge("created");
+ edge.sourceId("not-exist-source-id");
+ edge.targetId("not-exist-target-id");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testCreateExistVertex() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+ edgeAPI.create(edge);
+
+ edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", Utils.date("2017-03-24"));
+ edge.property("city", "Beijing");
+
+ Assert.assertEquals("created", edge.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("software", edge.targetLabel());
+ Assert.assertEquals(outVId, edge.sourceId());
+ Assert.assertEquals(inVId, edge.targetId());
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.date("2017-03-24"),
+ "city", "Beijing");
+ Assert.assertEquals(props, edge.properties());
+ }
+
+ @Test
+ public void testCreateWithNullableKeysAbsent() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ // Absent prop 'city'
+ edge.property("date", Utils.date("2017-03-24"));
+ edgeAPI.create(edge);
+
+ Assert.assertEquals("created", edge.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("software", edge.targetLabel());
+ Assert.assertEquals(outVId, edge.sourceId());
+ Assert.assertEquals(inVId, edge.targetId());
+ Map<String, Object> props = ImmutableMap.of("date",
+ Utils.date("2017-03-24"));
+ Assert.assertEquals(props, edge.properties());
+ }
+
+ @Test
+ public void testCreateWithNonNullKeysAbsent() {
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId("person:peter");
+ edge.targetId("software:lop");
+ // Absent prop 'date'
+ edge.property("city", "Beijing");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edge);
+ });
+ }
+
+ @Test
+ public void testAddEdgeWithTtl() {
+ SchemaManager schema = schema();
+ schema.propertyKey("place").asText().ifNotExist().create();
+ schema.edgeLabel("read").link("person", "book")
+ .properties("place", "date")
+ .ttl(3000L)
+ .enableLabelIndex(true)
+ .ifNotExist()
+ .create();
+
+ Vertex baby = graph().addVertex(T.label, "person", "name", "Baby",
+ "age", 3, "city", "Beijing");
+ Vertex java = graph().addVertex(T.label, "book", T.id, "java",
+ "name", "Java in action");
+ Edge edge = baby.addEdge("read", java, "place", "library of school",
+ "date", "2019-12-23 12:00:00");
+
+ Edge result = graph().getEdge(edge.id());
+ Assert.assertEquals("read", result.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("book", edge.targetLabel());
+ Assert.assertEquals(baby.id(), edge.sourceId());
+ Assert.assertEquals(java.id(), edge.targetId());
+ Map<String, Object> props = ImmutableMap.of("place",
+ "library of school",
+ "date",
+ "2019-12-23 12:00:00.000");
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getEdge(edge.id());
+ Assert.assertEquals("read", result.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("book", edge.targetLabel());
+ Assert.assertEquals(baby.id(), edge.sourceId());
+ Assert.assertEquals(java.id(), edge.targetId());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getEdge(edge.id());
+ Assert.assertEquals("read", result.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("book", edge.targetLabel());
+ Assert.assertEquals(baby.id(), edge.sourceId());
+ Assert.assertEquals(java.id(), edge.targetId());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().getEdge(edge.id());
+ }, e -> {
+ Assert.assertContains("does not exist", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testAddEdgeWithTtlAndTtlStartTime() {
+ SchemaManager schema = schema();
+ schema.propertyKey("place").asText().ifNotExist().create();
+ schema.edgeLabel("borrow").link("person", "book")
+ .properties("place", "date")
+ .ttl(3000L)
+ .ttlStartTime("date")
+ .enableLabelIndex(true)
+ .ifNotExist()
+ .create();
+
+ Vertex baby = graph().addVertex(T.label, "person", "name", "Baby",
+ "age", 3, "city", "Beijing");
+ Vertex java = graph().addVertex(T.label, "book", T.id, "java",
+ "name", "Java in action");
+ long date = DateUtil.now().getTime() - 1000L;
+ String dateString = Utils.formatDate(new Date(date));
+ Edge edge = baby.addEdge("borrow", java, "place", "library of school",
+ "date", date);
+
+ Edge result = graph().getEdge(edge.id());
+ Assert.assertEquals("borrow", result.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("book", edge.targetLabel());
+ Assert.assertEquals(baby.id(), edge.sourceId());
+ Assert.assertEquals(java.id(), edge.targetId());
+ Map<String, Object> props = ImmutableMap.of("place",
+ "library of school",
+ "date",
+ dateString);
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getEdge(edge.id());
+ Assert.assertEquals("borrow", result.label());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("book", edge.targetLabel());
+ Assert.assertEquals(baby.id(), edge.sourceId());
+ Assert.assertEquals(java.id(), edge.targetId());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().getEdge(edge.id());
+ }, e -> {
+ Assert.assertContains("does not exist", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testBatchCreateWithValidVertexAndCheck() {
+ VertexLabel person = schema().getVertexLabel("person");
+ VertexLabel software = schema().getVertexLabel("software");
+
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> createds = super.create50CreatedBatch();
+ List<Edge> knows = super.create50KnowsBatch();
+
+ List<String> createdIds = edgeAPI.create(createds, true);
+ List<String> knowsIds = edgeAPI.create(knows, true);
+
+ Assert.assertEquals(50, createdIds.size());
+ Assert.assertEquals(50, knowsIds.size());
+
+ for (int i = 0; i < 50; i++) {
+ Edge created = edgeAPI.get(createdIds.get(i));
+ Assert.assertEquals("created", created.label());
+ Assert.assertEquals("person", created.sourceLabel());
+ Assert.assertEquals("software", created.targetLabel());
+ Assert.assertEquals(person.id() + ":Person-" + i, created.sourceId());
+ Assert.assertEquals(software.id() + ":Software-" + i,
+ created.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date,
+ "city", "Hongkong");
+ Assert.assertEquals(props, created.properties());
+ }
+
+ for (int i = 0; i < 50; i++) {
+ Edge know = edgeAPI.get(knowsIds.get(i));
+ Assert.assertEquals("knows", know.label());
+ Assert.assertEquals("person", know.sourceLabel());
+ Assert.assertEquals("person", know.targetLabel());
+ Assert.assertEquals(person.id() + ":Person-" + i, know.sourceId());
+ Assert.assertEquals(person.id() + ":Person-" + (i + 50),
+ know.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date);
+ Assert.assertEquals(props, know.properties());
+ }
+ }
+
+ @Test
+ public void testBatchCreateContainsInvalidEdge() {
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> createds = super.create50CreatedBatch();
+ List<Edge> knows = super.create50KnowsBatch();
+
+ createds.get(0).sourceId("not-exist-sourceId-id");
+ knows.get(10).property("undefined-key", "undefined-value");
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(createds, true);
+ });
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(knows, true);
+ });
+ }
+
+ @Test
+ public void testBatchCreateWithMoreThanBatchSize() {
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> edges = new ArrayList<>(1000);
+ for (int i = 0; i < 1000; i++) {
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId("person:Person-" + i);
+ edge.targetId("software:Software-" + i);
+ edge.property("date", "2017-08-24");
+ edge.property("city", "Hongkong");
+ edges.add(edge);
+ }
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edges, true);
+ });
+ }
+
+ @Test
+ public void testBatchCreateWithValidVertexAndNotCheck() {
+ VertexLabel person = schema().getVertexLabel("person");
+ VertexLabel software = schema().getVertexLabel("software");
+
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> createds = super.create50CreatedBatch();
+ List<Edge> knows = super.create50KnowsBatch();
+
+ List<String> createdIds = edgeAPI.create(createds, false);
+ List<String> knowsIds = edgeAPI.create(knows, false);
+
+ Assert.assertEquals(50, createdIds.size());
+ Assert.assertEquals(50, knowsIds.size());
+
+ for (int i = 0; i < 50; i++) {
+ Edge created = edgeAPI.get(createdIds.get(i));
+ Assert.assertEquals("created", created.label());
+ Assert.assertEquals("person", created.sourceLabel());
+ Assert.assertEquals("software", created.targetLabel());
+ Assert.assertEquals(person.id() + ":Person-" + i, created.sourceId());
+ Assert.assertEquals(software.id() + ":Software-" + i,
+ created.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date,
+ "city", "Hongkong");
+ Assert.assertEquals(props, created.properties());
+ }
+
+ for (int i = 0; i < 50; i++) {
+ Edge know = edgeAPI.get(knowsIds.get(i));
+ Assert.assertEquals("knows", know.label());
+ Assert.assertEquals("person", know.sourceLabel());
+ Assert.assertEquals("person", know.targetLabel());
+ Assert.assertEquals(person.id() + ":Person-" + i, know.sourceId());
+ Assert.assertEquals(person.id() + ":Person-" + (i + 50),
+ know.targetId());
+ String date = Utils.formatDate("2017-03-24");
+ Map<String, Object> props = ImmutableMap.of("date", date);
+ Assert.assertEquals(props, know.properties());
+ }
+ }
+
+ @Test
+ public void testBatchCreateWithInvalidVertexIdAndCheck() {
+ List<Edge> edges = new ArrayList<>(2);
+
+ Edge edge1 = new Edge("created");
+ edge1.sourceLabel("person");
+ edge1.targetLabel("software");
+ edge1.sourceId("person:invalid");
+ edge1.targetId("software:lop");
+ edge1.property("date", "2017-03-24");
+ edge1.property("city", "Hongkong");
+ edges.add(edge1);
+
+ Edge edge2 = new Edge("knows");
+ edge2.sourceLabel("person");
+ edge2.targetLabel("person");
+ edge2.sourceId("person:peter");
+ edge2.targetId("person:invalid");
+ edge2.property("date", "2017-03-24");
+ edges.add(edge2);
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edges, true);
+ });
+ }
+
+ @Test
+ public void testBatchCreateWithInvalidVertexLabelAndCheck() {
+ List<Edge> edges = new ArrayList<>(2);
+
+ Edge edge1 = new Edge("created");
+ edge1.sourceId("person:peter");
+ edge1.targetId("software:lop");
+ edge1.property("date", "2017-03-24");
+ edge1.property("city", "Hongkong");
+ edges.add(edge1);
+
+ Edge edge2 = new Edge("knows");
+ edge2.sourceLabel("undefined");
+ edge2.targetLabel("undefined");
+ edge2.sourceId("person:peter");
+ edge2.targetId("person:marko");
+ edge2.property("date", "2017-03-24");
+ edges.add(edge2);
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edges, true);
+ });
+ }
+
+ /**
+ * Note: When the vertex of an edge is dirty (id), g.E() will
+ * construct the vertex, and then throw a illegal exception.
+ * That will lead clearData error.
+ * (Icafe: HugeGraph-768)
+ */
+// @Test
+// public void testBatchCreateWithInvalidVertexIdButNotCheck() {
+// List<Edge> edges = new ArrayList<>(2);
+//
+// Edge edge1 = new Edge("created");
+// edge1.sourceLabel("person");
+// edge1.targetLabel("software");
+// edge1.sourceId("person:invalid");
+// edge1.targetId("software:lop");
+// edge1.property("date", "2017-03-24");
+// edge1.property("city", "Hongkong");
+// edges.add(edge1);
+//
+// Edge edge2 = new Edge("knows");
+// edge2.sourceLabel("person");
+// edge2.targetLabel("person");
+// edge2.sourceId("person:peter");
+// edge2.targetId("person:invalid");
+// edge2.property("date", "2017-03-24");
+// edges.add(edge2);
+//
+// List<String> ids = edgeAPI.create(edges, false);
+// Assert.assertEquals(2, ids.size());
+//
+// Utils.assertErrorResponse(404, () -> {
+// edgeAPI.get(ids.get(0));
+// });
+// Utils.assertErrorResponse(404, () -> {
+// edgeAPI.get(ids.get(1));
+// });
+// }
+
+ @Test
+ public void testBatchCreateWithInvalidVertexLabelButNotCheck() {
+ List<Edge> edges = new ArrayList<>(2);
+
+ Edge edge1 = new Edge("created");
+ edge1.sourceId("person:peter");
+ edge1.targetId("software:lop");
+ edge1.property("date", "2017-03-24");
+ edge1.property("city", "Hongkong");
+ edges.add(edge1);
+
+ Edge edge2 = new Edge("knows");
+ edge2.sourceLabel("undefined");
+ edge2.targetLabel("undefined");
+ edge2.sourceId("person:peter");
+ edge2.targetId("person:marko");
+ edge2.property("date", "2017-03-24");
+ edges.add(edge2);
+
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.create(edges, false);
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge1 = new Edge("created");
+ edge1.sourceLabel("person");
+ edge1.targetLabel("software");
+ edge1.sourceId(outVId);
+ edge1.targetId(inVId);
+ edge1.property("date", "2017-03-24");
+ edge1.property("city", "Hongkong");
+
+ edge1 = edgeAPI.create(edge1);
+
+ Edge edge2 = edgeAPI.get(edge1.id());
+
+ Assert.assertEquals(edge1.label(), edge2.label());
+ Assert.assertEquals(edge1.sourceLabel(), edge2.sourceLabel());
+ Assert.assertEquals(edge1.targetLabel(), edge2.targetLabel());
+ Assert.assertEquals(edge1.sourceId(), edge2.sourceId());
+ Assert.assertEquals(edge1.targetId(), edge2.targetId());
+ Assert.assertEquals(edge1.properties(), edge2.properties());
+ }
+
+ @Test
+ public void testGetNotExist() {
+ String edgeId = "not-exist-edge-id";
+ Assert.assertThrows(ServerException.class, () -> {
+ // TODO: id to be modified
+ edgeAPI.get(edgeId);
+ }, e -> {
+ Assert.assertContains("Edge id must be formatted as 4~5 parts, " +
+ "but got 1 parts: 'not-exist-edge-id'",
+ e.getMessage());
+ Assert.assertInstanceOf(ServerException.class, e);
+ Assert.assertContains("NotFoundException",
+ ((ServerException) e).exception());
+ });
+ }
+
+ @Test
+ public void testList() {
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> createds = super.create50CreatedBatch();
+ List<Edge> knows = super.create50KnowsBatch();
+
+ edgeAPI.create(createds, true);
+ edgeAPI.create(knows, true);
+
+ List<Edge> edges = edgeAPI.list(-1).results();
+ Assert.assertEquals(100, edges.size());
+ }
+
+ @Test
+ public void testListWithLimit() {
+ List<Vertex> persons = super.create100PersonBatch();
+ List<Vertex> softwares = super.create50SoftwareBatch();
+
+ vertexAPI.create(persons);
+ vertexAPI.create(softwares);
+
+ List<Edge> createds = super.create50CreatedBatch();
+ List<Edge> knows = super.create50KnowsBatch();
+
+ edgeAPI.create(createds, true);
+ edgeAPI.create(knows, true);
+
+ List<Edge> edges = edgeAPI.list(10).results();
+ Assert.assertEquals(10, edges.size());
+ }
+
+ @Test
+ public void testDelete() {
+ Object outVId = getVertexId("person", "name", "peter");
+ Object inVId = getVertexId("software", "name", "lop");
+
+ Edge edge = new Edge("created");
+ edge.sourceLabel("person");
+ edge.targetLabel("software");
+ edge.sourceId(outVId);
+ edge.targetId(inVId);
+ edge.property("date", "2017-03-24");
+ edge.property("city", "Hongkong");
+
+ edge = edgeAPI.create(edge);
+
+ final String id = edge.id();
+ edgeAPI.delete(id);
+
+ Assert.assertThrows(ServerException.class, () -> {
+ edgeAPI.get(id);
+ }, e -> {
+ String expect = String.format("Edge '%s' does not exist", id);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.delete("S364:peter>213>>S365:not-found");
+ });
+ Utils.assertResponseError(400, () -> {
+ edgeAPI.delete("not-exist-e");
+ });
+ }
+
+ @SuppressWarnings("unused")
+ private static void assertContains(List<Edge> edges, Edge edge) {
+ Assert.assertTrue(Utils.contains(edges, edge));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeLabelApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeLabelApiTest.java
new file mode 100644
index 0000000..8e4b999
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/EdgeLabelApiTest.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Frequency;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class EdgeLabelApiTest extends BaseApiTest {
+
+ private static Function<String, EdgeLabel> fillEdgeLabel =
+ (name) -> schema().edgeLabel(name)
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .build();
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ List<Long> taskIds = new ArrayList<>();
+ edgeLabelAPI.list().forEach(el -> {
+ taskIds.add(edgeLabelAPI.delete(el.name()));
+ });
+ taskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+ }
+
+ @Test
+ public void testCreate() {
+ EdgeLabel edgeLabel = fillEdgeLabel.apply("created");
+ edgeLabel = edgeLabelAPI.create(edgeLabel);
+
+ Assert.assertEquals("created", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(true, edgeLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("date", "city");
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ }
+
+ @Test
+ public void testCreateWithFrequency() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .frequency(Frequency.SINGLE)
+ .properties("date", "city")
+ .enableLabelIndex(false)
+ .create();
+
+ Assert.assertEquals("created", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(false, edgeLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("date", "city");
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ }
+
+ @Test
+ public void testCreateWithEnableLabelIndexFalse() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .enableLabelIndex(false)
+ .create();
+
+ Assert.assertEquals("created", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(false, edgeLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("date", "city");
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ }
+
+ @Test
+ public void testCreateWithInvalidName() {
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(fillEdgeLabel.apply(""));
+ });
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(fillEdgeLabel.apply(" "));
+ });
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(fillEdgeLabel.apply(" "));
+ });
+ }
+
+ @Test
+ public void testCreateExistedEdgeLabel() {
+ edgeLabelAPI.create(fillEdgeLabel.apply("created"));
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(fillEdgeLabel.apply("created"));
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedPropertyKey() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("undefined", "city")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedSortKey() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .multiTimes()
+ .properties("date", "city")
+ .sortKeys("undefined")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedNullableKeys() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .nullableKeys("undefined")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithNonNullKeysIntersectSortKeys() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .multiTimes()
+ .properties("date", "city")
+ .sortKeys("date")
+ .nullableKeys("date")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedVertexLabel() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("programmer")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.create(edgeLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithTtl() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created1")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .frequency(Frequency.SINGLE)
+ .properties("date", "city")
+ .build();
+ edgeLabel = edgeLabelAPI.create(edgeLabel);
+
+ Assert.assertEquals("created1", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(true, edgeLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("date", "city");
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ Assert.assertEquals(0L, edgeLabel.ttl());
+ Assert.assertNull(edgeLabel.ttlStartTime());
+
+ edgeLabel = schema().edgeLabel("created2")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .frequency(Frequency.SINGLE)
+ .properties("date", "city")
+ .ttl(3000L)
+ .build();
+ edgeLabel = edgeLabelAPI.create(edgeLabel);
+
+ Assert.assertEquals("created2", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(true, edgeLabel.enableLabelIndex());
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ Assert.assertEquals(3000L, edgeLabel.ttl());
+ Assert.assertNull(edgeLabel.ttlStartTime());
+
+ edgeLabel = schema().edgeLabel("created3")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .frequency(Frequency.SINGLE)
+ .properties("date", "city")
+ .ttl(3000L)
+ .ttlStartTime("date")
+ .build();
+ edgeLabel = edgeLabelAPI.create(edgeLabel);
+
+ Assert.assertEquals("created3", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(true, edgeLabel.enableLabelIndex());
+ Assert.assertTrue(props.size() == edgeLabel.properties().size());
+ Assert.assertTrue(props.containsAll(edgeLabel.properties()));
+ Assert.assertEquals(3000L, edgeLabel.ttl());
+ Assert.assertEquals("date", edgeLabel.ttlStartTime());
+ }
+
+ @Test
+ public void testAppend() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date")
+ .build();
+
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Set<String> props = ImmutableSet.of("date");
+ Assert.assertEquals(props, edgeLabel1.properties());
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("created")
+ .properties("city")
+ .nullableKeys("city")
+ .build();
+ edgeLabel2 = edgeLabelAPI.append(edgeLabel2);
+
+ Assert.assertEquals("created", edgeLabel2.name());
+ Assert.assertEquals("person", edgeLabel2.sourceLabel());
+ Assert.assertEquals("software", edgeLabel2.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel2.frequency());
+ props = ImmutableSet.of("date", "city");
+ Set<String> nullableKeys = ImmutableSet.of("city");
+ Assert.assertEquals(props, edgeLabel2.properties());
+ Assert.assertEquals(nullableKeys, edgeLabel2.nullableKeys());
+ }
+
+ @Test
+ public void testAppendWithUndefinedPropertyKey() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date")
+ .build();
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Set<String> props = ImmutableSet.of("date");
+ Assert.assertEquals(props, edgeLabel1.properties());
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("created")
+ .properties("undefined")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.append(edgeLabel2);
+ });
+ }
+
+ @Test
+ public void testAppendWithUndefinedNullableKeys() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date")
+ .build();
+
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Set<String> props = ImmutableSet.of("date");
+ Assert.assertEquals(props, edgeLabel1.properties());
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("created")
+ .nullableKeys("undefined").build();
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.append(edgeLabel2);
+ });
+ }
+
+ @Test
+ public void testAppendWithSourceOrTargetLabel() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date")
+ .build();
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Set<String> props = ImmutableSet.of("date");
+ Assert.assertEquals(props, edgeLabel1.properties());
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .properties("city")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.append(edgeLabel2);
+ });
+ }
+
+ @Test
+ public void testEliminate() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date")
+ .build();
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Set<String> props = ImmutableSet.of("date");
+ Assert.assertEquals(props, edgeLabel1.properties());
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("created")
+ .properties("city").build();
+ Utils.assertResponseError(400, () -> {
+ edgeLabelAPI.eliminate(edgeLabel2);
+ });
+ }
+
+ @Test
+ public void testGet() {
+ EdgeLabel edgeLabel1 = edgeLabelAPI.create(
+ fillEdgeLabel.apply("created"));
+
+ EdgeLabel edgeLabel2 = edgeLabelAPI.get("created");
+
+ Assert.assertEquals(edgeLabel1.name(), edgeLabel2.name());
+ Assert.assertEquals(edgeLabel1.sourceLabel(), edgeLabel2.sourceLabel());
+ Assert.assertEquals(edgeLabel1.targetLabel(), edgeLabel2.targetLabel());
+ Assert.assertEquals(edgeLabel1.frequency(), edgeLabel2.frequency());
+ Assert.assertEquals(edgeLabel1.properties(), edgeLabel2.properties());
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ edgeLabelAPI.get("not-exist-el");
+ });
+ }
+
+ @Test
+ public void testList() {
+ EdgeLabel edgeLabel1 = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .build();
+ edgeLabel1 = edgeLabelAPI.create(edgeLabel1);
+
+ EdgeLabel edgeLabel2 = schema().edgeLabel("knows")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .singleTime()
+ .properties("date")
+ .build();
+ edgeLabel2 = edgeLabelAPI.create(edgeLabel2);
+
+ List<EdgeLabel> edgeLabels = edgeLabelAPI.list();
+ Assert.assertEquals(2, edgeLabels.size());
+ assertContains(edgeLabels, edgeLabel1);
+ assertContains(edgeLabels, edgeLabel2);
+ }
+
+ @Test
+ public void testListByNames() {
+ EdgeLabel created = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .build();
+ created = edgeLabelAPI.create(created);
+
+ EdgeLabel knows = schema().edgeLabel("knows")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .singleTime()
+ .properties("date")
+ .build();
+ knows = edgeLabelAPI.create(knows);
+
+ List<EdgeLabel> edgeLabels;
+
+ edgeLabels = edgeLabelAPI.list(ImmutableList.of("created"));
+ Assert.assertEquals(1, edgeLabels.size());
+ assertContains(edgeLabels, created);
+
+ edgeLabels = edgeLabelAPI.list(ImmutableList.of("knows"));
+ Assert.assertEquals(1, edgeLabels.size());
+ assertContains(edgeLabels, knows);
+
+ edgeLabels = edgeLabelAPI.list(ImmutableList.of("created", "knows"));
+ Assert.assertEquals(2, edgeLabels.size());
+ assertContains(edgeLabels, created);
+ assertContains(edgeLabels, knows);
+ }
+
+ @Test
+ public void testDelete() {
+ EdgeLabel edgeLabel = schema().edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .singleTime()
+ .properties("date", "city")
+ .build();
+ edgeLabelAPI.create(edgeLabel);
+
+ long taskId = edgeLabelAPI.delete("created");
+ waitUntilTaskCompleted(taskId);
+
+ Utils.assertResponseError(404, () -> {
+ edgeLabelAPI.get("created");
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(404, () -> {
+ edgeLabelAPI.delete("not-exist-el");
+ });
+ }
+
+ @Test
+ public void testAddEdgeLabelWithUserData() {
+ EdgeLabel father = schema().edgeLabel("father")
+ .link("person", "person")
+ .properties("weight")
+ .userdata("multiplicity", "one-to-many")
+ .build();
+ father = edgeLabelAPI.create(father);
+ Assert.assertEquals(2, father.userdata().size());
+ Assert.assertEquals("one-to-many",
+ father.userdata().get("multiplicity"));
+ String time = (String) father.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ EdgeLabel write = schema().edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("multiplicity", "many-to-many")
+ .build();
+ write = edgeLabelAPI.create(write);
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, write.userdata().size());
+ Assert.assertEquals("many-to-many",
+ write.userdata().get("multiplicity"));
+ time = (String) write.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GraphsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GraphsApiTest.java
new file mode 100644
index 0000000..b9c9cfb
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GraphsApiTest.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.After;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.rest.ClientException;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableSet;
+
+public class GraphsApiTest extends BaseApiTest {
+
+ private static final String GRAPH2 = "hugegraph2";
+ private static final String CONFIG2_PATH =
+ "src/test/resources/hugegraph-create.properties";
+
+ private static final String GRAPH3 = "hugegraph3";
+ private static final String CONFIG3_PATH =
+ "src/test/resources/hugegraph-clone.properties";
+
+ @Override
+ @After
+ public void teardown() {
+ for (String g : ImmutableSet.of(GRAPH2, GRAPH3)) {
+ try {
+ graphsAPI.get(g);
+ } catch (Exception ognored) {
+ continue;
+ }
+ graphsAPI.drop(g, "I'm sure to drop the graph");
+ }
+ }
+
+ @Test
+ public void testCreateAndDropGraph() {
+ int initialGraphNumber = graphsAPI.list().size();
+
+ // Create new graph dynamically
+ String config;
+ try {
+ config = FileUtils.readFileToString(new File(CONFIG2_PATH),
+ StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new ClientException("Failed to read config file: %s",
+ CONFIG2_PATH);
+ }
+ Map<String, String> result = graphsAPI.create(GRAPH2, null, config);
+ Assert.assertEquals(2, result.size());
+ Assert.assertEquals(GRAPH2, result.get("name"));
+ Assert.assertEquals("rocksdb", result.get("backend"));
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ HugeClient client = new HugeClient(baseClient(), GRAPH2);
+ // Insert graph schema and data
+ initPropertyKey(client);
+ initVertexLabel(client);
+ initEdgeLabel(client);
+
+ List<Vertex> vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex("person").property("name", "person" + i)
+ .property("city", "Beijing")
+ .property("age", 19);
+ vertices.add(vertex);
+ }
+ vertices = client.graph().addVertices(vertices);
+
+ List<Edge> edges = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Edge edge = new Edge("knows").source(vertices.get(i))
+ .target(vertices.get((i + 1) % 100))
+ .property("date", "2016-01-10");
+ edges.add(edge);
+ }
+ client.graph().addEdges(edges, false);
+
+ // Query vertices and edges count from new created graph
+ ResultSet resultSet = client.gremlin().gremlin("g.V().count()")
+ .execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ // Clear graph schema and data from new created graph
+ graphsAPI.clear(GRAPH2, "I'm sure to delete all data");
+
+ resultSet = client.gremlin().gremlin("g.V().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ Assert.assertTrue(client.schema().getPropertyKeys().isEmpty());
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ // Remove new created graph dynamically
+ graphsAPI.drop(GRAPH2, "I'm sure to drop the graph");
+
+ Assert.assertEquals(initialGraphNumber, graphsAPI.list().size());
+ }
+
+ @Test
+ public void testCloneAndDropGraph() {
+ int initialGraphNumber = graphsAPI.list().size();
+
+ // Clone a new graph from exist a graph dynamically
+ String config;
+ try {
+ config = FileUtils.readFileToString(new File(CONFIG3_PATH),
+ StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new ClientException("Failed to read config file: %s",
+ CONFIG3_PATH);
+ }
+ Map<String, String> result = graphsAPI.create(GRAPH3, "hugegraph",
+ config);
+ Assert.assertEquals(2, result.size());
+ Assert.assertEquals(GRAPH3, result.get("name"));
+ Assert.assertEquals("rocksdb", result.get("backend"));
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ HugeClient client = new HugeClient(baseClient(), GRAPH3);
+ // Insert graph schema and data
+ initPropertyKey(client);
+ initVertexLabel(client);
+ initEdgeLabel(client);
+
+ List<Vertex> vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex("person").property("name", "person" + i)
+ .property("city", "Beijing")
+ .property("age", 19);
+ vertices.add(vertex);
+ }
+ vertices = client.graph().addVertices(vertices);
+
+ List<Edge> edges = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Edge edge = new Edge("knows").source(vertices.get(i))
+ .target(vertices.get((i + 1) % 100))
+ .property("date", "2016-01-10");
+ edges.add(edge);
+ }
+ client.graph().addEdges(edges, false);
+
+ // Query vertices and edges count from new created graph
+ ResultSet resultSet = client.gremlin().gremlin("g.V().count()")
+ .execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ // Clear graph schema and data from new created graph
+ graphsAPI.clear(GRAPH3, "I'm sure to delete all data");
+
+ resultSet = client.gremlin().gremlin("g.V().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ Assert.assertTrue(client.schema().getPropertyKeys().isEmpty());
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ // Remove new created graph dynamically
+ graphsAPI.drop(GRAPH3, "I'm sure to drop the graph");
+
+ Assert.assertEquals(initialGraphNumber, graphsAPI.list().size());
+ }
+
+ @Test
+ public void testCloneAndDropGraphWithoutConfig() {
+ int initialGraphNumber = graphsAPI.list().size();
+
+ // Clone a new graph from exist a graph dynamically
+ String config = null;
+ Map<String, String> result = graphsAPI.create(GRAPH3, "hugegraph",
+ config);
+ Assert.assertEquals(2, result.size());
+ Assert.assertEquals(GRAPH3, result.get("name"));
+ Assert.assertEquals("rocksdb", result.get("backend"));
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ HugeClient client = new HugeClient(baseClient(), GRAPH3);
+ // Insert graph schema and data
+ initPropertyKey(client);
+ initVertexLabel(client);
+ initEdgeLabel(client);
+
+ List<Vertex> vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex("person").property("name", "person" + i)
+ .property("city", "Beijing")
+ .property("age", 19);
+ vertices.add(vertex);
+ }
+ vertices = client.graph().addVertices(vertices);
+
+ List<Edge> edges = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Edge edge = new Edge("knows").source(vertices.get(i))
+ .target(vertices.get((i + 1) % 100))
+ .property("date", "2016-01-10");
+ edges.add(edge);
+ }
+ client.graph().addEdges(edges, false);
+
+ // Query vertices and edges count from new created graph
+ ResultSet resultSet = client.gremlin().gremlin("g.V().count()")
+ .execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(100, resultSet.iterator().next().getInt());
+
+ // Clear graph schema and data from new created graph
+ graphsAPI.clear(GRAPH3, "I'm sure to delete all data");
+
+ resultSet = client.gremlin().gremlin("g.V().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ resultSet = client.gremlin().gremlin("g.E().count()").execute();
+ Assert.assertEquals(0, resultSet.iterator().next().getInt());
+
+ Assert.assertTrue(client.schema().getPropertyKeys().isEmpty());
+
+ Assert.assertEquals(initialGraphNumber + 1, graphsAPI.list().size());
+
+ // Remove new created graph dynamically
+ graphsAPI.drop(GRAPH3, "I'm sure to drop the graph");
+
+ Assert.assertEquals(initialGraphNumber, graphsAPI.list().size());
+ }
+
+ protected static void initPropertyKey(HugeClient client) {
+ SchemaManager schema = client.schema();
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("age").asInt().ifNotExist().create();
+ schema.propertyKey("city").asText().ifNotExist().create();
+ schema.propertyKey("lang").asText().ifNotExist().create();
+ schema.propertyKey("date").asDate().ifNotExist().create();
+ schema.propertyKey("price").asInt().ifNotExist().create();
+ schema.propertyKey("weight").asDouble().ifNotExist().create();
+ }
+
+ protected static void initVertexLabel(HugeClient client) {
+ SchemaManager schema = client.schema();
+
+ schema.vertexLabel("person")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("software")
+ .properties("name", "lang", "price")
+ .primaryKeys("name")
+ .nullableKeys("price")
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("book")
+ .useCustomizeStringId()
+ .properties("name", "price")
+ .nullableKeys("price")
+ .ifNotExist()
+ .create();
+ }
+
+ protected static void initEdgeLabel(HugeClient client) {
+ SchemaManager schema = client.schema();
+
+ schema.edgeLabel("knows")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .multiTimes()
+ .properties("date", "city")
+ .sortKeys("date")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("created")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .properties("date", "city")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GremlinApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GremlinApiTest.java
new file mode 100644
index 0000000..1962b92
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/GremlinApiTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.gremlin.GremlinRequest;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.GraphAttachable;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.Result;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class GremlinApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ }
+
+ @Before
+ public void prepareData() {
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testQueryAllVertices() {
+ GremlinRequest request = new GremlinRequest("g.V()");
+ ResultSet resultSet = gremlin().execute(request);
+
+ Assert.assertEquals(6, resultSet.size());
+
+ request = new GremlinRequest("g.V().drop()");
+ gremlin().execute(request);
+
+ request = new GremlinRequest("g.V()");
+ resultSet = gremlin().execute(request);
+
+ Assert.assertEquals(0, resultSet.size());
+ }
+
+ @Test
+ public void testQueryAllEdges() {
+ GremlinRequest request = new GremlinRequest("g.E()");
+ ResultSet resultSet = gremlin().execute(request);
+
+ Assert.assertEquals(6, resultSet.size());
+
+ request = new GremlinRequest("g.E().drop()");
+ gremlin().execute(request);
+
+ request = new GremlinRequest("g.E()");
+ resultSet = gremlin().execute(request);
+
+ Assert.assertEquals(0, resultSet.size());
+ }
+
+ @Test
+ public void testAsyncRemoveAllVertices() {
+ GremlinRequest request = new GremlinRequest("g.V()");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(6, resultSet.size());
+
+ String gremlin = "hugegraph.traversal().V().drop()";
+ request = new GremlinRequest(gremlin);
+ long id = gremlin().executeAsTask(request);
+ waitUntilTaskCompleted(id);
+
+ request = new GremlinRequest("g.V()");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(0, resultSet.size());
+ }
+
+ @Test
+ public void testAsyncRemoveAllEdges() {
+ GremlinRequest request = new GremlinRequest("g.E()");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(6, resultSet.size());
+
+ String gremlin = "g.E().drop()";
+ request = new GremlinRequest(gremlin);
+ long id = gremlin().executeAsTask(request);
+ waitUntilTaskCompleted(id);
+
+ request = new GremlinRequest("g.E()");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(0, resultSet.size());
+ }
+
+ @Test
+ public void testPrimitiveObject() {
+ GremlinRequest request = new GremlinRequest("1 + 2");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(1, resultSet.size());
+
+ Iterator<Result> results = resultSet.iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Object object = result.getObject();
+ Assert.assertEquals(Integer.class, object.getClass());
+ Assert.assertEquals(3, object);
+ }
+ }
+
+ @Test
+ public void testIterateEmptyResultSet() {
+ GremlinRequest request = new GremlinRequest("g.V().limit(0)");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(0, resultSet.size());
+ Assert.assertThrows(NoSuchElementException.class, () -> {
+ resultSet.iterator().next();
+ });
+ }
+
+ @Test
+ public void testAttachedManager() {
+ GremlinRequest request = new GremlinRequest("g.V()");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(6, resultSet.size());
+
+ Iterator<Result> results = resultSet.iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Object object = result.getObject();
+ Assert.assertEquals(Vertex.class, object.getClass());
+ Vertex vertex = (Vertex) object;
+ Assert.assertNotNull(Whitebox.getInternalState(vertex, "manager"));
+ }
+
+ request = new GremlinRequest("g.E()");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(6, resultSet.size());
+
+ results = resultSet.iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Object object = result.getObject();
+ Assert.assertEquals(Edge.class, object.getClass());
+ Edge edge = (Edge) object;
+ Assert.assertNotNull(Whitebox.getInternalState(edge, "manager"));
+ }
+
+ request = new GremlinRequest("g.V().outE().path()");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(6, resultSet.size());
+
+ results = resultSet.iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Object object = result.getObject();
+ Assert.assertEquals(Path.class, object.getClass());
+ Path path = (Path) object;
+ Assert.assertNotNull(path.objects());
+ for (Object pathObject : path.objects()) {
+ Assert.assertTrue(pathObject instanceof GraphAttachable);
+ Assert.assertNotNull(Whitebox.getInternalState(pathObject,
+ "manager"));
+ }
+ Assert.assertNull(path.crosspoint());
+ }
+ }
+
+ @Test
+ public void testInvalidGremlin() {
+ Assert.assertThrows(ServerException.class, () -> {
+ client().post("gremlin", "{");
+ }, e -> {
+ Assert.assertContains("body could not be parsed", e.getMessage());
+ });
+
+ GremlinRequest request = new GremlinRequest("g.V2()");
+ Assert.assertThrows(ServerException.class, () -> {
+ gremlin().execute(request);
+ }, e -> {
+ Assert.assertContains("No signature of method: ", e.getMessage());
+ Assert.assertContains(".V2() is applicable for argument types: ()",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testSecurityOperation() {
+ GremlinRequest request = new GremlinRequest("System.exit(-1)");
+ Assert.assertThrows(ServerException.class, () -> {
+ gremlin().execute(request);
+ }, e -> {
+ String msg = "Not allowed to call System.exit() via Gremlin";
+ Assert.assertContains(msg, e.getMessage());
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/IndexLabelApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/IndexLabelApiTest.java
new file mode 100644
index 0000000..f4bc7bf
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/IndexLabelApiTest.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.IndexType;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.baidu.hugegraph.util.VersionUtil;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class IndexLabelApiTest extends BaseApiTest {
+
+ private static Function<String, IndexLabel> fillIndexLabel =
+ (name) -> schema().indexLabel(name)
+ .onV("person")
+ .by("age")
+ .range()
+ .build();
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ List<Long> taskIds = new ArrayList<>();
+ indexLabelAPI.list().forEach(il -> {
+ taskIds.add(indexLabelAPI.delete(il.name()));
+ });
+ taskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+ }
+
+ @Test
+ public void testCreate() {
+ IndexLabel indexLabel = indexLabelAPI.create(
+ fillIndexLabel.apply("personByAge"))
+ .indexLabel();
+
+ Assert.assertEquals("personByAge", indexLabel.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel.baseType());
+ Assert.assertEquals("person", indexLabel.baseValue());
+ Assert.assertEquals(IndexType.RANGE, indexLabel.indexType());
+ List<String> fields = ImmutableList.of("age");
+ Assert.assertTrue(fields.size() == indexLabel.indexFields().size());
+ Assert.assertTrue(fields.containsAll(indexLabel.indexFields()));
+ }
+
+ @Test
+ public void testCreateWithIndexType() {
+ IndexLabel indexLabel = schema().indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .indexType(IndexType.RANGE)
+ .build();
+
+ Assert.assertEquals("personByAge", indexLabel.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel.baseType());
+ Assert.assertEquals("person", indexLabel.baseValue());
+ Assert.assertEquals(IndexType.RANGE, indexLabel.indexType());
+ List<String> fields = ImmutableList.of("age");
+ Assert.assertTrue(fields.size() == indexLabel.indexFields().size());
+ Assert.assertTrue(fields.containsAll(indexLabel.indexFields()));
+ }
+
+ @Test
+ public void testCreateWithInvalidName() {
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(fillIndexLabel.apply(""));
+ });
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(fillIndexLabel.apply(" "));
+ });
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(fillIndexLabel.apply(" "));
+ });
+ }
+
+ @Test
+ public void testCreateExistedIndexLabel() {
+ indexLabelAPI.create(fillIndexLabel.apply("personByAge"));
+
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(fillIndexLabel.apply("personByAge"));
+ });
+ }
+
+ @Test
+ public void testCreateOnUndefinedSchemaLabel() {
+ IndexLabel indexLabel1 = schema().indexLabel("authorByAge")
+ .onV("author")
+ .by("age")
+ .range()
+ .build();
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(indexLabel1);
+ });
+
+ IndexLabel indexLabel2 = schema().indexLabel("writeByDate")
+ .onE("write")
+ .by("date")
+ .secondary()
+ .build();
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(indexLabel2);
+ });
+ }
+
+ @Test
+ public void testCreateSearchIndexOnMultiProperties() {
+ IndexLabel indexLabel = schema().indexLabel("personByAgeAndCity")
+ .onV("person")
+ .by("age", "city")
+ .search()
+ .build();
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(indexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateSecondaryIndexOnMultiPropertiesOverrideExist() {
+ String personByCity = "personByCity";
+ String personByCityAndAge = "personByCityAndAge";
+ schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .create();
+ indexLabelAPI.get(personByCity);
+ Assert.assertThrows(ServerException.class, () -> {
+ indexLabelAPI.get(personByCityAndAge);
+ }, e -> {
+ String expect = String.format("index label with name '%s' does " +
+ "not exist", personByCityAndAge);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ schema().indexLabel(personByCityAndAge)
+ .onV("person")
+ .by("city", "age")
+ .secondary()
+ .create();
+ Assert.assertThrows(ServerException.class, () -> {
+ indexLabelAPI.get(personByCity);
+ }, e -> {
+ String expect = String.format("index label with name '%s' does " +
+ "not exist", personByCity);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ indexLabelAPI.get(personByCityAndAge);
+ }
+
+ @Test
+ public void testCreateShardIndexOnMultiPropertiesOverrideExist() {
+ String personByCity = "personByCity";
+ String personByCityAndAge = "personByCityAndAge";
+ schema().indexLabel(personByCity)
+ .onV("person")
+ .by("city")
+ .secondary()
+ .create();
+ indexLabelAPI.get(personByCity);
+ Assert.assertThrows(ServerException.class, () -> {
+ indexLabelAPI.get(personByCityAndAge);
+ }, e -> {
+ String expect = String.format("index label with name '%s' does " +
+ "not exist", personByCityAndAge);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ schema().indexLabel(personByCityAndAge)
+ .onV("person")
+ .by("city", "age")
+ .shard()
+ .create();
+ Assert.assertThrows(ServerException.class, () -> {
+ indexLabelAPI.get(personByCity);
+ }, e -> {
+ String expect = String.format("index label with name '%s' does " +
+ "not exist", personByCity);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ indexLabelAPI.get(personByCityAndAge);
+ }
+
+ @Test
+ public void testCreateRangeIndexOnNotNumberProperty() {
+ IndexLabel indexLabel = schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .range()
+ .build();
+ Utils.assertResponseError(400, () -> {
+ indexLabelAPI.create(indexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateUniqueIndex() {
+ IndexLabel indexLabel = schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .unique()
+ .create();
+ Assert.assertEquals("personByCity", indexLabel.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel.baseType());
+ Assert.assertEquals("person", indexLabel.baseValue());
+ Assert.assertEquals(IndexType.UNIQUE, indexLabel.indexType());
+ List<String> fields = ImmutableList.of("city");
+ Assert.assertTrue(fields.size() == indexLabel.indexFields().size());
+ Assert.assertTrue(fields.containsAll(indexLabel.indexFields()));
+ }
+
+ @Test
+ public void testGet() {
+ IndexLabel indexLabel1 = schema().indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .range()
+ .build();
+
+ indexLabel1 = indexLabelAPI.create(indexLabel1).indexLabel();
+
+ IndexLabel indexLabel2 = indexLabelAPI.get("personByAge");
+
+ Assert.assertEquals(indexLabel1.name(), indexLabel2.name());
+ Assert.assertEquals(indexLabel1.baseType(), indexLabel2.baseType());
+ Assert.assertEquals(indexLabel1.baseValue(), indexLabel2.baseValue());
+ Assert.assertEquals(indexLabel1.indexType(), indexLabel2.indexType());
+ Assert.assertEquals(indexLabel1.indexFields(),
+ indexLabel2.indexFields());
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ indexLabelAPI.get("not-exist-il");
+ });
+ }
+
+ @Test
+ public void testList() {
+ IndexLabel indexLabel1 = indexLabelAPI.create(
+ fillIndexLabel.apply("personByAge"))
+ .indexLabel();
+
+ IndexLabel indexLabel2 = schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .build();
+ indexLabel2 = indexLabelAPI.create(indexLabel2).indexLabel();
+
+ List<IndexLabel> indexLabels = indexLabelAPI.list();
+ Assert.assertEquals(2, indexLabels.size());
+ assertContains(indexLabels, indexLabel1);
+ assertContains(indexLabels, indexLabel2);
+ }
+
+ @Test
+ public void testListByNames() {
+ IndexLabel personByAge = fillIndexLabel.apply("personByAge");
+ personByAge = indexLabelAPI.create(personByAge).indexLabel();
+
+ IndexLabel personByCity = schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .build();
+ personByCity = indexLabelAPI.create(personByCity).indexLabel();
+
+ List<IndexLabel> indexLabels;
+
+ indexLabels = indexLabelAPI.list(ImmutableList.of("personByAge"));
+ Assert.assertEquals(1, indexLabels.size());
+ assertContains(indexLabels, personByAge);
+
+ indexLabels = indexLabelAPI.list(ImmutableList.of("personByCity"));
+ Assert.assertEquals(1, indexLabels.size());
+ assertContains(indexLabels, personByCity);
+
+ indexLabels = indexLabelAPI.list(ImmutableList.of("personByAge",
+ "personByCity"));
+ Assert.assertEquals(2, indexLabels.size());
+ assertContains(indexLabels, personByAge);
+ assertContains(indexLabels, personByCity);
+ }
+
+ @Test
+ public void testDelete() {
+ String name = "personByAge";
+ indexLabelAPI.create(fillIndexLabel.apply(name));
+
+ long taskId = indexLabelAPI.delete(name);
+ waitUntilTaskCompleted(taskId);
+
+ Assert.assertThrows(ServerException.class, () -> {
+ indexLabelAPI.get(name);
+ }, e -> {
+ String expect = String.format("index label with name '%s' does " +
+ "not exist", name);
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(404, () -> {
+ indexLabelAPI.delete("not-exist-il");
+ });
+ }
+
+ @Test
+ public void testAddIndexLabelWithUserData() {
+ IndexLabel personByAge = schema().indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .range()
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .build();
+ personByAge = indexLabelAPI.create(personByAge).indexLabel();
+ Assert.assertEquals(3, personByAge.userdata().size());
+ Assert.assertEquals(0, personByAge.userdata().get("min"));
+ Assert.assertEquals(100, personByAge.userdata().get("max"));
+ String time = (String) personByAge.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ IndexLabel personByCity = schema().indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .userdata("length", 15)
+ .userdata("length", 18)
+ .build();
+ personByCity = indexLabelAPI.create(personByCity).indexLabel();
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, personByCity.userdata().size());
+ Assert.assertEquals(18, personByCity.userdata().get("length"));
+ time = (String) personByCity.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ IndexLabel bookByName = schema().indexLabel("bookByName")
+ .onV("book")
+ .by("name")
+ .secondary()
+ .userdata("option",
+ ImmutableList.of("xx", "yy"))
+ .build();
+ bookByName = indexLabelAPI.create(bookByName).indexLabel();
+ Assert.assertEquals(2, bookByName.userdata().size());
+ Assert.assertEquals(ImmutableList.of("xx", "yy"),
+ bookByName.userdata().get("option"));
+ time = (String) bookByName.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAddIndexLabelWithUserDataWithApiVersion49() {
+ VersionUtil.Version originApiVersion = client().apiVersion();
+ VersionUtil.Version apiVersion = VersionUtil.Version.of("0.49");
+ client().apiVersion(apiVersion);
+
+ IndexLabel personByAge = schema().indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .range()
+ .build();
+ personByAge = indexLabelAPI.create(personByAge).indexLabel();
+ Assert.assertEquals(1, personByAge.userdata().size());
+
+ IndexLabel personByAge1 = schema().indexLabel("personByAge1")
+ .onV("person")
+ .by("age")
+ .range()
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .build();
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ indexLabelAPI.create(personByAge1).indexLabel();
+ }, (e) -> {
+ String msg = "Not support userdata of index label until api " +
+ "version 0.50";
+ Assert.assertContains(msg, e.getMessage());
+ });
+
+ IndexLabel personByAge2 = schema().indexLabel("personByAge2")
+ .onV("person")
+ .by("age")
+ .userdata("min", 0)
+ .build();
+ Assert.assertThrows(NotSupportException.class, () -> {
+ indexLabelAPI.append(personByAge2);
+ }, (e) -> {
+ Assert.assertContains("action append on index label",
+ e.getMessage());
+ });
+
+ IndexLabel personByAge3 = schema().indexLabel("personByAge3")
+ .onV("person")
+ .by("age")
+ .userdata("min", 0)
+ .build();
+ Assert.assertThrows(NotSupportException.class, () -> {
+ indexLabelAPI.eliminate(personByAge3);
+ }, (e) -> {
+ Assert.assertContains("action eliminate on index label",
+ e.getMessage());
+ });
+
+ client().apiVersion(originApiVersion);
+ }
+
+ @Test
+ public void testAddIndexLabelWithRebuildFalse() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+
+ vertex = vertexAPI.create(vertex);
+
+ IndexLabel personByAge = schema().indexLabel("personByAge")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .rebuild(false)
+ .build();
+ IndexLabel.IndexLabelWithTask il = indexLabelAPI.create(personByAge);
+ IndexLabel created = il.indexLabel();
+ Assert.assertEquals(personByAge.name(), created.name());
+ Assert.assertEquals(personByAge.baseType(), created.baseType());
+ Assert.assertEquals(personByAge.baseValue(), created.baseValue());
+ Assert.assertEquals(personByAge.indexType(), created.indexType());
+ Assert.assertEquals(Task.TASK_ID_NULL, il.taskId());
+
+ Map<String, Object> properties = ImmutableMap.of("city", "Beijing");
+ List<Vertex> vertices = vertexAPI.list("person", properties, 0,
+ null, 10).results();
+ Assert.assertEquals(0, vertices.size());
+
+ long taskId = rebuildAPI.rebuild(personByAge);
+ waitUntilTaskCompleted(taskId);
+
+ vertices = vertexAPI.list("person", properties, 0, null, 10)
+ .results();
+ Assert.assertEquals(1, vertices.size());
+ Assert.assertEquals(vertex, vertices.get(0));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/JobApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/JobApiTest.java
new file mode 100644
index 0000000..d00a561
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/JobApiTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class JobApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ taskAPI.list(null, -1).forEach(task -> taskAPI.delete(task.id()));
+ }
+
+ @Test
+ public void testRebuildVertexLabel() {
+ VertexLabel person = schema().getVertexLabel("person");
+ long taskId = rebuildAPI.rebuild(person);
+ Task task = taskAPI.get(taskId);
+ Assert.assertNotNull(task);
+ Assert.assertEquals(taskId, task.id());
+ waitUntilTaskCompleted(taskId);
+ }
+
+ @Test
+ public void testRebuildEdgeLabel() {
+ EdgeLabel created = schema().getEdgeLabel("created");
+ long taskId = rebuildAPI.rebuild(created);
+ Task task = taskAPI.get(taskId);
+ Assert.assertNotNull(task);
+ Assert.assertEquals(taskId, task.id());
+ waitUntilTaskCompleted(taskId);
+ }
+
+ @Test
+ public void testRebuildIndexLabel() {
+ IndexLabel personByCity = schema().getIndexLabel("personByAge");
+ long taskId = rebuildAPI.rebuild(personByCity);
+ Task task = taskAPI.get(taskId);
+ Assert.assertNotNull(task);
+ Assert.assertEquals(taskId, task.id());
+ waitUntilTaskCompleted(taskId);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/PropertyKeyApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/PropertyKeyApiTest.java
new file mode 100644
index 0000000..17d158f
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/PropertyKeyApiTest.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.AggregateType;
+import com.baidu.hugegraph.structure.constant.Cardinality;
+import com.baidu.hugegraph.structure.constant.DataType;
+import com.baidu.hugegraph.structure.constant.WriteType;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public class PropertyKeyApiTest extends BaseApiTest {
+
+ @After
+ public void teardown() throws Exception {
+ List<Long> pkTaskIds = new ArrayList<>();
+ propertyKeyAPI.list().forEach(propertyKey -> {
+ pkTaskIds.add(propertyKeyAPI.delete(propertyKey.name()));
+ });
+ pkTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+ }
+
+ @Test
+ public void testCreate() {
+ PropertyKey propertyKey = schema().propertyKey("name")
+ .asText()
+ .valueSingle()
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("name", propertyKey.name());
+ Assert.assertEquals(DataType.TEXT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ }
+
+ @Test
+ public void testCreateWithDataType() {
+ PropertyKey propertyKey = schema().propertyKey("name")
+ .dataType(DataType.LONG)
+ .valueSingle()
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("name", propertyKey.name());
+ Assert.assertEquals(DataType.LONG, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ }
+
+ @Test
+ public void testCreateWithCardinality() {
+ PropertyKey propertyKey = schema().propertyKey("name")
+ .asText()
+ .cardinality(Cardinality.SET)
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("name", propertyKey.name());
+ Assert.assertEquals(DataType.TEXT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SET, propertyKey.cardinality());
+ }
+
+ @Test
+ public void testCreateWithAggregateType() {
+ PropertyKey propertyKey = schema().propertyKey("name")
+ .asText().valueSingle()
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("name", propertyKey.name());
+ Assert.assertEquals(DataType.TEXT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.NONE, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isNone());
+ Assert.assertFalse(propertyKey.aggregateType().isNumber());
+ Assert.assertTrue(propertyKey.aggregateType().isIndexable());
+
+ propertyKey = schema().propertyKey("no")
+ .asText().valueSingle()
+ .calcOld()
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("no", propertyKey.name());
+ Assert.assertEquals(DataType.TEXT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.OLD, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isOld());
+ Assert.assertTrue(propertyKey.aggregateType().isIndexable());
+ Assert.assertFalse(propertyKey.aggregateType().isNumber());
+
+ propertyKey = schema().propertyKey("max")
+ .asInt().valueSingle()
+ .calcMax()
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("max", propertyKey.name());
+ Assert.assertEquals(DataType.INT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.MAX, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isMax());
+ Assert.assertTrue(propertyKey.aggregateType().isIndexable());
+ Assert.assertTrue(propertyKey.aggregateType().isNumber());
+
+ propertyKey = schema().propertyKey("min")
+ .asInt().valueSingle()
+ .calcMin()
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("min", propertyKey.name());
+ Assert.assertEquals(DataType.INT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.MIN, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isMin());
+ Assert.assertTrue(propertyKey.aggregateType().isIndexable());
+ Assert.assertTrue(propertyKey.aggregateType().isNumber());
+
+ propertyKey = schema().propertyKey("sum")
+ .asInt().valueSingle()
+ .calcSum()
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("sum", propertyKey.name());
+ Assert.assertEquals(DataType.INT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.SUM, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isSum());
+ Assert.assertFalse(propertyKey.aggregateType().isIndexable());
+ Assert.assertTrue(propertyKey.aggregateType().isNumber());
+
+ propertyKey = schema().propertyKey("total")
+ .asInt().valueSingle()
+ .aggregateType(AggregateType.SUM)
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("total", propertyKey.name());
+ Assert.assertEquals(DataType.INT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(AggregateType.SUM, propertyKey.aggregateType());
+ Assert.assertTrue(propertyKey.aggregateType().isSum());
+ Assert.assertFalse(propertyKey.aggregateType().isIndexable());
+ Assert.assertTrue(propertyKey.aggregateType().isNumber());
+
+ propertyKey = schema().propertyKey("nameV46")
+ .asText().valueSingle()
+ .build();
+ PropertyKey.PropertyKeyV46 pk = propertyKey.switchV46();
+ Assert.assertEquals("nameV46", pk.name());
+ Assert.assertEquals(DataType.TEXT, pk.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, pk.cardinality());
+
+ propertyKey = schema().propertyKey("nameV58")
+ .asText().valueSingle()
+ .calcOld()
+ .build();
+ PropertyKey.PropertyKeyV58 pk1 = propertyKey.switchV58();
+ Assert.assertEquals("nameV58", pk1.name());
+ Assert.assertEquals(DataType.TEXT, pk1.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, pk1.cardinality());
+ Assert.assertEquals(AggregateType.OLD, pk1.aggregateType());
+ }
+
+ @Test
+ public void testCreateWithInvalidName() {
+ Utils.assertResponseError(400, () -> {
+ propertyKeyAPI.create(new PropertyKey(""));
+ });
+ Utils.assertResponseError(400, () -> {
+ propertyKeyAPI.create(new PropertyKey(" "));
+ });
+ Utils.assertResponseError(400, () -> {
+ propertyKeyAPI.create(new PropertyKey(" "));
+ });
+ }
+
+ @Test
+ public void testCreateExistedPropertyKey() {
+ PropertyKey propertyKey = new PropertyKey("name");
+ propertyKeyAPI.create(propertyKey);
+
+ Utils.assertResponseError(400, () -> {
+ propertyKeyAPI.create(new PropertyKey("name"));
+ });
+ }
+
+ @Test
+ public void testGet() {
+ PropertyKey propertyKey1 = schema().propertyKey("name")
+ .asText()
+ .valueSingle()
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey1);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey1 = propertyKeyWithTask.propertyKey();
+
+ PropertyKey propertyKey2 = propertyKeyAPI.get("name");
+
+ Assert.assertEquals(propertyKey1.name(), propertyKey2.name());
+ Assert.assertEquals(propertyKey1.dataType(), propertyKey2.dataType());
+ Assert.assertEquals(propertyKey1.cardinality(),
+ propertyKey2.cardinality());
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ propertyKeyAPI.get("not-exist-pk");
+ });
+ }
+
+ @Test
+ public void testList() {
+ PropertyKey propertyKey1 = schema().propertyKey("name")
+ .asText()
+ .valueSingle()
+ .build();
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey1);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey1 = propertyKeyWithTask.propertyKey();
+
+ PropertyKey propertyKey2 = schema().propertyKey("age")
+ .asInt()
+ .valueSingle()
+ .build();
+ propertyKeyWithTask = propertyKeyAPI.create(propertyKey2);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ propertyKey2 = propertyKeyWithTask.propertyKey();
+
+ List<PropertyKey> propertyKeys = propertyKeyAPI.list();
+ Assert.assertEquals(2, propertyKeys.size());
+ assertContains(propertyKeys, propertyKey1);
+ assertContains(propertyKeys, propertyKey2);
+ }
+
+ @Test
+ public void testListByNames() {
+ PropertyKey name = schema().propertyKey("name").asText().build();
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(name);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ name = propertyKeyWithTask.propertyKey();
+
+ PropertyKey age = schema().propertyKey("age").asInt().build();
+ propertyKeyWithTask = propertyKeyAPI.create(age);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ age = propertyKeyWithTask.propertyKey();
+
+ List<PropertyKey> propertyKeys;
+
+ propertyKeys = propertyKeyAPI.list(ImmutableList.of("name"));
+ Assert.assertEquals(1, propertyKeys.size());
+ assertContains(propertyKeys, name);
+
+ propertyKeys = propertyKeyAPI.list(ImmutableList.of("age"));
+ Assert.assertEquals(1, propertyKeys.size());
+ assertContains(propertyKeys, age);
+
+ propertyKeys = propertyKeyAPI.list(ImmutableList.of("name", "age"));
+ Assert.assertEquals(2, propertyKeys.size());
+ assertContains(propertyKeys, name);
+ assertContains(propertyKeys, age);
+ }
+
+ @Test
+ public void testDelete() {
+ PropertyKey propertyKey = schema().propertyKey("name")
+ .asText()
+ .valueSingle()
+ .build();
+ propertyKeyAPI.create(propertyKey);
+ propertyKeyAPI.delete("name");
+
+ Utils.assertResponseError(404, () -> {
+ propertyKeyAPI.get("name");
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(404, () -> {
+ propertyKeyAPI.delete("not-exist-pk");
+ });
+ }
+
+ @Test
+ public void testAddPropertyKeyWithUserData() {
+ PropertyKey age = schema().propertyKey("age")
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .build();
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(age);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ age = propertyKeyWithTask.propertyKey();
+ Assert.assertEquals(3, age.userdata().size());
+ Assert.assertEquals(0, age.userdata().get("min"));
+ Assert.assertEquals(100, age.userdata().get("max"));
+ String time = (String) age.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ PropertyKey id = schema().propertyKey("id")
+ .userdata("length", 15)
+ .userdata("length", 18)
+ .build();
+ propertyKeyWithTask = propertyKeyAPI.create(id);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ id = propertyKeyWithTask.propertyKey();
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, id.userdata().size());
+ Assert.assertEquals(18, id.userdata().get("length"));
+ time = (String) id.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ PropertyKey sex = schema().propertyKey("sex")
+ .userdata("range",
+ ImmutableList.of("male", "female"))
+ .build();
+ propertyKeyWithTask = propertyKeyAPI.create(sex);
+ Assert.assertEquals(0L, propertyKeyWithTask.taskId());
+ sex = propertyKeyWithTask.propertyKey();
+ Assert.assertEquals(2, sex.userdata().size());
+ Assert.assertEquals(ImmutableList.of("male", "female"),
+ sex.userdata().get("range"));
+ time = (String) sex.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAddOlapPropertyKey() {
+ PropertyKey pagerank = schema().propertyKey("pagerank")
+ .asDouble()
+ .writeType(WriteType.OLAP_RANGE)
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(pagerank);
+ long taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ pagerank = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+
+ PropertyKey wcc = schema().propertyKey("wcc")
+ .asText()
+ .writeType(WriteType.OLAP_SECONDARY)
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(wcc);
+ taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ wcc = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("wcc", wcc.name());
+ Assert.assertEquals(WriteType.OLAP_SECONDARY, wcc.writeType());
+ Assert.assertEquals(DataType.TEXT, wcc.dataType());
+
+ PropertyKey none = schema().propertyKey("none")
+ .asText()
+ .writeType(WriteType.OLAP_COMMON)
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(none);
+ taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ none = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("none", none.name());
+ Assert.assertEquals(WriteType.OLAP_COMMON, none.writeType());
+ Assert.assertEquals(DataType.TEXT, none.dataType());
+ }
+
+ @Test
+ public void testClearOlapPropertyKey() {
+ PropertyKey pagerank = schema().propertyKey("pagerank")
+ .asDouble()
+ .writeType(WriteType.OLAP_RANGE)
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(pagerank);
+ long taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ pagerank = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+
+ propertyKeyWithTask = propertyKeyAPI.clear(pagerank);
+ taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ pagerank = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+
+ pagerank = propertyKeyAPI.get("pagerank");
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+ }
+
+ @Test
+ public void testDeleteOlapPropertyKey() {
+ PropertyKey pagerank = schema().propertyKey("pagerank")
+ .asDouble()
+ .writeType(WriteType.OLAP_RANGE)
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(pagerank);
+ long taskId = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ pagerank = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+
+ taskId = propertyKeyAPI.delete(pagerank.name());
+ Assert.assertNotEquals(0L, taskId);
+ waitUntilTaskCompleted(taskId);
+ pagerank = propertyKeyWithTask.propertyKey();
+
+ Assert.assertEquals("pagerank", pagerank.name());
+ Assert.assertEquals(WriteType.OLAP_RANGE, pagerank.writeType());
+ Assert.assertEquals(DataType.DOUBLE, pagerank.dataType());
+
+ Utils.assertResponseError(404, () -> {
+ propertyKeyAPI.get("pagerank");
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/RestoreApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/RestoreApiTest.java
new file mode 100644
index 0000000..50e8145
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/RestoreApiTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.GraphMode;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class RestoreApiTest extends BaseApiTest {
+
+ private static final String GRAPH = "hugegraph";
+
+ @Before
+ public void initGraphModeNone() {
+ graphsAPI.mode(GRAPH, GraphMode.NONE);
+ }
+
+ @Test
+ public void testGetGraphMode() {
+ GraphMode mode = graphsAPI.mode(GRAPH);
+ Assert.assertEquals(GraphMode.NONE, mode);
+ }
+
+ @Test
+ public void testSetGraphMode() {
+ GraphMode mode = graphsAPI.mode(GRAPH);
+ Assert.assertEquals(GraphMode.NONE, mode);
+
+ graphsAPI.mode(GRAPH, GraphMode.RESTORING);
+ mode = graphsAPI.mode(GRAPH);
+ Assert.assertEquals(GraphMode.RESTORING, mode);
+
+ graphsAPI.mode(GRAPH, GraphMode.MERGING);
+ mode = graphsAPI.mode(GRAPH);
+ Assert.assertEquals(GraphMode.MERGING, mode);
+
+ graphsAPI.mode(GRAPH, GraphMode.NONE);
+ mode = graphsAPI.mode(GRAPH);
+ Assert.assertEquals(GraphMode.NONE, mode);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/SchemaApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/SchemaApiTest.java
new file mode 100644
index 0000000..7a9ea69
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/SchemaApiTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class SchemaApiTest extends BaseApiTest {
+
+ @Test
+ public void testlist() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+
+ Map<String, List<SchemaElement>> schemas = schemaAPI.list();
+
+ Assert.assertEquals(4, schemas.size());
+ Assert.assertTrue(schemas.containsKey("propertykeys"));
+ Assert.assertTrue(schemas.containsKey("vertexlabels"));
+ Assert.assertTrue(schemas.containsKey("edgelabels"));
+ Assert.assertTrue(schemas.containsKey("indexlabels"));
+ Assert.assertEquals(7, schemas.get("propertykeys").size());
+ Assert.assertEquals(3, schemas.get("vertexlabels").size());
+ Assert.assertEquals(2, schemas.get("edgelabels").size());
+ Assert.assertTrue(schemas.get("indexlabels").isEmpty());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/TaskApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/TaskApiTest.java
new file mode 100644
index 0000000..60c1dec
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/TaskApiTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.gremlin.GremlinRequest;
+import com.baidu.hugegraph.api.task.TasksWithPage;
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+
+public class TaskApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ taskAPI.list(null, -1).forEach(task -> taskAPI.delete(task.id()));
+ }
+
+ @Test
+ public void testListAll() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ IndexLabel personByAge = schema().getIndexLabel("personByAge");
+ IndexLabel knowsByDate = schema().getIndexLabel("knowsByDate");
+ IndexLabel createdByDate = schema().getIndexLabel("createdByDate");
+
+ Set<Long> taskIds = new HashSet<>();
+ taskIds.add(rebuildAPI.rebuild(personByCity));
+ taskIds.add(rebuildAPI.rebuild(personByAge));
+ taskIds.add(rebuildAPI.rebuild(knowsByDate));
+ taskIds.add(rebuildAPI.rebuild(createdByDate));
+
+ List<Task> tasks = taskAPI.list(null, -1);
+
+ Assert.assertEquals(4, tasks.size());
+
+ Set<Long> listedTaskIds = new HashSet<>();
+ for (Task task : tasks) {
+ listedTaskIds.add(task.id());
+ }
+ Assert.assertEquals(taskIds.size(), listedTaskIds.size());
+ Assert.assertTrue(taskIds.containsAll(listedTaskIds));
+
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+
+ taskIds.forEach(id -> taskAPI.delete(id));
+
+ tasks = taskAPI.list(null, -1);
+ Assert.assertEquals(0, tasks.size());
+ }
+
+ @Test
+ public void testListByIds() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ IndexLabel personByAge = schema().getIndexLabel("personByAge");
+ IndexLabel knowsByDate = schema().getIndexLabel("knowsByDate");
+ IndexLabel createdByDate = schema().getIndexLabel("createdByDate");
+
+ List<Long> taskIds = new ArrayList<>();
+ taskIds.add(rebuildAPI.rebuild(personByCity));
+ taskIds.add(rebuildAPI.rebuild(personByAge));
+ taskIds.add(rebuildAPI.rebuild(knowsByDate));
+ taskIds.add(rebuildAPI.rebuild(createdByDate));
+
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+
+ List<Task> tasks = taskAPI.list(taskIds);
+
+ Assert.assertEquals(4, tasks.size());
+ Set<Long> listedTaskIds = new HashSet<>();
+ for (Task task : tasks) {
+ listedTaskIds.add(task.id());
+ }
+ Assert.assertEquals(taskIds.size(), listedTaskIds.size());
+ Assert.assertTrue(taskIds.containsAll(listedTaskIds));
+ }
+
+ @Test
+ public void testListByStatus() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ IndexLabel personByAge = schema().getIndexLabel("personByAge");
+ IndexLabel knowsByDate = schema().getIndexLabel("knowsByDate");
+ IndexLabel createdByDate = schema().getIndexLabel("createdByDate");
+
+ Set<Long> taskIds = new HashSet<>();
+ taskIds.add(rebuildAPI.rebuild(personByCity));
+ taskIds.add(rebuildAPI.rebuild(personByAge));
+ taskIds.add(rebuildAPI.rebuild(knowsByDate));
+ taskIds.add(rebuildAPI.rebuild(createdByDate));
+
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+
+ List<Task> tasks = taskAPI.list("SUCCESS", -1);
+
+ Assert.assertEquals(4, tasks.size());
+ Set<Long> listedTaskIds = new HashSet<>();
+ for (Task task : tasks) {
+ listedTaskIds.add(task.id());
+ }
+ Assert.assertEquals(taskIds.size(), listedTaskIds.size());
+ Assert.assertTrue(taskIds.containsAll(listedTaskIds));
+
+ tasks = taskAPI.list("SUCCESS", 3);
+
+ Assert.assertEquals(3, tasks.size());
+ Set<Long> listedTaskIds1 = new HashSet<>();
+ for (Task task : tasks) {
+ listedTaskIds1.add(task.id());
+ }
+ Assert.assertEquals(3, listedTaskIds1.size());
+ Assert.assertTrue(taskIds.containsAll(listedTaskIds));
+ }
+
+ @Test
+ public void testListByStatusAndPage() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ IndexLabel personByAge = schema().getIndexLabel("personByAge");
+ IndexLabel knowsByDate = schema().getIndexLabel("knowsByDate");
+ IndexLabel createdByDate = schema().getIndexLabel("createdByDate");
+
+ Set<Long> taskIds = new HashSet<>();
+ taskIds.add(rebuildAPI.rebuild(personByCity));
+ taskIds.add(rebuildAPI.rebuild(personByAge));
+ taskIds.add(rebuildAPI.rebuild(knowsByDate));
+ taskIds.add(rebuildAPI.rebuild(createdByDate));
+
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+
+ TasksWithPage tasksWithPage = taskAPI.list("SUCCESS", "", 2);
+
+ List<Task> tasks = tasksWithPage.tasks();
+ Assert.assertEquals(2, tasks.size());
+ Set<Long> listedTaskIds = new HashSet<>();
+ for (Task task : tasks) {
+ listedTaskIds.add(task.id());
+ }
+
+ tasksWithPage = taskAPI.list("SUCCESS", tasksWithPage.page(), 2);
+
+ List<Task> tasks1 = tasksWithPage.tasks();
+ Assert.assertEquals(2, tasks1.size());
+ Assert.assertNull(tasksWithPage.page());
+ for (Task task : tasks1) {
+ listedTaskIds.add(task.id());
+ }
+ Assert.assertEquals(taskIds.size(), listedTaskIds.size());
+ Assert.assertTrue(taskIds.containsAll(listedTaskIds));
+ }
+
+ @Test
+ public void testGet() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ long taskId = rebuildAPI.rebuild(personByCity);
+
+ Task task = taskAPI.get(taskId);
+
+ Assert.assertNotNull(task);
+ Assert.assertEquals(taskId, task.id());
+ waitUntilTaskCompleted(taskId);
+ }
+
+ @Test
+ public void testDelete() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ long taskId = rebuildAPI.rebuild(personByCity);
+
+ waitUntilTaskCompleted(taskId);
+ taskAPI.delete(taskId);
+
+ Utils.assertResponseError(404, () -> {
+ taskAPI.get(taskId);
+ });
+ }
+
+ @Test
+ public void testCancel() {
+ schema().vertexLabel("man").useAutomaticId().ifNotExist().create();
+
+ String groovy = "for (int i = 0; i < 10; i++) {" +
+ "hugegraph.addVertex(T.label, 'man');" +
+ "hugegraph.tx().commit();" +
+ "}";
+ // Insert 10 records in sync mode
+ GremlinRequest request = new GremlinRequest(groovy);
+ gremlin().execute(request);
+ // Verify insertion takes effect
+ groovy = "g.V()";
+ request = new GremlinRequest(groovy);
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(10, resultSet.size());
+ // Delete to prepare for insertion in async mode
+ groovy = "g.V().drop()";
+ request = new GremlinRequest(groovy);
+ gremlin().execute(request);
+
+ /*
+ * The asyn task scripts need to be able to handle interrupts,
+ * otherwise they cannot be cancelled
+ */
+ groovy = "for (int i = 0; i < 10; i++) {" +
+ "hugegraph.addVertex(T.label, 'man');" +
+ "hugegraph.tx().commit();" +
+ "try {" +
+ "sleep(1000);" +
+ "} catch (InterruptedException e) {" +
+ "break;" +
+ "}" +
+ "}";
+ request = new GremlinRequest(groovy);
+ long taskId = gremlin().executeAsTask(request);
+
+ groovy = "g.V()";
+ request = new GremlinRequest(groovy);
+ // Wait async task running
+ while (true) {
+ resultSet = gremlin().execute(request);
+ if (resultSet.size() > 0) {
+ break;
+ } else {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignored) {}
+ }
+ }
+ // Cancel async task
+ Task task = taskAPI.cancel(taskId);
+ Assert.assertTrue(task.cancelling());
+
+ try {
+ Thread.sleep(1000L);
+ } catch (InterruptedException e) {
+ // ignored
+ }
+
+ task = taskAPI.get(taskId);
+ Assert.assertTrue(task.cancelled());
+
+ resultSet = gremlin().execute(request);
+ Assert.assertTrue(resultSet.size() < 10);
+ }
+
+ @Test
+ public void testTaskAsMap() {
+ IndexLabel personByCity = schema().getIndexLabel("personByCity");
+ long taskId = rebuildAPI.rebuild(personByCity);
+
+ Task task = taskAPI.get(taskId);
+
+ Assert.assertNotNull(task);
+ Assert.assertEquals(taskId, task.id());
+ waitUntilTaskCompleted(taskId);
+
+ task = taskAPI.get(taskId);
+ Map<String, Object> taskMap = task.asMap();
+ Assert.assertEquals("rebuild_index", taskMap.get(Task.P.TYPE));
+ Assert.assertEquals("success", taskMap.get(Task.P.STATUS));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VariablesApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VariablesApiTest.java
new file mode 100644
index 0000000..4228689
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VariablesApiTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+
+public class VariablesApiTest extends BaseApiTest {
+
+ @Override
+ @Before
+ public void setup() {
+ clearVariables();
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ clearVariables();
+ }
+
+ public void clearVariables() {
+ variablesAPI.all().keySet().forEach(variableKey -> {
+ variablesAPI.remove(variableKey);
+ });
+ }
+
+ @Test
+ public void testSetWithStringValue() {
+ variablesAPI.set("version", "0.3.2");
+ Map<String, Object> variable = variablesAPI.get("version");
+ assertContains(variable, "version", "0.3.2");
+
+ variablesAPI.set("graph number", 10);
+ variable = variablesAPI.get("graph number");
+ assertContains(variable, "graph number", 10);
+
+ variablesAPI.set("graph weight", 1.0);
+ variable = variablesAPI.get("graph weight");
+ assertContains(variable, "graph weight", 1.0);
+
+ List<String> sList = Arrays.asList("first", "second", "third");
+ variablesAPI.set("graph list", sList);
+ variable = variablesAPI.get("graph list");
+ assertContains(variable, "graph list", sList);
+
+ Set<Integer> iSet = new HashSet<>();
+ iSet.add(1);
+ iSet.add(2);
+ iSet.add(3);
+ variablesAPI.set("graph set", iSet);
+ variable = variablesAPI.get("graph set");
+ assertContains(variable, "graph set", iSet);
+ }
+
+ @Test
+ public void testSetTwice() {
+ variablesAPI.set("version", 3);
+ variablesAPI.set("version", "0.3.2");
+
+ Map<String, Object> variables = variablesAPI.all();
+ Assert.assertEquals(1, variables.size());
+ }
+
+ @Test
+ public void testAll() {
+ variablesAPI.set("version", "0.3.2");
+ variablesAPI.set("superUser", "Tom");
+ variablesAPI.set("userNumber", 10);
+
+ Map<String, Object> variables = variablesAPI.all();
+ Assert.assertEquals(3, variables.size());
+ }
+
+ @Test
+ public void testGet() {
+ variablesAPI.set("version", "0.3.2");
+ Map<String, Object> variable = variablesAPI.get("version");
+ assertContains(variable, "version", "0.3.2");
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ variablesAPI.get("not-exist-variable");
+ });
+ }
+
+ @Test
+ public void testRemove() {
+ variablesAPI.set("version", "0.3.2");
+ variablesAPI.remove("version");
+ Utils.assertResponseError(404, () -> {
+ variablesAPI.get("version");
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(404, () -> {
+ vertexLabelAPI.delete("not-exist-variable");
+ });
+ }
+
+ private static void assertContains(Map<String, Object> variables,
+ String key, Object value) {
+ Assert.assertTrue(variables.containsKey(key));
+
+ if (variables.get(key) instanceof Collection) {
+ Assert.assertTrue(value instanceof Collection);
+ Collection<?> expect = (Collection<?>) value;
+ Collection<?> actual = (Collection<?>) variables.get(key);
+ Assert.assertTrue(expect.size()== actual.size());
+ actual.forEach(elem -> {
+ Assert.assertTrue((expect.contains(elem)));
+ });
+ } else {
+ Assert.assertTrue(variables.get(key).equals(value));
+ }
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexApiTest.java
new file mode 100644
index 0000000..9f8fc9d
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexApiTest.java
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.gremlin.GremlinRequest;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.GraphReadMode;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.constant.WriteType;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableMap;
+
+public class VertexApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ vertexAPI.list(-1).results().forEach(v -> vertexAPI.delete(v.id()));
+ }
+
+ @Test
+ public void testCreate() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+
+ vertex = vertexAPI.create(vertex);
+
+ Assert.assertEquals("person", vertex.label());
+ Map<String, Object> props = ImmutableMap.of("name", "James",
+ "city", "Beijing",
+ "age", 19);
+ Assert.assertEquals(props, vertex.properties());
+ }
+
+ @Test
+ public void testCreateWithUndefinedLabel() {
+ Vertex vertex = new Vertex("undefined");
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertex);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedProperty() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("not-exist-key", "not-exist-value");
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertex);
+ });
+ }
+
+ @Test
+ public void testCreateWithoutPrimaryKey() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertex);
+ });
+ }
+
+ @Test
+ public void testCreateWithCustomizeStringId() {
+ Vertex person = new Vertex("person");
+ person.property(T.id, "123456");
+ person.property("name", "James");
+ person.property("city", "Beijing");
+ person.property("age", 19);
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(person);
+ });
+
+ Vertex book = new Vertex("book");
+ book.id("ISBN-123456");
+ book.property("name", "spark graphx");
+
+ Vertex vertex = vertexAPI.create(book);
+ Assert.assertEquals("book", vertex.label());
+ Assert.assertEquals("ISBN-123456", vertex.id());
+ Map<String, Object> props = ImmutableMap.of("name", "spark graphx");
+ Assert.assertEquals(props, vertex.properties());
+ }
+
+ @Test
+ public void testCreateWithCustomizeNumberId() {
+ Vertex person = new Vertex("person");
+ person.property(T.id, 123456);
+ person.property("name", "James");
+ person.property("city", "Beijing");
+ person.property("age", 19);
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(person);
+ });
+
+ schema().vertexLabel("log")
+ .useCustomizeNumberId()
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ Vertex log = new Vertex("log");
+ log.id(123456);
+ log.property("date", "2018-01-01");
+
+ Vertex vertex = vertexAPI.create(log);
+ Assert.assertEquals("log", vertex.label());
+ Assert.assertEquals(123456, vertex.id());
+ String date = Utils.formatDate("2018-01-01");
+ Map<String, Object> props = ImmutableMap.of("date", date);
+ Assert.assertEquals(props, vertex.properties());
+ }
+
+ @Test
+ public void testCreateWithCustomizeUuidId() {
+ schema().vertexLabel("user")
+ .useCustomizeUuidId()
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ Vertex log = new Vertex("user");
+ log.id("835e1153-9281-4957-8691-cf79258e90eb");
+ log.property("date", "2018-01-01");
+
+ Vertex vertex = vertexAPI.create(log);
+ Assert.assertEquals("user", vertex.label());
+ Assert.assertEquals("835e1153-9281-4957-8691-cf79258e90eb",
+ vertex.id());
+ String date = Utils.formatDate("2018-01-01");
+ Map<String, Object> props = ImmutableMap.of("date", date);
+ Assert.assertEquals(props, vertex.properties());
+
+ // NOTE: must clean here due to type info
+ UUID id = UUID.fromString("835e1153-9281-4957-8691-cf79258e90eb");
+ vertexAPI.delete(id);
+ }
+
+ @Test
+ public void testCreateWithNullableKeysAbsent() {
+ Vertex vertex = new Vertex("person");
+ // Absent prop city
+ vertex.property("name", "James");
+ vertex.property("age", 19);
+
+ vertex = vertexAPI.create(vertex);
+
+ Assert.assertEquals("person", vertex.label());
+ Map<String, Object> props = ImmutableMap.of("name", "James",
+ "age", 19);
+ Assert.assertEquals(props, vertex.properties());
+ }
+
+ @Test
+ public void testCreateWithNonNullKeysAbsent() {
+ Vertex vertex = new Vertex("person");
+ // Absent prop 'age'
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertex);
+ });
+ }
+
+ @Test
+ public void testCreateExistVertex() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+ vertexAPI.create(vertex);
+
+ vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("city", "Shanghai");
+ vertex.property("age", 20);
+ vertex = vertexAPI.create(vertex);
+
+ Assert.assertEquals("person", vertex.label());
+ Map<String, Object> props = ImmutableMap.of("name", "James",
+ "city", "Shanghai",
+ "age", 20);
+ Assert.assertEquals(props, vertex.properties());
+ }
+
+ @Test
+ public void testCreateVertexWithTtl() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("fan")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .ttl(3000L)
+ .ifNotExist()
+ .create();
+
+ Vertex vertex = graph().addVertex(T.label, "fan", "name", "Baby",
+ "age", 3, "city", "Beijing");
+
+ Vertex result = graph().getVertex(vertex.id());
+ Assert.assertEquals("fan", result.label());
+ Map<String, Object> props = ImmutableMap.of("name", "Baby",
+ "city", "Beijing",
+ "age", 3);
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getVertex(vertex.id());
+ Assert.assertEquals("fan", result.label());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getVertex(vertex.id());
+ Assert.assertEquals("fan", result.label());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().getVertex(vertex.id());
+ }, e -> {
+ Assert.assertContains("does not exist", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testCreateVertexWithTtlAndTtlStartTime() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("follower")
+ .properties("name", "age", "city", "date")
+ .primaryKeys("name")
+ .ttl(3000L)
+ .ttlStartTime("date")
+ .ifNotExist()
+ .create();
+ long date = DateUtil.now().getTime() - 1000L;
+ String dateString = Utils.formatDate(new Date(date));
+ Vertex vertex = graph().addVertex(T.label, "follower", "name", "Baby",
+ "age", 3, "city", "Beijing",
+ "date", date);
+
+ Vertex result = graph().getVertex(vertex.id());
+ Assert.assertEquals("follower", result.label());
+ Map<String, Object> props = ImmutableMap.of("name", "Baby",
+ "city", "Beijing",
+ "age", 3,
+ "date", dateString);
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ result = graph().getVertex(vertex.id());
+ Assert.assertEquals("follower", result.label());
+ Assert.assertEquals(props, result.properties());
+
+ try {
+ Thread.sleep(1100L);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().getVertex(vertex.id());
+ }, e -> {
+ Assert.assertContains("does not exist", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testOlapPropertyWrite() {
+ List<Vertex> vertices = super.create100PersonBatch();
+ List<Object> ids = vertexAPI.create(vertices);
+
+ // Create olap property key
+ PropertyKey pagerank = schema().propertyKey("pagerank")
+ .asDouble()
+ .writeType(WriteType.OLAP_RANGE)
+ .build();
+
+ PropertyKey.PropertyKeyWithTask propertyKeyWithTask;
+ propertyKeyWithTask = propertyKeyAPI.create(pagerank);
+ long taskId1 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId1);
+
+ PropertyKey wcc = schema().propertyKey("wcc")
+ .asText()
+ .writeType(WriteType.OLAP_SECONDARY)
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(wcc);
+ long taskId2 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId2);
+
+ PropertyKey none = schema().propertyKey("none")
+ .asText()
+ .writeType(WriteType.OLAP_COMMON)
+ .build();
+
+ propertyKeyWithTask = propertyKeyAPI.create(none);
+ long taskId3 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId3);
+
+ waitUntilTaskCompleted(taskId1);
+ waitUntilTaskCompleted(taskId2);
+ waitUntilTaskCompleted(taskId3);
+
+ // Add olap properties
+ vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex(null);
+ vertex.id(ids.get(i));
+ vertex.property("pagerank", 0.1D * i);
+ vertices.add(vertex);
+ }
+
+ ids = vertexAPI.create(vertices);
+ Assert.assertEquals(100, ids.size());
+
+ vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex(null);
+ vertex.id(ids.get(i));
+ vertex.property("wcc", "wcc" + i);
+ vertices.add(vertex);
+ }
+
+ ids = vertexAPI.create(vertices);
+ Assert.assertEquals(100, ids.size());
+
+ vertices = new ArrayList<>(100);
+ for (int i = 0; i < 100; i++) {
+ Vertex vertex = new Vertex(null);
+ vertex.id(ids.get(i));
+ vertex.property("none", "none" + i);
+ vertices.add(vertex);
+ }
+
+ ids = vertexAPI.create(vertices);
+ Assert.assertEquals(100, ids.size());
+
+ // Query vertices by id before set graph read mode to 'ALL'
+ for (int i = 0; i < 100; i++) {
+ Vertex person = vertexAPI.get(ids.get(i));
+ Assert.assertEquals("person", person.label());
+ Map<String, Object> props = ImmutableMap.of("name", "Person-" + i,
+ "city", "Beijing",
+ "age", 30);
+ Assert.assertEquals(props, person.properties());
+ }
+
+ // Set graph read mode to 'ALL'
+ graphsAPI.readMode("hugegraph", GraphReadMode.ALL);
+
+ // Query vertices by id after set graph read mode to 'ALL'
+ for (int i = 0; i < 100; i++) {
+ Vertex person = vertexAPI.get(ids.get(i));
+ Assert.assertEquals("person", person.label());
+ Map<String, Object> props = ImmutableMap.<String, Object>builder()
+ .put("name", "Person-" + i)
+ .put("city", "Beijing")
+ .put("age", 30)
+ .put("pagerank", 0.1D * i)
+ .put("wcc", "wcc" + i)
+ .put("none", "none" + i)
+ .build();
+ Assert.assertEquals(props, person.properties());
+ }
+
+ // Query vertices by olap properties
+ GremlinRequest request = new GremlinRequest(
+ "g.V().has(\"pagerank\", P.gte(5))");
+ ResultSet resultSet = gremlin().execute(request);
+ Assert.assertEquals(50, resultSet.size());
+
+ request = new GremlinRequest(
+ "g.V().has(\"wcc\", P.within(\"wcc10\", \"wcc20\"))");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(2, resultSet.size());
+
+ // Clear olap property key
+ propertyKeyWithTask = propertyKeyAPI.clear(pagerank);
+ taskId1 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId1);
+
+ propertyKeyWithTask = propertyKeyAPI.clear(wcc);
+ taskId2 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId2);
+
+ propertyKeyWithTask = propertyKeyAPI.clear(none);
+ taskId3 = propertyKeyWithTask.taskId();
+ Assert.assertNotEquals(0L, taskId3);
+
+ waitUntilTaskCompleted(taskId1);
+ waitUntilTaskCompleted(taskId2);
+ waitUntilTaskCompleted(taskId3);
+
+ // Query after clear olap property key
+ request = new GremlinRequest("g.V().has(\"pagerank\", P.gte(5))");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(0, resultSet.size());
+
+ request = new GremlinRequest(
+ "g.V().has(\"wcc\", P.within(\"wcc10\", \"wcc20\"))");
+ resultSet = gremlin().execute(request);
+ Assert.assertEquals(0, resultSet.size());
+
+ // Delete olap property key
+ taskId1 = propertyKeyAPI.delete(pagerank.name());
+ Assert.assertNotEquals(0L, taskId1);
+
+ taskId2 = propertyKeyAPI.delete(wcc.name());
+ Assert.assertNotEquals(0L, taskId2);
+
+ taskId3 = propertyKeyAPI.delete(none.name());
+ Assert.assertNotEquals(0L, taskId3);
+
+ waitUntilTaskCompleted(taskId1);
+ waitUntilTaskCompleted(taskId2);
+ waitUntilTaskCompleted(taskId3);
+
+ // Query after delete olap property key
+ Assert.assertThrows(ServerException.class, () -> {
+ gremlin().execute(new GremlinRequest(
+ "g.V().has(\"pagerank\", P.gte(5))"));
+ }, e -> {
+ Assert.assertContains("Undefined property key: 'pagerank'",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ gremlin().execute(new GremlinRequest(
+ "g.V().has(\"wcc\", P.within(\"wcc10\", \"wcc20\"))"));
+ }, e -> {
+ Assert.assertContains("Undefined property key: 'wcc'",
+ e.getMessage());
+ });
+
+ // Resume graph read mode to 'OLTP_ONLY'
+ graphsAPI.readMode("hugegraph", GraphReadMode.OLTP_ONLY);
+ }
+
+ @Test
+ public void testBatchCreate() {
+ List<Vertex> vertices = super.create100PersonBatch();
+ vertexAPI.create(vertices);
+
+ List<Object> ids = vertexAPI.create(vertices);
+ Assert.assertEquals(100, ids.size());
+ for (int i = 0; i < 100; i++) {
+ Vertex person = vertexAPI.get(ids.get(i));
+ Assert.assertEquals("person", person.label());
+ Map<String, Object> props = ImmutableMap.of("name", "Person-" + i,
+ "city", "Beijing",
+ "age", 30);
+ Assert.assertEquals(props, person.properties());
+ }
+ }
+
+ @Test
+ public void testBatchCreateContainsInvalidVertex() {
+ List<Vertex> vertices = super.create100PersonBatch();
+ vertices.get(0).property("invalid-key", "invalid-value");
+ vertices.get(10).property("not-exist-key", "not-exist-value");
+
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertices);
+ });
+ }
+
+ @Test
+ public void testBatchCreateWithMoreThanBatchSize() {
+ List<Vertex> vertices = new ArrayList<>(1000);
+ for (int i = 0; i < 1000; i++) {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "Person" + "-" + i);
+ vertex.property("city", "Beijing");
+ vertex.property("age", 20);
+ vertices.add(vertex);
+ }
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.create(vertices);
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Vertex vertex1 = new Vertex("person");
+ vertex1.property("name", "James");
+ vertex1.property("city", "Beijing");
+ vertex1.property("age", 19);
+
+ vertex1 = vertexAPI.create(vertex1);
+
+ Vertex vertex2 = vertexAPI.get(vertex1.id());
+ Assert.assertEquals(vertex1.id(), vertex2.id());
+ Assert.assertEquals(vertex1.label(), vertex2.label());
+ Assert.assertEquals(vertex1.properties(), vertex2.properties());
+ }
+
+ @Test
+ public void testGetWithCustomizeStringId() {
+ Vertex vertex1 = new Vertex("book");
+ vertex1.id("ISBN-123456");
+ vertex1.property("name", "spark graphx");
+
+ vertex1 = vertexAPI.create(vertex1);
+
+ Vertex vertex2 = vertexAPI.get("ISBN-123456");
+ Assert.assertEquals(vertex1.id(), vertex2.id());
+ Assert.assertEquals(vertex1.label(), vertex2.label());
+ Assert.assertEquals(vertex1.properties(), vertex2.properties());
+ }
+
+ @Test
+ public void testGetWithCustomizeNumberId() {
+ schema().vertexLabel("log")
+ .useCustomizeNumberId()
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ Vertex vertex1 = new Vertex("log");
+ vertex1.id(123456);
+ vertex1.property("date", "2018-01-01");
+
+ vertex1 = vertexAPI.create(vertex1);
+
+ Vertex vertex2 = vertexAPI.get(123456);
+ Assert.assertEquals(vertex1.id(), vertex2.id());
+ Assert.assertEquals(vertex1.label(), vertex2.label());
+ Assert.assertEquals(vertex1.properties(), vertex2.properties());
+ }
+
+ @Test
+ public void testGetWithCustomizeUuidId() {
+ schema().vertexLabel("user")
+ .useCustomizeUuidId()
+ .properties("date")
+ .ifNotExist()
+ .create();
+
+ Vertex vertex1 = new Vertex("user");
+ vertex1.id("835e1153-9281-4957-8691-cf79258e90eb");
+ vertex1.property("date", "2018-01-01");
+
+ vertex1 = vertexAPI.create(vertex1);
+
+ UUID id = UUID.fromString("835e1153-9281-4957-8691-cf79258e90eb");
+ Vertex vertex2 = vertexAPI.get(id);
+ Assert.assertEquals(vertex1.id(), vertex2.id());
+ Assert.assertEquals(vertex1.label(), vertex2.label());
+ Assert.assertEquals(vertex1.properties(), vertex2.properties());
+
+ // NOTE: must clean here due to type info
+ vertexAPI.delete(id);
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ vertexAPI.get("not-exist-vertex-id");
+ });
+ }
+
+ @Test
+ public void testList() {
+ List<Vertex> vertices = super.create100PersonBatch();
+ vertexAPI.create(vertices);
+
+ vertices = vertexAPI.list(-1).results();
+ Assert.assertEquals(100, vertices.size());
+ }
+
+ @Test
+ public void testListWithLimit() {
+ List<Vertex> vertices = super.create100PersonBatch();
+ vertexAPI.create(vertices);
+
+ vertices = vertexAPI.list(10).results();
+ Assert.assertEquals(10, vertices.size());
+ }
+
+ @Test
+ public void testDelete() {
+ Vertex vertex = new Vertex("person");
+ vertex.property("name", "James");
+ vertex.property("city", "Beijing");
+ vertex.property("age", 19);
+
+ vertex = vertexAPI.create(vertex);
+
+ Object id = vertex.id();
+ vertexAPI.delete(id);
+
+ Utils.assertResponseError(404, () -> {
+ vertexAPI.get(id);
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(400, () -> {
+ vertexAPI.delete("not-exist-v");
+ });
+ }
+
+ @SuppressWarnings("unused")
+ private static void assertContains(List<Vertex> vertices, Vertex vertex) {
+ Assert.assertTrue(Utils.contains(vertices, vertex));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexLabelApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexLabelApiTest.java
new file mode 100644
index 0000000..5d86361
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/VertexLabelApiTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.IdStrategy;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class VertexLabelApiTest extends BaseApiTest {
+
+ @BeforeClass
+ public static void prepareSchema() {
+ BaseApiTest.initPropertyKey();
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ List<Long> taskIds = new ArrayList<>();
+ vertexLabelAPI.list().forEach(vl -> {
+ taskIds.add(vertexLabelAPI.delete(vl.name()));
+ });
+ taskIds.forEach(taskId -> waitUntilTaskCompleted(taskId));
+ }
+
+ @Test
+ public void testCreate() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("name", "age", "city");
+ Assert.assertEquals(props, vertexLabel.properties());
+ }
+
+ @Test
+ public void testCreateWithEnableLabelIndexFalse() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .enableLabelIndex(false)
+ .build();
+
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel.idStrategy());
+ Assert.assertEquals(false, vertexLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("name", "age", "city");
+ Assert.assertEquals(props, vertexLabel.properties());
+ }
+
+ @Test
+ public void testCreateWithUuidIdStrategy() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .useCustomizeUuidId()
+ .properties("name", "age", "city")
+ .build();
+
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.CUSTOMIZE_UUID, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("name", "age", "city");
+ Assert.assertEquals(props, vertexLabel.properties());
+
+ vertexLabel = schema().vertexLabel("person1")
+ .idStrategy(IdStrategy.CUSTOMIZE_UUID)
+ .properties("name", "age", "city")
+ .build();
+
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person1", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.CUSTOMIZE_UUID, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ props = ImmutableSet.of("name", "age", "city");
+ Assert.assertEquals(props, vertexLabel.properties());
+ }
+
+ @Test
+ public void testCreateWithInvalidName() {
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(new VertexLabel(""));
+ });
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(new VertexLabel(" "));
+ });
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(new VertexLabel(" "));
+ });
+ }
+
+ @Test
+ public void testCreateExistedVertexLabel() {
+ VertexLabel vertexLabel = new VertexLabel("name");
+ vertexLabelAPI.create(vertexLabel);
+
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(new VertexLabel("name"));
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedPropertyKey() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "undefined")
+ .primaryKeys("name")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedPrimaryKey() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "age", "city")
+ .primaryKeys("undefined")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithUndefinedNullableKeys() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("undefined")
+ .build();
+
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithNonNullKeysIntersectPrimaryKeys() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("name")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel1);
+ });
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "age", "city")
+ .primaryKeys("name", "age")
+ .nullableKeys("name")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel2);
+ });
+
+ VertexLabel vertexLabel3 = schema().vertexLabel("person")
+ .usePrimaryKeyId()
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("name", "age")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.create(vertexLabel3);
+ });
+ }
+
+ @Test
+ public void testCreateWithUnmatchIdStrategyAndProperties() {
+ Utils.assertResponseError(400, () -> {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .properties("name", "age", "city")
+ .useAutomaticId()
+ .primaryKeys("name")
+ .build();
+ vertexLabelAPI.create(vertexLabel);
+ });
+ Utils.assertResponseError(400, () -> {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .properties("name", "age", "city")
+ .useCustomizeStringId()
+ .primaryKeys("name")
+ .build();
+ vertexLabelAPI.create(vertexLabel);
+ });
+ Utils.assertResponseError(400, () -> {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .properties("name", "age", "city")
+ .usePrimaryKeyId()
+ .build();
+ vertexLabelAPI.create(vertexLabel);
+ });
+ }
+
+ @Test
+ public void testCreateWithTtl() {
+ VertexLabel vertexLabel = schema().vertexLabel("person1")
+ .useAutomaticId()
+ .properties("name", "age", "date")
+ .build();
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person1", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ Set<String> props = ImmutableSet.of("name", "age", "date");
+ Assert.assertEquals(props, vertexLabel.properties());
+ Assert.assertEquals(0L, vertexLabel.ttl());
+ Assert.assertNull(vertexLabel.ttlStartTime());
+
+ vertexLabel = schema().vertexLabel("person2")
+ .useAutomaticId()
+ .properties("name", "age", "date")
+ .ttl(3000L)
+ .build();
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person2", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ Assert.assertEquals(props, vertexLabel.properties());
+ Assert.assertEquals(3000L, vertexLabel.ttl());
+ Assert.assertNull(vertexLabel.ttlStartTime());
+
+ vertexLabel = schema().vertexLabel("person3")
+ .useAutomaticId()
+ .properties("name", "age", "date")
+ .ttl(3000L)
+ .ttlStartTime("date")
+ .build();
+ vertexLabel = vertexLabelAPI.create(vertexLabel);
+
+ Assert.assertEquals("person3", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel.idStrategy());
+ Assert.assertEquals(true, vertexLabel.enableLabelIndex());
+ Assert.assertEquals(props, vertexLabel.properties());
+ Assert.assertEquals(3000L, vertexLabel.ttl());
+ Assert.assertEquals("date", vertexLabel.ttlStartTime());
+ }
+
+ @Test
+ public void testAppend() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age")
+ .build();
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+ Assert.assertEquals("person", vertexLabel1.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel1.idStrategy());
+ Set<String> props = ImmutableSet.of("name", "age");
+ Set<String> nullableKeys = ImmutableSet.of();
+ Assert.assertEquals(props, vertexLabel1.properties());
+ Assert.assertEquals(nullableKeys, vertexLabel1.nullableKeys());
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("person")
+ .properties("city")
+ .nullableKeys("city")
+ .build();
+ vertexLabel2 = vertexLabelAPI.append(vertexLabel2);
+ Assert.assertEquals("person", vertexLabel2.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel2.idStrategy());
+ props = ImmutableSet.of("name", "age", "city");
+ nullableKeys = ImmutableSet.of("city");
+ Assert.assertEquals(props, vertexLabel2.properties());
+ Assert.assertEquals(nullableKeys, vertexLabel2.nullableKeys());
+ }
+
+ @Test
+ public void testAppendWithUndefinedPropertyKey() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age")
+ .build();
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+ Assert.assertEquals("person", vertexLabel1.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel1.idStrategy());
+ Set<String> props = ImmutableSet.of("name", "age");
+ Assert.assertEquals(props, vertexLabel1.properties());
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("person")
+ .properties("undefined")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.append(vertexLabel2);
+ });
+ }
+
+ @Test
+ public void testAppendWithUndefinedNullabelKeys() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age")
+ .build();
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+ Assert.assertEquals("person", vertexLabel1.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel1.idStrategy());
+ Set<String> props = ImmutableSet.of("name", "age");
+ Assert.assertEquals(props, vertexLabel1.properties());
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("person")
+ .nullableKeys("undefined")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.append(vertexLabel2);
+ });
+ }
+
+ @Test
+ public void testEliminate() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+ Assert.assertEquals("person", vertexLabel1.name());
+ Assert.assertEquals(IdStrategy.AUTOMATIC, vertexLabel1.idStrategy());
+ Set<String> props = ImmutableSet.of("name", "age", "city");
+ Assert.assertEquals(props, vertexLabel1.properties());
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("person")
+ .properties("city")
+ .build();
+ Utils.assertResponseError(400, () -> {
+ vertexLabelAPI.eliminate(vertexLabel2);
+ });
+ }
+
+ @Test
+ public void testGet() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+
+ VertexLabel vertexLabel2 = vertexLabelAPI.get("person");
+
+ Assert.assertEquals(vertexLabel1.name(), vertexLabel2.name());
+ Assert.assertEquals(vertexLabel1.idStrategy(),
+ vertexLabel2.idStrategy());
+ Assert.assertEquals(vertexLabel1.properties(),
+ vertexLabel2.properties());
+ }
+
+ @Test
+ public void testGetNotExist() {
+ Utils.assertResponseError(404, () -> {
+ vertexLabelAPI.get("not-exist-vl");
+ });
+ }
+
+ @Test
+ public void testList() {
+ VertexLabel vertexLabel1 = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+ vertexLabel1 = vertexLabelAPI.create(vertexLabel1);
+
+ VertexLabel vertexLabel2 = schema().vertexLabel("software")
+ .useCustomizeStringId()
+ .properties("name", "lang", "price")
+ .build();
+ vertexLabel2 = vertexLabelAPI.create(vertexLabel2);
+
+ List<VertexLabel> vertexLabels = vertexLabelAPI.list();
+ Assert.assertEquals(2, vertexLabels.size());
+ assertContains(vertexLabels, vertexLabel1);
+ assertContains(vertexLabels, vertexLabel2);
+ }
+
+ @Test
+ public void testListByNames() {
+ VertexLabel person = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+ person = vertexLabelAPI.create(person);
+
+ VertexLabel software = schema().vertexLabel("software")
+ .useCustomizeStringId()
+ .properties("name", "lang", "price")
+ .build();
+ software = vertexLabelAPI.create(software);
+
+ List<VertexLabel> vertexLabels;
+
+ vertexLabels = vertexLabelAPI.list(ImmutableList.of("person"));
+ Assert.assertEquals(1, vertexLabels.size());
+ assertContains(vertexLabels, person);
+
+ vertexLabels = vertexLabelAPI.list(ImmutableList.of("software"));
+ Assert.assertEquals(1, vertexLabels.size());
+ assertContains(vertexLabels, software);
+
+ vertexLabels = vertexLabelAPI.list(ImmutableList.of("person",
+ "software"));
+ Assert.assertEquals(2, vertexLabels.size());
+ assertContains(vertexLabels, person);
+ assertContains(vertexLabels, software);
+ }
+
+ @Test
+ public void testDelete() {
+ VertexLabel vertexLabel = schema().vertexLabel("person")
+ .useAutomaticId()
+ .properties("name", "age", "city")
+ .build();
+ vertexLabelAPI.create(vertexLabel);
+
+ long taskId = vertexLabelAPI.delete("person");
+ waitUntilTaskCompleted(taskId);
+
+ Utils.assertResponseError(404, () -> {
+ vertexLabelAPI.get("person");
+ });
+ }
+
+ @Test
+ public void testDeleteNotExist() {
+ Utils.assertResponseError(404, () -> {
+ vertexLabelAPI.delete("not-exist-vl");
+ });
+ }
+
+ @Test
+ public void testAddVertexLabelWithUserData() {
+ VertexLabel player = schema().vertexLabel("player")
+ .properties("name")
+ .userdata("super_vl", "person")
+ .build();
+ player = vertexLabelAPI.create(player);
+ Assert.assertEquals(2, player.userdata().size());
+ Assert.assertEquals("person", player.userdata().get("super_vl"));
+ String time = (String) player.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ VertexLabel runner = schema().vertexLabel("runner")
+ .properties("name")
+ .userdata("super_vl", "person")
+ .userdata("super_vl", "player")
+ .build();
+ runner = vertexLabelAPI.create(runner);
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, runner.userdata().size());
+ Assert.assertEquals("player", runner.userdata().get("super_vl"));
+ time = (String) runner.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AccessApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AccessApiTest.java
new file mode 100644
index 0000000..7b78d14
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AccessApiTest.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Access;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.structure.auth.HugePermission;
+import com.baidu.hugegraph.structure.auth.HugeResourceType;
+import com.baidu.hugegraph.structure.auth.Target;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class AccessApiTest extends AuthApiTest {
+
+ private static AccessAPI api;
+
+ private static Target gremlin;
+ private static Group group;
+
+ @BeforeClass
+ public static void init() {
+ api = new AccessAPI(initClient(), GRAPH);
+
+ TargetApiTest.init();
+ GroupApiTest.init();
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<Access> accesss = api.list(null, null, -1);
+ for (Access access : accesss) {
+ api.delete(access.id());
+ }
+
+ TargetApiTest.clear();
+ GroupApiTest.clear();
+ }
+
+ @Before
+ @Override
+ public void setup() {
+ gremlin = TargetApiTest.createTarget("gremlin", HugeResourceType.GREMLIN);
+ group = GroupApiTest.createGroup("group-beijing", "group for beijing");
+ }
+
+ @After
+ @Override
+ public void teardown() {
+ clear();
+ }
+
+ @Test
+ public void testCreate() {
+ Access access1 = new Access();
+ access1.group(group);
+ access1.target(gremlin);
+ access1.permission(HugePermission.EXECUTE);
+ access1.description("group beijing execute gremlin");
+
+ Access access2 = new Access();
+ access2.group(group);
+ access2.target(gremlin);
+ access2.permission(HugePermission.READ);
+ access2.description("group beijing read gremlin");
+
+ Access result1 = api.create(access1);
+ Access result2 = api.create(access2);
+
+ Assert.assertEquals(group.id(), result1.group());
+ Assert.assertEquals(gremlin.id(), result1.target());
+ Assert.assertEquals(HugePermission.EXECUTE, result1.permission());
+ Assert.assertEquals("group beijing execute gremlin",
+ result1.description());
+
+ Assert.assertEquals(group.id(), result2.group());
+ Assert.assertEquals(gremlin.id(), result2.target());
+ Assert.assertEquals(HugePermission.READ, result2.permission());
+ Assert.assertEquals("group beijing read gremlin",
+ result2.description());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(access1);
+ }, e -> {
+ Assert.assertContains("Can't save access", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Access access3 = new Access();
+ access3.group(group);
+ access3.target(gremlin);
+ access3.permission(HugePermission.READ);
+ access3.description("group beijing read gremlin");
+ api.create(access3);
+ }, e -> {
+ Assert.assertContains("Can't save access", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Access access1 = createAccess(HugePermission.WRITE, "description 1");
+ Access access2 = createAccess(HugePermission.READ, "description 2");
+
+ Assert.assertEquals("description 1", access1.description());
+ Assert.assertEquals("description 2", access2.description());
+
+ access1 = api.get(access1.id());
+ access2 = api.get(access2.id());
+
+ Assert.assertEquals(group.id(), access1.group());
+ Assert.assertEquals(gremlin.id(), access1.target());
+ Assert.assertEquals(HugePermission.WRITE, access1.permission());
+ Assert.assertEquals("description 1", access1.description());
+
+ Assert.assertEquals(group.id(), access2.group());
+ Assert.assertEquals(gremlin.id(), access2.target());
+ Assert.assertEquals(HugePermission.READ, access2.permission());
+ Assert.assertEquals("description 2", access2.description());
+ }
+
+ @Test
+ public void testList() {
+ createAccess(HugePermission.READ, "description 1");
+ createAccess(HugePermission.WRITE, "description 2");
+ createAccess(HugePermission.EXECUTE, "description 3");
+
+ List<Access> accesss = api.list(null, null, -1);
+ Assert.assertEquals(3, accesss.size());
+
+ accesss.sort((t1, t2) -> t1.permission().compareTo(t2.permission()));
+ Assert.assertEquals("description 1", accesss.get(0).description());
+ Assert.assertEquals("description 2", accesss.get(1).description());
+ Assert.assertEquals("description 3", accesss.get(2).description());
+
+ accesss = api.list(null, null, 1);
+ Assert.assertEquals(1, accesss.size());
+
+ accesss = api.list(null, null, 2);
+ Assert.assertEquals(2, accesss.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(null, null, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testListByGroup() {
+ createAccess(HugePermission.READ, "description 1");
+ createAccess(HugePermission.WRITE, "description 2");
+ createAccess(HugePermission.EXECUTE, "description 3");
+
+ Group hk = GroupApiTest.createGroup("group-hk", "group for hongkong");
+ createAccess(hk, gremlin, HugePermission.READ, "description 4");
+ createAccess(hk, gremlin, HugePermission.WRITE, "description 5");
+
+ List<Access> accesss = api.list(null, null, -1);
+ Assert.assertEquals(5, accesss.size());
+
+ accesss = api.list(hk, null, -1);
+ Assert.assertEquals(2, accesss.size());
+ accesss.sort((t1, t2) -> t1.permission().compareTo(t2.permission()));
+ Assert.assertEquals("description 4", accesss.get(0).description());
+ Assert.assertEquals("description 5", accesss.get(1).description());
+
+ accesss = api.list(group, null, -1);
+ Assert.assertEquals(3, accesss.size());
+ accesss.sort((t1, t2) -> t1.permission().compareTo(t2.permission()));
+ Assert.assertEquals("description 1", accesss.get(0).description());
+ Assert.assertEquals("description 2", accesss.get(1).description());
+ Assert.assertEquals("description 3", accesss.get(2).description());
+
+ accesss = api.list(group, null, 1);
+ Assert.assertEquals(1, accesss.size());
+
+ accesss = api.list(group, null, 2);
+ Assert.assertEquals(2, accesss.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(group, null, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.list(group, gremlin, -1);
+ }, e -> {
+ Assert.assertContains("Can't pass both group and target " +
+ "at the same time", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testListByTarget() {
+ createAccess(HugePermission.READ, "description 1");
+ createAccess(HugePermission.WRITE, "description 2");
+ createAccess(HugePermission.EXECUTE, "description 3");
+
+ Group hk = GroupApiTest.createGroup("group-hk", "group for hongkong");
+ createAccess(hk, gremlin, HugePermission.READ, "description 4");
+ createAccess(hk, gremlin, HugePermission.WRITE, "description 5");
+
+ Target task = TargetApiTest.createTarget("task", HugeResourceType.TASK);
+ createAccess(hk, task, HugePermission.READ, "description 6");
+ createAccess(hk, task, HugePermission.WRITE, "description 7");
+
+ List<Access> accesss = api.list(null, null, -1);
+ Assert.assertEquals(7, accesss.size());
+
+ accesss = api.list(null, task, -1);
+ Assert.assertEquals(2, accesss.size());
+ accesss.sort((t1, t2) -> t1.permission().compareTo(t2.permission()));
+ Assert.assertEquals("description 6", accesss.get(0).description());
+ Assert.assertEquals("description 7", accesss.get(1).description());
+
+ accesss = api.list(null, gremlin, -1);
+ Assert.assertEquals(5, accesss.size());
+ accesss.sort((t1, t2) -> {
+ String s1 = "" + t1.group() + t1.permission().ordinal();
+ String s2 = "" + t2.group() + t2.permission().ordinal();
+ return s1.compareTo(s2);
+ });
+ Assert.assertEquals("description 1", accesss.get(0).description());
+ Assert.assertEquals("description 2", accesss.get(1).description());
+ Assert.assertEquals("description 3", accesss.get(2).description());
+ Assert.assertEquals("description 4", accesss.get(3).description());
+ Assert.assertEquals("description 5", accesss.get(4).description());
+
+ accesss = api.list(null, gremlin, 1);
+ Assert.assertEquals(1, accesss.size());
+
+ accesss = api.list(null, gremlin, 2);
+ Assert.assertEquals(2, accesss.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(null, gremlin, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.list(hk, task, -1);
+ }, e -> {
+ Assert.assertContains("Can't pass both group and target " +
+ "at the same time", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testUpdate() {
+ Access access1 = createAccess(HugePermission.WRITE, "description 1");
+ Access access2 = createAccess(HugePermission.READ, "description 2");
+
+ Assert.assertEquals("description 1", access1.description());
+ Assert.assertEquals("description 2", access2.description());
+
+ access1.description("description updated");
+ Access updated = api.update(access1);
+ Assert.assertEquals("description updated", updated.description());
+ Assert.assertNotEquals(access1.updateTime(), updated.updateTime());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Group hk = GroupApiTest.createGroup("group-hk", "");
+ access2.group(hk);
+ api.update(access2);
+ }, e -> {
+ Assert.assertContains("The group of access can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Target task = TargetApiTest.createTarget("task",
+ HugeResourceType.TASK);
+ access2.group(group);
+ access2.target(task);
+ api.update(access2);
+ }, e -> {
+ Assert.assertContains("The target of access can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(access2, "id", "fake-id");
+ api.update(access2);
+ }, e -> {
+ Assert.assertContains("Invalid access id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDelete() {
+ Access access1 = createAccess(HugePermission.WRITE, "description 1");
+ Access access2 = createAccess(HugePermission.READ, "description 2");
+
+ Assert.assertEquals(2, api.list(null, null, -1).size());
+ api.delete(access1.id());
+
+ Assert.assertEquals(1, api.list(null, null, -1).size());
+ Assert.assertEquals(access2, api.list(null, null, -1).get(0));
+
+ api.delete(access2.id());
+ Assert.assertEquals(0, api.list(null, null, -1).size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(access2.id());
+ }, e -> {
+ Assert.assertContains("Invalid access id:", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete("fake-id");
+ }, e -> {
+ Assert.assertContains("Invalid access id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ private Access createAccess(HugePermission perm, String description) {
+ return createAccess(group, gremlin, perm, description);
+ }
+
+ private Access createAccess(Group group, Target target,
+ HugePermission perm, String description) {
+ Access access = new Access();
+ access.group(group);
+ access.target(target);
+ access.permission(perm);
+ access.description(description);
+ return api.create(access);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AuthApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AuthApiTest.java
new file mode 100644
index 0000000..346c33e
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/AuthApiTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+
+public class AuthApiTest extends BaseApiTest {
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/BelongApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/BelongApiTest.java
new file mode 100644
index 0000000..d90bb50
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/BelongApiTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Belong;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class BelongApiTest extends AuthApiTest {
+
+ private static BelongAPI api;
+
+ private static User user1;
+ private static User user2;
+ private static Group group1;
+ private static Group group2;
+
+ @BeforeClass
+ public static void init() {
+ api = new BelongAPI(initClient(), GRAPH);
+
+ UserApiTest.init();
+ GroupApiTest.init();
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<Belong> belongs = api.list(null, null, -1);
+ for (Belong belong : belongs) {
+ api.delete(belong.id());
+ }
+
+ UserApiTest.clear();
+ GroupApiTest.clear();
+ }
+
+ @Before
+ @Override
+ public void setup() {
+ user1 = UserApiTest.createUser("user-1", "p1");
+ user2 = UserApiTest.createUser("user-2", "p2");
+ group1 = GroupApiTest.createGroup("group-1", "group 1");
+ group2 = GroupApiTest.createGroup("group-2", "group 2");
+ }
+
+ @After
+ @Override
+ public void teardown() {
+ clear();
+ }
+
+ @Test
+ public void testCreate() {
+ Belong belong1 = new Belong();
+ belong1.user(user1);
+ belong1.group(group1);
+ belong1.description("user 1 => group 1");
+
+ Belong belong2 = new Belong();
+ belong2.user(user1);
+ belong2.group(group2);
+ belong2.description("user 1 => group 2");
+
+ Belong belong3 = new Belong();
+ belong3.user(user2);
+ belong3.group(group2);
+ belong3.description("user 2 => group 2");
+
+ Belong result1 = api.create(belong1);
+ Belong result2 = api.create(belong2);
+ Belong result3 = api.create(belong3);
+
+ Assert.assertEquals(user1.id(), result1.user());
+ Assert.assertEquals(group1.id(), result1.group());
+ Assert.assertEquals("user 1 => group 1", result1.description());
+
+ Assert.assertEquals(user1.id(), result2.user());
+ Assert.assertEquals(group2.id(), result2.group());
+ Assert.assertEquals("user 1 => group 2", result2.description());
+
+ Assert.assertEquals(user2.id(), result3.user());
+ Assert.assertEquals(group2.id(), result3.group());
+ Assert.assertEquals("user 2 => group 2", result3.description());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(belong1);
+ }, e -> {
+ Assert.assertContains("Can't save belong", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Belong belong4 = new Belong();
+ belong4.user(user1);
+ belong4.group(group1);
+ api.create(belong3);
+ }, e -> {
+ Assert.assertContains("Can't save belong", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Belong belong1 = createBelong(user1, group1, "user1 => group1");
+ Belong belong2 = createBelong(user2, group2, "user2 => group2");
+
+ Assert.assertEquals("user1 => group1", belong1.description());
+ Assert.assertEquals("user2 => group2", belong2.description());
+
+ belong1 = api.get(belong1.id());
+ belong2 = api.get(belong2.id());
+
+ Assert.assertEquals(user1.id(), belong1.user());
+ Assert.assertEquals(group1.id(), belong1.group());
+ Assert.assertEquals("user1 => group1", belong1.description());
+
+ Assert.assertEquals(user2.id(), belong2.user());
+ Assert.assertEquals(group2.id(), belong2.group());
+ Assert.assertEquals("user2 => group2", belong2.description());
+ }
+
+ @Test
+ public void testList() {
+ createBelong(user1, group1, "user1 => group1");
+ createBelong(user1, group2, "user1 => group2");
+ createBelong(user2, group2, "user2 => group2");
+
+ List<Belong> belongs = api.list(null, null, -1);
+ Assert.assertEquals(3, belongs.size());
+
+ belongs.sort((t1, t2) -> t1.description().compareTo(t2.description()));
+ Assert.assertEquals("user1 => group1", belongs.get(0).description());
+ Assert.assertEquals("user1 => group2", belongs.get(1).description());
+ Assert.assertEquals("user2 => group2", belongs.get(2).description());
+
+ belongs = api.list(null, null, 1);
+ Assert.assertEquals(1, belongs.size());
+
+ belongs = api.list(null, null, 2);
+ Assert.assertEquals(2, belongs.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(null, null, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testListByUser() {
+ createBelong(user1, group1, "user1 => group1");
+ createBelong(user1, group2, "user1 => group2");
+ createBelong(user2, group2, "user2 => group2");
+
+ List<Belong> belongs = api.list(null, null, -1);
+ Assert.assertEquals(3, belongs.size());
+
+ belongs = api.list(user2, null, -1);
+ Assert.assertEquals(1, belongs.size());
+ Assert.assertEquals("user2 => group2", belongs.get(0).description());
+
+ belongs = api.list(user1, null, -1);
+ Assert.assertEquals(2, belongs.size());
+
+ belongs.sort((t1, t2) -> t1.description().compareTo(t2.description()));
+ Assert.assertEquals("user1 => group1", belongs.get(0).description());
+ Assert.assertEquals("user1 => group2", belongs.get(1).description());
+
+ belongs = api.list(user1, null, 1);
+ Assert.assertEquals(1, belongs.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(user1, null, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.list(user1, group1, -1);
+ }, e -> {
+ Assert.assertContains("Can't pass both user and group " +
+ "at the same time", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testListByGroup() {
+ createBelong(user1, group1, "user1 => group1");
+ createBelong(user1, group2, "user1 => group2");
+ createBelong(user2, group2, "user2 => group2");
+
+ List<Belong> belongs = api.list(null, null, -1);
+ Assert.assertEquals(3, belongs.size());
+
+ belongs = api.list(null, group1, -1);
+ Assert.assertEquals(1, belongs.size());
+ Assert.assertEquals("user1 => group1", belongs.get(0).description());
+
+ belongs = api.list(null, group2, -1);
+ Assert.assertEquals(2, belongs.size());
+
+ belongs.sort((t1, t2) -> t1.description().compareTo(t2.description()));
+ Assert.assertEquals("user1 => group2", belongs.get(0).description());
+ Assert.assertEquals("user2 => group2", belongs.get(1).description());
+
+ belongs = api.list(null, group2, 1);
+ Assert.assertEquals(1, belongs.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(null, group2, 0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.list(user2, group2, -1);
+ }, e -> {
+ Assert.assertContains("Can't pass both user and group " +
+ "at the same time", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testUpdate() {
+ Belong belong1 = createBelong(user1, group1, "user1 => group1");
+ Belong belong2 = createBelong(user2, group2, "user2 => group2");
+
+ Assert.assertEquals("user1 => group1", belong1.description());
+ Assert.assertEquals("user2 => group2", belong2.description());
+
+ belong1.description("description updated");
+ Belong updated = api.update(belong1);
+ Assert.assertEquals("description updated", updated.description());
+ Assert.assertNotEquals(belong1.updateTime(), updated.updateTime());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ belong2.user(user1);
+ api.update(belong2);
+ }, e -> {
+ Assert.assertContains("The user of belong can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ belong2.user(user2);
+ belong2.group(group1);
+ api.update(belong2);
+ }, e -> {
+ Assert.assertContains("The group of belong can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(belong2, "id", "fake-id");
+ api.update(belong2);
+ }, e -> {
+ Assert.assertContains("Invalid belong id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDelete() {
+ Belong belong1 = createBelong(user1, group1, "user1 => group1");
+ Belong belong2 = createBelong(user2, group2, "user2 => group2");
+
+ Assert.assertEquals(2, api.list(null, null, -1).size());
+ api.delete(belong1.id());
+
+ Assert.assertEquals(1, api.list(null, null, -1).size());
+ Assert.assertEquals(belong2, api.list(null, null, -1).get(0));
+
+ api.delete(belong2.id());
+ Assert.assertEquals(0, api.list(null, null, -1).size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(belong2.id());
+ }, e -> {
+ Assert.assertContains("Invalid belong id:", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete("fake-id");
+ }, e -> {
+ Assert.assertContains("Invalid belong id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ private Belong createBelong(User user, Group group, String description) {
+ Belong belong = new Belong();
+ belong.user(user);
+ belong.group(group);
+ belong.description(description);
+ return api.create(belong);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/GroupApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/GroupApiTest.java
new file mode 100644
index 0000000..f0fe4ca
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/GroupApiTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class GroupApiTest extends AuthApiTest {
+
+ private static GroupAPI api;
+
+ @BeforeClass
+ public static void init() {
+ api = new GroupAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<Group> groups = api.list(-1);
+ for (Group group : groups) {
+ api.delete(group.id());
+ }
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ clear();
+ }
+
+ @Test
+ public void testCreate() {
+ Group group1 = new Group();
+ group1.name("group-beijing");
+ group1.description("group for users in beijing");
+
+ Group group2 = new Group();
+ group2.name("group-shanghai");
+ group2.description("group for users in shanghai");
+
+ Group result1 = api.create(group1);
+ Group result2 = api.create(group2);
+
+ Assert.assertEquals("group-beijing", result1.name());
+ Assert.assertEquals("group for users in beijing",
+ result1.description());
+ Assert.assertEquals("group-shanghai", result2.name());
+ Assert.assertEquals("group for users in shanghai",
+ result2.description());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(group1);
+ }, e -> {
+ Assert.assertContains("Can't save group", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(new Group());
+ }, e -> {
+ Assert.assertContains("The name of group can't be null",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Group group1 = createGroup("test1", "description 1");
+ Group group2 = createGroup("test2", "description 2");
+
+ Assert.assertEquals("description 1", group1.description());
+ Assert.assertEquals("description 2", group2.description());
+
+ group1 = api.get(group1.id());
+ group2 = api.get(group2.id());
+
+ Assert.assertEquals("test1", group1.name());
+ Assert.assertEquals("description 1", group1.description());
+
+ Assert.assertEquals("test2", group2.name());
+ Assert.assertEquals("description 2", group2.description());
+ }
+
+ @Test
+ public void testList() {
+ createGroup("test1", "description 1");
+ createGroup("test2", "description 2");
+ createGroup("test3", "description 3");
+
+ List<Group> groups = api.list(-1);
+ Assert.assertEquals(3, groups.size());
+
+ groups.sort((t1, t2) -> t1.name().compareTo(t2.name()));
+ Assert.assertEquals("test1", groups.get(0).name());
+ Assert.assertEquals("test2", groups.get(1).name());
+ Assert.assertEquals("test3", groups.get(2).name());
+ Assert.assertEquals("description 1", groups.get(0).description());
+ Assert.assertEquals("description 2", groups.get(1).description());
+ Assert.assertEquals("description 3", groups.get(2).description());
+
+ groups = api.list(1);
+ Assert.assertEquals(1, groups.size());
+
+ groups = api.list(2);
+ Assert.assertEquals(2, groups.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testUpdate() {
+ Group group1 = createGroup("test1", "description 1");
+ Group group2 = createGroup("test2", "description 2");
+
+ Assert.assertEquals("description 1", group1.description());
+ Assert.assertEquals("description 2", group2.description());
+
+ group1.description("description updated");
+ Group updated = api.update(group1);
+ Assert.assertEquals("description updated", updated.description());
+ Assert.assertNotEquals(group1.updateTime(), updated.updateTime());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ group2.name("test2-updated");
+ api.update(group2);
+ }, e -> {
+ Assert.assertContains("The name of group can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(group2, "id", "fake-id");
+ api.update(group2);
+ }, e -> {
+ Assert.assertContains("Invalid group id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDelete() {
+ Group group1 = createGroup("test1", "description 1");
+ Group group2 = createGroup("test2", "description 2");
+
+ Assert.assertEquals(2, api.list(-1).size());
+ api.delete(group1.id());
+
+ Assert.assertEquals(1, api.list(-1).size());
+ Assert.assertEquals(group2, api.list(-1).get(0));
+
+ api.delete(group2.id());
+ Assert.assertEquals(0, api.list(-1).size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(group2.id());
+ }, e -> {
+ Assert.assertContains("Invalid group id:", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete("fake-id");
+ }, e -> {
+ Assert.assertContains("Invalid group id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ protected static Group createGroup(String name, String description) {
+ Group group = new Group();
+ group.name(name);
+ group.description(description);
+ return api.create(group);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LoginApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LoginApiTest.java
new file mode 100644
index 0000000..6b02c40
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LoginApiTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class LoginApiTest extends AuthApiTest {
+
+ private static LoginAPI loginAPI;
+ private static UserAPI userAPI;
+
+ @BeforeClass
+ public static void init() {
+ loginAPI = new LoginAPI(initClient(), GRAPH);
+ userAPI = new UserAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<User> users = userAPI.list(-1);
+ for (User user : users) {
+ if (user.name().equals("admin")) {
+ continue;
+ }
+ userAPI.delete(user.id());
+ }
+ }
+
+ @Test
+ public void testLogin() {
+ Login login = new Login();
+ login.name("user1");
+ login.password("p1");
+ Assert.assertThrows(ServerException.class, () -> {
+ loginAPI.login(login);
+ }, e -> {
+ Assert.assertContains("Incorrect username or password",
+ e.getMessage());
+ });
+
+ User user1 = new User();
+ user1.name("user1");
+ user1.password("p1");
+ userAPI.create(user1);
+
+ LoginResult result = loginAPI.login(login);
+ Assert.assertNotNull(result);
+ Assert.assertNotNull(result.token());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LogoutApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LogoutApiTest.java
new file mode 100644
index 0000000..4a91a21
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/LogoutApiTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class LogoutApiTest extends AuthApiTest {
+
+ private static LogoutAPI logoutAPI;
+ private static LoginAPI loginAPI;
+ private static UserAPI userAPI;
+
+ @BeforeClass
+ public static void init() {
+ logoutAPI = new LogoutAPI(initClient(), GRAPH);
+ loginAPI = new LoginAPI(initClient(), GRAPH);
+ userAPI = new UserAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<User> users = userAPI.list(-1);
+ for (User user : users) {
+ if (user.name().equals("admin")) {
+ continue;
+ }
+ userAPI.delete(user.id());
+ }
+ }
+
+ @Test
+ public void testLogout() {
+ User user1 = new User();
+ user1.name("user1");
+ user1.password("p1");
+ userAPI.create(user1);
+
+ Login login = new Login();
+ login.name("user1");
+ login.password("p1");
+ LoginResult result = loginAPI.login(login);
+ Assert.assertNotNull(result);
+ Assert.assertNotNull(result.token());
+
+ // Client will set Authentication Header use Basic
+ Assert.assertThrows(ServerException.class, () -> {
+ logoutAPI.logout();
+ }, e -> {
+ Assert.assertContains("Only HTTP Bearer authentication is supported",
+ e.getMessage());
+ });
+
+ String token = result.token();
+ RestClient client = Whitebox.getInternalState(logoutAPI, "client");
+ client.setAuthContext("Bearer " + token);
+ logoutAPI.logout();
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/ProjectApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/ProjectApiTest.java
new file mode 100644
index 0000000..bc4c5fb
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/ProjectApiTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.API;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Project;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableSet;
+
+public class ProjectApiTest extends AuthApiTest {
+
+ private static ProjectAPI api;
+
+ @BeforeClass
+ public static void init() {
+ api = new ProjectAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<Project> projects = api.list(-1);
+ for (Project project : projects) {
+ Set<String> graphs = project.graphs();
+ if (CollectionUtils.isNotEmpty(graphs)) {
+ api.removeGraphs(project, graphs);
+ }
+ api.delete(project.id());
+ }
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ super.teardown();
+ clear();
+ }
+
+ @Test
+ public void testGet() {
+ Project createdProject = createProject("project_test");
+ Project fetchedProject = api.get(createdProject);
+ Assert.assertEquals(createdProject, fetchedProject);
+ }
+
+ @Test
+ public void testCreate() {
+ Project paramsProject = new Project("project_test",
+ "project_description");
+ Project createdProject = api.create(paramsProject);
+ Assert.assertEquals(paramsProject.name(), createdProject.name());
+ Assert.assertEquals(paramsProject.description(),
+ createdProject.description());
+ }
+
+ @Test
+ public void testList() {
+ Project project1 = createProject("project_test1");
+ Project project2 = createProject("project_test2");
+ Project project3 = createProject("project_test3");
+ List<Project> allProject = api.list(API.NO_LIMIT);
+ Assert.assertTrue(allProject.contains(project1));
+ Assert.assertTrue(allProject.contains(project2));
+ Assert.assertTrue(allProject.contains(project3));
+ List<Project> projects = api.list(1);
+ Assert.assertEquals(1, projects.size());
+ Project project = projects.get(0);
+ Assert.assertTrue(StringUtils.isNotEmpty(project.adminGroup()));
+ Assert.assertTrue(StringUtils.isNotEmpty(project.opGroup()));
+ Assert.assertTrue(StringUtils.isNotEmpty(project.target()));
+ Assert.assertTrue(StringUtils.isNotEmpty(project.creator()));
+ Assert.assertNotNull(project.createTime());
+ Assert.assertNotNull(project.updateTime());
+ }
+
+ @Test
+ public void testDelete() {
+ Project project = createProject("project_test");
+ Assert.assertNotNull(project);
+ Assert.assertEquals(project, api.get(project));
+ api.delete(project);
+ Assert.assertThrows(ServerException.class, () -> {
+ api.get(project);
+ }, e -> {
+ Assert.assertTrue(e.getMessage().contains("Invalid project id"));
+ });
+ }
+
+ @Test
+ public void testAddGraph() {
+ Project project = createProject("project_test");
+ api.addGraphs(project, ImmutableSet.of("test_graph"));
+ project = getProject(project);
+ Assert.assertEquals(1, project.graphs().size());
+ Assert.assertTrue(project.graphs().contains("test_graph"));
+ api.addGraphs(project, ImmutableSet.of("test_graph1"));
+ project = getProject(project);
+ Assert.assertEquals(2, project.graphs().size());
+ Assert.assertTrue(project.graphs().contains("test_graph1"));
+ }
+
+ @Test
+ public void testRemoveGraph() {
+ Set<String> graphs = ImmutableSet.of("test_graph1",
+ "test_graph2",
+ "test_graph3");
+ Project project = createProject("project_test", graphs);
+ graphs = new HashSet<>(graphs);
+ Assert.assertTrue(graphs.containsAll(project.graphs()));
+ project = api.removeGraphs(project, ImmutableSet.of("test_graph1"));
+ graphs.remove("test_graph1");
+ Assert.assertTrue(graphs.containsAll(project.graphs()));
+ project = api.removeGraphs(project, ImmutableSet.of("test_graph2"));
+ graphs.remove("test_graph2");
+ Assert.assertTrue(graphs.containsAll(project.graphs()));
+ project = api.removeGraphs(project, ImmutableSet.of("test_graph3"));
+ graphs.remove("test_graph3");
+ Assert.assertEquals(0, graphs.size());
+ Assert.assertNull(project.graphs());
+ }
+
+ private static Project createProject(String name) {
+ Project project = new Project(name);
+ return api.create(project);
+ }
+
+ private static Project createProject(String name, Set<String> graphs) {
+ Project project = new Project(name);
+ project = api.create(project);
+ project = api.addGraphs(project.id(), graphs);
+ return project;
+ }
+
+ private static Project getProject(Object id) {
+ return api.get(id);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TargetApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TargetApiTest.java
new file mode 100644
index 0000000..0770ed4
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TargetApiTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.HugeResource;
+import com.baidu.hugegraph.structure.auth.HugeResourceType;
+import com.baidu.hugegraph.structure.auth.Target;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class TargetApiTest extends AuthApiTest {
+
+ private static TargetAPI api;
+
+ @BeforeClass
+ public static void init() {
+ api = new TargetAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<Target> targets = api.list(-1);
+ for (Target target : targets) {
+ api.delete(target.id());
+ }
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ clear();
+ }
+
+ @Test
+ public void testCreate() {
+ Target target1 = new Target();
+ target1.name("gremlin");
+ target1.graph("hugegraph");
+ target1.url("127.0.0.1:8080");
+ HugeResource gremlin = new HugeResource(HugeResourceType.GREMLIN);
+ target1.resources(gremlin);
+
+ Target target2 = new Target();
+ target2.name("task");
+ target2.graph("hugegraph2");
+ target2.url("127.0.0.1:8081");
+ HugeResource task = new HugeResource(HugeResourceType.TASK);
+ target2.resources(task);
+
+ Target result1 = api.create(target1);
+ Target result2 = api.create(target2);
+
+ Assert.assertEquals("gremlin", result1.name());
+ Assert.assertEquals("hugegraph", result1.graph());
+ Assert.assertEquals("127.0.0.1:8080", result1.url());
+ Assert.assertEquals(Arrays.asList(gremlin), result1.resources());
+
+ Assert.assertEquals("task", result2.name());
+ Assert.assertEquals("hugegraph2", result2.graph());
+ Assert.assertEquals("127.0.0.1:8081", result2.url());
+ Assert.assertEquals(Arrays.asList(task), result2.resources());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(target1);
+ }, e -> {
+ Assert.assertContains("Can't save target", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Target target3 = new Target();
+ api.create(target3);
+ }, e -> {
+ Assert.assertContains("The name of target can't be null",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Target target3 = new Target();
+ target3.name("test");
+ api.create(target3);
+ }, e -> {
+ Assert.assertContains("The graph of target can't be null",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Target target3 = new Target();
+ target3.name("test");
+ target3.graph("hugegraph3");
+ api.create(target3);
+ }, e -> {
+ Assert.assertContains("The url of target can't be null",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testGet() {
+ Target target1 = createTarget("test1", HugeResourceType.VERTEX);
+ Target target2 = createTarget("test2", HugeResourceType.EDGE);
+
+ Assert.assertEquals(HugeResourceType.VERTEX,
+ target1.resource().resourceType());
+ Assert.assertEquals(HugeResourceType.EDGE,
+ target2.resource().resourceType());
+
+ target1 = api.get(target1.id());
+ target2 = api.get(target2.id());
+
+ Assert.assertEquals("test1", target1.name());
+ Assert.assertEquals(HugeResourceType.VERTEX,
+ target1.resource().resourceType());
+
+ Assert.assertEquals("test2", target2.name());
+ Assert.assertEquals(HugeResourceType.EDGE,
+ target2.resource().resourceType());
+ }
+
+ @Test
+ public void testList() {
+ createTarget("test1", HugeResourceType.VERTEX);
+ createTarget("test2", HugeResourceType.EDGE);
+ createTarget("test3", HugeResourceType.ALL);
+
+ List<Target> targets = api.list(-1);
+ Assert.assertEquals(3, targets.size());
+
+ targets.sort((t1, t2) -> t1.name().compareTo(t2.name()));
+ Assert.assertEquals("test1", targets.get(0).name());
+ Assert.assertEquals("test2", targets.get(1).name());
+ Assert.assertEquals("test3", targets.get(2).name());
+ Assert.assertEquals(HugeResourceType.VERTEX,
+ targets.get(0).resource().resourceType());
+ Assert.assertEquals(HugeResourceType.EDGE,
+ targets.get(1).resource().resourceType());
+ Assert.assertEquals(HugeResourceType.ALL,
+ targets.get(2).resource().resourceType());
+
+ targets = api.list(1);
+ Assert.assertEquals(1, targets.size());
+
+ targets = api.list(2);
+ Assert.assertEquals(2, targets.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testUpdate() {
+ Target target1 = createTarget("test1", HugeResourceType.VERTEX);
+ Target target2 = createTarget("test2", HugeResourceType.EDGE);
+
+ Assert.assertEquals(HugeResourceType.VERTEX,
+ target1.resource().resourceType());
+ Assert.assertEquals(HugeResourceType.EDGE,
+ target2.resource().resourceType());
+
+ target1.resources(new HugeResource(HugeResourceType.ALL));
+ Target updated = api.update(target1);
+ Assert.assertEquals(HugeResourceType.ALL,
+ updated.resource().resourceType());
+ Assert.assertNotEquals(target1.updateTime(), updated.updateTime());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ target2.name("test2-updated");
+ api.update(target2);
+ }, e -> {
+ Assert.assertContains("The name of target can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(target2, "id", "fake-id");
+ api.update(target2);
+ }, e -> {
+ Assert.assertContains("Invalid target id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDelete() {
+ Target target1 = createTarget("test1", HugeResourceType.VERTEX);
+ Target target2 = createTarget("test2", HugeResourceType.EDGE);
+
+ Assert.assertEquals(2, api.list(-1).size());
+ api.delete(target1.id());
+
+ Assert.assertEquals(1, api.list(-1).size());
+ Assert.assertEquals(target2, api.list(-1).get(0));
+
+ api.delete(target2.id());
+ Assert.assertEquals(0, api.list(-1).size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(target2.id());
+ }, e -> {
+ Assert.assertContains("Invalid target id:", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete("fake-id");
+ }, e -> {
+ Assert.assertContains("Invalid target id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ protected static Target createTarget(String name, HugeResourceType res) {
+ Target target = new Target();
+ target.name(name);
+ target.graph("hugegraph");
+ target.url("127.0.0.1:8080");
+ target.resources(new HugeResource(res));
+ return api.create(target);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TokenApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TokenApiTest.java
new file mode 100644
index 0000000..56d0afc
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/TokenApiTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.client.RestClient;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.auth.TokenPayload;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class TokenApiTest extends AuthApiTest {
+
+ private static TokenAPI tokenAPI;
+ private static LogoutAPI logoutAPI;
+ private static LoginAPI loginAPI;
+ private static UserAPI userAPI;
+
+ @BeforeClass
+ public static void init() {
+ tokenAPI = new TokenAPI(initClient(), GRAPH);
+ logoutAPI = new LogoutAPI(initClient(), GRAPH);
+ loginAPI = new LoginAPI(initClient(), GRAPH);
+ userAPI = new UserAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<User> users = userAPI.list(-1);
+ for (User user : users) {
+ if (user.name().equals("admin")) {
+ continue;
+ }
+ userAPI.delete(user.id());
+ }
+ }
+
+ @Test
+ public void testVerify() {
+ User user1 = new User();
+ user1.name("user1");
+ user1.password("p1");
+ User user = userAPI.create(user1);
+
+ Login login = new Login();
+ login.name("user1");
+ login.password("p1");
+ LoginResult result = loginAPI.login(login);
+ Assert.assertNotNull(result);
+ Assert.assertNotNull(result.token());
+
+ // Client will set Authentication Header use Basic
+ Assert.assertThrows(ServerException.class, () -> {
+ tokenAPI.verifyToken();
+ }, e -> {
+ Assert.assertContains("Only HTTP Bearer authentication is supported",
+ e.getMessage());
+ });
+
+ String token = result.token();
+ RestClient client = Whitebox.getInternalState(tokenAPI, "client");
+ client.setAuthContext("Bearer " + token);
+
+ TokenPayload payload = tokenAPI.verifyToken();
+ Assert.assertEquals("user1", payload.username());
+ Assert.assertEquals(user.id(), payload.userId());
+
+ client.setAuthContext("Bearer qweqwaasa");
+ Assert.assertThrows(ServerException.class, () -> {
+ tokenAPI.verifyToken();
+ }, e -> {
+ Assert.assertContains("Invalid token", e.getMessage());
+ });
+
+ RestClient client2 = Whitebox.getInternalState(logoutAPI, "client");
+ Assert.assertThrows(ServerException.class, () -> {
+ logoutAPI.logout();
+ }, e -> {
+ Assert.assertContains("Only HTTP Bearer authentication is supported",
+ e.getMessage());
+ });
+
+ client2.setAuthContext("Bearer " + token);
+ logoutAPI.logout();
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/UserApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/UserApiTest.java
new file mode 100644
index 0000000..cbf0a63
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/auth/UserApiTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.auth;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.structure.auth.User.UserRole;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+
+public class UserApiTest extends AuthApiTest {
+
+ private static UserAPI api;
+
+ @BeforeClass
+ public static void init() {
+ api = new UserAPI(initClient(), GRAPH);
+ }
+
+ @AfterClass
+ public static void clear() {
+ List<User> users = api.list(-1);
+ for (User user : users) {
+ if (user.name().equals("admin")) {
+ continue;
+ }
+ api.delete(user.id());
+ }
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ clear();
+ }
+
+ @Test
+ public void testCreate() {
+ User user1 = new User();
+ user1.name("user1");
+ user1.password("p1");
+ user1.email("user1@hugegraph.com");
+ user1.phone("123456789");
+ user1.avatar("image1.jpg");
+
+ User user2 = new User();
+ user2.name("user2");
+ user2.password("p2");
+ user2.email("user2@hugegraph.com");
+ user2.phone("1357924680");
+ user2.avatar("image2.jpg");
+
+ User result1 = api.create(user1);
+ User result2 = api.create(user2);
+
+ Assert.assertEquals("user1", result1.name());
+ Assert.assertNotEquals("p1", result1.password());
+ Assert.assertEquals("user1@hugegraph.com", result1.email());
+ Assert.assertEquals("123456789", result1.phone());
+ Assert.assertEquals("image1.jpg", result1.avatar());
+
+ Assert.assertEquals("user2", result2.name());
+ Assert.assertNotEquals("p2", result2.password());
+ Assert.assertEquals("user2@hugegraph.com", result2.email());
+ Assert.assertEquals("1357924680", result2.phone());
+ Assert.assertEquals("image2.jpg", result2.avatar());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(new User());
+ }, e -> {
+ Assert.assertContains("The name of user can't be null",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ User user3 = new User();
+ user3.name("test");
+ api.create(user3);
+ }, e -> {
+ Assert.assertContains("The password of user can't be null",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.create(user1);
+ }, e -> {
+ Assert.assertContains("Can't save user", e.getMessage());
+ Assert.assertContains("that already exists", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ user1.name("admin");
+ api.create(user1);
+ }, e -> {
+ Assert.assertContains("Invalid user name 'admin'", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testGet() {
+ User user1 = createUser("test1", "psw1");
+ User user2 = createUser("test2", "psw2");
+
+ Assert.assertEquals("test1", user1.name());
+ Assert.assertEquals("test2", user2.name());
+
+ user1 = api.get(user1.id());
+ user2 = api.get(user2.id());
+
+ Assert.assertEquals("test1", user1.name());
+ Assert.assertEquals("test2", user2.name());
+ }
+
+ @Test
+ public void testGetUserRole() {
+ User user1 = createUser("test1", "psw1");
+ User user2 = createUser("test2", "psw2");
+
+ Assert.assertEquals("test1", user1.name());
+ Assert.assertEquals("test2", user2.name());
+
+ UserRole role1 = api.getUserRole(user1.id());
+ UserRole role2 = api.getUserRole(user2.id());
+
+ Assert.assertEquals("{\"roles\":{}}", role1.toString());
+ Assert.assertEquals("{\"roles\":{}}", role2.toString());
+ }
+
+ @Test
+ public void testList() {
+ createUser("test1", "psw1");
+ createUser("test2", "psw2");
+ createUser("test3", "psw3");
+
+ List<User> users = api.list(-1);
+ Assert.assertEquals(4, users.size());
+
+ users.sort((t1, t2) -> t1.name().compareTo(t2.name()));
+ Assert.assertEquals("admin", users.get(0).name());
+ Assert.assertEquals("test1", users.get(1).name());
+ Assert.assertEquals("test2", users.get(2).name());
+ Assert.assertEquals("test3", users.get(3).name());
+
+ users = api.list(1);
+ Assert.assertEquals(1, users.size());
+
+ users = api.list(2);
+ Assert.assertEquals(2, users.size());
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ api.list(0);
+ }, e -> {
+ Assert.assertContains("Limit must be > 0 or == -1", e.getMessage());
+ });
+ }
+
+ @Test
+ public void testUpdate() {
+ User user1 = createUser("test1", "psw1");
+ User user2 = createUser("test2", "psw2");
+
+ Assert.assertEquals("test@hugegraph.com", user1.email());
+ Assert.assertEquals("16812345678", user1.phone());
+ Assert.assertEquals("image.jpg", user1.avatar());
+
+ String oldPassw = user1.password();
+ Assert.assertNotEquals("psw1", oldPassw);
+
+ user1.password("psw-udated");
+ user1.email("test_updated@hugegraph.com");
+ user1.phone("1357924680");
+ user1.avatar("image-updated.jpg");
+
+ User updated = api.update(user1);
+ Assert.assertNotEquals(oldPassw, updated.password());
+ Assert.assertEquals("test_updated@hugegraph.com", updated.email());
+ Assert.assertEquals("1357924680", updated.phone());
+ Assert.assertEquals("image-updated.jpg", updated.avatar());
+ Assert.assertNotEquals(user1.updateTime(), updated.updateTime());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ user2.name("test2-updated");
+ api.update(user2);
+ }, e -> {
+ Assert.assertContains("The name of user can't be updated",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ Whitebox.setInternalState(user2, "id", "fake-id");
+ api.update(user2);
+ }, e -> {
+ Assert.assertContains("Invalid user id: fake-id",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testDelete() {
+ User user1 = createUser("test1", "psw1");
+ User user2 = createUser("test2", "psw2");
+
+ List<User> users = api.list(-1);
+ Assert.assertEquals(3, users.size());
+ Assert.assertTrue(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+ api.delete(user1.id());
+
+ users = api.list(-1);
+ Assert.assertEquals(2, users.size());
+ Assert.assertFalse(users.contains(user1));
+ Assert.assertTrue(users.contains(user2));
+
+ api.delete(user2.id());
+ users = api.list(-1);
+ Assert.assertEquals(1, users.size());
+ Assert.assertEquals("admin", users.get(0).name());
+
+ User admin = users.get(0);
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(admin.id());
+ }, e -> {
+ Assert.assertContains("Can't delete user 'admin'", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete(user2.id());
+ }, e -> {
+ Assert.assertContains("Invalid user id:", e.getMessage());
+ });
+
+ Assert.assertThrows(ServerException.class, () -> {
+ api.delete("fake-id");
+ }, e -> {
+ Assert.assertContains("Invalid user id: fake-id", e.getMessage());
+ });
+ }
+
+ protected static User createUser(String name, String password) {
+ User user = new User();
+ user.name(name);
+ user.password(password);
+ user.email("test@hugegraph.com");
+ user.phone("16812345678");
+ user.avatar("image.jpg");
+ return api.create(user);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/AllShortestPathsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/AllShortestPathsApiTest.java
new file mode 100644
index 0000000..c5c66a9
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/AllShortestPathsApiTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class AllShortestPathsApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+ Vertex v4 = graph().addVertex(T.label, "node", T.id, 4);
+ Vertex v5 = graph().addVertex(T.label, "node", T.id, 5);
+ Vertex v6 = graph().addVertex(T.label, "node", T.id, 6);
+ Vertex v7 = graph().addVertex(T.label, "node", T.id, 7);
+ Vertex v8 = graph().addVertex(T.label, "node", T.id, 8);
+ Vertex v9 = graph().addVertex(T.label, "node", T.id, 9);
+ Vertex v10 = graph().addVertex(T.label, "node", T.id, 10);
+ Vertex v11 = graph().addVertex(T.label, "node", T.id, 11);
+ Vertex v12 = graph().addVertex(T.label, "node", T.id, 12);
+ Vertex v13 = graph().addVertex(T.label, "node", T.id, 13);
+ Vertex v14 = graph().addVertex(T.label, "node", T.id, 14);
+ Vertex v15 = graph().addVertex(T.label, "node", T.id, 15);
+ Vertex v16 = graph().addVertex(T.label, "node", T.id, 16);
+ Vertex v17 = graph().addVertex(T.label, "node", T.id, 17);
+ Vertex v18 = graph().addVertex(T.label, "node", T.id, 18);
+ Vertex v19 = graph().addVertex(T.label, "node", T.id, 19);
+ Vertex v20 = graph().addVertex(T.label, "node", T.id, 20);
+ Vertex v21 = graph().addVertex(T.label, "node", T.id, 21);
+ Vertex v22 = graph().addVertex(T.label, "node", T.id, 22);
+ Vertex v23 = graph().addVertex(T.label, "node", T.id, 23);
+ Vertex v24 = graph().addVertex(T.label, "node", T.id, 24);
+ Vertex v25 = graph().addVertex(T.label, "node", T.id, 25);
+ Vertex v26 = graph().addVertex(T.label, "node", T.id, 26);
+ Vertex v27 = graph().addVertex(T.label, "node", T.id, 27);
+ Vertex v28 = graph().addVertex(T.label, "node", T.id, 28);
+
+ // Path length 5
+ v1.addEdge("link", v2);
+ v2.addEdge("link", v3);
+ v3.addEdge("link", v4);
+ v4.addEdge("link", v5);
+ v5.addEdge("link", v6);
+
+ v1.addEdge("link", v25);
+ v25.addEdge("link", v26);
+ v26.addEdge("link", v27);
+ v27.addEdge("link", v28);
+ v28.addEdge("link", v6);
+
+ // Path length 4
+ v1.addEdge("link", v7);
+ v7.addEdge("link", v8);
+ v8.addEdge("link", v9);
+ v9.addEdge("link", v6);
+
+ // Path length 3
+ v1.addEdge("link", v10);
+ v10.addEdge("link", v11);
+ v11.addEdge("link", v6);
+
+ v1.addEdge("link", v19);
+ v19.addEdge("link", v20);
+ v20.addEdge("link", v6);
+
+ v1.addEdge("link", v21);
+ v21.addEdge("link", v22);
+ v22.addEdge("link", v6);
+
+ v1.addEdge("link", v23);
+ v23.addEdge("link", v24);
+ v24.addEdge("link", v6);
+
+ // Add other 3 neighbor for v7
+ v7.addEdge("link", v12);
+ v7.addEdge("link", v13);
+ v7.addEdge("link", v14);
+
+ // Add other 4 neighbor for v10
+ v10.addEdge("link", v15);
+ v10.addEdge("link", v16);
+ v10.addEdge("link", v17);
+ v10.addEdge("link", v18);
+ }
+
+ @Test
+ public void testAllShortestPath() {
+ List<Path> paths = allShortestPathsAPI.get(1, 6, Direction.BOTH,
+ null, 6, -1L, 0L, -1L);
+ Assert.assertEquals(4, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 10, 11, 6),
+ ImmutableList.of(1, 19, 20, 6),
+ ImmutableList.of(1, 21, 22, 6),
+ ImmutableList.of(1, 23, 24, 6)
+ );
+ for (Path path : paths){
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testAllShortestPathWithLabel() {
+ List<Path> paths = allShortestPathsAPI.get(1, 6, Direction.BOTH,
+ "link", 6, -1L, 0L,
+ -1L);
+ Assert.assertEquals(4, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 10, 11, 6),
+ ImmutableList.of(1, 19, 20, 6),
+ ImmutableList.of(1, 21, 22, 6),
+ ImmutableList.of(1, 23, 24, 6)
+ );
+ for (Path path : paths){
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testAllShortestPathWithDegree() {
+ List<Path> paths = allShortestPathsAPI.get(1, 6, Direction.OUT,
+ null, 6, 1L, 0L, -1L);
+ /*
+ * Following results can be guaranteed in RocksDB backend,
+ * but different results exist in table type backend(like Cassandra).
+ */
+ Assert.assertEquals(1, paths.size());
+ List<Object> expected = ImmutableList.of(1, 2, 3, 4, 5, 6);
+ Assert.assertEquals(expected, paths.iterator().next().objects());
+ }
+
+ @Test
+ public void testAllShortestPathWithCapacity() {
+ List<Path> paths = allShortestPathsAPI.get(1, 6, Direction.BOTH,
+ null, 6, -1L, 0L, -1L);
+ Assert.assertEquals(4, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 10, 11, 6),
+ ImmutableList.of(1, 19, 20, 6),
+ ImmutableList.of(1, 21, 22, 6),
+ ImmutableList.of(1, 23, 24, 6)
+ );
+ for (Path path : paths){
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ Assert.assertThrows(ServerException.class, () -> {
+ allShortestPathsAPI.get(1, 6, Direction.BOTH, null, 6, 6,
+ 0L, 10L);
+ }, e -> {
+ String expect = "Exceed capacity '10' while finding shortest path";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testAllShortestPathWithMaxDepth() {
+ List<Path> paths = allShortestPathsAPI.get(14, 6, Direction.BOTH,
+ null, 4, 5L, 0L, 19L);
+ Assert.assertEquals(1, paths.size());
+ Path path = paths.get(0);
+ Assert.assertEquals(ImmutableList.of(14, 7, 8, 9, 6), path.objects());
+
+ paths = allShortestPathsAPI.get(14, 6, Direction.BOTH,
+ null, 3, 5L, 0L, 19L);
+ Assert.assertEquals(0, paths.size());
+ }
+
+ @Test
+ public void testAllShortestPathWithIllegalArgs() {
+ // The max depth shouldn't be 0 or negative
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, -1, 1L, 0L, 2L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 0, 1L, 0L, 2L);
+ });
+
+ // The degree shouldn't be 0 or negative but NO_LIMIT(-1)
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, 0L, 0L, 2L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, -3L, 0L, 2L);
+ });
+
+ // The skipped degree shouldn't be negative
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, 1L, -1L, 2L);
+ });
+
+ // The skipped degree shouldn't be >= capacity
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, 1L, 2L, 2L);
+ });
+
+ // The skipped degree shouldn't be < degree
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, 3L, 2L, 10L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ allShortestPathsAPI.get("a", "b", Direction.BOTH,
+ null, 5, -1L, 2L, 10L);
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CommonTraverserApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CommonTraverserApiTest.java
new file mode 100644
index 0000000..77574e1
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CommonTraverserApiTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import static com.baidu.hugegraph.structure.constant.Traverser.DEFAULT_PAGE_LIMIT;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Edges;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Shard;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.graph.Vertices;
+import com.baidu.hugegraph.structure.traverser.CrosspointsRequest;
+import com.baidu.hugegraph.structure.traverser.CustomizedCrosspoints;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class CommonTraverserApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testCrosspoints() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ List<Path> paths = crosspointsAPI.get(markoId, peterId, Direction.OUT,
+ null, 3, -1L, -1L, 10);
+ Assert.assertEquals(2, paths.size());
+ Path crosspoint1 = new Path(lopId,
+ ImmutableList.of(markoId, lopId, peterId));
+ Path crosspoint2 = new Path(lopId, ImmutableList.of(markoId, joshId,
+ lopId, peterId));
+
+ List<Path> crosspoints = ImmutableList.of(crosspoint1, crosspoint2);
+ Assert.assertTrue(crosspoints.contains(paths.get(0)));
+ Assert.assertTrue(crosspoints.contains(paths.get(1)));
+ }
+
+ @Test
+ public void testCrosspointsWithCapacity() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ crosspointsAPI.get(markoId, peterId, Direction.OUT,
+ null, 3, -1L, 2L, 10);
+ }, e -> {
+ String expect = "Exceed capacity '2' while finding paths";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testCustomizedCrosspoints() {
+ Object lopId = getVertexId("software", "name", "lop");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ CrosspointsRequest.Builder builder = CrosspointsRequest.builder();
+ builder.sources().ids(lopId, rippleId);
+ builder.pathPatterns().steps().direction(Direction.IN)
+ .labels("created").degree(-1);
+ builder.withPath(true).withVertex(true).capacity(-1).limit(-1);
+
+ CustomizedCrosspoints customizedCrosspoints =
+ customizedCrosspointsAPI.post(builder.build());
+ List<Object> crosspoints = customizedCrosspoints.crosspoints();
+ Assert.assertEquals(1, crosspoints.size());
+ Assert.assertEquals(joshId, crosspoints.get(0));
+
+ List<Path> paths = customizedCrosspoints.paths();
+ Assert.assertEquals(2, paths.size());
+
+ List<Object> path1 = ImmutableList.of(rippleId, joshId);
+ List<Object> path2 = ImmutableList.of(lopId, joshId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+
+ Set<?> vertices = customizedCrosspoints.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ List<Object> expectedVids = ImmutableList.of(rippleId, joshId, lopId);
+ Assert.assertTrue(expectedVids.containsAll(vertices));
+ }
+
+ @Test
+ public void testVertices() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Object> ids = ImmutableList.of(markoId, vadasId, joshId,
+ peterId, lopId, rippleId);
+ List<Vertex> vertices = verticesAPI.list(ids);
+
+ Assert.assertEquals(6, vertices.size());
+
+ Assert.assertEquals(markoId, vertices.get(0).id());
+ Assert.assertEquals(vadasId, vertices.get(1).id());
+ Assert.assertEquals(joshId, vertices.get(2).id());
+ Assert.assertEquals(peterId, vertices.get(3).id());
+ Assert.assertEquals(lopId, vertices.get(4).id());
+ Assert.assertEquals(rippleId, vertices.get(5).id());
+
+ Map<String, Object> props = ImmutableMap.of("name", "josh",
+ "city", "Beijing",
+ "age", 32);
+ Assert.assertEquals(props, vertices.get(2).properties());
+ }
+
+ @Test
+ public void testEdges() {
+ String date2012Id = getEdgeId("knows", "date", "2012-01-10");
+ String date2013Id = getEdgeId("knows", "date", "2013-01-10");
+ String date2014Id = getEdgeId("created", "date", "2014-01-10");
+ String date2015Id = getEdgeId("created", "date", "2015-01-10");
+ String date2016Id = getEdgeId("created", "date", "2016-01-10");
+ String date2017Id = getEdgeId("created", "date", "2017-01-10");
+
+ List<String> ids = ImmutableList.of(date2012Id, date2013Id, date2014Id,
+ date2015Id, date2016Id, date2017Id);
+
+ List<Edge> edges = edgesAPI.list(ids);
+
+ Assert.assertEquals(6, edges.size());
+
+ Assert.assertEquals(date2012Id, edges.get(0).id());
+ Assert.assertEquals(date2013Id, edges.get(1).id());
+ Assert.assertEquals(date2014Id, edges.get(2).id());
+ Assert.assertEquals(date2015Id, edges.get(3).id());
+ Assert.assertEquals(date2016Id, edges.get(4).id());
+ Assert.assertEquals(date2017Id, edges.get(5).id());
+
+ String date = Utils.formatDate("2014-01-10");
+ Map<String, Object> props = ImmutableMap.of("date", date,
+ "city", "Shanghai");
+ Assert.assertEquals(props, edges.get(2).properties());
+ }
+
+ @Test
+ public void testScanVertex() {
+ List<Shard> shards = verticesAPI.shards(1 * 1024 * 1024);
+ List<Vertex> vertices = new LinkedList<>();
+ for (Shard shard : shards) {
+ Vertices results = verticesAPI.scan(shard, null, 0L);
+ vertices.addAll(ImmutableList.copyOf(results.results()));
+ Assert.assertNull(results.page());
+ }
+ Assert.assertEquals(6, vertices.size());
+ }
+
+ @Test
+ public void testScanVertexInPaging() {
+ List<Shard> shards = verticesAPI.shards(1 * 1024 * 1024);
+ List<Vertex> vertices = new LinkedList<>();
+ for (Shard shard : shards) {
+ String page = "";
+ while (page != null) {
+ Vertices results = verticesAPI.scan(shard, page, DEFAULT_PAGE_LIMIT);
+ vertices.addAll(ImmutableList.copyOf(results.results()));
+ page = results.page();
+ }
+ }
+ Assert.assertEquals(6, vertices.size());
+ }
+
+ @Test
+ public void testScanVertexInPagingWithNegativeLimit() {
+ List<Shard> shards = verticesAPI.shards(1 * 1024 * 1024);
+ for (Shard shard : shards) {
+ String page = "";
+ Assert.assertThrows(ServerException.class, () -> {
+ verticesAPI.scan(shard, page, -1);
+ }, e -> {
+ Assert.assertContains("Invalid limit -1", e.getMessage());
+ });
+ }
+ }
+
+ @Test
+ public void testScanVertexWithSplitSizeLt1MB() {
+ Assert.assertThrows(ServerException.class, () -> {
+ verticesAPI.shards(1 * 1024 * 1024 - 1);
+ }, e -> {
+ String expect = "The split-size must be >= 1048576 bytes, " +
+ "but got 1048575";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testScanEdge() {
+ List<Shard> shards = edgesAPI.shards(1 * 1024 * 1024);
+ List<Edge> edges = new LinkedList<>();
+ for (Shard shard : shards) {
+ Edges results = edgesAPI.scan(shard, null, 0L);
+ Assert.assertNull(results.page());
+ edges.addAll(ImmutableList.copyOf(results.results()));
+ }
+ Assert.assertEquals(6, edges.size());
+ }
+
+ @Test
+ public void testScanEdgeInPaging() {
+ List<Shard> shards = edgesAPI.shards(1 * 1024 * 1024);
+ List<Edge> edges = new LinkedList<>();
+ for (Shard shard : shards) {
+ String page = "";
+ while (page != null) {
+ Edges results = edgesAPI.scan(shard, page, DEFAULT_PAGE_LIMIT);
+ edges.addAll(ImmutableList.copyOf(results.results()));
+ page = results.page();
+ }
+ }
+ Assert.assertEquals(6, edges.size());
+ }
+
+ @Test
+ public void testScanEdgeInPagingWithNegativeLimit() {
+ List<Shard> shards = edgesAPI.shards(1 * 1024 * 1024);
+ for (Shard shard : shards) {
+ String page = "";
+ Assert.assertThrows(ServerException.class, () -> {
+ edgesAPI.scan(shard, page, -1);
+ }, e -> {
+ String expect = "Invalid limit -1";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+ }
+
+ @Test
+ public void testScanEdgeWithSplitSizeLt1MB() {
+ Assert.assertThrows(ServerException.class, () -> {
+ edgesAPI.shards(1 * 1024 * 1024 - 1);
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CountApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CountApiTest.java
new file mode 100644
index 0000000..0001ac2
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CountApiTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.CountRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class CountApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initGraph() {
+ schema().propertyKey("time")
+ .asDate()
+ .ifNotExist()
+ .create();
+
+ schema().propertyKey("weight")
+ .asDouble()
+ .ifNotExist()
+ .create();
+
+ schema().vertexLabel("node")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .properties("time")
+ .multiTimes().sortKeys("time")
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ Vertex va = graph().addVertex(T.label, "node", T.id, "A");
+ Vertex vb = graph().addVertex(T.label, "node", T.id, "B");
+ Vertex vc = graph().addVertex(T.label, "node", T.id, "C");
+ Vertex vd = graph().addVertex(T.label, "node", T.id, "D");
+ Vertex ve = graph().addVertex(T.label, "node", T.id, "E");
+ Vertex vf = graph().addVertex(T.label, "node", T.id, "F");
+ Vertex vg = graph().addVertex(T.label, "node", T.id, "G");
+ Vertex vh = graph().addVertex(T.label, "node", T.id, "H");
+ Vertex vi = graph().addVertex(T.label, "node", T.id, "I");
+ Vertex vj = graph().addVertex(T.label, "node", T.id, "J");
+ Vertex vk = graph().addVertex(T.label, "node", T.id, "K");
+ Vertex vl = graph().addVertex(T.label, "node", T.id, "L");
+ Vertex vm = graph().addVertex(T.label, "node", T.id, "M");
+ Vertex vn = graph().addVertex(T.label, "node", T.id, "N");
+ Vertex vo = graph().addVertex(T.label, "node", T.id, "O");
+ Vertex vp = graph().addVertex(T.label, "node", T.id, "P");
+ Vertex vq = graph().addVertex(T.label, "node", T.id, "Q");
+ Vertex vr = graph().addVertex(T.label, "node", T.id, "R");
+ Vertex vs = graph().addVertex(T.label, "node", T.id, "S");
+ Vertex vt = graph().addVertex(T.label, "node", T.id, "T");
+ Vertex vu = graph().addVertex(T.label, "node", T.id, "U");
+ Vertex vv = graph().addVertex(T.label, "node", T.id, "V");
+ Vertex vw = graph().addVertex(T.label, "node", T.id, "W");
+ Vertex vx = graph().addVertex(T.label, "node", T.id, "X");
+ Vertex vy = graph().addVertex(T.label, "node", T.id, "Y");
+ Vertex vz = graph().addVertex(T.label, "node", T.id, "Z");
+
+ /*
+ *
+ * c -----> f
+ * ^
+ * / d -----> g
+ * / ^
+ * / /
+ * b ---> e -----> h
+ * ^
+ * / j <----- m
+ * / /
+ * / /
+ * / <
+ * a <--- i <--- k <--- n
+ * . ^
+ * . \
+ * . \
+ * . l <------ o
+ * >
+ * p ...> q ...> v
+ * ...> r ...> w
+ * ...> s ...> x
+ * ...> t ...> y
+ * ...> u ...> z
+ *
+ * Description:
+ * 1. ">","<","^" means arrow
+ * 2. "---" means "link" edge
+ * 3. "..." means "relateTo" edge
+ *
+ */
+ va.addEdge("link", vb, "time", "2020-01-01");
+
+ vb.addEdge("link", vc, "time", "2020-01-02");
+ vb.addEdge("link", vd, "time", "2020-01-03");
+ vb.addEdge("link", ve, "time", "2020-01-04");
+
+ vc.addEdge("link", vf, "time", "2020-01-05");
+ vd.addEdge("link", vg, "time", "2020-01-06");
+ ve.addEdge("link", vh, "time", "2020-01-07");
+
+ vi.addEdge("link", va, "time", "2020-01-08");
+
+ vj.addEdge("link", vi, "time", "2020-01-09");
+ vk.addEdge("link", vi, "time", "2020-01-10");
+ vl.addEdge("link", vi, "time", "2020-01-11");
+
+ vm.addEdge("link", vj, "time", "2020-01-12");
+ vn.addEdge("link", vk, "time", "2020-01-13");
+ vo.addEdge("link", vl, "time", "2020-01-14");
+
+ va.addEdge("relateTo", vp, "weight", 0.0D);
+
+ vp.addEdge("relateTo", vq, "weight", 0.1D);
+ vp.addEdge("relateTo", vr, "weight", 0.2D);
+ vp.addEdge("relateTo", vs, "weight", 0.3D);
+ vp.addEdge("relateTo", vt, "weight", 0.4D);
+ vp.addEdge("relateTo", vu, "weight", 0.5D);
+
+ vq.addEdge("relateTo", vv, "weight", 0.6D);
+ vr.addEdge("relateTo", vw, "weight", 0.7D);
+ vs.addEdge("relateTo", vx, "weight", 0.8D);
+ vt.addEdge("relateTo", vy, "weight", 0.9D);
+ vu.addEdge("relateTo", vz, "weight", 1.0D);
+ }
+
+ @Test
+ public void testCount() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+ }
+
+ @Test
+ public void testCountWithContainsTraversed() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(19L, count);
+ }
+
+ @Test
+ public void testCountWithDirection() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(19L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.IN);
+ builder.steps().direction(Direction.IN);
+ builder.steps().direction(Direction.IN);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(3L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.IN);
+ builder.steps().direction(Direction.IN);
+ builder.steps().direction(Direction.IN);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+ }
+
+ @Test
+ public void testCountWithLabel() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(3L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+ }
+
+ @Test
+ public void testCountWithProperties() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(1L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.lt(\"2020-01-06\")");
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(6L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.gt(\"2020-01-03\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(1L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(true);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "P.gt(\"2020-01-03\")");
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(4L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"));
+ builder.steps().direction(Direction.OUT)
+ .labels(ImmutableList.of("link"))
+ .properties("time", "2020-01-07");
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(1L, count);
+ }
+
+ @Test
+ public void testCountWithDegree() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT).degree(1);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(3L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT).degree(2);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(4L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT).degree(4);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(7L, count);
+ }
+
+ @Test
+ public void testCountWithSkipDegree() {
+ CountRequest.Builder builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ CountRequest request = builder.build();
+
+ long count = countAPI.post(request);
+ Assert.assertEquals(8L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT).degree(3).skipDegree(5);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(3L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT).degree(2).skipDegree(3);
+ builder.steps().direction(Direction.OUT);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(0L, count);
+
+ builder = CountRequest.builder();
+ builder.source("A").containsTraversed(false);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT).degree(3).skipDegree(4);
+ request = builder.build();
+
+ count = countAPI.post(request);
+ Assert.assertEquals(3L, count);
+ }
+
+ @Test
+ public void testCountWithIllegalArgument() {
+ CountRequest.Builder builder = CountRequest.builder();
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ builder.source(null);
+ }, e -> {
+ Assert.assertContains("The source can't be null", e.getMessage());
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ builder.dedupSize(-5);
+ }, e -> {
+ Assert.assertContains("The dedup size must be >= 0 or == -1, " +
+ "but got: ", e.getMessage());
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ builder.steps().degree(0);
+ }, e -> {
+ Assert.assertContains("Degree must be > 0 or == -1, but got: ",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ builder.steps().skipDegree(-3);
+ }, e -> {
+ Assert.assertContains("The skipped degree must be >= 0, but got",
+ e.getMessage());
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ builder.steps().degree(5).skipDegree(3);
+ }, e -> {
+ Assert.assertContains("The skipped degree must be >= max degree",
+ e.getMessage());
+ });
+
+ CountRequest.Builder builder1 = CountRequest.builder();
+ Assert.assertThrows(ServerException.class, () -> {
+ builder1.source("A").containsTraversed(false);
+ builder1.steps().properties(ImmutableMap.of("weight", 3.3D));
+ countAPI.post(builder1.build());
+ }, e -> {
+ Assert.assertContains("The properties filter condition can be " +
+ "set only if just set one edge label",
+ e.getMessage());
+ });
+
+ CountRequest.Builder builder2 = CountRequest.builder();
+ Assert.assertThrows(ServerException.class, () -> {
+ builder2.source("A").containsTraversed(false);
+ builder2.steps().labels(ImmutableList.of("link", "relateTo"))
+ .properties(ImmutableMap.of("weight", 3.3D));
+ countAPI.post(builder2.build());
+ }, e -> {
+ Assert.assertContains("The properties filter condition can be " +
+ "set only if just set one edge label",
+ e.getMessage());
+ });
+
+ CountRequest.Builder builder3 = CountRequest.builder();
+ builder3.source("A").containsTraversed(false);
+ builder3.steps().labels(ImmutableList.of("link"))
+ .properties(ImmutableMap.of("time", "2020-01-01"));
+ countAPI.post(builder3.build());
+
+ CountRequest.Builder builder4 = CountRequest.builder();
+ Assert.assertThrows(ServerException.class, () -> {
+ builder4.source("A").containsTraversed(false);
+ builder4.steps().labels(ImmutableList.of("link"))
+ .properties(ImmutableMap.of("weight", 3.3D));
+ countAPI.post(builder4.build());
+ }, e -> {
+ Assert.assertContains("does not match sort keys of edge label",
+ e.getMessage());
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CustomizedPathsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CustomizedPathsApiTest.java
new file mode 100644
index 0000000..755cbc4
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/CustomizedPathsApiTest.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.structure.traverser.CustomizedPathsRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class CustomizedPathsApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initVertex();
+ }
+
+ @Override
+ @Before
+ public void setup() {
+ initEdgesWithWeights();
+ }
+
+ @Override
+ @After
+ public void teardown() {
+ removeEdgesWithWeights();
+ }
+
+ @Test
+ public void testCustomizedPathsSourceLabelProperty() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().label("person").property("name", "marko");
+ builder.steps().direction(Direction.OUT).labels("knows1")
+ .weightBy("weight").degree(-1);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(-1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(2, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, joshId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, joshId, rippleId);
+ Assert.assertEquals(path1, paths.get(0).objects());
+ Assert.assertEquals(path2, paths.get(1).objects());
+
+ List<Double> weights1 = ImmutableList.of(1.0D, 0.4D);
+ List<Double> weights2 = ImmutableList.of(1.0D, 1.0D);
+ Assert.assertEquals(weights1, paths.get(0).weights());
+ Assert.assertEquals(weights2, paths.get(1).weights());
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(4, vertices.size());
+ Set<?> expectedVertices = ImmutableSet.of(markoId, lopId,
+ rippleId, joshId);
+ Assert.assertEquals(expectedVertices, vertices);
+ }
+
+ @Test
+ public void testCustomizedPathsSourceIds() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().ids(markoId, peterId);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(-1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(2, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(peterId, lopId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+
+ List<Double> weights1 = ImmutableList.of(0.2D);
+ List<Double> weights2 = ImmutableList.of(0.4D);
+ List<List<Double>> expectedWeights = ImmutableList.of(weights1,
+ weights2);
+ Assert.assertTrue(expectedWeights.contains(paths.get(0).weights()));
+ Assert.assertTrue(expectedWeights.contains(paths.get(1).weights()));
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(3, vertices.size());
+ Set<?> expectedVertices = ImmutableSet.of(markoId, lopId, peterId);
+ Assert.assertEquals(expectedVertices, vertices);
+ }
+
+ @Test
+ public void testCustomizedPathsSourceLabelPropertyMultiValue() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ List<String> names = ImmutableList.of("marko", "peter");
+ builder.sources().label("person").property("name", names);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(-1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(2, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(peterId, lopId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+
+ List<Double> weights1 = ImmutableList.of(0.2D);
+ List<Double> weights2 = ImmutableList.of(0.4D);
+ List<List<Double>> expectedWeights = ImmutableList.of(weights1,
+ weights2);
+ Assert.assertTrue(expectedWeights.contains(paths.get(0).weights()));
+ Assert.assertTrue(expectedWeights.contains(paths.get(1).weights()));
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(3, vertices.size());
+ Set<?> expectedVertices = ImmutableSet.of(markoId, lopId, peterId);
+ Assert.assertEquals(expectedVertices, vertices);
+ }
+
+ @Test
+ public void testCustomizedPathsWithSample() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().label("person").property("name", "marko");
+ builder.steps().direction(Direction.OUT).labels("knows1")
+ .weightBy("weight").degree(-1);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1).sample(1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(-1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(1, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path2 = ImmutableList.of(markoId, joshId, lopId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+
+ List<Double> weights1 = ImmutableList.of(1D, 0.4D);
+ List<Double> weights2 = ImmutableList.of(1D, 1D);
+
+ Assert.assertTrue(weights1.equals(paths.get(0).weights()) ||
+ weights2.equals(paths.get(0).weights()));
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(3, vertices.size());
+ Assert.assertTrue(path1.containsAll(vertices) ||
+ path2.containsAll(vertices));
+ }
+
+ @Test
+ public void testCustomizedPathsWithDecr() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().label("person").property("name", "marko");
+ builder.steps().direction(Direction.OUT).labels("knows1")
+ .weightBy("weight").degree(-1);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.DECR).withVertex(true)
+ .capacity(-1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(2, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path2 = ImmutableList.of(markoId, joshId, lopId);
+ Assert.assertEquals(path1, paths.get(0).objects());
+ Assert.assertEquals(path2, paths.get(1).objects());
+
+ List<Double> weights1 = ImmutableList.of(1.0D, 1.0D);
+ List<Double> weights2 = ImmutableList.of(1.0D, 0.4D);
+ Assert.assertEquals(weights1, paths.get(0).weights());
+ Assert.assertEquals(weights2, paths.get(1).weights());
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(4, vertices.size());
+ Set<?> expectedVertices = ImmutableSet.of(markoId, lopId,
+ rippleId, joshId);
+ Assert.assertEquals(expectedVertices, vertices);
+ }
+
+ @Test
+ public void testCustomizedPathsWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().label("person").property("name", "marko");
+ builder.steps().direction(Direction.OUT).labels("knows1")
+ .weightBy("weight").degree(-1);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(-1).limit(1);
+ CustomizedPathsRequest request = builder.build();
+
+ PathsWithVertices customizedPaths = customizedPathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = customizedPaths.paths();
+
+ Assert.assertEquals(1, paths.size());
+
+ List<Object> path1 = ImmutableList.of(markoId, joshId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, joshId, rippleId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+
+ List<Double> weights1 = ImmutableList.of(1.0D, 0.4D);
+ List<Double> weights2 = ImmutableList.of(1.0D, 1.0D);
+ List<List<Double>> expectedWeights = ImmutableList.of(weights1,
+ weights2);
+ Assert.assertTrue(expectedWeights.contains(paths.get(0).weights()));
+
+ Set<?> vertices = customizedPaths.vertices().stream()
+ .map(Vertex::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(3, vertices.size());
+ Set<?> vertices1 = ImmutableSet.of(markoId, lopId, joshId);
+ Set<?> vertices2 = ImmutableSet.of(markoId, lopId, rippleId);
+ Set<Set<?>> expectedVertices = ImmutableSet.of(vertices1, vertices2);
+ Assert.assertTrue(expectedVertices.contains(vertices));
+ }
+
+ @Test
+ public void testCustomizedPathsWithCapacity() {
+ CustomizedPathsRequest.Builder builder = CustomizedPathsRequest.builder();
+ builder.sources().label("person").property("name", "marko");
+ builder.steps().direction(Direction.OUT).labels("knows1")
+ .weightBy("weight").degree(-1);
+ builder.steps().direction(Direction.OUT).labels("created1")
+ .weightBy("weight").degree(-1);
+ builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true)
+ .capacity(1).limit(-1);
+ CustomizedPathsRequest request = builder.build();
+
+ Assert.assertThrows(ServerException.class, () -> {
+ customizedPathsAPI.post(request);
+ }, e -> {
+ String expect = "Exceed capacity '1' while finding customized paths";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ private static void initEdgesWithWeights() {
+ SchemaManager schema = schema();
+ schema.edgeLabel("knows1")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .properties("date", "weight")
+ .nullableKeys("weight")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("created1")
+ .sourceLabel("person")
+ .targetLabel("software")
+ .properties("date", "weight")
+ .nullableKeys("weight")
+ .ifNotExist()
+ .create();
+
+ Vertex marko = getVertex("person", "name", "marko");
+ Vertex vadas = getVertex("person", "name", "vadas");
+ Vertex lop = getVertex("software", "name", "lop");
+ Vertex josh = getVertex("person", "name", "josh");
+ Vertex ripple = getVertex("software", "name", "ripple");
+ Vertex peter = getVertex("person", "name", "peter");
+
+ marko.addEdge("knows1", vadas, "date", "2016-01-10", "weight", 0.5);
+ marko.addEdge("knows1", josh, "date", "2013-02-20", "weight", 1.0);
+ marko.addEdge("created1", lop, "date", "2017-12-10", "weight", 0.4);
+ josh.addEdge("created1", lop, "date", "2009-11-11", "weight", 0.4);
+ josh.addEdge("created1", ripple, "date", "2017-12-10", "weight", 1.0);
+ peter.addEdge("created1", lop, "date", "2017-03-24", "weight", 0.2);
+ }
+
+ private static void removeEdgesWithWeights() {
+ List<Long> elTaskIds = new ArrayList<>();
+ EdgeLabel knows1 = schema().getEdgeLabel("knows1");
+ EdgeLabel created1 = schema().getEdgeLabel("created1");
+ ImmutableList.of(knows1, created1).forEach(edgeLabel -> {
+ elTaskIds.add(edgeLabelAPI.delete(edgeLabel.name()));
+ });
+ elTaskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityApiTest.java
new file mode 100644
index 0000000..a14b34f
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/FusiformSimilarityApiTest.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarity;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarity.Similar;
+import com.baidu.hugegraph.structure.traverser.FusiformSimilarityRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class FusiformSimilarityApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ schema().propertyKey("name").asText().ifNotExist().create();
+ schema().propertyKey("city").asText().ifNotExist().create();
+ schema().propertyKey("time").asDate().ifNotExist().create();
+
+ schema().vertexLabel("person")
+ .properties("name", "city")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+ schema().vertexLabel("book")
+ .properties("name")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("read")
+ .sourceLabel("person").targetLabel("book")
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("write")
+ .sourceLabel("person").targetLabel("book")
+ .properties("time")
+ .multiTimes()
+ .sortKeys("time")
+ .ifNotExist()
+ .create();
+
+ Vertex p1 = graph().addVertex(T.label, "person", "name", "p1",
+ "city", "Beijing");
+ Vertex p2 = graph().addVertex(T.label, "person", "name", "p2",
+ "city", "Shanghai");
+ Vertex p3 = graph().addVertex(T.label, "person", "name", "p3",
+ "city", "Beijing");
+
+ Vertex b1 = graph().addVertex(T.label, "book", "name", "b1");
+ Vertex b2 = graph().addVertex(T.label, "book", "name", "b2");
+ Vertex b3 = graph().addVertex(T.label, "book", "name", "b3");
+ Vertex b4 = graph().addVertex(T.label, "book", "name", "b4");
+ Vertex b5 = graph().addVertex(T.label, "book", "name", "b5");
+ Vertex b6 = graph().addVertex(T.label, "book", "name", "b6");
+ Vertex b7 = graph().addVertex(T.label, "book", "name", "b7");
+ Vertex b8 = graph().addVertex(T.label, "book", "name", "b8");
+ Vertex b9 = graph().addVertex(T.label, "book", "name", "b9");
+ Vertex b10 = graph().addVertex(T.label, "book", "name", "b10");
+
+ // p1 read b1-b9 (9 books)
+ p1.addEdge("read", b1);
+ p1.addEdge("read", b2);
+ p1.addEdge("read", b3);
+ p1.addEdge("read", b4);
+ p1.addEdge("read", b5);
+ p1.addEdge("read", b6);
+ p1.addEdge("read", b7);
+ p1.addEdge("read", b8);
+ p1.addEdge("read", b9);
+ // p2 read b2-b10 (9 books)
+ p2.addEdge("read", b2);
+ p2.addEdge("read", b3);
+ p2.addEdge("read", b4);
+ p2.addEdge("read", b5);
+ p2.addEdge("read", b6);
+ p2.addEdge("read", b7);
+ p2.addEdge("read", b8);
+ p2.addEdge("read", b9);
+ p2.addEdge("read", b10);
+ // p3 read b3-b9 (7 books)
+ p3.addEdge("read", b3);
+ p3.addEdge("read", b4);
+ p3.addEdge("read", b5);
+ p3.addEdge("read", b6);
+ p3.addEdge("read", b7);
+ p3.addEdge("read", b8);
+ p3.addEdge("read", b9);
+
+ p1.addEdge("write", b1, "time", "2019-11-13 00:00:00");
+ p1.addEdge("write", b1, "time", "2019-11-13 00:01:00");
+ p1.addEdge("write", b1, "time", "2019-11-13 00:02:00");
+
+ p1.addEdge("write", b2, "time", "2019-11-13 00:00:00");
+ p1.addEdge("write", b2, "time", "2019-11-13 00:01:00");
+
+ p1.addEdge("write", b3, "time", "2019-11-13 00:00:00");
+ p1.addEdge("write", b3, "time", "2019-11-13 00:01:00");
+
+ p2.addEdge("write", b1, "time", "2019-11-13 00:00:00");
+ p2.addEdge("write", b1, "time", "2019-11-13 00:01:00");
+ p2.addEdge("write", b1, "time", "2019-11-13 00:02:00");
+
+ p3.addEdge("write", b2, "time", "2019-11-13 00:00:00");
+ p3.addEdge("write", b2, "time", "2019-11-13 00:01:00");
+
+ p3.addEdge("write", b3, "time", "2019-11-13 00:00:00");
+ p3.addEdge("write", b3, "time", "2019-11-13 00:01:00");
+ p3.addEdge("write", b3, "time", "2019-11-13 00:02:00");
+ }
+
+ @Test
+ public void testFusiformSimilarity() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Object> expected = ImmutableSet.of(p2, p3);
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFusiformSimilarityLessThanMinEdgeCount() {
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(10)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(0, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(9)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p2");
+ builder.label("read").direction(Direction.OUT).minNeighbors(10)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(0, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p2");
+ builder.label("read").direction(Direction.OUT).minNeighbors(9)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p3");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(0, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p2");
+ builder.label("read").direction(Direction.OUT).minNeighbors(7)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ }
+
+ @Test
+ public void testFusiformSimilarityAlpha() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Object> expected = ImmutableSet.of(p2, p3);
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(6)
+ .alpha(0.83D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+
+ entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(1, entry.getValue().size());
+ expected = ImmutableSet.of(p2);
+ actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFusiformSimilarityMinSimilars() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).minSimilars(3).groupProperty("city")
+ .minGroups(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(0, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).minSimilars(2).groupProperty("city")
+ .minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Object> expected = ImmutableSet.of(p2, p3);
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).minSimilars(1).groupProperty("city")
+ .minGroups(2);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Assert.assertEquals(expected, actual);
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ FusiformSimilarityRequest.builder().minSimilars(0);
+ });
+ }
+
+ @Test
+ public void testFusiformSimilarityTop() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).top(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Object> expected = ImmutableSet.of(p2, p3);
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(6)
+ .alpha(0.8D).top(1);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+
+ entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(1, entry.getValue().size());
+ expected = ImmutableSet.of(p2);
+ actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testFusiformSimilarityMinGroupCount() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.7D).groupProperty("city").minGroups(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1, entry.getKey());
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Object> expected = ImmutableSet.of(p2, p3);
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected, actual);
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(6)
+ .alpha(0.8D).groupProperty("city").minGroups(3);
+ builder.capacity(-1).limit(-1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(0, results.size());
+ }
+
+ @Test
+ public void testFusiformSimilarityCapacity() {
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.8D).groupProperty("city").minGroups(2);
+ builder.capacity(10).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ Assert.assertThrows(ServerException.class, () -> {
+ fusiformSimilarityAPI.post(request);
+ }, e -> {
+ String expect = "Exceed capacity '10' while " +
+ "finding fusiform similarity";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testFusiformSimilarityLimit() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().ids(p1, p2, p3);
+ builder.label("read").direction(Direction.OUT).minNeighbors(5)
+ .alpha(0.8D).top(2);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(3, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().ids(p1, p2, p3);
+ builder.label("read").direction(Direction.OUT).minNeighbors(5)
+ .alpha(0.8D).top(2);
+ builder.capacity(-1).limit(2);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(2, results.size());
+
+ builder = FusiformSimilarityRequest.builder();
+ builder.sources().ids(p1, p2, p3);
+ builder.label("read").direction(Direction.OUT).minNeighbors(5)
+ .alpha(0.8D).top(2);
+ builder.capacity(-1).limit(1);
+ request = builder.build();
+ results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ }
+
+ @Test
+ public void testFusiformSimilarityWithMultiTimesEdges() {
+ Vertex p1 = getVertex("person", "name", "p1");
+ Vertex p2 = getVertex("person", "name", "p2");
+ Vertex p3 = getVertex("person", "name", "p3");
+
+ Object id1 = p1.id();
+ Object id2 = p2.id();
+ Object id3 = p3.id();
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().ids(id1, id2, id3);
+ builder.label("write").direction(Direction.OUT).minNeighbors(3)
+ .alpha(0.666D);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(id1, entry.getKey());
+ Assert.assertEquals(1, entry.getValue().size());
+ Set<Object> actual = entry.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(ImmutableSet.of(id3), actual);
+ }
+
+ @Test
+ public void testFusiformSimilarityWithoutEdgeLabel() {
+ Object p1 = getVertexId("person", "name", "p1");
+ Object p2 = getVertexId("person", "name", "p2");
+ Object p3 = getVertexId("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().ids(p1, p2, p3);
+ builder.direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.875D);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(3, results.size());
+ Map<Object, Set<Object>> expected = ImmutableMap.of(
+ p1, ImmutableSet.of(p2, p3),
+ p2, ImmutableSet.of(p1),
+ p3, ImmutableSet.of(p1, p2)
+ );
+
+ for (Map.Entry<Object, Set<Similar>> e : results.similarsMap()
+ .entrySet()) {
+ Object key = e.getKey();
+ Set<Object> actual = e.getValue().stream().map(Similar::id)
+ .collect(Collectors.toSet());
+ Assert.assertEquals(expected.get(key), actual);
+ }
+ }
+
+ @Test
+ public void testFusiformSimilarityWithIntermediaryAndVertex() {
+ Vertex p1 = getVertex("person", "name", "p1");
+ Vertex p2 = getVertex("person", "name", "p2");
+ Vertex p3 = getVertex("person", "name", "p3");
+
+ Vertex b2 = getVertex("book", "name", "b2");
+ Vertex b3 = getVertex("book", "name", "b3");
+ Vertex b4 = getVertex("book", "name", "b4");
+ Vertex b5 = getVertex("book", "name", "b5");
+ Vertex b6 = getVertex("book", "name", "b6");
+ Vertex b7 = getVertex("book", "name", "b7");
+ Vertex b8 = getVertex("book", "name", "b8");
+ Vertex b9 = getVertex("book", "name", "b9");
+
+ Object p1Id = p1.id();
+ Object p2Id = p2.id();
+ Object p3Id = p3.id();
+
+ Object b2Id = b2.id();
+ Object b3Id = b3.id();
+ Object b4Id = b4.id();
+ Object b5Id = b5.id();
+ Object b6Id = b6.id();
+ Object b7Id = b7.id();
+ Object b8Id = b8.id();
+ Object b9Id = b9.id();
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).groupProperty("city").minGroups(2)
+ .withIntermediary(true).withVertex(true);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1Id, entry.getKey());
+
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Similar> similars = entry.getValue();
+ Set<Object> p2Inter = ImmutableSet.of(b2Id, b3Id, b4Id, b5Id,
+ b6Id, b7Id, b8Id, b9Id);
+ Set<Object> p3Inter = ImmutableSet.of(b3Id, b4Id, b5Id, b6Id,
+ b7Id, b8Id, b9Id);
+ for (Similar similar : similars) {
+ if (similar.id().equals(p2Id)) {
+ Assert.assertEquals(p2Inter, similar.intermediaries());
+ } else {
+ Assert.assertEquals(p3Id, similar.id());
+ Assert.assertEquals(p3Inter, similar.intermediaries());
+ }
+ }
+ Set<Vertex> vertices = ImmutableSet.of(p1, p2, p3, b2, b3, b4,
+ b5, b6, b7, b8, b9);
+ Assert.assertEquals(vertices, results.vertices());
+ }
+
+ @Test
+ public void testFusiformSimilarityWithIntermediaryWithoutVertex() {
+ Vertex p1 = getVertex("person", "name", "p1");
+ Vertex p2 = getVertex("person", "name", "p2");
+ Vertex p3 = getVertex("person", "name", "p3");
+
+ Vertex b2 = getVertex("book", "name", "b2");
+ Vertex b3 = getVertex("book", "name", "b3");
+ Vertex b4 = getVertex("book", "name", "b4");
+ Vertex b5 = getVertex("book", "name", "b5");
+ Vertex b6 = getVertex("book", "name", "b6");
+ Vertex b7 = getVertex("book", "name", "b7");
+ Vertex b8 = getVertex("book", "name", "b8");
+ Vertex b9 = getVertex("book", "name", "b9");
+
+ Object p1Id = p1.id();
+ Object p2Id = p2.id();
+ Object p3Id = p3.id();
+
+ Object b2Id = b2.id();
+ Object b3Id = b3.id();
+ Object b4Id = b4.id();
+ Object b5Id = b5.id();
+ Object b6Id = b6.id();
+ Object b7Id = b7.id();
+ Object b8Id = b8.id();
+ Object b9Id = b9.id();
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).groupProperty("city").minGroups(2)
+ .withIntermediary(true).withVertex(false);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1Id, entry.getKey());
+
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Similar> similars = entry.getValue();
+ Set<Object> p2Inter = ImmutableSet.of(b2Id, b3Id, b4Id, b5Id,
+ b6Id, b7Id, b8Id, b9Id);
+ Set<Object> p3Inter = ImmutableSet.of(b3Id, b4Id, b5Id, b6Id,
+ b7Id, b8Id, b9Id);
+ for (Similar similar : similars) {
+ if (similar.id().equals(p2Id)) {
+ Assert.assertEquals(p2Inter, similar.intermediaries());
+ } else {
+ Assert.assertEquals(p3Id, similar.id());
+ Assert.assertEquals(p3Inter, similar.intermediaries());
+ }
+ }
+ Set<Vertex> vertices = ImmutableSet.of();
+ Assert.assertEquals(vertices, results.vertices());
+ }
+
+ @Test
+ public void testFusiformSimilarityWithoutIntermediaryWithVertex() {
+ Vertex p1 = getVertex("person", "name", "p1");
+ Vertex p2 = getVertex("person", "name", "p2");
+ Vertex p3 = getVertex("person", "name", "p3");
+
+ FusiformSimilarityRequest.Builder builder =
+ FusiformSimilarityRequest.builder();
+ builder.sources().label("person").property("name", "p1");
+ builder.label("read").direction(Direction.OUT).minNeighbors(8)
+ .alpha(0.75D).groupProperty("city").minGroups(2)
+ .withIntermediary(false).withVertex(true);
+ builder.capacity(-1).limit(-1);
+ FusiformSimilarityRequest request = builder.build();
+
+ FusiformSimilarity results = fusiformSimilarityAPI.post(request);
+ Assert.assertEquals(1, results.size());
+ Map.Entry<Object, Set<Similar>> entry = results.first();
+ Assert.assertEquals(p1.id(), entry.getKey());
+
+ Assert.assertEquals(2, entry.getValue().size());
+ Set<Similar> similars = entry.getValue();
+ for (Similar similar : similars) {
+ if (similar.id().equals(p2.id())) {
+ Assert.assertEquals(ImmutableSet.of(),
+ similar.intermediaries());
+ } else {
+ Assert.assertEquals(p3.id(), similar.id());
+ Assert.assertEquals(ImmutableSet.of(),
+ similar.intermediaries());
+ }
+ }
+ Set<Vertex> vertices = ImmutableSet.of(p1, p2, p3);
+ Assert.assertEquals(vertices, results.vertices());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityApiTest.java
new file mode 100644
index 0000000..ad9e64c
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/JaccardSimilarityApiTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest;
+
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableSet;
+
+public class JaccardSimilarityApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+ Vertex v4 = graph().addVertex(T.label, "node", T.id, 4);
+ Vertex v5 = graph().addVertex(T.label, "node", T.id, 5);
+ Vertex v6 = graph().addVertex(T.label, "node", T.id, 6);
+ Vertex v7 = graph().addVertex(T.label, "node", T.id, 7);
+ Vertex v8 = graph().addVertex(T.label, "node", T.id, 8);
+ Vertex v9 = graph().addVertex(T.label, "node", T.id, 9);
+ Vertex v10 = graph().addVertex(T.label, "node", T.id, 10);
+
+ v1.addEdge("link", v3);
+ v2.addEdge("link", v3);
+ v4.addEdge("link", v1);
+ v4.addEdge("link", v2);
+
+ v1.addEdge("relateTo", v5);
+ v2.addEdge("relateTo", v5);
+ v6.addEdge("relateTo", v1);
+ v6.addEdge("relateTo", v2);
+
+ v1.addEdge("link", v7);
+ v8.addEdge("link", v1);
+ v2.addEdge("link", v9);
+ v10.addEdge("link", v2);
+ }
+
+ @Test
+ public void testJaccardSimilarity() {
+ double jaccard = jaccardSimilarityAPI.get(1, 2, Direction.BOTH,
+ null, -1);
+ Assert.assertEquals(0.5D, jaccard, Double.MIN_VALUE);
+ }
+
+ @Test
+ public void testJaccardSimilarityWithDirection() {
+ double jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT,
+ null, -1);
+ Assert.assertEquals(0.5, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN,
+ null, -1);
+ Assert.assertEquals(0.5, jaccard, Double.MIN_VALUE);
+ }
+
+ @Test
+ public void testJaccardSimilarityWithLabel() {
+ double jaccard = jaccardSimilarityAPI.get(1, 2, Direction.BOTH,
+ "link", -1);
+ Assert.assertEquals(0.3333333333333333D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT,
+ "link", -1);
+ Assert.assertEquals(0.3333333333333333D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN,
+ "link", -1);
+ Assert.assertEquals(0.3333333333333333D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.BOTH,
+ "relateTo", -1);
+ Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT,
+ "relateTo", -1);
+ Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN,
+ "relateTo", -1);
+ Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE);
+ }
+
+ @Test
+ public void testJaccardSimilarityWithDegree() {
+ double jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT,
+ null, 6);
+ Assert.assertEquals(0.5D, jaccard, Double.MIN_VALUE);
+
+ jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT,
+ null, 1);
+ Assert.assertEquals(1D, jaccard, Double.MIN_VALUE);
+ }
+
+ @Test
+ public void testJaccardSimilar() {
+ SingleSourceJaccardSimilarityRequest.Builder builder =
+ SingleSourceJaccardSimilarityRequest.builder();
+ builder.vertex(4);
+ builder.step().direction(Direction.BOTH);
+ SingleSourceJaccardSimilarityRequest request = builder.build();
+ Map<Object, Double> results = jaccardSimilarityAPI.post(request);
+
+ Assert.assertEquals(9, results.size());
+ Set<Object> expected = ImmutableSet.of("1", "2", "3", "5", "6",
+ "7", "8", "9", "10");
+ Assert.assertEquals(expected, results.keySet());
+
+ Assert.assertEquals(1.0, results.get("3"));
+ Assert.assertEquals(1.0, results.get("5"));
+ Assert.assertEquals(1.0, results.get("6"));
+ Assert.assertEquals(0.5, results.get("7"));
+ Assert.assertEquals(0.5, results.get("8"));
+ Assert.assertEquals(0.5, results.get("9"));
+ Assert.assertEquals(0.5, results.get("10"));
+ Assert.assertEquals(0.0, results.get("1"));
+ Assert.assertEquals(0.0, results.get("2"));
+ }
+
+ @Test
+ public void testJaccardSimilarWithTop() {
+ SingleSourceJaccardSimilarityRequest.Builder builder =
+ SingleSourceJaccardSimilarityRequest.builder();
+ builder.vertex(4);
+ builder.step().direction(Direction.BOTH);
+ builder.top(5);
+ SingleSourceJaccardSimilarityRequest request = builder.build();
+ Map<Object, Double> results = jaccardSimilarityAPI.post(request);
+
+ Assert.assertEquals(5, results.size());
+ Set<Object> expected = ImmutableSet.of("3", "5", "6", "7", "8");
+ Assert.assertEquals(expected, results.keySet());
+
+ Assert.assertEquals(1.0, results.get("3"));
+ Assert.assertEquals(1.0, results.get("5"));
+ Assert.assertEquals(1.0, results.get("6"));
+ Assert.assertEquals(0.5, results.get("7"));
+ Assert.assertEquals(0.5, results.get("8"));
+ }
+
+ @Test
+ public void testJaccardSimilarWithLabel() {
+ SingleSourceJaccardSimilarityRequest.Builder builder =
+ SingleSourceJaccardSimilarityRequest.builder();
+ builder.vertex(4);
+ builder.step().direction(Direction.BOTH).labels("link");
+ SingleSourceJaccardSimilarityRequest request = builder.build();
+ Map<Object, Double> results = jaccardSimilarityAPI.post(request);
+
+ Assert.assertEquals(7, results.size());
+ Set<Object> expected = ImmutableSet.of("3", "7", "8", "9",
+ "10", "1", "2");
+ Assert.assertEquals(expected, results.keySet());
+
+ Assert.assertEquals(1.0, results.get("3"));
+ Assert.assertEquals(0.5, results.get("7"));
+ Assert.assertEquals(0.5, results.get("8"));
+ Assert.assertEquals(0.5, results.get("9"));
+ Assert.assertEquals(0.5, results.get("10"));
+ Assert.assertEquals(0.0, results.get("1"));
+ Assert.assertEquals(0.0, results.get("2"));
+ }
+
+ @Test
+ public void testJaccardSimilarWithDirection() {
+ SingleSourceJaccardSimilarityRequest.Builder builder =
+ SingleSourceJaccardSimilarityRequest.builder();
+ builder.vertex(4);
+ builder.step().direction(Direction.OUT);
+ SingleSourceJaccardSimilarityRequest request = builder.build();
+ Map<Object, Double> results = jaccardSimilarityAPI.post(request);
+
+ Assert.assertEquals(6, results.size());
+ Set<Object> expected = ImmutableSet.of("1", "2", "3",
+ "5", "7", "9");
+ Assert.assertEquals(expected, results.keySet());
+
+ Assert.assertEquals(0.0, results.get("3"));
+ Assert.assertEquals(0.0, results.get("5"));
+ Assert.assertEquals(0.0, results.get("7"));
+ Assert.assertEquals(0.0, results.get("9"));
+ Assert.assertEquals(0.0, results.get("1"));
+ Assert.assertEquals(0.0, results.get("2"));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KneighborApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KneighborApiTest.java
new file mode 100644
index 0000000..e87aeca
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KneighborApiTest.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.Kneighbor;
+import com.baidu.hugegraph.structure.traverser.KneighborRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class KneighborApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testKneighborGet() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ long personId = vertexLabelAPI.get("person").id();
+ long softwareId = vertexLabelAPI.get("software").id();
+
+ List<Object> vertices = kneighborAPI.get(markoId, Direction.OUT,
+ null, 2, -1L, -1L);
+ Assert.assertEquals(4, vertices.size());
+ Assert.assertTrue(vertices.contains(softwareId + ":lop"));
+ Assert.assertTrue(vertices.contains(softwareId + ":ripple"));
+ Assert.assertTrue(vertices.contains(personId + ":vadas"));
+ Assert.assertTrue(vertices.contains(personId + ":josh"));
+ }
+
+ @Test
+ public void testKneighborPost() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId, peterId, rippleId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ }
+
+ @Test
+ public void testKneighborPostWithPath() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(true);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(3, kneighborResult.paths().size());
+ List<Object> expectedPaths = ImmutableList.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId)
+ );
+ for (Path path : kneighborResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(true);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, peterId, joshId, lopId, rippleId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(5, kneighborResult.paths().size());
+ expectedPaths = ImmutableList.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (Path path : kneighborResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testKneighborPostWithVertex() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(false);
+ builder.withVertex(true);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(3, kneighborResult.vertices().size());
+ Set<Object> expectedVids = ImmutableSet.of(vadasId, lopId, joshId);
+ for (Vertex vertex : kneighborResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(false);
+ builder.withVertex(true);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId, peterId, rippleId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(5, kneighborResult.vertices().size());
+ expectedVids = ImmutableSet.of(vadasId, lopId, joshId,
+ peterId, rippleId);
+ for (Vertex vertex : kneighborResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(true);
+ builder.withVertex(true);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(3, kneighborResult.paths().size());
+ Set<List<Object>> expectedPaths = ImmutableSet.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId)
+ );
+ for (Path path : kneighborResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ Assert.assertEquals(4, kneighborResult.vertices().size());
+ expectedVids = ImmutableSet.of(markoId, vadasId, lopId, joshId);
+ for (Vertex vertex : kneighborResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(true);
+ builder.withVertex(true);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ expected = ImmutableSet.of(peterId, lopId, joshId, rippleId, vadasId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ Assert.assertEquals(5, kneighborResult.paths().size());
+ expectedPaths = ImmutableSet.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (Path path : kneighborResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ Assert.assertEquals(6, kneighborResult.vertices().size());
+ expectedVids = ImmutableSet.of(markoId, peterId, lopId,
+ joshId, rippleId, vadasId);
+ for (Vertex vertex : kneighborResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+ }
+
+ @Test
+ public void testKneighborPostWithLabel() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("created");
+ builder.maxDepth(1);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(1, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(lopId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("created");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ expected = ImmutableSet.of(lopId, peterId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("knows");
+ builder.maxDepth(1);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(2, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("knows");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(2, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ }
+
+ @Test
+ public void testKneighborPostWithDirection() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.OUT);
+ builder.maxDepth(1);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.OUT);
+ builder.maxDepth(2);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(4, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId, rippleId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ }
+
+ @Test
+ public void testKneighborPostWithProperties() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(1);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(1, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(lopId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ expected = ImmutableSet.of(lopId, peterId, joshId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(3);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(4, kneighborResult.size());
+ expected = ImmutableSet.of(lopId, peterId, joshId, rippleId);
+ Assert.assertEquals(expected, kneighborResult.ids());
+ }
+
+ @Test
+ public void testKneighborPostWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ // 1 depth with in&out edges
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.limit(3);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertTrue(expected.containsAll(kneighborResult.ids()));
+
+ // 2 depth with out edges
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.OUT);
+ builder.maxDepth(2);
+ builder.limit(5);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(4, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId, rippleId);
+ Assert.assertTrue(expected.containsAll(kneighborResult.ids()));
+
+ // 2 depth with in&out edges
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.limit(5);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId, peterId, rippleId);
+ Assert.assertTrue(expected.containsAll(kneighborResult.ids()));
+ }
+
+ @Test
+ public void testKneighborPostWithCountOnly() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ KneighborRequest.Builder builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ KneighborRequest request = builder.build();
+
+ Kneighbor kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(3, kneighborResult.size());
+ Assert.assertTrue(kneighborResult.ids().isEmpty());
+ Assert.assertTrue(kneighborResult.paths().isEmpty());
+ Assert.assertTrue(kneighborResult.vertices().isEmpty());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.countOnly(true);
+ request = builder.build();
+
+ kneighborResult = kneighborAPI.post(request);
+
+ Assert.assertEquals(5, kneighborResult.size());
+ Assert.assertTrue(kneighborResult.ids().isEmpty());
+ Assert.assertTrue(kneighborResult.paths().isEmpty());
+ Assert.assertTrue(kneighborResult.vertices().isEmpty());
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ builder.withPath(true);
+
+ KneighborRequest.Builder finalBuilder = builder;
+ Assert.assertThrows(IllegalArgumentException.class, ()-> {
+ finalBuilder.build();
+ });
+
+ builder = KneighborRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ builder.withVertex(true);
+
+ KneighborRequest.Builder finalBuilder1 = builder;
+ Assert.assertThrows(IllegalArgumentException.class, ()-> {
+ finalBuilder1.build();
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KoutApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KoutApiTest.java
new file mode 100644
index 0000000..3a8cef0
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/KoutApiTest.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.Kout;
+import com.baidu.hugegraph.structure.traverser.KoutRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class KoutApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testKoutGetNearest() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ long softwareId = vertexLabelAPI.get("software").id();
+
+ List<Object> vertices = koutAPI.get(markoId, Direction.OUT,
+ null, 2, true, -1L, -1L, -1L);
+ Assert.assertEquals(1, vertices.size());
+ Assert.assertTrue(vertices.contains(softwareId + ":ripple"));
+ }
+
+ @Test
+ public void testKoutGetAll() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ long softwareId = vertexLabelAPI.get("software").id();
+
+ List<Object> vertices = koutAPI.get(markoId, Direction.OUT, null,
+ 2, false, -1L, -1L, -1L);
+ Assert.assertEquals(2, vertices.size());
+ Assert.assertTrue(vertices.contains(softwareId + ":lop"));
+ Assert.assertTrue(vertices.contains(softwareId + ":ripple"));
+ }
+
+ @Test
+ public void testKoutGetBothNearest() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ long personId = vertexLabelAPI.get("person").id();
+ long softwareId = vertexLabelAPI.get("software").id();
+
+ List<Object> vertices = koutAPI.get(markoId, Direction.BOTH,
+ null, 2, true, -1L, -1L, -1L);
+ Assert.assertEquals(2, vertices.size());
+ Assert.assertTrue(vertices.contains(personId + ":peter"));
+ Assert.assertTrue(vertices.contains(softwareId + ":ripple"));
+ }
+
+ @Test
+ public void testKoutGetBothAll() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ long personId = vertexLabelAPI.get("person").id();
+ long softwareId = vertexLabelAPI.get("software").id();
+
+ List<Object> vertices = koutAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, -1L, -1L);
+ Assert.assertEquals(4, vertices.size());
+ Assert.assertTrue(vertices.contains(personId + ":josh"));
+ Assert.assertTrue(vertices.contains(personId + ":peter"));
+ Assert.assertTrue(vertices.contains(softwareId + ":lop"));
+ Assert.assertTrue(vertices.contains(softwareId + ":ripple"));
+ }
+
+ @Test
+ public void testKoutGetBothAllWithCapacity() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ koutAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, 1L, 2L);
+ }, e -> {
+ String expect = "Capacity can't be less than limit, " +
+ "but got capacity '1' and limit '2'";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testKoutGetBothAllWithCapacityNoLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ koutAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, 1L, -1L);
+ }, e -> {
+ String expect = "Capacity can't be less than limit, " +
+ "but got capacity '1' and limit '-1'";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testKoutPost() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ }
+
+ @Test
+ public void testKoutPostWithNearest() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.nearest(false);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.nearest(false);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(4, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+ }
+
+ @Test
+ public void testKoutPostWithPath() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(true);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(3, koutResult.paths().size());
+ List<Object> expectedPaths = ImmutableList.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId)
+ );
+ for (Path path : koutResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(true);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(2, koutResult.paths().size());
+ expectedPaths = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (Path path : koutResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testKoutPostWithVertex() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(false);
+ builder.withVertex(true);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(3, koutResult.vertices().size());
+ Set<Object> expectedVids = ImmutableSet.of(vadasId, lopId, joshId);
+ for (Vertex vertex : koutResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(false);
+ builder.withVertex(true);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(2, koutResult.vertices().size());
+ expectedVids = ImmutableSet.of(peterId, rippleId);
+ for (Vertex vertex : koutResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.withPath(true);
+ builder.withVertex(true);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(3, koutResult.paths().size());
+ Set<List<Object>> expectedPaths = ImmutableSet.of(
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, joshId)
+ );
+ for (Path path : koutResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ Assert.assertEquals(4, koutResult.vertices().size());
+ expectedVids = ImmutableSet.of(markoId, vadasId, lopId, joshId);
+ for (Vertex vertex : koutResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.withPath(true);
+ builder.withVertex(true);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ Assert.assertEquals(2, koutResult.paths().size());
+ expectedPaths = ImmutableSet.of(
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (Path path : koutResult.paths()) {
+ Assert.assertTrue(expectedPaths.contains(path.objects()));
+ }
+ Assert.assertEquals(5, koutResult.vertices().size());
+ expectedVids = ImmutableSet.of(markoId, peterId, lopId,
+ joshId, rippleId);
+ for (Vertex vertex : koutResult.vertices()) {
+ Assert.assertTrue(expectedVids.contains(vertex.id()));
+ }
+ }
+
+ @Test
+ public void testKoutPostWithSingleLabel() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("created");
+ builder.maxDepth(1);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(1, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(lopId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("created");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("knows");
+ builder.maxDepth(1);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(vadasId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH).labels("knows");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(0, koutResult.size());
+ }
+
+ @Test
+ public void testKoutPostWithMultiLabels() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .labels("knows").labels("created");
+ builder.maxDepth(1);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, joshId, lopId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .labels("knows").labels("created");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ }
+
+ @Test
+ public void testKoutPostWithDirection() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.OUT);
+ builder.maxDepth(1);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.OUT);
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(1, koutResult.size());
+ expected = ImmutableSet.of(rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ }
+
+ @Test
+ public void testKoutPostWithProperties() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(1);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(1, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(lopId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(2);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ expected = ImmutableSet.of(peterId, joshId);
+ Assert.assertEquals(expected, koutResult.ids());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(3);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(1, koutResult.size());
+ expected = ImmutableSet.of(rippleId);
+ Assert.assertEquals(expected, koutResult.ids());
+ }
+
+ @Test
+ public void testKoutPostWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.limit(2);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ Set<Object> expected = ImmutableSet.of(vadasId, lopId, joshId);
+ Assert.assertTrue(expected.containsAll(koutResult.ids()));
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.limit(1);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(1, koutResult.size());
+ expected = ImmutableSet.of(peterId, rippleId);
+ Assert.assertTrue(expected.containsAll(koutResult.ids()));
+ }
+
+ @Test
+ public void testKoutPostWithCountOnly() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ KoutRequest.Builder builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ KoutRequest request = builder.build();
+
+ Kout koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(3, koutResult.size());
+ Assert.assertTrue(koutResult.ids().isEmpty());
+ Assert.assertTrue(koutResult.paths().isEmpty());
+ Assert.assertTrue(koutResult.vertices().isEmpty());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+ builder.countOnly(true);
+ request = builder.build();
+
+ koutResult = koutAPI.post(request);
+
+ Assert.assertEquals(2, koutResult.size());
+ Assert.assertTrue(koutResult.ids().isEmpty());
+ Assert.assertTrue(koutResult.paths().isEmpty());
+ Assert.assertTrue(koutResult.vertices().isEmpty());
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ builder.withPath(true);
+
+ KoutRequest.Builder finalBuilder = builder;
+ Assert.assertThrows(IllegalArgumentException.class, ()-> {
+ finalBuilder.build();
+ });
+
+ builder = KoutRequest.builder();
+ builder.source(markoId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+ builder.countOnly(true);
+ builder.withVertex(true);
+
+ KoutRequest.Builder finalBuilder1 = builder;
+ Assert.assertThrows(IllegalArgumentException.class, ()-> {
+ finalBuilder1.build();
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java
new file mode 100644
index 0000000..2c8fec0
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.MultiNodeShortestPathRequest;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class MultiNodeShortestPathApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testMultiNodeShortestPath() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ MultiNodeShortestPathRequest.Builder builder =
+ MultiNodeShortestPathRequest.builder();
+ builder.vertices().ids(markoId, rippleId, joshId,
+ lopId, vadasId, peterId);
+ builder.step().direction(Direction.BOTH);
+
+ MultiNodeShortestPathRequest request = builder.build();
+ PathsWithVertices pathsWithVertices =
+ multiNodeShortestPathAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(15, paths.size());
+
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(peterId, lopId),
+ ImmutableList.of(peterId, lopId, markoId, vadasId),
+ ImmutableList.of(peterId, lopId, joshId),
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(vadasId, markoId, lopId),
+ ImmutableList.of(vadasId, markoId, joshId),
+ ImmutableList.of(peterId, lopId, joshId, rippleId),
+ ImmutableList.of(lopId, joshId, rippleId),
+ ImmutableList.of(lopId, joshId),
+ ImmutableList.of(rippleId, joshId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId),
+ ImmutableList.of(vadasId, markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(contains(expected, path));
+ }
+ }
+
+ @Test
+ public void testMultiNodeShortestPathWithMaxDepth() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ MultiNodeShortestPathRequest.Builder builder =
+ MultiNodeShortestPathRequest.builder();
+ builder.vertices().ids(markoId, rippleId, joshId,
+ lopId, vadasId, peterId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(2);
+
+ MultiNodeShortestPathRequest request = builder.build();
+ PathsWithVertices pathsWithVertices =
+ multiNodeShortestPathAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(12, paths.size());
+
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(peterId, lopId),
+ ImmutableList.of(peterId, lopId, joshId),
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(vadasId, markoId, lopId),
+ ImmutableList.of(vadasId, markoId, joshId),
+ ImmutableList.of(lopId, joshId, rippleId),
+ ImmutableList.of(lopId, joshId),
+ ImmutableList.of(rippleId, joshId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(contains(expected, path));
+ }
+
+ builder = MultiNodeShortestPathRequest.builder();
+ builder.vertices().ids(markoId, rippleId, joshId,
+ lopId, vadasId, peterId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(1);
+
+ request = builder.build();
+ pathsWithVertices = multiNodeShortestPathAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(6, paths.size());
+
+ expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(peterId, lopId),
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(lopId, joshId),
+ ImmutableList.of(rippleId, joshId),
+ ImmutableList.of(markoId, lopId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(contains(expected, path));
+ }
+ }
+
+ @Test
+ public void testMultiNodeShortestPathWithVertex() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+
+ MultiNodeShortestPathRequest.Builder builder =
+ MultiNodeShortestPathRequest.builder();
+ builder.vertices().ids(markoId, rippleId, joshId,
+ lopId, vadasId, peterId);
+ builder.step().direction(Direction.BOTH);
+ builder.withVertex(true);
+
+ MultiNodeShortestPathRequest request = builder.build();
+ PathsWithVertices pathsWithVertices =
+ multiNodeShortestPathAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(15, paths.size());
+
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId),
+ ImmutableList.of(peterId, lopId),
+ ImmutableList.of(peterId, lopId, markoId, vadasId),
+ ImmutableList.of(peterId, lopId, joshId),
+ ImmutableList.of(markoId, vadasId),
+ ImmutableList.of(vadasId, markoId, lopId),
+ ImmutableList.of(vadasId, markoId, joshId),
+ ImmutableList.of(peterId, lopId, joshId, rippleId),
+ ImmutableList.of(lopId, joshId, rippleId),
+ ImmutableList.of(lopId, joshId),
+ ImmutableList.of(rippleId, joshId),
+ ImmutableList.of(markoId, lopId),
+ ImmutableList.of(markoId, lopId, peterId),
+ ImmutableList.of(markoId, joshId, rippleId),
+ ImmutableList.of(vadasId, markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(contains(expected, path));
+ }
+
+ Set<Vertex> vertices = pathsWithVertices.vertices();
+ Assert.assertEquals(6, vertices.size());
+ Set<Object> vertexIds = ImmutableSet.of(markoId, rippleId, joshId,
+ lopId, vadasId, peterId);
+ for (Vertex vertex : vertices) {
+ Assert.assertTrue(vertexIds.contains(vertex.id()));
+ }
+ }
+
+ private static boolean contains(List<List<Object>> expected,
+ PathsWithVertices.Paths path) {
+ List<Object> objects = path.objects();
+ if (expected.contains(objects)) {
+ return true;
+ }
+ Collections.reverse(objects);
+ return expected.contains(objects);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/NeighborRankApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/NeighborRankApiTest.java
new file mode 100644
index 0000000..540219a
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/NeighborRankApiTest.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.Ranks;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableMap;
+
+public class NeighborRankApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initNeighborRankGraph() {
+ GraphManager graph = graph();
+ SchemaManager schema = schema();
+
+ schema.propertyKey("name").asText().ifNotExist().create();
+
+ schema.vertexLabel("person")
+ .properties("name")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("movie")
+ .properties("name")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("follow")
+ .sourceLabel("person")
+ .targetLabel("person")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("like")
+ .sourceLabel("person")
+ .targetLabel("movie")
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("directedBy")
+ .sourceLabel("movie")
+ .targetLabel("person")
+ .ifNotExist()
+ .create();
+
+ Vertex O = graph.addVertex(T.label, "person", T.id, "O", "name", "O");
+
+ Vertex A = graph.addVertex(T.label, "person", T.id, "A", "name", "A");
+ Vertex B = graph.addVertex(T.label, "person", T.id, "B", "name", "B");
+ Vertex C = graph.addVertex(T.label, "person", T.id, "C", "name", "C");
+ Vertex D = graph.addVertex(T.label, "person", T.id, "D", "name", "D");
+
+ Vertex E = graph.addVertex(T.label, "movie", T.id, "E", "name", "E");
+ Vertex F = graph.addVertex(T.label, "movie", T.id, "F", "name", "F");
+ Vertex G = graph.addVertex(T.label, "movie", T.id, "G", "name", "G");
+ Vertex H = graph.addVertex(T.label, "movie", T.id, "H", "name", "H");
+ Vertex I = graph.addVertex(T.label, "movie", T.id, "I", "name", "I");
+ Vertex J = graph.addVertex(T.label, "movie", T.id, "J", "name", "J");
+
+ Vertex K = graph.addVertex(T.label, "person", T.id, "K", "name", "K");
+ Vertex L = graph.addVertex(T.label, "person", T.id, "L", "name", "L");
+ Vertex M = graph.addVertex(T.label, "person", T.id, "M", "name", "M");
+
+ O.addEdge("follow", A);
+ O.addEdge("follow", B);
+ O.addEdge("follow", C);
+ D.addEdge("follow", O);
+
+ A.addEdge("follow", B);
+ A.addEdge("like", E);
+ A.addEdge("like", F);
+
+ B.addEdge("like", G);
+ B.addEdge("like", H);
+
+ C.addEdge("like", I);
+ C.addEdge("like", J);
+
+ E.addEdge("directedBy", K);
+ F.addEdge("directedBy", B);
+ F.addEdge("directedBy", L);
+
+ G.addEdge("directedBy", M);
+ }
+
+ @AfterClass
+ public static void clearNeighborRankGraph() {
+ List<Long> taskIds = new ArrayList<>();
+ taskIds.add(edgeLabelAPI.delete("directedBy"));
+ taskIds.add(edgeLabelAPI.delete("like"));
+ taskIds.add(edgeLabelAPI.delete("follow"));
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+ taskIds.clear();
+ taskIds.add(vertexLabelAPI.delete("movie"));
+ taskIds.add(vertexLabelAPI.delete("person"));
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+ }
+
+ @Test
+ public void testNeighborRank() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.alpha(0.9).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.4305D, "A", 0.3D, "C", 0.3D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.builder()
+ .put("G", 0.17550000000000002D)
+ .put("H", 0.17550000000000002D)
+ .put("I", 0.135D)
+ .put("J", 0.135D)
+ .put("E", 0.09000000000000001D)
+ .put("F", 0.09000000000000001D)
+ .build(),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("M", 0.15795D,
+ "K", 0.08100000000000002D,
+ "L", 0.04050000000000001D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithOtherAlpha() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.steps().direction(Direction.OUT).degree(-1).top(10);
+ builder.alpha(1.0).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.5D,
+ "A", 0.3333333333333333D,
+ "C", 0.3333333333333333D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.builder()
+ .put("G", 0.2222222222222222D)
+ .put("H", 0.2222222222222222D)
+ .put("I", 0.16666666666666666D)
+ .put("J", 0.16666666666666666D)
+ .put("E", 0.1111111111111111D)
+ .put("F", 0.1111111111111111D)
+ .build(),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("M", 0.2222222222222222D,
+ "K", 0.1111111111111111D,
+ "L", 0.05555555555555555D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithDirection() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.BOTH);
+ builder.steps().direction(Direction.IN);
+ builder.steps().direction(Direction.OUT);
+ builder.alpha(0.9).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("A", 0.32625000000000004D,
+ "B", 0.27056250000000004D,
+ "C", 0.225D,
+ "D", 0.225D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.of("F", 0.10125D),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("L", 0.045562500000000006D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithLabels() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().labels("follow").direction(Direction.OUT);
+ builder.steps().labels("like").direction(Direction.OUT);
+ builder.steps().labels("directedBy").direction(Direction.OUT);
+ builder.alpha(0.9).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.36075D,
+ "A", 0.3D,
+ "C", 0.3D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.builder()
+ .put("E", 0.135)
+ .put("F", 0.135)
+ .put("G", 0.135)
+ .put("H", 0.135)
+ .put("I", 0.135)
+ .put("J", 0.135)
+ .build(),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("K", 0.12150000000000001D,
+ "M", 0.12150000000000001D,
+ "L", 0.060750000000000005D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithTop() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT).degree(-1).top(2);
+ builder.steps().direction(Direction.OUT).degree(-1).top(3);
+ builder.steps().direction(Direction.OUT).degree(-1).top(2);
+ builder.alpha(0.9).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.4305D, "A", 0.3D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.of("G", 0.17550000000000002D,
+ "H", 0.17550000000000002D,
+ "I", 0.135D),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("M", 0.15795D,
+ "K", 0.08100000000000002D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithDegree() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT).degree(2);
+ builder.steps().direction(Direction.OUT).degree(1);
+ builder.steps().direction(Direction.OUT).degree(1);
+ builder.alpha(0.9).capacity(-1);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.855D, "A", 0.45D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.of("G", 0.7695D), ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("M", 0.69255D), ranks.get(3));
+
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT).degree(2);
+ builder.steps().direction(Direction.OUT).degree(2);
+ builder.steps().direction(Direction.OUT).degree(1);
+ builder.alpha(0.9).capacity(-1);
+ request = builder.build();
+
+ ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(4, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("O", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of("B", 0.6525000000000001D,
+ "A", 0.45D),
+ ranks.get(1));
+ Assert.assertEquals(ImmutableMap.of("G", 0.293625D,
+ "H", 0.293625D,
+ "E", 0.2025D),
+ ranks.get(2));
+ Assert.assertEquals(ImmutableMap.of("M", 0.2642625D,
+ "K", 0.18225000000000002D),
+ ranks.get(3));
+ }
+
+ @Test
+ public void testNeighborRankWithCapacity() {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("O");
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.steps().direction(Direction.OUT);
+ builder.alpha(0.9).capacity(1);
+ NeighborRankAPI.Request request = builder.build();
+
+ Assert.assertThrows(ServerException.class, () -> {
+ neighborRankAPI.post(request);
+ }, e -> {
+ String expect = "Exceed capacity '1' while finding neighbor rank";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testNeighborRankWithIsolatedVertex() {
+ Vertex isolate = graph().addVertex(T.label, "person", T.id, "isolate",
+ "name", "isolate-vertex");
+
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("isolate").alpha(0.9);
+ builder.steps().direction(Direction.BOTH);
+ NeighborRankAPI.Request request = builder.build();
+
+ List<Ranks> ranks = neighborRankAPI.post(request);
+ Assert.assertEquals(2, ranks.size());
+ Assert.assertEquals(ImmutableMap.of("isolate", 1.0D), ranks.get(0));
+ Assert.assertEquals(ImmutableMap.of(), ranks.get(1));
+
+ graph().removeVertex(isolate.id());
+ }
+
+ @Test
+ public void testNeighborRankWithInvalidParams() {
+ // Invalid source
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source(null);
+ });
+
+ // Invalid degree
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.steps().degree(-2);
+ });
+
+ // Invalid top
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.steps().top(0);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.steps().top(1001);
+ });
+
+ // Invalid alpha
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.alpha(0.0);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.alpha(1.1);
+ });
+
+ // Invalid capacity
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.capacity(-2);
+ });
+
+ // Without steps
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ NeighborRankAPI.Request.Builder builder;
+ builder = NeighborRankAPI.Request.builder();
+ builder.source("A");
+ builder.build();
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PathsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PathsApiTest.java
new file mode 100644
index 0000000..e281a1c
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PathsApiTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.PathsRequest;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class PathsApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+ }
+
+ @Test
+ public void testPathsGet() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Path> paths = pathsAPI.get(markoId, rippleId, Direction.BOTH,
+ null, 3, -1L, -1L, 10);
+ Assert.assertEquals(2, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path2 = ImmutableList.of(markoId, lopId, joshId, rippleId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ }
+
+ @Test
+ public void testPathsGetWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Path> paths = pathsAPI.get(markoId, rippleId, Direction.BOTH,
+ null, 3, -1L, -1L, 1);
+ Assert.assertEquals(1, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, joshId, rippleId);
+ Assert.assertEquals(path1, paths.get(0).objects());
+ }
+
+ @Test
+ public void testPathsGetWithCapacity() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ pathsAPI.get(markoId, rippleId, Direction.BOTH,
+ null, 3, -1L, 2L, 1);
+ }, e -> {
+ String expect = "Exceed capacity '2' while finding paths";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testPathsPost() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(3);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testPathsPostWithVertex() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(3);
+ builder.withVertex(true);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<Object> expectedIds = ImmutableList.of(markoId, lopId,
+ joshId, rippleId);
+ List<List<Object>> expected = ImmutableList.of(
+ expectedIds,
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ Set<Vertex> vertices = pathsWithVertices.vertices();
+ Assert.assertEquals(4, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertTrue(expectedIds.contains(v.id()));
+ }
+ }
+
+ @Test
+ public void testPathsPostWithLabel() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(3);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH).labels("created");
+ builder.maxDepth(3);
+ request = builder.build();
+ pathsWithVertices = pathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testPathsPostWithNearest() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+
+ Vertex tom = graph().addVertex(T.label, "person", "name", "Tom",
+ "age", 29, "city", "Shanghai");
+ Vertex jim = graph().addVertex(T.label, "person", "name", "Jim",
+ "age", 29, "city", "Shanghai");
+ Vertex java = graph().addVertex(T.label, "software", "name", "java",
+ "lang", "java", "price", 199);
+ Object tomId = tom.id();
+ Object jimId = jim.id();
+ Object javaId = java.id();
+ graph().addEdge(tomId, "created", rippleId,
+ "date", "2016-01-10", "city", "Beijing");
+ graph().addEdge(tomId, "created", javaId,
+ "date", "2017-01-10", "city", "Hongkong");
+ graph().addEdge(jimId, "created", javaId,
+ "date", "2017-01-10", "city", "Hongkong");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(jimId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(6);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId,
+ rippleId, tomId, javaId, jimId),
+ ImmutableList.of(markoId, joshId, rippleId,
+ tomId, javaId, jimId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(jimId);
+ builder.step().direction(Direction.BOTH);
+ builder.nearest(true);
+ builder.maxDepth(6);
+ request = builder.build();
+ pathsWithVertices = pathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId, rippleId,
+ tomId, javaId, jimId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testPathsPostWithProperties() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(3);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH)
+ .properties("date", "P.gt(\"2014-01-01 00:00:00\")");
+ builder.maxDepth(3);
+ request = builder.build();
+ pathsWithVertices = pathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testPathsWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object rippleId = getVertexId("software", "name", "ripple");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ PathsRequest.Builder builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.maxDepth(3);
+ PathsRequest request = builder.build();
+
+ PathsWithVertices pathsWithVertices = pathsAPI.post(request);
+
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(markoId, lopId, joshId, rippleId),
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = PathsRequest.builder();
+ builder.sources().ids(markoId);
+ builder.targets().ids(rippleId);
+ builder.step().direction(Direction.BOTH);
+ builder.limit(1);
+ builder.maxDepth(3);
+ request = builder.build();
+ pathsWithVertices = pathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(markoId, joshId, rippleId)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PersonalRankApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PersonalRankApiTest.java
new file mode 100644
index 0000000..c9596e8
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/PersonalRankApiTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableMap;
+
+public class PersonalRankApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initPersonalRankGraph() {
+ GraphManager graph = graph();
+ SchemaManager schema = schema();
+
+ schema.propertyKey("name").asText().ifNotExist().create();
+
+ schema.vertexLabel("person")
+ .properties("name")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema.vertexLabel("movie")
+ .properties("name")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema.edgeLabel("like")
+ .sourceLabel("person")
+ .targetLabel("movie")
+ .ifNotExist()
+ .create();
+
+ Vertex A = graph.addVertex(T.label, "person", T.id, "A", "name", "A");
+ Vertex B = graph.addVertex(T.label, "person", T.id, "B", "name", "B");
+ Vertex C = graph.addVertex(T.label, "person", T.id, "C", "name", "C");
+
+ Vertex a = graph.addVertex(T.label, "movie", T.id, "a", "name", "a");
+ Vertex b = graph.addVertex(T.label, "movie", T.id, "b", "name", "b");
+ Vertex c = graph.addVertex(T.label, "movie", T.id, "c", "name", "c");
+ Vertex d = graph.addVertex(T.label, "movie", T.id, "d", "name", "d");
+
+ A.addEdge("like", a);
+ A.addEdge("like", c);
+
+ B.addEdge("like", a);
+ B.addEdge("like", b);
+ B.addEdge("like", c);
+ B.addEdge("like", d);
+
+ C.addEdge("like", c);
+ C.addEdge("like", d);
+ }
+
+ @AfterClass
+ public static void clearPersonalRankGraph() {
+ List<Long> taskIds = new ArrayList<>();
+ taskIds.add(edgeLabelAPI.delete("like"));
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+ taskIds.clear();
+ taskIds.add(vertexLabelAPI.delete("movie"));
+ taskIds.add(vertexLabelAPI.delete("person"));
+ taskIds.forEach(BaseApiTest::waitUntilTaskCompleted);
+ }
+
+ @Test
+ public void testPersonalRank() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).maxDepth(50);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "B", 0.2065750574989044D,
+ "C", 0.09839507219265439D,
+ "d", 0.08959757100230095D,
+ "b", 0.04589958822642998D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithWithLabel() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).maxDepth(50)
+ .withLabel(PersonalRankAPI.Request.WithLabel.SAME_LABEL);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "B", 0.2065750574989044D,
+ "C", 0.09839507219265439D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).maxDepth(50)
+ .withLabel(PersonalRankAPI.Request.WithLabel.OTHER_LABEL);
+ request = builder.build();
+
+ ranks = personalRankAPI.post(request);
+ expectedRanks = ImmutableMap.of(
+ "d", 0.08959757100230095D,
+ "b", 0.04589958822642998D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithOtherAlpha() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(1).maxDepth(50);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "B", 0.5D,
+ "C", 0.24999999999999956D,
+ "b", 0.0D,
+ "d", 0.0D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithDegree() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+
+ builder.source("A").label("like").alpha(0.9).degree(1).maxDepth(2);
+ PersonalRankAPI.Request request = builder.build();
+
+ // Removed root and direct neighbors of root
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ assertDoublesEquals(ImmutableMap.of(), ranks);
+
+ builder.source("A").label("like").alpha(0.9).degree(1).maxDepth(3);
+ request = builder.build();
+
+ ranks = personalRankAPI.post(request);
+ assertDoublesEquals(ImmutableMap.of(), ranks);
+
+ builder.source("A").label("like").alpha(0.9).degree(2).maxDepth(2);
+ request = builder.build();
+
+ ranks = personalRankAPI.post(request);
+ assertDoublesEquals(ImmutableMap.of("B", 0.405D), ranks);
+
+ builder.source("A").label("like").alpha(0.9).degree(2).maxDepth(3);
+ request = builder.build();
+
+ ranks = personalRankAPI.post(request);
+ Assert.assertEquals(2, ranks.size());
+ }
+
+ @Test
+ public void testPersonalRankWithLimit() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).limit(3).maxDepth(50);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "B", 0.2065750574989044D,
+ "C", 0.09839507219265439D,
+ "d", 0.08959757100230095D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithMaxDepth() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).maxDepth(20);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "B", 0.23414889646372697D,
+ "C", 0.11218194186115384D,
+ "d", 0.07581065434649958D,
+ "b", 0.03900612828909826D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithUnsorted() {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("A").label("like").alpha(0.9).maxDepth(50).sorted(false);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ Map<Object, Double> expectedRanks = ImmutableMap.of(
+ "b", 0.04589958822642998D,
+ "B", 0.2065750574989044D,
+ "C", 0.09839507219265439D,
+ "d", 0.08959757100230095D
+ );
+ assertDoublesEquals(expectedRanks, ranks);
+ }
+
+ @Test
+ public void testPersonalRankWithIsolatedVertex() {
+ Vertex isolate = graph().addVertex(T.label, "person", T.id, "isolate",
+ "name", "isolate-vertex");
+
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source("isolate").label("like").alpha(0.9).maxDepth(50);
+ PersonalRankAPI.Request request = builder.build();
+
+ Map<Object, Double> ranks = personalRankAPI.post(request);
+ assertDoublesEquals(ImmutableMap.of(), ranks);
+
+ graph().removeVertex(isolate.id());
+ }
+
+ @Test
+ public void testPersonalRankWithInvalidParams() {
+ // Invalid source
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.source(null);
+ });
+
+ // Invalid label
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.label(null);
+ });
+
+ // Invalid alpha
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.alpha(0.0);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.alpha(1.1);
+ });
+
+ // Invalid degree
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.degree(-2);
+ });
+
+ // Invalid limit
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.limit(-2);
+ });
+
+ // Invalid maxDepth
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.maxDepth(0);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ PersonalRankAPI.Request.Builder builder;
+ builder = PersonalRankAPI.Request.builder();
+ builder.maxDepth(10000);
+ });
+ }
+
+ private static void assertDoublesEquals(Map<Object, Double> expects,
+ Map<Object, Double> actuals) {
+ Assert.assertEquals(expects.size(), actuals.size());
+ Assert.assertTrue(expects.keySet().containsAll(actuals.keySet()));
+ for (Object expectKey : expects.keySet()) {
+ Double expectValue = expects.get(expectKey);
+ Double actualValue = actuals.get(expectKey);
+ Assert.assertTrue(String.format("expected %s, actual %s",
+ expectValue, actualValue),
+ Math.abs(expectValue - actualValue) <
+ Math.pow(1, -10));
+ }
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/RingsRaysApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/RingsRaysApiTest.java
new file mode 100644
index 0000000..20b86fa
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/RingsRaysApiTest.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class RingsRaysApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void prepareSchemaAndGraph() {
+ BaseApiTest.initPropertyKey();
+ BaseApiTest.initVertexLabel();
+ BaseApiTest.initEdgeLabel();
+ BaseApiTest.initIndexLabel();
+ BaseApiTest.initVertex();
+ BaseApiTest.initEdge();
+
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+
+ // Path length 5
+ v1.addEdge("link", v2);
+ v2.addEdge("link", v3);
+ v3.addEdge("link", v1);
+ v3.addEdge("link", v2);
+ }
+
+ @Test
+ public void testRings() {
+ Object markoId = getVertexId("person", "name", "marko");
+ List<Path> paths = ringsAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, -1L, -1L);
+ Assert.assertEquals(0, paths.size());
+ }
+
+ @Test
+ public void testRingsWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ List<Path> paths = ringsAPI.get(markoId, Direction.BOTH, null,
+ 3, false, -1L, -1L, 1L);
+ Assert.assertEquals(1, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, joshId, lopId, markoId);
+ List<Object> path2 = ImmutableList.of(markoId, lopId, joshId, markoId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ }
+
+ @Test
+ public void testRingsWithDepth() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ List<Path> paths = ringsAPI.get(markoId, Direction.BOTH, null,
+ 1, false, -1L, -1L, -1L);
+ Assert.assertEquals(0, paths.size());
+
+ paths = ringsAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, -1L, -1L);
+ Assert.assertEquals(0, paths.size());
+
+ paths = ringsAPI.get(markoId, Direction.BOTH, null,
+ 3, false, -1L, -1L, -1L);
+ Assert.assertEquals(1, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, joshId, lopId, markoId);
+ List<Object> path2 = ImmutableList.of(markoId, lopId, joshId, markoId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ }
+
+ @Test
+ public void testRingsWithCapacity() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ ringsAPI.get(markoId, Direction.BOTH, null,
+ 2, false, -1L, 1L, -1L);
+ }, e -> {
+ String expect = "Exceed capacity '1' while finding rings";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testRingsSourceInRing() {
+ List<Path> paths = ringsAPI.get(1, Direction.BOTH, null,
+ 3, true, -1L, -1L, -1L);
+ Assert.assertEquals(1, paths.size());
+ List<Object> path1 = ImmutableList.of(1, 3, 2, 1);
+ List<Object> path2 = ImmutableList.of(1, 2, 3, 1);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+
+ paths = ringsAPI.get(2, Direction.OUT, null,
+ 2, true, -1L, -1L, -1L);
+ Assert.assertEquals(1, paths.size());
+ List<Object> path3 = ImmutableList.of(2, 3, 2);
+ Assert.assertEquals(path3, paths.get(0).objects());
+
+ paths = ringsAPI.get(2, Direction.BOTH, null,
+ 2, true, -1L, -1L, -1L);
+ Assert.assertEquals(1, paths.size());
+ Assert.assertEquals(path3, paths.get(0).objects());
+ }
+
+ @Test
+ public void testRingsWithoutSourceInRing() {
+ List<Path> paths = ringsAPI.get(1, Direction.BOTH, null,
+ 3, false, -1L, -1L, -1L);
+ Assert.assertEquals(3, paths.size());
+ List<Object> path1 = ImmutableList.of(1, 3, 2, 3);
+ List<Object> path2 = ImmutableList.of(1, 3, 2, 1);
+ List<Object> path3 = ImmutableList.of(1, 2, 3, 1);
+ List<Object> path4 = ImmutableList.of(1, 2, 3, 2);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2,
+ path3, path4);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+
+ paths = ringsAPI.get(2, Direction.OUT, null,
+ 3, false, -1L, -1L, -1L);
+ Assert.assertEquals(2, paths.size());
+ List<Object> path5 = ImmutableList.of(2, 3, 2);
+ List<Object> path6 = ImmutableList.of(2, 3, 1, 2);
+ expectedPaths = ImmutableList.of(path5, path6);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+
+ paths = ringsAPI.get(2, Direction.BOTH, null,
+ 3, false, -1L, -1L, -1L);
+ Assert.assertEquals(2, paths.size());
+ List<Object> path7 = ImmutableList.of(2, 3, 1, 2);
+ List<Object> path8 = ImmutableList.of(2, 1, 3, 2);
+ expectedPaths = ImmutableList.of(path5, path7, path8);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ }
+
+ @Test
+ public void testRays() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Path> paths = raysAPI.get(markoId, Direction.OUT, null,
+ 2, -1L, -1L, -1L);
+ Assert.assertEquals(4, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, vadasId);
+ List<Object> path3 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path4 = ImmutableList.of(markoId, joshId, lopId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2,
+ path3, path4);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(3).objects()));
+ }
+
+ @Test
+ public void testRaysWithLimit() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ List<Path> paths = raysAPI.get(markoId, Direction.OUT, null,
+ 2, -1L, -1L, 2L);
+ Assert.assertEquals(2, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, vadasId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ }
+
+ @Test
+ public void testRaysWithDepth() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Path> paths = raysAPI.get(markoId, Direction.OUT, null,
+ 1, -1L, -1L, -1L);
+ Assert.assertEquals(3, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, vadasId);
+ List<Object> path3 = ImmutableList.of(markoId, joshId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2,
+ path3);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+
+ paths = raysAPI.get(markoId, Direction.OUT, null,
+ 2, -1L, -1L, -1L);
+ Assert.assertEquals(4, paths.size());
+ List<Object> path4 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path5 = ImmutableList.of(markoId, joshId, lopId);
+ expectedPaths = ImmutableList.of(path1, path2, path4, path5);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(3).objects()));
+
+ paths = raysAPI.get(markoId, Direction.OUT, null,
+ 3, -1L, -1L, -1L);
+ Assert.assertEquals(4, paths.size());
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(3).objects()));
+ }
+
+ @Test
+ public void testRaysWithCapacity() {
+ Object markoId = getVertexId("person", "name", "marko");
+
+ Assert.assertThrows(ServerException.class, () -> {
+ raysAPI.get(markoId, Direction.OUT, null,
+ 2, -1L, 1L, -1L);
+ }, e -> {
+ String expect = "Exceed capacity '1' while finding rays";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testRaysWithBoth() {
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Path> paths = raysAPI.get(markoId, Direction.BOTH, null,
+ 1, -1L, -1L, -1L);
+ Assert.assertEquals(3, paths.size());
+ List<Object> path1 = ImmutableList.of(markoId, lopId);
+ List<Object> path2 = ImmutableList.of(markoId, vadasId);
+ List<Object> path3 = ImmutableList.of(markoId, joshId);
+ List<List<Object>> expectedPaths = ImmutableList.of(path1, path2,
+ path3);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+
+ paths = raysAPI.get(markoId, Direction.BOTH, null,
+ 2, -1L, -1L, -1L);
+ Assert.assertEquals(5, paths.size());
+ List<Object> path4 = ImmutableList.of(markoId, vadasId);
+ List<Object> path5 = ImmutableList.of(markoId, lopId, joshId);
+ List<Object> path6 = ImmutableList.of(markoId, lopId, peterId);
+ List<Object> path7 = ImmutableList.of(markoId, joshId, lopId);
+ List<Object> path8 = ImmutableList.of(markoId, joshId, rippleId);
+ expectedPaths = ImmutableList.of(path4, path5, path6, path7, path8);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(3).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(4).objects()));
+
+ paths = raysAPI.get(markoId, Direction.BOTH, null,
+ 3, -1L, -1L, -1L);
+ Assert.assertEquals(5, paths.size());
+ List<Object> path9 = ImmutableList.of(markoId, vadasId);
+ List<Object> path10 = ImmutableList.of(markoId, joshId, rippleId);
+ List<Object> path11 = ImmutableList.of(markoId, lopId, peterId);
+ List<Object> path12 = ImmutableList.of(markoId, joshId, lopId, peterId);
+ List<Object> path13 = ImmutableList.of(markoId, lopId,
+ joshId, rippleId);
+ expectedPaths = ImmutableList.of(path9, path10, path11, path12, path13);
+ Assert.assertTrue(expectedPaths.contains(paths.get(0).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(1).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(2).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(3).objects()));
+ Assert.assertTrue(expectedPaths.contains(paths.get(4).objects()));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SameNeighborsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SameNeighborsApiTest.java
new file mode 100644
index 0000000..0b0275d
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SameNeighborsApiTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class SameNeighborsApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+ Vertex v4 = graph().addVertex(T.label, "node", T.id, 4);
+ Vertex v5 = graph().addVertex(T.label, "node", T.id, 5);
+ Vertex v6 = graph().addVertex(T.label, "node", T.id, 6);
+ Vertex v7 = graph().addVertex(T.label, "node", T.id, 7);
+ Vertex v8 = graph().addVertex(T.label, "node", T.id, 8);
+ Vertex v9 = graph().addVertex(T.label, "node", T.id, 9);
+ Vertex v10 = graph().addVertex(T.label, "node", T.id, 10);
+
+ v1.addEdge("link", v3);
+ v2.addEdge("link", v3);
+ v4.addEdge("link", v1);
+ v4.addEdge("link", v2);
+
+ v1.addEdge("relateTo", v5);
+ v2.addEdge("relateTo", v5);
+ v6.addEdge("relateTo", v1);
+ v6.addEdge("relateTo", v2);
+
+ v1.addEdge("link", v7);
+ v8.addEdge("link", v1);
+ v2.addEdge("link", v9);
+ v10.addEdge("link", v2);
+ }
+
+ @Test
+ public void testSameNeighbors() {
+ List<Object> neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ null, -1, -1L);
+ Assert.assertEquals(4, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 4, 5, 6)));
+ }
+
+ @Test
+ public void testSameNeighborsWithDirection() {
+ List<Object> neighbors = sameNeighborsAPI.get(1, 2, Direction.OUT,
+ null, -1, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 5)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.IN,
+ null, -1, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(4, 6)));
+ }
+
+ @Test
+ public void testSameNeighborsWithLabel() {
+ List<Object> neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ "link", -1, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 4)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.OUT,
+ "link", -1, -1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(3));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.IN,
+ "link", -1, -1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(4));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ "relateTo", -1, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(5, 6)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.OUT,
+ "relateTo", -1, -1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(5));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.IN,
+ "relateTo", -1, -1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(6));
+ }
+
+ @Test
+ public void testSameNeighborsWithDegree() {
+ List<Object> neighbors = sameNeighborsAPI.get(1, 2, Direction.OUT,
+ null, 6, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 5)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.OUT,
+ null, 1, -1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(3));
+ }
+
+ @Test
+ public void testSameNeighborsWithLimit() {
+ List<Object> neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ "link", 6, -1L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 4)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ "link", 6, 2L);
+ Assert.assertEquals(2, neighbors.size());
+
+ Assert.assertTrue(neighbors.containsAll(ImmutableList.of(3, 4)));
+
+ neighbors = sameNeighborsAPI.get(1, 2, Direction.BOTH,
+ "link", 6, 1L);
+ Assert.assertEquals(1, neighbors.size());
+
+ Assert.assertTrue(neighbors.contains(3));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/ShortestPathApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/ShortestPathApiTest.java
new file mode 100644
index 0000000..8c07e5a
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/ShortestPathApiTest.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class ShortestPathApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+ Vertex v4 = graph().addVertex(T.label, "node", T.id, 4);
+ Vertex v5 = graph().addVertex(T.label, "node", T.id, 5);
+ Vertex v6 = graph().addVertex(T.label, "node", T.id, 6);
+ Vertex v7 = graph().addVertex(T.label, "node", T.id, 7);
+ Vertex v8 = graph().addVertex(T.label, "node", T.id, 8);
+ Vertex v9 = graph().addVertex(T.label, "node", T.id, 9);
+ Vertex v10 = graph().addVertex(T.label, "node", T.id, 10);
+ Vertex v11 = graph().addVertex(T.label, "node", T.id, 11);
+ Vertex v12 = graph().addVertex(T.label, "node", T.id, 12);
+ Vertex v13 = graph().addVertex(T.label, "node", T.id, 13);
+ Vertex v14 = graph().addVertex(T.label, "node", T.id, 14);
+ Vertex v15 = graph().addVertex(T.label, "node", T.id, 15);
+ Vertex v16 = graph().addVertex(T.label, "node", T.id, 16);
+ Vertex v17 = graph().addVertex(T.label, "node", T.id, 17);
+ Vertex v18 = graph().addVertex(T.label, "node", T.id, 18);
+
+ // Path length 5
+ v1.addEdge("link", v2);
+ v2.addEdge("link", v3);
+ v3.addEdge("link", v4);
+ v4.addEdge("link", v5);
+ v5.addEdge("link", v6);
+
+ // Path length 4
+ v1.addEdge("link", v7);
+ v7.addEdge("link", v8);
+ v8.addEdge("link", v9);
+ v9.addEdge("link", v6);
+
+ // Path length 3
+ v1.addEdge("link", v10);
+ v10.addEdge("link", v11);
+ v11.addEdge("link", v6);
+
+ // Add other 3 neighbor for v7
+ v7.addEdge("link", v12);
+ v7.addEdge("link", v13);
+ v7.addEdge("link", v14);
+
+ // Add other 4 neighbor for v10
+ v10.addEdge("link", v15);
+ v10.addEdge("link", v16);
+ v10.addEdge("link", v17);
+ v10.addEdge("link", v18);
+ }
+
+ @Test
+ public void testShortestPath() {
+ Path path = shortestPathAPI.get(1, 6, Direction.BOTH,
+ null, 6, -1L, 0L, -1L);
+ Assert.assertEquals(4, path.size());
+ Assert.assertEquals(ImmutableList.of(1, 10, 11, 6), path.objects());
+ }
+
+ @Test
+ public void testShortestPathWithLabel() {
+ Path path = shortestPathAPI.get(1, 6, Direction.BOTH,
+ "link", 6, -1L, 0L, -1L);
+ Assert.assertEquals(4, path.size());
+ Assert.assertEquals(ImmutableList.of(1, 10, 11, 6), path.objects());
+ }
+
+ @Test
+ public void testShortestPathWithDegree() {
+ Path path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 6, 1L, 0L, -1L);
+ /*
+ * Following results can be guaranteed in RocksDB backend,
+ * but different results exist in table type backend(like Cassandra).
+ */
+ Assert.assertEquals(6, path.size());
+ Assert.assertEquals(ImmutableList.of(1, 2, 3, 4, 5, 6), path.objects());
+ }
+
+ @Test
+ public void testShortestPathWithCapacity() {
+ Path path = shortestPathAPI.get(14, 6, Direction.BOTH,
+ null, 6, 5L, 0L, 19L);
+ Assert.assertEquals(5, path.size());
+ Assert.assertEquals(ImmutableList.of(14, 7, 8, 9, 6), path.objects());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ shortestPathAPI.get(14, 6, Direction.BOTH, null, 6, 1L, 0L, 2L);
+ }, e -> {
+ String expect = "Exceed capacity '2' while finding shortest path";
+ Assert.assertContains(expect, e.getMessage());
+ });
+ }
+
+ @Test
+ public void testShortestPathWithMaxDepth() {
+ Path path = shortestPathAPI.get(14, 6, Direction.BOTH,
+ null, 4, 5L, 0L, 19L);
+ Assert.assertEquals(5, path.size());
+ Assert.assertEquals(ImmutableList.of(14, 7, 8, 9, 6), path.objects());
+
+ path = shortestPathAPI.get(14, 6, Direction.BOTH,
+ null, 3, 5L, 0L, 19L);
+ Assert.assertEquals(0, path.size());
+ }
+
+ @Test
+ public void testShortestPathWithSkipDegree() {
+ // Path length 5 with min degree 3(v1 degree is 3)
+ List<Object> path1 = ImmutableList.of(1, 2, 3, 4, 5, 6);
+ // Path length 4 with middle degree 4(v7 degree is 4)
+ List<Object> path2 = ImmutableList.of(1, 7, 8, 9, 6);
+ // Path length 3 with max degree 5(v10 degree is 5)
+ List<Object> path3 = ImmutableList.of(1, 10, 11, 6);
+
+ // (skipped degree == degree) > max degree
+ Path path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 6L, 6L, -1L);
+ Assert.assertEquals(4, path.size());
+ Assert.assertEquals(path3, path.objects());
+
+ // (skipped degree == degree) == max degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 5L, 5L, -1L);
+ Assert.assertEquals(5, path.size());
+ Assert.assertEquals(path2, path.objects());
+
+ // min degree < (skipped degree == degree) == middle degree < max degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 4L, 4L, -1L);
+ Assert.assertEquals(6, path.size());
+ Assert.assertEquals(path1, path.objects());
+
+ // (skipped degree == degree) <= min degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 3L, 3L, -1L);
+ Assert.assertEquals(0, path.size());
+
+ // Skipped degree > max degree, degree <= min degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 3L, 6L, -1L);
+ Assert.assertTrue(path.size() == 4 ||
+ path.size() == 5 ||
+ path.size() == 6);
+ List<List<Object>> paths = ImmutableList.of(path1, path2, path3);
+ Assert.assertTrue(paths.contains(path.objects()));
+
+ // Skipped degree > max degree, min degree < degree < max degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 4L, 6L, -1L);
+ Assert.assertTrue(path.size() == 4 || path.size() == 5);
+ Assert.assertTrue(path2.equals(path.objects()) ||
+ path3.equals(path.objects()));
+
+ // Skipped degree > max degree, degree >= max degree
+ path = shortestPathAPI.get(1, 6, Direction.OUT,
+ null, 5, 5L, 6L, -1L);
+ Assert.assertEquals(4, path.size());
+ Assert.assertEquals(path3, path.objects());
+ }
+
+ @Test
+ public void testShortestPathWithIllegalArgs() {
+ // The max depth shouldn't be 0 or negative
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, -1, 1L, 0L, 2L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 0, 1L, 0L, 2L);
+ });
+
+ // The degree shouldn't be 0 or negative but NO_LIMIT(-1)
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, 0L, 0L, 2L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, -3L, 0L, 2L);
+ });
+
+ // The skipped degree shouldn't be negative
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, 1L, -1L, 2L);
+ });
+
+ // The skipped degree shouldn't be >= capacity
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, 1L, 2L, 2L);
+ });
+
+ // The skipped degree shouldn't be < degree
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, 3L, 2L, 10L);
+ });
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ shortestPathAPI.get("a", "b", Direction.BOTH,
+ null, 5, -1L, 2L, 10L);
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java
new file mode 100644
index 0000000..bb92b21
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.WeightedPath;
+import com.baidu.hugegraph.structure.traverser.WeightedPaths;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class SingleSourceShortestPathApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().propertyKey("weight")
+ .asDouble()
+ .ifNotExist()
+ .create();
+
+ schema().vertexLabel("node")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ Vertex va = graph().addVertex(T.label, "node", T.id, "A");
+ Vertex vb = graph().addVertex(T.label, "node", T.id, "B");
+ Vertex vc = graph().addVertex(T.label, "node", T.id, "C");
+ Vertex vd = graph().addVertex(T.label, "node", T.id, "D");
+ Vertex ve = graph().addVertex(T.label, "node", T.id, "E");
+ Vertex vf = graph().addVertex(T.label, "node", T.id, "F");
+ Vertex vg = graph().addVertex(T.label, "node", T.id, "G");
+ Vertex vh = graph().addVertex(T.label, "node", T.id, "H");
+ Vertex vi = graph().addVertex(T.label, "node", T.id, "I");
+ Vertex vj = graph().addVertex(T.label, "node", T.id, "J");
+ Vertex vk = graph().addVertex(T.label, "node", T.id, "K");
+ Vertex vl = graph().addVertex(T.label, "node", T.id, "L");
+ Vertex vm = graph().addVertex(T.label, "node", T.id, "M");
+ Vertex vn = graph().addVertex(T.label, "node", T.id, "N");
+ Vertex vo = graph().addVertex(T.label, "node", T.id, "O");
+ Vertex vp = graph().addVertex(T.label, "node", T.id, "P");
+ Vertex vq = graph().addVertex(T.label, "node", T.id, "Q");
+ Vertex vr = graph().addVertex(T.label, "node", T.id, "R");
+ Vertex vz = graph().addVertex(T.label, "node", T.id, "Z");
+
+ /*
+ * "link":
+ * A --0.2--> B --0.4--> C --0.8--> D --0.6--> Z
+ * ----------------10---------------------->
+ * <--0.5-- E <--0.3-- F <--0.4-- G <--0.1--
+ * <-------------------8--------------------
+ * --0.1--> H --0.1--> I <--0.1-- J <--0.2--
+ * -----0.4----> K -----0.5-----> L <--0.3--
+ * "relateTo":
+ * -----1.4-----> M -----3.8----> N --3.5-->
+ * <----2.2------ O <----3.3----- P <-1.6---
+ * -----3.1-----> Q <----2.0----- R --1.3-->
+ */
+ va.addEdge("link", vb, "weight", 0.2D);
+ vb.addEdge("link", vc, "weight", 0.4D);
+ vc.addEdge("link", vd, "weight", 0.8D);
+ vd.addEdge("link", vz, "weight", 0.6D);
+
+ va.addEdge("link", vz, "weight", 10.0D);
+
+ vz.addEdge("link", vg, "weight", 0.1D);
+ vg.addEdge("link", vf, "weight", 0.4D);
+ vf.addEdge("link", ve, "weight", 0.3D);
+ ve.addEdge("link", va, "weight", 0.5D);
+
+ vz.addEdge("link", va, "weight", 8.0D);
+
+ va.addEdge("link", vh, "weight", 0.1D);
+ vh.addEdge("link", vi, "weight", 0.1D);
+ vz.addEdge("link", vj, "weight", 0.2D);
+ vj.addEdge("link", vi, "weight", 0.1D);
+
+ va.addEdge("link", vk, "weight", 0.4D);
+ vk.addEdge("link", vl, "weight", 0.5D);
+ vz.addEdge("link", vl, "weight", 0.3D);
+
+ va.addEdge("relateTo", vm, "weight", 1.4D);
+ vm.addEdge("relateTo", vn, "weight", 3.8D);
+ vn.addEdge("relateTo", vz, "weight", 3.5D);
+
+ vz.addEdge("relateTo", vp, "weight", 1.6D);
+ vp.addEdge("relateTo", vo, "weight", 3.3D);
+ vo.addEdge("relateTo", va, "weight", 2.2D);
+
+ va.addEdge("relateTo", vq, "weight", 3.1D);
+ vr.addEdge("relateTo", vq, "weight", 2.0D);
+ vr.addEdge("relateTo", vz, "weight", 1.3D);
+ }
+
+ @Test
+ public void testSingleSourceShortestPath() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.BOTH, null, "weight",
+ -1, 0, -1, -1, false);
+ Assert.assertEquals(18, weightedPaths.paths().size());
+
+ WeightedPath.Path path = weightedPaths.paths().get("B");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B"), path.vertices());
+
+ path = weightedPaths.paths().get("C");
+ Assert.assertEquals(0.6000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C"), path.vertices());
+
+ path = weightedPaths.paths().get("D");
+ Assert.assertEquals(1.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "D"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("E");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E"), path.vertices());
+
+ path = weightedPaths.paths().get("F");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E", "F"), path.vertices());
+
+ path = weightedPaths.paths().get("G");
+ Assert.assertEquals(0.6D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "G"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("H");
+ Assert.assertEquals(0.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H"), path.vertices());
+
+ path = weightedPaths.paths().get("I");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I"), path.vertices());
+
+ path = weightedPaths.paths().get("J");
+ Assert.assertEquals(0.30000000000000004D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("K");
+ Assert.assertEquals(0.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "K"), path.vertices());
+
+ path = weightedPaths.paths().get("L");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "L"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("M");
+ Assert.assertEquals(1.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "M"), path.vertices());
+
+ path = weightedPaths.paths().get("N");
+ Assert.assertEquals(4.0D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "N"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("O");
+ Assert.assertEquals(2.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "O"), path.vertices());
+
+ path = weightedPaths.paths().get("P");
+ Assert.assertEquals(2.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "P"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Q");
+ Assert.assertEquals(3.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q"), path.vertices());
+
+ path = weightedPaths.paths().get("R");
+ Assert.assertEquals(1.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "R"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"),
+ path.vertices());
+ }
+
+ @Test
+ public void testSingleSourceShortestPathWithLabel() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.BOTH, "link", "weight",
+ -1, 0, -1, -1, false);
+ Assert.assertEquals(12, weightedPaths.paths().size());
+
+ WeightedPath.Path path = weightedPaths.paths().get("B");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B"), path.vertices());
+
+ path = weightedPaths.paths().get("C");
+ Assert.assertEquals(0.6000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C"), path.vertices());
+
+ path = weightedPaths.paths().get("D");
+ Assert.assertEquals(1.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "D"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("E");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E"), path.vertices());
+
+ path = weightedPaths.paths().get("F");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E", "F"), path.vertices());
+
+ path = weightedPaths.paths().get("G");
+ Assert.assertEquals(0.6D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "G"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("H");
+ Assert.assertEquals(0.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H"), path.vertices());
+
+ path = weightedPaths.paths().get("I");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I"), path.vertices());
+
+ path = weightedPaths.paths().get("J");
+ Assert.assertEquals(0.30000000000000004D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("K");
+ Assert.assertEquals(0.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "K"), path.vertices());
+
+ path = weightedPaths.paths().get("L");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "L"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"),
+ path.vertices());
+
+ weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.BOTH, "relateTo", "weight",
+ -1, 0, -1, -1, false);
+
+ Assert.assertEquals(7, weightedPaths.paths().size());
+
+ path = weightedPaths.paths().get("M");
+ Assert.assertEquals(1.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "M"), path.vertices());
+
+ path = weightedPaths.paths().get("N");
+ Assert.assertEquals(5.199999999999999D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "M", "N"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("O");
+ Assert.assertEquals(2.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "O"), path.vertices());
+
+ path = weightedPaths.paths().get("P");
+ Assert.assertEquals(5.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "O", "P"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Q");
+ Assert.assertEquals(3.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q"), path.vertices());
+
+ path = weightedPaths.paths().get("R");
+ Assert.assertEquals(5.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q", "R"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(6.3999999999999995D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q", "R", "Z"),
+ path.vertices());
+ }
+
+ @Test
+ public void testSingleSourceShortestPathWithDirection() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.OUT, null, "weight",
+ -1, 0, -1, -1, false);
+
+ Assert.assertEquals(17, weightedPaths.paths().size());
+
+ WeightedPath.Path path = weightedPaths.paths().get("B");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B"), path.vertices());
+
+ path = weightedPaths.paths().get("C");
+ Assert.assertEquals(0.6000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C"), path.vertices());
+
+ path = weightedPaths.paths().get("D");
+ Assert.assertEquals(1.4000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("E");
+ Assert.assertEquals(2.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D",
+ "Z", "G", "F", "E"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("F");
+ Assert.assertEquals(2.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z", "G", "F"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("G");
+ Assert.assertEquals(2.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z", "G"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("H");
+ Assert.assertEquals(0.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H"), path.vertices());
+
+ path = weightedPaths.paths().get("I");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I"), path.vertices());
+
+ path = weightedPaths.paths().get("J");
+ Assert.assertEquals(2.2D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z", "J"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("K");
+ Assert.assertEquals(0.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "K"), path.vertices());
+
+ path = weightedPaths.paths().get("L");
+ Assert.assertEquals(0.9D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "K", "L"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("M");
+ Assert.assertEquals(1.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "M"), path.vertices());
+
+ path = weightedPaths.paths().get("N");
+ Assert.assertEquals(5.199999999999999D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "M", "N"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("O");
+ Assert.assertEquals(6.9D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z", "P", "O"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("P");
+ Assert.assertEquals(3.6D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z", "P"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Q");
+ Assert.assertEquals(3.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q"), path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(2.0D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"),
+ path.vertices());
+ }
+
+ @Test
+ public void testSingleSourceShortestPathWithDegree() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.OUT, null, "weight",
+ 1, 0, -1, -1, false);
+ Assert.assertEquals(4, weightedPaths.paths().size());
+
+ WeightedPath.Path path = weightedPaths.paths().get("B");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B"), path.vertices());
+
+ path = weightedPaths.paths().get("C");
+ Assert.assertEquals(0.6000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C"), path.vertices());
+
+ path = weightedPaths.paths().get("D");
+ Assert.assertEquals(1.4000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(2.0D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"),
+ path.vertices());
+ }
+
+ @Test
+ public void testSingleSourceShortestPathWithLimit() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.BOTH, null, "weight",
+ -1, 0, -1, 11, false);
+ Assert.assertEquals(11, weightedPaths.paths().size());
+
+ WeightedPath.Path path = weightedPaths.paths().get("B");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B"), path.vertices());
+
+ path = weightedPaths.paths().get("C");
+ Assert.assertEquals(0.6000000000000001D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C"), path.vertices());
+
+ path = weightedPaths.paths().get("E");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E"), path.vertices());
+
+ path = weightedPaths.paths().get("F");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "E", "F"), path.vertices());
+
+ path = weightedPaths.paths().get("L");
+ Assert.assertEquals(0.8D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "L"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("G");
+ Assert.assertEquals(0.6D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z", "G"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("H");
+ Assert.assertEquals(0.1D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H"), path.vertices());
+
+ path = weightedPaths.paths().get("I");
+ Assert.assertEquals(0.2D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I"), path.vertices());
+
+ path = weightedPaths.paths().get("J");
+ Assert.assertEquals(0.30000000000000004D, path.weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J"),
+ path.vertices());
+
+ path = weightedPaths.paths().get("K");
+ Assert.assertEquals(0.4D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "K"), path.vertices());
+
+ path = weightedPaths.paths().get("Z");
+ Assert.assertEquals(0.5D, path.weight(), Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"),
+ path.vertices());
+ }
+
+ @Test
+ public void testSingleSourceShortestPathWithVertex() {
+ WeightedPaths weightedPaths = singleSourceShortestPathAPI.get(
+ "A", Direction.BOTH, null, "weight",
+ -1, 0, -1, -1, true);
+ Assert.assertEquals(18, weightedPaths.paths().size());
+ Assert.assertEquals(19, weightedPaths.vertices().size());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TemplatePathsApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TemplatePathsApiTest.java
new file mode 100644
index 0000000..e7504bf
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TemplatePathsApiTest.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+import java.util.Set;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.PathsWithVertices;
+import com.baidu.hugegraph.structure.traverser.TemplatePathsRequest;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class TemplatePathsApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().propertyKey("weight")
+ .asDouble()
+ .ifNotExist()
+ .create();
+
+ schema().vertexLabel("node")
+ .useCustomizeNumberId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "node", T.id, 1);
+ Vertex v2 = graph().addVertex(T.label, "node", T.id, 2);
+ Vertex v3 = graph().addVertex(T.label, "node", T.id, 3);
+ Vertex v4 = graph().addVertex(T.label, "node", T.id, 4);
+ Vertex v5 = graph().addVertex(T.label, "node", T.id, 5);
+ Vertex v6 = graph().addVertex(T.label, "node", T.id, 6);
+ Vertex v7 = graph().addVertex(T.label, "node", T.id, 7);
+ Vertex v8 = graph().addVertex(T.label, "node", T.id, 8);
+ Vertex v9 = graph().addVertex(T.label, "node", T.id, 9);
+ Vertex v10 = graph().addVertex(T.label, "node", T.id, 10);
+ Vertex v11 = graph().addVertex(T.label, "node", T.id, 11);
+ Vertex v12 = graph().addVertex(T.label, "node", T.id, 12);
+ Vertex v13 = graph().addVertex(T.label, "node", T.id, 13);
+ Vertex v14 = graph().addVertex(T.label, "node", T.id, 14);
+ Vertex v15 = graph().addVertex(T.label, "node", T.id, 15);
+ Vertex v16 = graph().addVertex(T.label, "node", T.id, 16);
+ Vertex v17 = graph().addVertex(T.label, "node", T.id, 17);
+
+ v1.addEdge("link", v2);
+ v2.addEdge("link", v3);
+ v3.addEdge("link", v4);
+ v4.addEdge("link", v5);
+ v5.addEdge("link", v6);
+ v6.addEdge("link", v7);
+ v8.addEdge("link", v7);
+ v9.addEdge("link", v8);
+ v10.addEdge("link", v9);
+
+ v1.addEdge("link", v11);
+ v11.addEdge("link", v12);
+ v12.addEdge("link", v13);
+ v13.addEdge("link", v14);
+ v10.addEdge("link", v15);
+ v15.addEdge("link", v14);
+
+ v1.addEdge("link", v16);
+ v16.addEdge("link", v17);
+ v10.addEdge("link", v17);
+
+ v1.addEdge("relateTo", v16, "weight", 0.8D);
+ v16.addEdge("relateTo", v17, "weight", 0.5D);
+ v10.addEdge("relateTo", v17, "weight", 0.6D);
+ v17.addEdge("relateTo", v16, "weight", 0.3D);
+ }
+
+ @Test
+ public void testTemplatePaths() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).maxTimes(3);
+ builder.steps().direction(Direction.OUT).maxTimes(3);
+ builder.steps().direction(Direction.IN).maxTimes(3);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(3, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
+ ImmutableList.of(1, 11, 12, 13, 14, 15, 10),
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testTemplatePathsWithVertex() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3);
+ builder.withVertex(true);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ List<Object> expectedIds = ImmutableList.of(1, 16, 17, 10);
+ List<List<Object>> expectedPath = ImmutableList.of(expectedIds);
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expectedPath.contains(path.objects()));
+ }
+ Set<Vertex> vertices = pathsWithVertices.vertices();
+ Assert.assertEquals(4, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertTrue(expectedIds.contains(v.id()));
+ }
+ }
+
+ @Test
+ public void testTemplatePathsWithLabel() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("link").maxTimes(3);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(3, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
+ ImmutableList.of(1, 11, 12, 13, 14, 15, 10),
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3);
+ request = builder.build();
+ pathsWithVertices = templatePathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testTemplatePathsWithRing() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3);
+ builder.withRing(true);
+ request = builder.build();
+ pathsWithVertices = templatePathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(4, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 16, 17, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testTemplatePathsWithProperties() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3);
+ builder.withRing(true);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(4, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 16, 17, 10),
+ ImmutableList.of(1, 16, 17, 16, 17, 16, 17, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("relateTo")
+ .properties("weight", "P.gt(0.4)").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("relateTo")
+ .properties("weight", "P.gt(0.4)").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("relateTo")
+ .properties("weight", "P.gt(0.4)").maxTimes(3);
+ builder.withRing(true);
+ request = builder.build();
+ pathsWithVertices = templatePathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(1, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+
+ @Test
+ public void testTemplatePathsWithLimit() {
+ TemplatePathsRequest.Builder builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("link").maxTimes(3);
+ TemplatePathsRequest request = builder.build();
+ PathsWithVertices pathsWithVertices = templatePathsAPI.post(request);
+ List<PathsWithVertices.Paths> paths = pathsWithVertices.paths();
+ Assert.assertEquals(3, paths.size());
+ List<List<Object>> expected = ImmutableList.of(
+ ImmutableList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
+ ImmutableList.of(1, 11, 12, 13, 14, 15, 10),
+ ImmutableList.of(1, 16, 17, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+
+ builder = TemplatePathsRequest.builder();
+ builder.sources().ids(1);
+ builder.targets().ids(10);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.OUT).labels("link").maxTimes(3);
+ builder.steps().direction(Direction.IN).labels("link").maxTimes(3);
+ builder.limit(2);
+ request = builder.build();
+ pathsWithVertices = templatePathsAPI.post(request);
+ paths = pathsWithVertices.paths();
+ Assert.assertEquals(2, paths.size());
+ expected = ImmutableList.of(
+ ImmutableList.of(1, 16, 17, 10),
+ ImmutableList.of(1, 11, 12, 13, 14, 15, 10)
+ );
+ for (PathsWithVertices.Paths path : paths) {
+ Assert.assertTrue(expected.contains(path.objects()));
+ }
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TraverserApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TraverserApiTest.java
new file mode 100644
index 0000000..5100798
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/TraverserApiTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import org.junit.BeforeClass;
+
+import com.baidu.hugegraph.api.BaseApiTest;
+
+public class TraverserApiTest extends BaseApiTest {
+
+ protected static SameNeighborsAPI sameNeighborsAPI;
+ protected static JaccardSimilarityAPI jaccardSimilarityAPI;
+ protected static ShortestPathAPI shortestPathAPI;
+ protected static AllShortestPathsAPI allShortestPathsAPI;
+ protected static SingleSourceShortestPathAPI singleSourceShortestPathAPI;
+ protected static WeightedShortestPathAPI weightedShortestPathAPI;
+ protected static MultiNodeShortestPathAPI multiNodeShortestPathAPI;
+ protected static PathsAPI pathsAPI;
+ protected static CrosspointsAPI crosspointsAPI;
+ protected static KoutAPI koutAPI;
+ protected static KneighborAPI kneighborAPI;
+
+ protected static RingsAPI ringsAPI;
+ protected static RaysAPI raysAPI;
+
+ protected static CountAPI countAPI;
+
+ protected static CustomizedPathsAPI customizedPathsAPI;
+ protected static CustomizedCrosspointsAPI customizedCrosspointsAPI;
+ protected static FusiformSimilarityAPI fusiformSimilarityAPI;
+ protected static TemplatePathsAPI templatePathsAPI;
+
+ protected static NeighborRankAPI neighborRankAPI;
+ protected static PersonalRankAPI personalRankAPI;
+
+ protected static VerticesAPI verticesAPI;
+ protected static EdgesAPI edgesAPI;
+
+ @BeforeClass
+ public static void init() {
+ BaseApiTest.init();
+
+ sameNeighborsAPI = new SameNeighborsAPI(client, GRAPH);
+ jaccardSimilarityAPI = new JaccardSimilarityAPI(client, GRAPH);
+ shortestPathAPI = new ShortestPathAPI(client, GRAPH);
+ allShortestPathsAPI = new AllShortestPathsAPI(client, GRAPH);
+ singleSourceShortestPathAPI = new SingleSourceShortestPathAPI(client,
+ GRAPH);
+ weightedShortestPathAPI = new WeightedShortestPathAPI(client, GRAPH);
+ multiNodeShortestPathAPI = new MultiNodeShortestPathAPI(client, GRAPH);
+ pathsAPI = new PathsAPI(client, GRAPH);
+ crosspointsAPI = new CrosspointsAPI(client, GRAPH);
+ koutAPI = new KoutAPI(client, GRAPH);
+ kneighborAPI = new KneighborAPI(client, GRAPH);
+
+ ringsAPI = new RingsAPI(client, GRAPH);
+ raysAPI = new RaysAPI(client, GRAPH);
+
+ countAPI = new CountAPI(client, GRAPH);
+
+ customizedPathsAPI = new CustomizedPathsAPI(client, GRAPH);
+ customizedCrosspointsAPI = new CustomizedCrosspointsAPI(client, GRAPH);
+ fusiformSimilarityAPI = new FusiformSimilarityAPI(client, GRAPH);
+ templatePathsAPI = new TemplatePathsAPI(client, GRAPH);
+
+ neighborRankAPI = new NeighborRankAPI(client, GRAPH);
+ personalRankAPI = new PersonalRankAPI(client, GRAPH);
+
+ verticesAPI = new VerticesAPI(client, GRAPH);
+ edgesAPI = new EdgesAPI(client, GRAPH);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathApiTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathApiTest.java
new file mode 100644
index 0000000..1891145
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/api/traverser/WeightedShortestPathApiTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.api.traverser;
+
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.traverser.WeightedPath;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+
+public class WeightedShortestPathApiTest extends TraverserApiTest {
+
+ @BeforeClass
+ public static void initShortestPathGraph() {
+ schema().propertyKey("weight")
+ .asDouble()
+ .ifNotExist()
+ .create();
+
+ schema().vertexLabel("node")
+ .useCustomizeStringId()
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("link")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ schema().edgeLabel("relateTo")
+ .sourceLabel("node").targetLabel("node")
+ .properties("weight")
+ .ifNotExist()
+ .create();
+
+ Vertex va = graph().addVertex(T.label, "node", T.id, "A");
+ Vertex vb = graph().addVertex(T.label, "node", T.id, "B");
+ Vertex vc = graph().addVertex(T.label, "node", T.id, "C");
+ Vertex vd = graph().addVertex(T.label, "node", T.id, "D");
+ Vertex ve = graph().addVertex(T.label, "node", T.id, "E");
+ Vertex vf = graph().addVertex(T.label, "node", T.id, "F");
+ Vertex vg = graph().addVertex(T.label, "node", T.id, "G");
+ Vertex vh = graph().addVertex(T.label, "node", T.id, "H");
+ Vertex vi = graph().addVertex(T.label, "node", T.id, "I");
+ Vertex vj = graph().addVertex(T.label, "node", T.id, "J");
+ Vertex vk = graph().addVertex(T.label, "node", T.id, "K");
+ Vertex vl = graph().addVertex(T.label, "node", T.id, "L");
+ Vertex vm = graph().addVertex(T.label, "node", T.id, "M");
+ Vertex vn = graph().addVertex(T.label, "node", T.id, "N");
+ Vertex vo = graph().addVertex(T.label, "node", T.id, "O");
+ Vertex vp = graph().addVertex(T.label, "node", T.id, "P");
+ Vertex vq = graph().addVertex(T.label, "node", T.id, "Q");
+ Vertex vr = graph().addVertex(T.label, "node", T.id, "R");
+ Vertex vz = graph().addVertex(T.label, "node", T.id, "Z");
+
+ /*
+ * "link":
+ * A --0.2--> B --0.4--> C --0.8--> D --0.6--> Z
+ * ----------------10---------------------->
+ * <--0.5-- E <--0.3-- F <--0.4-- G <--0.1--
+ * <-------------------8--------------------
+ * --0.1--> H --0.1--> I <--0.1-- J <--0.2--
+ * -----0.4----> K -----0.5-----> L <--0.3--
+ * "relateTo":
+ * -----1.4-----> M -----3.8----> N --3.5-->
+ * <----2.2------ O <----3.3----- P <-1.6---
+ * -----3.1-----> Q <----2.0----- R --1.3-->
+ */
+ va.addEdge("link", vb, "weight", 0.2D);
+ vb.addEdge("link", vc, "weight", 0.4D);
+ vc.addEdge("link", vd, "weight", 0.8D);
+ vd.addEdge("link", vz, "weight", 0.6D);
+
+ va.addEdge("link", vz, "weight", 10.0D);
+
+ vz.addEdge("link", vg, "weight", 0.1D);
+ vg.addEdge("link", vf, "weight", 0.4D);
+ vf.addEdge("link", ve, "weight", 0.3D);
+ ve.addEdge("link", va, "weight", 0.5D);
+
+ vz.addEdge("link", va, "weight", 8.0D);
+
+ va.addEdge("link", vh, "weight", 0.1D);
+ vh.addEdge("link", vi, "weight", 0.1D);
+ vz.addEdge("link", vj, "weight", 0.2D);
+ vj.addEdge("link", vi, "weight", 0.1D);
+
+ va.addEdge("link", vk, "weight", 0.4D);
+ vk.addEdge("link", vl, "weight", 0.5D);
+ vz.addEdge("link", vl, "weight", 0.3D);
+
+ va.addEdge("relateTo", vm, "weight", 1.4D);
+ vm.addEdge("relateTo", vn, "weight", 3.8D);
+ vn.addEdge("relateTo", vz, "weight", 3.5D);
+
+ vz.addEdge("relateTo", vp, "weight", 1.6D);
+ vp.addEdge("relateTo", vo, "weight", 3.3D);
+ vo.addEdge("relateTo", va, "weight", 2.2D);
+
+ va.addEdge("relateTo", vq, "weight", 3.1D);
+ vr.addEdge("relateTo", vq, "weight", 2.0D);
+ vr.addEdge("relateTo", vz, "weight", 1.3D);
+ }
+
+ @Test
+ public void testWeightedShortestPath() {
+ WeightedPath weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.BOTH, null,
+ "weight", -1, 0, -1, false);
+ Assert.assertTrue(weightedPath.vertices().isEmpty());
+ Assert.assertEquals(0.5D, weightedPath.path().weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"),
+ weightedPath.path().vertices());
+ }
+
+ @Test
+ public void testWeightedShortestPathWithLabel() {
+ WeightedPath weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.BOTH, "link",
+ "weight", -1, 0, -1, false);
+ Assert.assertTrue(weightedPath.vertices().isEmpty());
+ Assert.assertEquals(0.5D, weightedPath.path().weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"),
+ weightedPath.path().vertices());
+
+ weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.BOTH, "relateTo",
+ "weight", -1, 0, -1, false);
+ Assert.assertTrue(weightedPath.vertices().isEmpty());
+ Assert.assertEquals(6.3999999999999995D, weightedPath.path().weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "Q", "R", "Z"),
+ weightedPath.path().vertices());
+ }
+
+ @Test
+ public void testWeightedShortestPathWithDirection() {
+ WeightedPath weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.OUT, null,
+ "weight", -1, 0, -1, false);
+ Assert.assertTrue(weightedPath.vertices().isEmpty());
+ Assert.assertEquals(2.0D, weightedPath.path().weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"),
+ weightedPath.path().vertices());
+ }
+
+ @Test
+ public void testWeightedShortestPathWithDegree() {
+ WeightedPath weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.OUT, null,
+ "weight", 1L, 0L, -1L, false);
+ Assert.assertTrue(weightedPath.vertices().isEmpty());
+ Assert.assertEquals(2.0D, weightedPath.path().weight(),
+ Double.MIN_VALUE);
+ Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"),
+ weightedPath.path().vertices());
+ }
+
+ @Test
+ public void testWeightedShortestPathWithVertex() {
+ WeightedPath weightedPath = weightedShortestPathAPI.get(
+ "A", "Z", Direction.BOTH, null, "weight",
+ -1, 0, -1, true);
+ Assert.assertEquals(5, weightedPath.vertices().size());
+ List<Object> expected = ImmutableList.of("A", "H", "I", "J", "Z");
+ for (Vertex vertex : weightedPath.vertices()) {
+ Assert.assertTrue(expected.contains(vertex.id()));
+ }
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/AuthManagerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/AuthManagerTest.java
new file mode 100644
index 0000000..4c37b76
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/AuthManagerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.structure.auth.Access;
+import com.baidu.hugegraph.structure.auth.Belong;
+import com.baidu.hugegraph.structure.auth.Group;
+import com.baidu.hugegraph.structure.auth.HugePermission;
+import com.baidu.hugegraph.structure.auth.HugeResource;
+import com.baidu.hugegraph.structure.auth.HugeResourceType;
+import com.baidu.hugegraph.structure.auth.Login;
+import com.baidu.hugegraph.structure.auth.LoginResult;
+import com.baidu.hugegraph.structure.auth.Project;
+import com.baidu.hugegraph.structure.auth.Target;
+import com.baidu.hugegraph.structure.auth.TokenPayload;
+import com.baidu.hugegraph.structure.auth.User;
+import com.baidu.hugegraph.structure.auth.User.UserRole;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableSet;
+
+public class AuthManagerTest extends BaseFuncTest {
+
+ @Override
+ @Before
+ public void setup() {
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ auth().deleteAll();
+ }
+
+ @Test
+ public void testAuth() {
+ User user = new User();
+ user.name("bob");
+ user.password("123456");
+ user = auth().createUser(user);
+
+ Group group = new Group();
+ group.name("managers");
+ group = auth().createGroup(group);
+
+ Target gremlin = new Target();
+ gremlin.name("gremlin");
+ gremlin.graph("hugegraph");
+ gremlin.url("127.0.0.1:8080");
+ gremlin.resources(new HugeResource(HugeResourceType.GREMLIN));
+ gremlin = auth().createTarget(gremlin);
+
+ Target task = new Target();
+ task.name("task");
+ task.graph("hugegraph");
+ task.url("127.0.0.1:8080");
+ task.resources(new HugeResource(HugeResourceType.TASK));
+ task = auth().createTarget(task);
+
+ Belong belong = new Belong();
+ belong.user(user);
+ belong.group(group);
+ belong = auth().createBelong(belong);
+
+ Access access1 = new Access();
+ access1.group(group);
+ access1.target(gremlin);
+ access1.permission(HugePermission.EXECUTE);
+ access1 = auth().createAccess(access1);
+
+ Access access2 = new Access();
+ access2.group(group);
+ access2.target(task);
+ access2.permission(HugePermission.READ);
+ access2 = auth().createAccess(access2);
+
+ Project project1 = new Project("test");
+ project1 = auth().createProject(project1);
+ Assert.assertEquals("test", project1.name());
+
+ Project project2 = new Project("test2");
+ project2 = auth().createProject(project2);
+ Assert.assertEquals("test2", project2.name());
+
+ Project newProject1 = auth().getProject(project1);
+ Assert.assertEquals(newProject1.id(), project1.id());
+ Assert.assertTrue(CollectionUtils.isEmpty(newProject1.graphs()));
+
+ List<Project> projects = auth().listProjects();
+ Assert.assertNotNull(projects);
+ Assert.assertEquals(2, projects.size());
+
+ Set<String> graphs = ImmutableSet.of("graph1", "graph2");
+ newProject1 = auth().projectAddGraphs(project1, graphs);
+ Assert.assertNotNull(newProject1);
+ Assert.assertEquals(graphs, newProject1.graphs());
+
+ graphs = ImmutableSet.of("graph2");
+ newProject1 = auth().projectRemoveGraphs(project1,
+ ImmutableSet.of("graph1"));
+ Assert.assertNotNull(newProject1);
+ Assert.assertEquals(graphs, newProject1.graphs());
+
+ Object project1Id = project1.id();
+ project1 = new Project(project1Id);
+ project1.description("test description");
+ newProject1 = auth().updateProject(project1);
+ Assert.assertEquals(newProject1.description(), project1.description());
+
+ auth().deleteProject(project2);
+ projects.remove(project2);
+ List<Project> newProjects = auth().listProjects();
+ Assert.assertEquals(newProjects, projects);
+
+ UserRole role = auth().getUserRole(user);
+ String r = "{\"roles\":{\"hugegraph\":" +
+ "{\"READ\":[{\"type\":\"TASK\",\"label\":\"*\",\"properties\":null}]," +
+ "\"EXECUTE\":[{\"type\":\"GREMLIN\",\"label\":\"*\",\"properties\":null}]}}}";
+ Assert.assertEquals(r, role.toString());
+
+ Login login = new Login();
+ login.name("bob");
+ login.password("123456");
+ LoginResult result = auth().login(login);
+
+ String token = result.token();
+
+ HugeClient client = baseClient();
+ client.setAuthContext("Bearer " + token);
+
+ TokenPayload payload = auth().verifyToken();
+ Assert.assertEquals("bob", payload.username());
+ Assert.assertEquals(user.id(), payload.userId());
+
+ auth().logout();
+ client.resetAuthContext();
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BaseFuncTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BaseFuncTest.java
new file mode 100644
index 0000000..301f3e8
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BaseFuncTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.baidu.hugegraph.BaseClientTest;
+
+public class BaseFuncTest extends BaseClientTest {
+
+ @BeforeClass
+ public static void init() {
+ BaseClientTest.init();
+ BaseFuncTest.clearData();
+ }
+
+ @AfterClass
+ public static void clear() throws Exception {
+ BaseFuncTest.clearData();
+ BaseClientTest.clear();
+ }
+
+ protected static void clearData() {
+ // Clear edge
+ graph().listEdges().forEach(e -> graph().removeEdge(e.id()));
+ // Clear vertex
+ graph().listVertices().forEach(v -> graph().removeVertex(v.id()));
+
+ List<Long> ilTaskIds = new ArrayList<>();
+ // Clear schema
+ schema().getIndexLabels().forEach(il -> {
+ ilTaskIds.add(schema().removeIndexLabelAsync(il.name()));
+ });
+ ilTaskIds.forEach(BaseFuncTest::waitUntilTaskCompleted);
+
+ List<Long> elTaskIds = new ArrayList<>();
+ schema().getEdgeLabels().forEach(el -> {
+ elTaskIds.add(schema().removeEdgeLabelAsync(el.name()));
+ });
+ elTaskIds.forEach(BaseFuncTest::waitUntilTaskCompleted);
+
+ List<Long> vlTaskIds = new ArrayList<>();
+ schema().getVertexLabels().forEach(vl -> {
+ vlTaskIds.add(schema().removeVertexLabelAsync(vl.name()));
+ });
+ vlTaskIds.forEach(BaseFuncTest::waitUntilTaskCompleted);
+
+ schema().getPropertyKeys().forEach(pk -> {
+ schema().removePropertyKey(pk.name());
+ });
+ }
+
+ protected static void runWithThreads(int threads, Runnable task) {
+ ExecutorService executor = Executors.newFixedThreadPool(threads);
+ List<Future<?>> futures = new ArrayList<>();
+ for (int i = 0; i < threads; i++) {
+ futures.add(executor.submit(task));
+ }
+ for (Future<?> future : futures) {
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ protected static void waitUntilTaskCompleted(long taskId) {
+ task().waitUntilTaskCompleted(taskId, 3L);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BatchInsertTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BatchInsertTest.java
new file mode 100644
index 0000000..b761ce0
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/BatchInsertTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class BatchInsertTest extends BaseFuncTest {
+
+ private static final int BATCH_SIZE = 500;
+
+ @Before
+ public void setup() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testBatchInsertInOneLoop() {
+ List<Vertex> vertices = new ArrayList<>(BATCH_SIZE);
+ List<Edge> edges = new ArrayList<>(BATCH_SIZE);
+ for (int i = 1; i <= BATCH_SIZE; i++) {
+ Vertex vertex1 = new Vertex("person").property("name", "P-" + i)
+ .property("age", i);
+ Vertex vertex2 = new Vertex("software").property("name", "S-" + i)
+ .property("lang", "java");
+ Edge edge = new Edge("created").source(vertex1).target(vertex2)
+ .property("date", "2018-12-25");
+
+ vertices.add(vertex1);
+ vertices.add(vertex2);
+ edges.add(edge);
+
+ if (vertices.size() >= BATCH_SIZE) {
+ graph().addVertices(vertices);
+ graph().addEdges(edges);
+
+ vertices.clear();
+ edges.clear();
+ }
+ }
+
+ Assert.assertEquals(2 * BATCH_SIZE, graph().listVertices(-1).size());
+ Assert.assertEquals(BATCH_SIZE, graph().listEdges(-1).size());
+ }
+
+ @Test
+ public void testBatchInsertInOneLoopButAddEdgesBeforeVertices() {
+ List<Vertex> vertices = new ArrayList<>(BATCH_SIZE);
+ List<Edge> edges = new ArrayList<>(BATCH_SIZE);
+ for (int i = 1; i <= BATCH_SIZE; i++) {
+ Vertex vertex1 = new Vertex("person").property("name", "P-" + i)
+ .property("age", i);
+ Vertex vertex2 = new Vertex("software").property("name", "S-" + i)
+ .property("lang", "java");
+ Edge edge = new Edge("created").source(vertex1).target(vertex2)
+ .property("date", "2018-12-25");
+
+ vertices.add(vertex1);
+ vertices.add(vertex2);
+ edges.add(edge);
+
+ if (vertices.size() >= BATCH_SIZE) {
+ // Must add vertices before edges
+ Assert.assertThrows(InvalidOperationException.class, () -> {
+ graph().addEdges(edges);
+ });
+ graph().addVertices(vertices);
+
+ vertices.clear();
+ edges.clear();
+ }
+ }
+
+ Assert.assertEquals(2 * BATCH_SIZE, graph().listVertices(-1).size());
+ Assert.assertEquals(0, graph().listEdges(-1).size());
+ }
+
+ @Test
+ public void testBatchInsertInTwoLoops() {
+ int vertexCount = BATCH_SIZE;
+ List<Vertex> persons = new ArrayList<>(BATCH_SIZE);
+ List<Vertex> softwares = new ArrayList<>(BATCH_SIZE);
+ List<Edge> edges = new ArrayList<>(BATCH_SIZE);
+
+ for (int i = 1; i <= vertexCount; i++) {
+ Vertex person = new Vertex("person").property("name", "P-" + i)
+ .property("age", i);
+ Vertex software = new Vertex("software").property("name", "S-" + i)
+ .property("lang", "java");
+
+ persons.add(person);
+ softwares.add(software);
+ }
+ graph().addVertices(persons);
+ graph().addVertices(softwares);
+
+ for (int i = 0; i < vertexCount; i++) {
+ Vertex person = persons.get(i);
+ Vertex software = softwares.get(i);
+
+ Edge edge = new Edge("created").source(person).target(software)
+ .property("date", "2018-12-25");
+
+ edges.add(edge);
+ }
+ graph().addEdges(edges);
+
+ Assert.assertEquals(2 * BATCH_SIZE, graph().listVertices(-1).size());
+ Assert.assertEquals(BATCH_SIZE, graph().listEdges(-1).size());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeLabelTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeLabelTest.java
new file mode 100644
index 0000000..f77fc71
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeLabelTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public class EdgeLabelTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ BaseFuncTest.initPropertyKey();
+ BaseFuncTest.initVertexLabel();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testLinkedVertexLabel() {
+ SchemaManager schema = schema();
+ EdgeLabel father = schema.edgeLabel("father")
+ .link("person", "person")
+ .properties("weight")
+ .userdata("multiplicity", "one-to-many")
+ .create();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("multiplicity", "many-to-many")
+ .create();
+
+ Assert.assertTrue(father.linkedVertexLabel("person"));
+ Assert.assertFalse(father.linkedVertexLabel("book"));
+ Assert.assertTrue(write.linkedVertexLabel("person"));
+ Assert.assertTrue(write.linkedVertexLabel("book"));
+ }
+
+ @Test
+ public void testAddEdgeLabelWithUserData() {
+ SchemaManager schema = schema();
+ EdgeLabel father = schema.edgeLabel("father")
+ .link("person", "person")
+ .properties("weight")
+ .userdata("multiplicity", "one-to-many")
+ .create();
+ Assert.assertEquals(2, father.userdata().size());
+ Assert.assertEquals("one-to-many",
+ father.userdata().get("multiplicity"));
+ String time = (String) father.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("multiplicity", "many-to-many")
+ .create();
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, write.userdata().size());
+ Assert.assertEquals("many-to-many",
+ write.userdata().get("multiplicity"));
+ time = (String) write.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAppendEdgeLabelWithUserData() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("person")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .nullableKeys("city")
+ .ifNotExist()
+ .create();
+
+ EdgeLabel father = schema.edgeLabel("father")
+ .link("person", "person")
+ .properties("weight")
+ .create();
+ Assert.assertEquals(1, father.userdata().size());
+ String time = (String) father.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ father = schema.edgeLabel("father")
+ .userdata("multiplicity", "one-to-many")
+ .append();
+ Assert.assertEquals(2, father.userdata().size());
+ Assert.assertEquals("one-to-many",
+ father.userdata().get("multiplicity"));
+ time = (String) father.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testEliminateEdgeLabelWithUserData() {
+ SchemaManager schema = schema();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+ Assert.assertEquals(3, write.userdata().size());
+ Assert.assertEquals("one-to-many",
+ write.userdata().get("multiplicity"));
+ Assert.assertEquals("picture2", write.userdata().get("icon"));
+ String time = (String) write.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ write = schema.edgeLabel("write")
+ .userdata("icon", "")
+ .eliminate();
+ Assert.assertEquals(2, write.userdata().size());
+ Assert.assertEquals("one-to-many",
+ write.userdata().get("multiplicity"));
+ time = (String) write.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testRemoveEdgeLabelSync() {
+ SchemaManager schema = schema();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+
+ Assert.assertNotNull(write);
+ // Remove edge label sync
+ schema.removeEdgeLabel("write");
+
+ write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+
+ Assert.assertNotNull(write);
+ // Remove edge label sync with timeout
+ schema.removeEdgeLabel("write", 10);
+ }
+
+ @Test
+ public void testRemoveEdgeLabelAsync() {
+ SchemaManager schema = schema();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+ Assert.assertNotNull(write);
+ // Remove edge label async and wait
+ long taskId = schema.removeEdgeLabelAsync("write");
+ Task task = task().waitUntilTaskCompleted(taskId, 10);
+ Assert.assertTrue(task.completed());
+ }
+
+ @Test
+ public void testListByNames() {
+ SchemaManager schema = schema();
+ EdgeLabel father = schema.edgeLabel("father")
+ .link("person", "person")
+ .create();
+
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .create();
+
+ List<EdgeLabel> edgeLabels;
+
+ edgeLabels = schema.getEdgeLabels(ImmutableList.of("father"));
+ Assert.assertEquals(1, edgeLabels.size());
+ assertContains(edgeLabels, father);
+
+ edgeLabels = schema.getEdgeLabels(ImmutableList.of("write"));
+ Assert.assertEquals(1, edgeLabels.size());
+ assertContains(edgeLabels, write);
+
+ edgeLabels = schema.getEdgeLabels(ImmutableList.of("father", "write"));
+ Assert.assertEquals(2, edgeLabels.size());
+ assertContains(edgeLabels, father);
+ assertContains(edgeLabels, write);
+ }
+
+ @Test
+ public void testResetEdgeLabelId() {
+ SchemaManager schema = schema();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+ Assert.assertTrue(write.id() > 0);
+ write.resetId();
+ Assert.assertEquals(0L, write.id());
+ }
+
+ @Test
+ public void testSetCheckExist() {
+ SchemaManager schema = schema();
+ EdgeLabel write = schema.edgeLabel("write")
+ .link("person", "book")
+ .properties("date", "weight")
+ .userdata("multiplicity", "one-to-many")
+ .userdata("icon", "picture2")
+ .create();
+ Assert.assertTrue(write.checkExist());
+ write.checkExist(false);
+ Assert.assertFalse(write.checkExist());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeTest.java
new file mode 100644
index 0000000..ae2ec0b
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/EdgeTest.java
@@ -0,0 +1,799 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.Direction;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.Result;
+import com.baidu.hugegraph.structure.gremlin.ResultSet;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+
+public class EdgeTest extends BaseFuncTest {
+
+ @Override
+ @Before
+ public void setup() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+ BaseClientTest.initVertex();
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testLinkedVertex() {
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24");
+ Assert.assertTrue(created.linkedVertex(peterId));
+ Assert.assertTrue(created.linkedVertex(lopId));
+ }
+
+ @Test
+ public void testAddEdgeProperty() {
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24");
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("city", "HongKong");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"),
+ "city", "HongKong");
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testUpdateEdgeProperty() {
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24");
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("date", "2017-08-08");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-08-08"));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testAddEdgePropertyValueList() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueList()
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("created")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24",
+ "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("time", "2014-02-14");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2014-02-14")));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testAddEdgePropertyValueSet() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueSet()
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("created")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24",
+ "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("time", "2014-02-14");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2014-02-14")));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testAddEdgePropertyValueListWithSameValue() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueList()
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("created")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24",
+ "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("time", "2012-10-10");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testAddEdgePropertyValueSetWithSameValue() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueSet()
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("created")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24",
+ "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+
+ created.property("time", "2012-10-10");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testAddEdgeWithMapProperties() {
+ Vertex peter = getVertex("person", "name", "peter");
+ Vertex lop = getVertex("software", "name", "lop");
+
+ Map<String, Object> properties = ImmutableMap.of("date", "2017-03-24",
+ "city", "HongKong");
+ Edge created = graph().addEdge(peter, "created", lop, properties);
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "city", "HongKong");
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testRemoveEdgeProperty() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueSet()
+ .ifNotExist()
+ .create();
+ schema().edgeLabel("created")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24",
+ "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"),
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, created.properties());
+
+ created.removeProperty("time");
+ props = ImmutableMap.of("date", Utils.formatDate("2017-03-24"));
+ Assert.assertEquals(props, created.properties());
+ }
+
+ @Test
+ public void testRemoveEdgePropertyNotExist() {
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ Edge created = graph().addEdge(peterId, "created", lopId,
+ "date", "2017-03-24");
+ Map<String, Object> props = ImmutableMap.of(
+ "date", Utils.formatDate("2017-03-24"));
+ Assert.assertEquals(props, created.properties());
+
+ Assert.assertThrows(InvalidOperationException.class, () -> {
+ created.removeProperty("not-exist");
+ });
+ }
+
+ @Test
+ public void testName() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ List<Edge> edges = graph().getEdges(markoId, Direction.OUT, "knows");
+ Assert.assertEquals(2, edges.size());
+ Edge edge1 = edges.get(0);
+ Edge edge2 = edges.get(1);
+ Date date1 = DateUtil.parse((String) edge1.property("date"));
+ Date date2 = DateUtil.parse((String) edge2.property("date"));
+ String name1 = edge1.name();
+ String name2 = edge2.name();
+ if (date1.before(date2)) {
+ Assert.assertTrue(name1.compareTo(name2) < 0);
+ } else {
+ Assert.assertTrue(name1.compareTo(name2) >= 0);
+ }
+ }
+
+ @Test
+ public void testGetAllEdges() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Edge> edges = graph().listEdges();
+ Assert.assertEquals(6, edges.size());
+ assertContains(edges, markoId, "knows", vadasId,
+ "date", Utils.formatDate("2012-01-10"));
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ assertContains(edges, markoId, "created", lopId,
+ "date", Utils.formatDate("2014-01-10"),
+ "city", "Shanghai");
+ assertContains(edges, joshId, "created", rippleId,
+ "date", Utils.formatDate("2015-01-10"),
+ "city", "Beijing");
+ assertContains(edges, joshId, "created", lopId,
+ "date", Utils.formatDate("2016-01-10"),
+ "city", "Beijing");
+ assertContains(edges, peterId, "created", lopId,
+ "date", Utils.formatDate("2017-01-10"),
+ "city", "Hongkong");
+ }
+
+ @Test
+ public void testGetAllEdgesWithNoLimit() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object peterId = getVertexId("person", "name", "peter");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Edge> edges = graph().listEdges(-1);
+ Assert.assertEquals(6, edges.size());
+ assertContains(edges, markoId, "knows", vadasId,
+ "date", Utils.formatDate("2012-01-10"));
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ assertContains(edges, markoId, "created", lopId,
+ "date", Utils.formatDate("2014-01-10"),
+ "city", "Shanghai");
+ assertContains(edges, joshId, "created", rippleId,
+ "date", Utils.formatDate("2015-01-10"),
+ "city", "Beijing");
+ assertContains(edges, joshId, "created", lopId,
+ "date", Utils.formatDate("2016-01-10"),
+ "city", "Beijing");
+ assertContains(edges, peterId, "created", lopId,
+ "date", Utils.formatDate("2017-01-10"),
+ "city", "Hongkong");
+ }
+
+ @Test
+ public void testGetEdgesByVertexId() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object vadasId = getVertexId("person", "name", "vadas");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+
+ List<Edge> edges = graph().getEdges(markoId);
+ Assert.assertEquals(3, edges.size());
+ assertContains(edges, markoId, "knows", vadasId,
+ "date", Utils.formatDate("2012-01-10"));
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ assertContains(edges, markoId, "created", lopId,
+ "date", Utils.formatDate("2014-01-10"),
+ "city", "Shanghai");
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdWithLimit2() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+
+ List<Edge> edges = graph().getEdges(markoId, 2);
+ Assert.assertEquals(2, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(markoId, edge.sourceId());
+ }
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirection() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT);
+ Assert.assertEquals(2, edges.size());
+ assertContains(edges, joshId, "created", rippleId,
+ "date", Utils.formatDate("2015-01-10"),
+ "city", "Beijing");
+ assertContains(edges, joshId, "created", lopId,
+ "date", Utils.formatDate("2016-01-10"),
+ "city", "Beijing");
+
+ edges = graph().getEdges(joshId, Direction.IN);
+ Assert.assertEquals(1, edges.size());
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirectionWithLimit1() {
+ BaseClientTest.initEdge();
+
+ Object joshId = getVertexId("person", "name", "josh");
+
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT, 1);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ // TODO: Whether need to add direction property in Edge?
+ Assert.assertEquals(joshId, edge.sourceId());
+ }
+
+ edges = graph().getEdges(joshId, Direction.IN, 1);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(joshId, edge.targetId());
+ }
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirectionLabel() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object lopId = getVertexId("software", "name", "lop");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT,
+ "created");
+ Assert.assertEquals(2, edges.size());
+ assertContains(edges, joshId, "created", rippleId,
+ "date", Utils.formatDate("2015-01-10"),
+ "city", "Beijing");
+ assertContains(edges, joshId, "created", lopId,
+ "date", Utils.formatDate("2016-01-10"),
+ "city", "Beijing");
+
+ edges = graph().getEdges(joshId, Direction.IN, "knows");
+ Assert.assertEquals(1, edges.size());
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirectionLabelWithLimit1() {
+ BaseClientTest.initEdge();
+
+ Object joshId = getVertexId("person", "name", "josh");
+
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT,
+ "created", 1);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(joshId, edge.sourceId());
+ Assert.assertEquals("created", edge.label());
+ }
+
+ edges = graph().getEdges(joshId, Direction.IN, "knows", 1);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(joshId, edge.targetId());
+ Assert.assertEquals("knows", edge.label());
+ }
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirectionLabelProperties() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+ Object joshId = getVertexId("person", "name", "josh");
+ Object rippleId = getVertexId("software", "name", "ripple");
+
+ Map<String, Object> properties = ImmutableMap.of(
+ "date",
+ Utils.formatDate("2015-01-10"));
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT,
+ "created", properties);
+ Assert.assertEquals(1, edges.size());
+ assertContains(edges, joshId, "created", rippleId,
+ "date", Utils.formatDate("2015-01-10"),
+ "city", "Beijing");
+
+ properties = ImmutableMap.of("date", Utils.formatDate("2013-01-10"));
+ edges = graph().getEdges(joshId, Direction.IN, "knows", properties);
+ Assert.assertEquals(1, edges.size());
+ assertContains(edges, markoId, "knows", joshId,
+ "date", Utils.formatDate("2013-01-10"));
+ }
+
+ @Test
+ public void testGetEdgesByVertexIdDirectionLabelPropertiesWithLimit1() {
+ BaseClientTest.initEdge();
+
+ Object joshId = getVertexId("person", "name", "josh");
+
+ Map<String, Object> properties = ImmutableMap.of(
+ "date",
+ Utils.formatDate("2015-01-10"));
+ List<Edge> edges = graph().getEdges(joshId, Direction.OUT,
+ "created", properties);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(joshId, edge.sourceId());
+ Assert.assertEquals("created", edge.label());
+ }
+
+ properties = ImmutableMap.of("date", Utils.formatDate("2013-01-10"));
+ edges = graph().getEdges(joshId, Direction.IN, "knows", properties);
+ Assert.assertEquals(1, edges.size());
+ for (Edge edge : edges) {
+ Assert.assertEquals(joshId, edge.targetId());
+ Assert.assertEquals("knows", edge.label());
+ }
+ }
+
+ @Test
+ public void testGetEdgesByLabelAndPropertiesWithRangeCondition()
+ throws ParseException {
+ schema().indexLabel("knowsByDate").range()
+ .onE("knows").by("date").create();
+ schema().indexLabel("createdByDate").range()
+ .onE("created").by("date").create();
+
+ BaseClientTest.initEdge();
+
+ Date expected = DateUtil.parse("2014-01-10");
+ Date expected2 = DateUtil.parse("2016-01-10");
+
+ Map<String, Object> properties = ImmutableMap.of(
+ "date", "P.eq(\"2014-1-10\")");
+ List<Edge> edges = graph().listEdges("created", properties);
+
+ Date time;
+ Assert.assertEquals(1, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertEquals(expected.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date", "P.gt(\"2014-1-10\")");
+ edges = graph().listEdges("created", properties);
+ Assert.assertEquals(3, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertGt(expected.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date", "P.gte(\"2014-1-10\")");
+ edges = graph().listEdges("created", properties);
+ Assert.assertEquals(4, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertGte(expected.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date", "P.lt(\"2014-1-10\")");
+ edges = graph().listEdges(null, properties);
+ Assert.assertEquals(2, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("knows", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertLt(expected.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date", "P.lte(\"2014-1-10\")");
+ edges = graph().listEdges(null, properties);
+ Assert.assertEquals(3, edges.size());
+ for (Edge e : edges) {
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertLte(expected.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date",
+ "P.between(\"2014-1-10\",\"2016-1-10\")");
+ edges = graph().listEdges(null, properties);
+ Assert.assertEquals(2, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertGte(expected.getTime(), time.getTime());
+ Assert.assertLt(expected2.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date",
+ "P.inside(\"2014-1-10\",\"2016-1-10\")");
+ edges = graph().listEdges(null, properties);
+ Assert.assertEquals(1, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertGt(expected.getTime(), time.getTime());
+ Assert.assertLt(expected2.getTime(), time.getTime());
+ }
+
+ properties = ImmutableMap.of("date",
+ "P.within(\"2014-1-10\",\"2016-1-10\")");
+ edges = graph().listEdges(null, properties);
+ Assert.assertEquals(2, edges.size());
+ for (Edge e : edges) {
+ Assert.assertEquals("created", e.label());
+ time = DateUtil.parse((String) e.property("date"));
+ Assert.assertGte(expected.getTime(), time.getTime());
+ Assert.assertLte(expected2.getTime(), time.getTime());
+ }
+ }
+
+ @Test
+ public void testGetEdgesByLabelAndPropertiesWithKeepP()
+ throws ParseException {
+ schema().indexLabel("createdByCity").secondary()
+ .onE("created").by("city").create();
+ schema().indexLabel("createdByDate").secondary()
+ .onE("created").by("date").create();
+
+ BaseClientTest.initEdge();
+
+ Map<String, Object> properties = ImmutableMap.of(
+ "date", "P.eq(\"2014-1-10\")");
+ List<Edge> edges = graph().listEdges("created", properties, false);
+ Assert.assertEquals(1, edges.size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().listEdges("created", properties, true);
+ }, e -> {
+ Assert.assertContains("Expected date format is:", e.getMessage());
+ });
+
+ Map<String, Object> properties2 = ImmutableMap.of("city", "P.gt(1)");
+ edges = graph().listEdges("created", properties2, true);
+ Assert.assertEquals(0, edges.size());
+
+ edges = graph().listEdges("created", properties2, true, 3);
+ Assert.assertEquals(0, edges.size());
+ }
+
+ @Test
+ public void testIterateEdgesByLabel() {
+ BaseClientTest.initEdge();
+
+ Iterator<Edge> edges = graph().iterateEdges("created", 1);
+ Assert.assertEquals(4, Iterators.size(edges));
+
+ edges = graph().iterateEdges("knows", 1);
+ Assert.assertEquals(2, Iterators.size(edges));
+ }
+
+ @Test
+ public void testIterateEdgesByVertexId() {
+ BaseClientTest.initEdge();
+
+ Object markoId = getVertexId("person", "name", "marko");
+
+ Iterator<Edge> edges = graph().iterateEdges(markoId, 1);
+ Assert.assertEquals(3, Iterators.size(edges));
+
+ edges = graph().iterateEdges(markoId, Direction.OUT, 1);
+ Assert.assertEquals(3, Iterators.size(edges));
+
+ edges = graph().iterateEdges(markoId, Direction.OUT, "knows", 1);
+ Assert.assertEquals(2, Iterators.size(edges));
+
+ edges = graph().iterateEdges(markoId, Direction.OUT, "created", 1);
+ Assert.assertEquals(1, Iterators.size(edges));
+
+ Map<String, Object> properties = ImmutableMap.of("date",
+ "P.gt(\"2012-1-1\")");
+ Iterator<Edge> iter = graph().iterateEdges(markoId, Direction.OUT,
+ "knows", properties, 1);
+ Assert.assertEquals(2, Iterators.size(iter));
+ }
+
+ @Test
+ public void testQueryByPagingAndFiltering() {
+ SchemaManager schema = schema();
+ schema.propertyKey("no").asText().create();
+ schema.propertyKey("location").asText().create();
+ schema.propertyKey("callType").asText().create();
+ schema.propertyKey("calltime").asDate().create();
+ schema.propertyKey("duration").asInt().create();
+ schema.vertexLabel("phone")
+ .properties("no")
+ .primaryKeys("no")
+ .enableLabelIndex(false)
+ .create();
+ schema.edgeLabel("call").multiTimes()
+ .properties("location", "callType", "duration", "calltime")
+ .sourceLabel("phone").targetLabel("phone")
+ .sortKeys("location", "callType", "duration", "calltime")
+ .create();
+
+ Vertex v1 = graph().addVertex(T.label, "phone", "no", "13812345678");
+ Vertex v2 = graph().addVertex(T.label, "phone", "no", "13866668888");
+ Vertex v10086 = graph().addVertex(T.label, "phone", "no", "10086");
+
+ v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
+ "duration", 3, "calltime", "2017-5-1 23:00:00");
+ v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
+ "duration", 3, "calltime", "2017-5-2 12:00:01");
+ v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
+ "duration", 3, "calltime", "2017-5-3 12:08:02");
+ v1.addEdge("call", v2, "location", "Beijing", "callType", "work",
+ "duration", 8, "calltime", "2017-5-3 22:22:03");
+ v1.addEdge("call", v2, "location", "Beijing", "callType", "fun",
+ "duration", 10, "calltime", "2017-5-4 20:33:04");
+
+ v1.addEdge("call", v10086, "location", "Nanjing", "callType", "work",
+ "duration", 12, "calltime", "2017-5-2 15:30:05");
+ v1.addEdge("call", v10086, "location", "Nanjing", "callType", "work",
+ "duration", 14, "calltime", "2017-5-3 14:56:06");
+ v2.addEdge("call", v10086, "location", "Nanjing", "callType", "fun",
+ "duration", 15, "calltime", "2017-5-3 17:28:07");
+
+ ResultSet resultSet = gremlin().gremlin("g.V(vid).outE('call')" +
+ ".has('location', 'Beijing')" +
+ ".has('callType', 'work')" +
+ ".has('duration', 3)" +
+ ".has('calltime', " +
+ "P.between('2017-5-2', " +
+ "'2017-5-4'))" +
+ ".toList()")
+ .binding("vid", v1.id())
+ .execute();
+ Iterator<Result> results = resultSet.iterator();
+ Assert.assertEquals(2, Iterators.size(results));
+
+ Assert.assertThrows(ServerException.class, () -> {
+ // no location
+ gremlin().gremlin("g.V(vid).outE('call').has('callType', 'work')" +
+ ".has('duration', 3).has('calltime', " +
+ "P.between('2017-5-2', '2017-5-4'))" +
+ ".has('~page', '')")
+ .binding("vid", v1.id())
+ .execute();
+ }, e -> {
+ Assert.assertContains("Can't query by paging and filtering",
+ e.getMessage());
+ });
+ }
+
+ private static void assertContains(List<Edge> edges, Object source,
+ String label, Object target,
+ Object... keyValues) {
+ Map<String, Object> properties = Utils.asMap(keyValues);
+
+ Edge edge = new Edge(label);
+ edge.sourceId(source);
+ edge.targetId(target);
+ for (String key : properties.keySet()) {
+ edge.property(key, properties.get(key));
+ }
+
+ Assert.assertTrue(Utils.contains(edges, edge));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/FuncTestSuite.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/FuncTestSuite.java
new file mode 100644
index 0000000..05351f9
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/FuncTestSuite.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ PropertyKeyTest.class,
+ VertexLabelTest.class,
+ EdgeLabelTest.class,
+ IndexLabelTest.class,
+ SchemaTest.class,
+ VertexTest.class,
+ EdgeTest.class,
+ BatchInsertTest.class,
+ GraphManagerTest.class,
+ AuthManagerTest.class,
+ TraverserManagerTest.class,
+ MetricsManagerTest.class,
+ HugeClientHttpsTest.class,
+ HugeClientTest.class
+})
+public class FuncTestSuite {
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphManagerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphManagerTest.java
new file mode 100644
index 0000000..f421aec
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class GraphManagerTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+ BaseClientTest.initVertex();
+ BaseClientTest.initEdge();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testIterateVertices() {
+ Iterator<Vertex> vertices = graph().iterateVertices(1);
+ List<Vertex> results = IteratorUtils.toList(vertices);
+ Assert.assertEquals(6, results.size());
+
+ vertices = graph().iterateVertices(6);
+ results = IteratorUtils.toList(vertices);
+ Assert.assertEquals(6, results.size());
+
+ vertices = graph().iterateVertices(100);
+ results = IteratorUtils.toList(vertices);
+ Assert.assertEquals(6, results.size());
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testIterateEdgesWithPageLtThanTotal() {
+ Iterator<Edge> edes = graph().iterateEdges(1);
+ List<Edge> results = IteratorUtils.toList(edes);
+ Assert.assertEquals(6, results.size());
+
+ edes = graph().iterateEdges(6);
+ results = IteratorUtils.toList(edes);
+ Assert.assertEquals(6, results.size());
+
+ edes = graph().iterateEdges(100);
+ results = IteratorUtils.toList(edes);
+ Assert.assertEquals(6, results.size());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphModeTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphModeTest.java
new file mode 100644
index 0000000..2182aa6
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/GraphModeTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.GraphsManager;
+import com.baidu.hugegraph.structure.constant.GraphMode;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class GraphModeTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ graphs().mode(GRAPH, GraphMode.NONE);
+ }
+
+ @After
+ public void teardown() throws Exception {
+ graphs().mode(GRAPH, GraphMode.NONE);
+ }
+
+ @Test
+ public void testGetGraphMode() {
+ GraphsManager graphs = graphs();
+ Assert.assertEquals(GraphMode.NONE, graphs.mode(GRAPH));
+ }
+
+ @Test
+ public void testSetGraphMode() {
+ GraphsManager graphs = graphs();
+ graphs.mode(GRAPH, GraphMode.MERGING);
+ Assert.assertEquals(GraphMode.MERGING, graphs.mode(GRAPH));
+ graphs.mode(GRAPH, GraphMode.RESTORING);
+ Assert.assertEquals(GraphMode.RESTORING, graphs.mode(GRAPH));
+ graphs.mode(GRAPH, GraphMode.RESTORING);
+ Assert.assertEquals(GraphMode.RESTORING, graphs.mode(GRAPH));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientHttpsTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientHttpsTest.java
new file mode 100644
index 0000000..a3263eb
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientHttpsTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableMap;
+
+public class HugeClientHttpsTest {
+
+ private static final String BASE_URL = "https://127.0.0.1:8443";
+ private static final String GRAPH = "hugegraph";
+ private static final String USERNAME = "admin";
+ private static final String PASSWORD = "pa";
+ private static final int TIMEOUT = 10;
+ private static final int MAX_CONNS_PER_ROUTE = 10;
+ private static final int MAX_CONNS = 10;
+ private static final int IDLE_TIME = 30;
+ private static final String TRUST_STORE_FILE =
+ "src/test/resources/hugegraph.truststore";
+ private static final String TRUST_STORE_PASSWORD = "hugegraph";
+
+ private static HugeClient client;
+
+ @After
+ public void teardown() throws Exception {
+ Assert.assertNotNull("Client is not opened", client);
+ client.close();
+ }
+
+ @Test
+ public void testHttpsClientBuilderWithConnection() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientWithConnectionPoolNoUserParam() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configTimeout(TIMEOUT)
+ .configPool(MAX_CONNS, MAX_CONNS_PER_ROUTE)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientWithConnectionPoolNoTimeOutParam() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .configPool(MAX_CONNS, MAX_CONNS_PER_ROUTE)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientNewBuilderWithConnectionNoPoolParam() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .configTimeout(TIMEOUT)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientNewBuilderWithConnectionPool() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .configTimeout(TIMEOUT)
+ .configPool(MAX_CONNS, MAX_CONNS_PER_ROUTE)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .configIdleTime(IDLE_TIME)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientNewBuilderZeroPoolParam() {
+ client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .configTimeout(TIMEOUT)
+ .configPool(0, 0)
+ .configSSL(TRUST_STORE_FILE, TRUST_STORE_PASSWORD)
+ .build();
+ Assert.assertTrue(client.graphs().listGraph().contains("hugegraph"));
+ this.addVertexAndCheckPropertyValue();
+ }
+
+ @Test
+ public void testHttpsClientBuilderWithConnectionPoolNoParam() {
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ HugeClient.builder(BASE_URL, GRAPH)
+ .configUrl(null)
+ .configGraph(null)
+ .configSSL("", "")
+ .build();
+ }, e -> {
+ Assert.assertContains("The url parameter can't be null",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testHttpsClientBuilderWithConnectionPoolNoGraphParam() {
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ HugeClient.builder(BASE_URL, GRAPH)
+ .configGraph(null)
+ .configSSL("", "")
+ .build();
+ }, e -> {
+ Assert.assertContains("The graph parameter can't be null",
+ e.getMessage());
+ });
+ }
+
+ @Test
+ public void testHttpsClientBuilderWithConnectionPoolZeroIdleTimeParam() {
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ HugeClient.builder(BASE_URL, GRAPH)
+ .configIdleTime(0)
+ .build();
+ }, e -> {
+ Assert.assertContains("The idleTime parameter must be > 0, but got",
+ e.getMessage());
+ });
+ }
+
+ private void addVertexAndCheckPropertyValue() {
+ SchemaManager schema = client.schema();
+ schema.propertyKey("name").asText().ifNotExist().create();
+ schema.propertyKey("age").asInt().ifNotExist().create();
+ schema.propertyKey("city").asText().ifNotExist().create();
+ schema.vertexLabel("person")
+ .properties("name", "age", "city")
+ .primaryKeys("name")
+ .ifNotExist()
+ .create();
+ GraphManager graph = client.graph();
+ Vertex marko = graph.addVertex(T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ Map<String, Object> props = ImmutableMap.of("name", "marko",
+ "age", 29,
+ "city", "Beijing");
+ Assert.assertEquals(props, marko.properties());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientTest.java
new file mode 100644
index 0000000..06086ac
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/HugeClientTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.HugeClient;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class HugeClientTest {
+
+ protected static final String BASE_URL = "http://127.0.0.1:8080";
+ protected static final String GRAPH = "hugegraph";
+ protected static final String USERNAME = "admin";
+ protected static final String PASSWORD = "pa";
+
+ @Test
+ public void testContext() {
+ HugeClient client = HugeClient.builder(BASE_URL, GRAPH)
+ .configUser(USERNAME, PASSWORD)
+ .build();
+
+ String token = "Bearer token";
+ client.setAuthContext(token);
+ Assert.assertEquals(token, client.getAuthContext());
+
+ client.resetAuthContext();
+ Assert.assertNull(client.getAuthContext());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/IndexLabelTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/IndexLabelTest.java
new file mode 100644
index 0000000..a9192ea
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/IndexLabelTest.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public class IndexLabelTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ BaseFuncTest.initPropertyKey();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testAddIndexLabelWithUserData() {
+ SchemaManager schema = schema();
+ BaseFuncTest.initVertexLabel();
+
+ IndexLabel personByCity = schema.indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .userdata("type", "secondary")
+ .ifNotExist()
+ .create();
+ Assert.assertEquals(2, personByCity.userdata().size());
+ Assert.assertEquals("secondary", personByCity.userdata().get("type"));
+ String time = (String) personByCity.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ IndexLabel personByAge = schema.indexLabel("personByAge")
+ .onV("person")
+ .by("age")
+ .range()
+ .userdata("type", "secondary")
+ .userdata("type", "range")
+ .ifNotExist()
+ .create();
+ Assert.assertEquals(2, personByAge.userdata().size());
+ Assert.assertEquals("range", personByAge.userdata().get("type"));
+ time = (String) personByAge.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAppendIndexLabelWithUserData() {
+ SchemaManager schema = schema();
+ BaseFuncTest.initVertexLabel();
+
+ IndexLabel personByCity = schema.indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .ifNotExist()
+ .create();
+ Assert.assertEquals(1, personByCity.userdata().size());
+ String time = (String) personByCity.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ personByCity = schema.indexLabel("personByCity")
+ .userdata("type", "secondary")
+ .append();
+ Assert.assertEquals(2, personByCity.userdata().size());
+ Assert.assertEquals("secondary", personByCity.userdata().get("type"));
+ time = (String) personByCity.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testEliminateIndexLabelWithUserData() {
+ SchemaManager schema = schema();
+ BaseFuncTest.initVertexLabel();
+
+ IndexLabel personByCity = schema.indexLabel("personByCity")
+ .onV("person")
+ .by("city")
+ .secondary()
+ .userdata("type", "secondary")
+ .userdata("icon", "picture")
+ .ifNotExist()
+ .create();
+ Assert.assertEquals(3, personByCity.userdata().size());
+ Assert.assertEquals("secondary", personByCity.userdata().get("type"));
+ Assert.assertEquals("picture", personByCity.userdata().get("icon"));
+ String time = (String) personByCity.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ personByCity = schema.indexLabel("personByCity")
+ .userdata("type", "secondary")
+ .eliminate();
+ Assert.assertEquals(2, personByCity.userdata().size());
+ Assert.assertEquals("picture", personByCity.userdata().get("icon"));
+ time = (String) personByCity.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testRemoveIndexLabelSync() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("player")
+ .properties("name", "age")
+ .create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .on(true, "player")
+ .secondary()
+ .by("name")
+ .create();
+
+ Assert.assertNotNull(playerByName);
+ // Remove index label sync
+ schema.removeIndexLabel("playerByName");
+
+ playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .create();
+
+ Assert.assertNotNull(playerByName);
+ // Remove index label sync with timeout
+ schema.removeIndexLabel("playerByName", 10);
+ }
+
+ @Test
+ public void testRemoveIndexLabelAsync() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("player")
+ .properties("name", "age")
+ .create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .create();
+ Assert.assertNotNull(playerByName);
+ // Remove index label async
+ schema.removeIndexLabelAsync("playerByName");
+ }
+
+ @Test
+ public void testAddIndexLabelAsync() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("player")
+ .properties("name", "age")
+ .create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .build();
+ long task = schema.addIndexLabelAsync(playerByName);
+ waitUntilTaskCompleted(task);
+
+ playerByName = schema.getIndexLabel(playerByName.name());
+ Assert.assertNotNull(playerByName);
+ }
+
+ @Test
+ public void testListByNames() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("player").properties("name", "age").create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .create();
+ IndexLabel playerByAge = schema.indexLabel("playerByAge")
+ .onV("player")
+ .by("age")
+ .range()
+ .create();
+
+ List<IndexLabel> indexLabels;
+
+ indexLabels = schema.getIndexLabels(ImmutableList.of("playerByName"));
+ Assert.assertEquals(1, indexLabels.size());
+ assertContains(indexLabels, playerByName);
+
+ indexLabels = schema.getIndexLabels(ImmutableList.of("playerByAge"));
+ Assert.assertEquals(1, indexLabels.size());
+ assertContains(indexLabels, playerByAge);
+
+ indexLabels = schema.getIndexLabels(ImmutableList.of("playerByName",
+ "playerByAge"));
+ Assert.assertEquals(2, indexLabels.size());
+ assertContains(indexLabels, playerByName);
+ assertContains(indexLabels, playerByAge);
+ }
+
+ @Test
+ public void testResetVertexLabelId() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("player")
+ .properties("name", "age")
+ .create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .create();
+ Assert.assertTrue(playerByName.id() > 0);
+ playerByName.resetId();
+ Assert.assertEquals(0L, playerByName.id());
+ }
+
+ @Test
+ public void testSetCheckExist() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("player")
+ .properties("name", "age")
+ .create();
+ IndexLabel playerByName = schema.indexLabel("playerByName")
+ .onV("player")
+ .by("name")
+ .secondary()
+ .create();
+ Assert.assertTrue(playerByName.checkExist());
+ playerByName.checkExist(false);
+ Assert.assertFalse(playerByName.checkExist());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/MetricsManagerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/MetricsManagerTest.java
new file mode 100644
index 0000000..066053a
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/MetricsManagerTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableSet;
+
+public class MetricsManagerTest extends BaseFuncTest {
+
+ @Test
+ public void testSystemMetrics() {
+ Map<String, Map<String, Object>> results = metrics().system();
+ Assert.assertEquals(ImmutableSet.of("basic", "heap", "nonheap",
+ "thread", "class_loading",
+ "garbage_collector"),
+ results.keySet());
+ }
+
+ @Test
+ public void testBackendMetrics() {
+ Map<String, Map<String, Object>> results = metrics().backend();
+ Assert.assertEquals(ImmutableSet.of("hugegraph"), results.keySet());
+
+ Map<String, Object> graphResults = metrics().backend("hugegraph");
+ Assert.assertFalse(graphResults.isEmpty());
+ }
+
+ @Test
+ public void testAllMetrics() {
+ Map<String, Map<String, Object>> results = metrics().all();
+ Assert.assertEquals(ImmutableSet.of("gauges", "counters", "histograms",
+ "meters", "timers"),
+ results.keySet());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/PropertyKeyTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/PropertyKeyTest.java
new file mode 100644
index 0000000..df5203d
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/PropertyKeyTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.constant.WriteType;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public class PropertyKeyTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testAddPropertyKeyWithUserData() {
+ SchemaManager schema = schema();
+
+ PropertyKey age = schema.propertyKey("age")
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .create();
+ Assert.assertEquals(3, age.userdata().size());
+ Assert.assertEquals(0, age.userdata().get("min"));
+ Assert.assertEquals(100, age.userdata().get("max"));
+ String time = (String) age.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ PropertyKey id = schema.propertyKey("id")
+ .userdata("length", 15)
+ .userdata("length", 18)
+ .create();
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, id.userdata().size());
+ Assert.assertEquals(18, id.userdata().get("length"));
+ time = (String) id.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ PropertyKey sex = schema.propertyKey("sex")
+ .userdata("range",
+ ImmutableList.of("male", "female"))
+ .create();
+ Assert.assertEquals(2, sex.userdata().size());
+ Assert.assertEquals(ImmutableList.of("male", "female"),
+ sex.userdata().get("range"));
+ time = (String) sex.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAppendPropertyKeyWithUserData() {
+ SchemaManager schema = schema();
+ PropertyKey age = schema.propertyKey("age")
+ .userdata("min", 0)
+ .create();
+ Assert.assertEquals(2, age.userdata().size());
+ Assert.assertEquals(0, age.userdata().get("min"));
+ String time = (String) age.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ age = schema.propertyKey("age")
+ .userdata("min", 1)
+ .userdata("max", 100)
+ .append();
+ Assert.assertEquals(3, age.userdata().size());
+ Assert.assertEquals(1, age.userdata().get("min"));
+ Assert.assertEquals(100, age.userdata().get("max"));
+ time = (String) age.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testEliminatePropertyKeyWithUserData() {
+ SchemaManager schema = schema();
+ PropertyKey age = schema.propertyKey("age")
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .create();
+ Assert.assertEquals(3, age.userdata().size());
+ Assert.assertEquals(0, age.userdata().get("min"));
+ Assert.assertEquals(100, age.userdata().get("max"));
+ String time = (String) age.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ age = schema.propertyKey("age")
+ .userdata("max", "")
+ .eliminate();
+ Assert.assertEquals(2, age.userdata().size());
+ Assert.assertEquals(0, age.userdata().get("min"));
+ time = (String) age.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testListByNames() {
+ SchemaManager schema = schema();
+
+ PropertyKey age = schema.propertyKey("age").create();
+ PropertyKey id = schema.propertyKey("id").create();
+
+ List<PropertyKey> propertyKeys;
+
+ propertyKeys = schema.getPropertyKeys(ImmutableList.of("age"));
+ Assert.assertEquals(1, propertyKeys.size());
+ assertContains(propertyKeys, age);
+
+ propertyKeys = schema.getPropertyKeys(ImmutableList.of("id"));
+ Assert.assertEquals(1, propertyKeys.size());
+ assertContains(propertyKeys, id);
+
+ propertyKeys = schema.getPropertyKeys(ImmutableList.of("age", "id"));
+ Assert.assertEquals(2, propertyKeys.size());
+ assertContains(propertyKeys, age);
+ assertContains(propertyKeys, id);
+ }
+
+ @Test
+ public void testResetPropertyKeyId() {
+ SchemaManager schema = schema();
+ PropertyKey age = schema.propertyKey("age")
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .create();
+ Assert.assertTrue(age.id() > 0);
+ age.resetId();
+ Assert.assertEquals(0L, age.id());
+ }
+
+ @Test
+ public void testSetCheckExist() {
+ SchemaManager schema = schema();
+ PropertyKey age = schema.propertyKey("age")
+ .userdata("min", 0)
+ .userdata("max", 100)
+ .create();
+ Assert.assertTrue(age.checkExist());
+ age.checkExist(false);
+ Assert.assertFalse(age.checkExist());
+ }
+
+ @Test
+ public void testOlapPropertyKey() {
+ SchemaManager schema = schema();
+ PropertyKey pagerank = schema.propertyKey("pagerank")
+ .asDouble()
+ .writeType(WriteType.OLAP_RANGE)
+ .build();
+ schema.addPropertyKey(pagerank);
+ schema.getPropertyKey(pagerank.name());
+ schema.clearPropertyKey(pagerank);
+ schema.removePropertyKey(pagerank.name());
+ Utils.assertResponseError(404, () -> {
+ schema.getPropertyKey(pagerank.name());
+ });
+
+ long task = schema.addPropertyKeyAsync(pagerank);
+ waitUntilTaskCompleted(task);
+
+ schema.getPropertyKey(pagerank.name());
+
+ task = schema.clearPropertyKeyAsync(pagerank);
+ waitUntilTaskCompleted(task);
+
+ task = schema.removePropertyKeyAsync(pagerank.name());
+ waitUntilTaskCompleted(task);
+
+ Utils.assertResponseError(404, () -> {
+ schema.getPropertyKey(pagerank.name());
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/SchemaTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/SchemaTest.java
new file mode 100644
index 0000000..4689859
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/SchemaTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.structure.SchemaElement;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class SchemaTest extends BaseFuncTest {
+
+ @Test
+ public void testlist() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+
+ Map<String, List<SchemaElement>> schemas = schema().getSchema();
+
+ Assert.assertEquals(4, schemas.size());
+ Assert.assertTrue(schemas.containsKey("propertykeys"));
+ Assert.assertTrue(schemas.containsKey("vertexlabels"));
+ Assert.assertTrue(schemas.containsKey("edgelabels"));
+ Assert.assertTrue(schemas.containsKey("indexlabels"));
+ Assert.assertEquals(7, schemas.get("propertykeys").size());
+ Assert.assertEquals(3, schemas.get("vertexlabels").size());
+ Assert.assertEquals(2, schemas.get("edgelabels").size());
+ Assert.assertTrue(schemas.get("indexlabels").isEmpty());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/TraverserManagerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/TraverserManagerTest.java
new file mode 100644
index 0000000..93ca797
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/TraverserManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Shard;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class TraverserManagerTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+ BaseClientTest.initVertex();
+ BaseClientTest.initEdge();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testIterateVerticesByShard() {
+ List<Shard> shards = traverser().vertexShards(1 * 1024 * 1024);
+ List<Vertex> vertices = new LinkedList<>();
+ for (Shard shard : shards) {
+ Iterator<Vertex> iter = traverser().iteratorVertices(shard, 1);
+ while (iter.hasNext()) {
+ vertices.add(iter.next());
+ }
+ }
+ Assert.assertEquals(6, vertices.size());
+ }
+
+ @Test
+ public void testIterateEdgesByShard() {
+ List<Shard> shards = traverser().edgeShards(1 * 1024 * 1024);
+ List<Edge> edges = new LinkedList<>();
+ for (Shard shard : shards) {
+ Iterator<Edge> iter = traverser().iteratorEdges(shard, 1);
+ while (iter.hasNext()) {
+ edges.add(iter.next());
+ }
+ }
+ Assert.assertEquals(6, edges.size());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexLabelTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexLabelTest.java
new file mode 100644
index 0000000..151390c
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexLabelTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Date;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.driver.SchemaManager;
+import com.baidu.hugegraph.structure.Task;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public class VertexLabelTest extends BaseFuncTest {
+
+ @Before
+ public void setup() {
+ BaseFuncTest.initPropertyKey();
+ }
+
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testAddVertexLabelWithUserData() {
+ SchemaManager schema = schema();
+
+ VertexLabel player = schema.vertexLabel("player")
+ .properties("name")
+ .userdata("super_vl", "person")
+ .create();
+ Assert.assertEquals(2, player.userdata().size());
+ Assert.assertEquals("person", player.userdata().get("super_vl"));
+ String time = (String) player.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ VertexLabel runner = schema.vertexLabel("runner")
+ .properties("name")
+ .userdata("super_vl", "person")
+ .userdata("super_vl", "player")
+ .create();
+ // The same key user data will be overwritten
+ Assert.assertEquals(2, runner.userdata().size());
+ Assert.assertEquals("player", runner.userdata().get("super_vl"));
+ time = (String) runner.userdata().get("~create_time");
+ createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+ }
+
+ @Test
+ public void testAppendVertexLabelWithUserData() {
+ SchemaManager schema = schema();
+
+ VertexLabel player = schema.vertexLabel("player")
+ .properties("name")
+ .create();
+ Assert.assertEquals(1, player.userdata().size());
+ String time = (String) player.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ Assert.fail(e.getMessage());
+ }
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ player = schema.vertexLabel("player")
+ .userdata("super_vl", "person")
+ .append();
+ Assert.assertEquals(2, player.userdata().size());
+ Assert.assertEquals("person", player.userdata().get("super_vl"));
+ time = (String) player.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testEliminateVertexLabelWithUserData() {
+ SchemaManager schema = schema();
+
+ VertexLabel player = schema.vertexLabel("player")
+ .properties("name")
+ .userdata("super_vl", "person")
+ .userdata("icon", "picture1")
+ .create();
+ Assert.assertEquals(3, player.userdata().size());
+ Assert.assertEquals("person", player.userdata().get("super_vl"));
+ Assert.assertEquals("picture1", player.userdata().get("icon"));
+ String time = (String) player.userdata().get("~create_time");
+ Date createTime = DateUtil.parse(time);
+ Assert.assertTrue(createTime.before(DateUtil.now()));
+
+ player = schema.vertexLabel("player")
+ .userdata("icon", "")
+ .eliminate();
+ Assert.assertEquals(2, player.userdata().size());
+ Assert.assertEquals("person", player.userdata().get("super_vl"));
+ time = (String) player.userdata().get("~create_time");
+ Assert.assertEquals(createTime, DateUtil.parse(time));
+ }
+
+ @Test
+ public void testRemoveVertexLabelSync() {
+ SchemaManager schema = schema();
+
+ schema.vertexLabel("player").properties("name").create();
+ // Remove vertex label sync
+ schema.removeVertexLabel("player");
+
+ schema.vertexLabel("player").properties("name").create();
+ // Remove vertex label sync with timeout
+ schema.removeVertexLabel("player", 10);
+ }
+
+ @Test
+ public void testRemoveVertexLabelASync() {
+ SchemaManager schema = schema();
+ schema.vertexLabel("player").properties("name").create();
+
+ // Remove vertex label async and wait
+ long taskId = schema.removeVertexLabelAsync("player");
+ Task task = task().waitUntilTaskCompleted(taskId, 10);
+ Assert.assertTrue(task.completed());
+ }
+
+ @Test
+ public void testListByNames() {
+ SchemaManager schema = schema();
+
+ VertexLabel player = schema.vertexLabel("player").create();
+ VertexLabel runner = schema.vertexLabel("runner").create();
+
+ List<VertexLabel> vertexLabels;
+
+ vertexLabels = schema.getVertexLabels(ImmutableList.of("player"));
+ Assert.assertEquals(1, vertexLabels.size());
+ assertContains(vertexLabels, player);
+
+ vertexLabels = schema.getVertexLabels(ImmutableList.of("runner"));
+ Assert.assertEquals(1, vertexLabels.size());
+ assertContains(vertexLabels, runner);
+
+ vertexLabels = schema.getVertexLabels(ImmutableList.of("player",
+ "runner"));
+ Assert.assertEquals(2, vertexLabels.size());
+ assertContains(vertexLabels, player);
+ assertContains(vertexLabels, runner);
+ }
+
+ @Test
+ public void testResetVertexLabelId() {
+ SchemaManager schema = schema();
+ VertexLabel player = schema.vertexLabel("player")
+ .properties("name").create();
+ Assert.assertTrue(player.id() > 0);
+ player.resetId();
+ Assert.assertEquals(0L, player.id());
+ }
+
+ @Test
+ public void testSetCheckExist() {
+ SchemaManager schema = schema();
+ VertexLabel player = schema.vertexLabel("player")
+ .properties("name").build();
+ Assert.assertTrue(player.checkExist());
+ player.checkExist(false);
+ Assert.assertFalse(player.checkExist());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexTest.java
new file mode 100644
index 0000000..fab5c97
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/functional/VertexTest.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.functional;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.baidu.hugegraph.BaseClientTest;
+import com.baidu.hugegraph.exception.InvalidOperationException;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+
+public class VertexTest extends BaseFuncTest {
+
+ @Override
+ @Before
+ public void setup() {
+ BaseClientTest.initPropertyKey();
+ BaseClientTest.initVertexLabel();
+ BaseClientTest.initEdgeLabel();
+ }
+
+ @Override
+ @After
+ public void teardown() throws Exception {
+ BaseFuncTest.clearData();
+ }
+
+ @Test
+ public void testAddVertexProperty() {
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19);
+ Map<String, Object> props = ImmutableMap.of("name", "vadas",
+ "age", 19);
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("city", "Beijing");
+ props = ImmutableMap.of("name", "vadas", "age", 19,
+ "city", "Beijing");
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testUpdateVertexProperty() {
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19);
+ Map<String, Object> props = ImmutableMap.of("name", "vadas",
+ "age", 19);
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("age", 20);
+ props = ImmutableMap.of("name", "vadas", "age", 20);
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testAddVertexPropertyValueList() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueList()
+ .ifNotExist()
+ .create();
+ schema().vertexLabel("person")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("time", "2014-02-14");
+ props = ImmutableMap.of("name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2014-02-14")));
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testAddVertexPropertyValueSet() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueSet()
+ .ifNotExist()
+ .create();
+ schema().vertexLabel("person")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("time", "2014-02-14");
+ props = ImmutableMap.of("name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2014-02-14")));
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testAddVertexPropertyValueListWithSameValue() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueList()
+ .ifNotExist()
+ .create();
+ schema().vertexLabel("person")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("time", "2012-10-10");
+ props = ImmutableMap.of("name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10"),
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testAddVertexPropertyValueSetWithSameValue() {
+ schema().propertyKey("time")
+ .asDate()
+ .valueSet()
+ .ifNotExist()
+ .create();
+ schema().vertexLabel("person")
+ .properties("time")
+ .nullableKeys("time")
+ .append();
+
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "time", "2012-10-10");
+
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.property("time", "2012-10-10");
+ props = ImmutableMap.of("name", "vadas", "age", 19,
+ "time", ImmutableList.of(
+ Utils.formatDate("2012-10-10")));
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testAddVertexWithMapProperties() {
+ Map<String, Object> properties = ImmutableMap.of("name", "vadas",
+ "age", 19,
+ "city", "Beijing");
+ // 1st param is label
+ Vertex vadas = graph().addVertex("person", properties);
+ Map<String, Object> props = ImmutableMap.of("name", "vadas",
+ "age", 19,
+ "city", "Beijing");
+ Assert.assertEquals(props, vadas.properties());
+
+ properties = ImmutableMap.of("name", "java in action", "price", 88);
+ // 1st param is label, 2nd param is id
+ Vertex java = graph().addVertex("book", "ISBN-1", properties);
+ props = ImmutableMap.of("name", "java in action", "price", 88);
+ Assert.assertEquals(props, java.properties());
+ }
+
+ @Test
+ public void testRemoveVertexProperty() {
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "city", "Beijing");
+ Map<String, Object> props = ImmutableMap.of("name", "vadas",
+ "age", 19,
+ "city", "Beijing");
+ Assert.assertEquals(props, vadas.properties());
+
+ vadas.removeProperty("city");
+ props = ImmutableMap.of("name", "vadas", "age", 19);
+ Assert.assertEquals(props, vadas.properties());
+ }
+
+ @Test
+ public void testRemoveVertexPropertyNotExist() {
+ Vertex vadas = graph().addVertex(T.label, "person", "name", "vadas",
+ "age", 19, "city", "Beijing");
+ Map<String, Object> props = ImmutableMap.of("name", "vadas",
+ "age", 19,
+ "city", "Beijing");
+ Assert.assertEquals(props, vadas.properties());
+
+ Assert.assertThrows(InvalidOperationException.class, () -> {
+ vadas.removeProperty("not-exist");
+ });
+ }
+
+ @Test
+ public void testGetAllVertices() {
+ BaseClientTest.initVertex();
+
+ List<Vertex> vertices = graph().listVertices();
+ Assert.assertEquals(6, vertices.size());
+ assertContains(vertices, T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ assertContains(vertices, T.label, "person", "name", "vadas",
+ "age", 27, "city", "Hongkong");
+ assertContains(vertices, T.label, "software", "name", "lop",
+ "lang", "java", "price", 328);
+ assertContains(vertices, T.label, "person", "name", "josh",
+ "age", 32, "city", "Beijing");
+ assertContains(vertices, T.label, "software", "name", "ripple",
+ "lang", "java", "price", 199);
+ assertContains(vertices, T.label, "person", "name", "peter",
+ "age", 29, "city", "Shanghai");
+ }
+
+ @Test
+ public void testGetVerticesWithNoLimit() {
+ BaseClientTest.initVertex();
+
+ List<Vertex> vertices = graph().listVertices(-1);
+ Assert.assertEquals(6, vertices.size());
+ assertContains(vertices, T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ assertContains(vertices, T.label, "person", "name", "vadas",
+ "age", 27, "city", "Hongkong");
+ assertContains(vertices, T.label, "software", "name", "lop",
+ "lang", "java", "price", 328);
+ assertContains(vertices, T.label, "person", "name", "josh",
+ "age", 32, "city", "Beijing");
+ assertContains(vertices, T.label, "software", "name", "ripple",
+ "lang", "java", "price", 199);
+ assertContains(vertices, T.label, "person", "name", "peter",
+ "age", 29, "city", "Shanghai");
+ }
+
+ @Test
+ public void testGetVerticesByLabel() {
+ BaseClientTest.initVertex();
+
+ List<Vertex> vertices = graph().listVertices("person");
+ Assert.assertEquals(4, vertices.size());
+ assertContains(vertices, T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ assertContains(vertices, T.label, "person", "name", "vadas",
+ "age", 27, "city", "Hongkong");
+ assertContains(vertices, T.label, "person", "name", "josh",
+ "age", 32, "city", "Beijing");
+ assertContains(vertices, T.label, "person", "name", "peter",
+ "age", 29, "city", "Shanghai");
+ }
+
+ @Test
+ public void testGetVerticesByLabelWithLimit2() {
+ BaseClientTest.initVertex();
+
+ List<Vertex> vertices = graph().listVertices("person", 2);
+ Assert.assertEquals(2, vertices.size());
+ for (Vertex vertex : vertices) {
+ Assert.assertEquals("person", vertex.label());
+ }
+ }
+
+ @Test
+ public void testGetVerticesByLabelAndProperties() {
+ schema().indexLabel("personByAge").range()
+ .onV("person").by("age").create();
+ BaseClientTest.initVertex();
+
+ Map<String, Object> properties = ImmutableMap.of("age", 29);
+ List<Vertex> vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(2, vertices.size());
+ assertContains(vertices, T.label, "person", "name", "marko",
+ "age", 29, "city", "Beijing");
+ assertContains(vertices, T.label, "person", "name", "peter",
+ "age", 29, "city", "Shanghai");
+ }
+
+ @Test
+ public void testGetVerticesByLabelAndPropertiesWithLimit1() {
+ schema().indexLabel("personByAge").range()
+ .onV("person").by("age").create();
+ BaseClientTest.initVertex();
+
+ Map<String, Object> properties = ImmutableMap.of("age", 29);
+ List<Vertex> vertices = graph().listVertices("person", properties, 1);
+ Assert.assertEquals(1, vertices.size());
+ Assert.assertEquals("person", vertices.get(0).label());
+ }
+
+ @Test
+ public void testGetVerticesByLabelAndPropertiesWithRangeCondition() {
+ schema().indexLabel("personByAge").range()
+ .onV("person").by("age").create();
+ BaseClientTest.initVertex();
+
+ Map<String, Object> properties = ImmutableMap.of("age", "P.eq(29)");
+ List<Vertex> vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(2, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertEquals(29, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.gt(29)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(1, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertGt(29, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.gte(29)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(3, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertGte(29, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.lt(29)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(1, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertLt(29, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.lte(29)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(3, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertLte(29, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.between(29,32)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(2, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertGte(29, v.property("age"));
+ Assert.assertLt(31, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.inside(27,32)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(2, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertGt(27, v.property("age"));
+ Assert.assertLt(32, v.property("age"));
+ }
+
+ properties = ImmutableMap.of("age", "P.within(27,32)");
+ vertices = graph().listVertices("person", properties);
+ Assert.assertEquals(2, vertices.size());
+ for (Vertex v : vertices) {
+ Assert.assertEquals("person", v.label());
+ Assert.assertThat(v.property("age"), CoreMatchers.anyOf(
+ CoreMatchers.is(27), CoreMatchers.is(32)));
+ }
+ }
+
+ @Test
+ public void testGetVerticesByLabelAndPropertiesWithKeepP() {
+ schema().indexLabel("personByAge").range()
+ .onV("person").by("age").create();
+ schema().indexLabel("personByCity").secondary()
+ .onV("person").by("city").create();
+ BaseClientTest.initVertex();
+
+ Map<String, Object> properties = ImmutableMap.of("age", "P.eq(29)");
+ List<Vertex> vertices = graph().listVertices("person", properties,
+ false);
+ Assert.assertEquals(2, vertices.size());
+
+ Assert.assertThrows(ServerException.class, () -> {
+ graph().listVertices("person", properties, true);
+ }, e -> {
+ Assert.assertContains("expect INT for 'age'", e.getMessage());
+ });
+
+ Map<String, Object> properties2 = ImmutableMap.of("city", "P.gt(1)");
+ vertices = graph().listVertices("person", properties2, true);
+ Assert.assertEquals(0, vertices.size());
+
+ vertices = graph().listVertices("person", properties2, true, 3);
+ Assert.assertEquals(0, vertices.size());
+ }
+
+ @Test
+ public void testIterateVerticesByLabel() {
+ BaseClientTest.initVertex();
+
+ Iterator<Vertex> vertices = graph().iterateVertices("person", 1);
+ Assert.assertEquals(4, Iterators.size(vertices));
+
+ vertices = graph().iterateVertices("software", 1);
+ Assert.assertEquals(2, Iterators.size(vertices));
+ }
+
+ @Test
+ public void testIterateVerticesByLabelAndProperties() {
+ schema().indexLabel("personByCity").secondary()
+ .onV("person").by("city").create();
+ BaseClientTest.initVertex();
+
+ Map<String, Object> properties = ImmutableMap.of("city", "Beijing");
+ Iterator<Vertex> vertices = graph().iterateVertices("person",
+ properties, 1);
+ Assert.assertEquals(2, Iterators.size(vertices));
+ }
+
+ private static void assertContains(List<Vertex> vertices,
+ Object... keyValues) {
+ String label = Utils.getLabelValue(keyValues).get();
+ Map<String, Object> properties = Utils.asMap(keyValues);
+
+ Vertex vertex = new Vertex(label);
+ for (String key : properties.keySet()) {
+ if (key.equals(T.label)) {
+ continue;
+ }
+ vertex.property(key, properties.get(key));
+ }
+
+ Assert.assertTrue(Utils.contains(vertices, vertex));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/testutil/Utils.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/testutil/Utils.java
new file mode 100644
index 0000000..9cdad4a
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/testutil/Utils.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.testutil;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.baidu.hugegraph.date.SafeDateFormat;
+import com.baidu.hugegraph.exception.ServerException;
+import com.baidu.hugegraph.structure.GraphElement;
+import com.baidu.hugegraph.structure.constant.T;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert.ThrowableRunnable;
+import com.baidu.hugegraph.util.DateUtil;
+import com.google.common.collect.ImmutableList;
+
+public final class Utils {
+
+ private static final String DF = "yyyy-MM-dd HH:mm:ss.SSS";
+ private static final SafeDateFormat DATE_FORMAT = new SafeDateFormat(DF);
+
+ public static void assertResponseError(int status, ThrowableRunnable run) {
+ Assert.assertThrows(ServerException.class, run, (e) -> {
+ if (e instanceof ServerException) {
+ Assert.assertEquals("The rest status code is not matched",
+ status, ((ServerException) e).status());
+ }
+ });
+ }
+
+ public static void assertGraphEqual(ImmutableList<Vertex> vertices,
+ ImmutableList<Edge> edges,
+ List<Object> objects) {
+ for (Object object : objects) {
+ Assert.assertTrue(object instanceof GraphElement);
+ if (object instanceof Vertex) {
+ Assert.assertTrue(Utils.contains(vertices, (Vertex) object));
+ } else {
+ Assert.assertTrue(object instanceof Edge);
+ Assert.assertTrue(Utils.contains(edges, (Edge) object));
+ }
+ }
+ }
+
+ public static boolean contains(List<PropertyKey> propertyKeys,
+ PropertyKey propertyKey) {
+ for (PropertyKey pk : propertyKeys) {
+ if (equalPropertyKey(pk, propertyKey)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(List<VertexLabel> vertexLabels,
+ VertexLabel vertexLabel) {
+ for (VertexLabel vl : vertexLabels) {
+ if (equalVertexLabel(vl, vertexLabel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(List<EdgeLabel> edgeLabels,
+ EdgeLabel edgeLabel) {
+ for (EdgeLabel el : edgeLabels) {
+ if (equalEdgeLabel(el, edgeLabel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(List<IndexLabel> indexLabels,
+ IndexLabel indexLabel) {
+ for (IndexLabel il : indexLabels) {
+ if (equalIndexLabel(il, indexLabel)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(List<Vertex> vertices, Vertex vertex) {
+ for (Vertex v : vertices) {
+ if (equalVertex(v, vertex)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean contains(List<Edge> edges, Edge edge) {
+ for (Edge e : edges) {
+ if (equalEdge(e, edge)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean equalPropertyKey(PropertyKey left,
+ PropertyKey right) {
+ if (!left.name().equals(right.name())) {
+ return false;
+ }
+ if (left.dataType() != right.dataType()) {
+ return false;
+ }
+ if (left.cardinality() != right.cardinality()) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean equalVertexLabel(VertexLabel left,
+ VertexLabel right) {
+ assert left != null;
+ assert right != null;
+
+ if (!left.name().equals(right.name())) {
+ return false;
+ }
+ if (left.idStrategy() != right.idStrategy()) {
+ return false;
+ }
+ if (left.properties().size() != right.properties().size() ||
+ !left.properties().containsAll(right.properties())) {
+ return false;
+ }
+ if (left.primaryKeys().size() != right.primaryKeys().size() ||
+ !left.primaryKeys().containsAll(right.primaryKeys())) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean equalEdgeLabel(EdgeLabel left, EdgeLabel right) {
+ assert left != null;
+ assert right != null;
+
+ if (!left.name().equals(right.name())) {
+ return false;
+ }
+ if (!left.sourceLabel().equals(right.sourceLabel())) {
+ return false;
+ }
+ if (!left.targetLabel().equals(right.targetLabel())) {
+ return false;
+ }
+ if (left.frequency() != right.frequency()) {
+ return false;
+ }
+ if (left.properties().size() != right.properties().size() ||
+ !left.properties().containsAll(right.properties())) {
+ return false;
+ }
+ if (left.sortKeys().size() != right.sortKeys().size() ||
+ !left.sortKeys().containsAll(right.sortKeys())) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean equalIndexLabel(IndexLabel left,
+ IndexLabel right) {
+ assert left != null;
+ assert right != null;
+
+ if (!left.name().equals(right.name())) {
+ return false;
+ }
+ if (left.baseType() != right.baseType()) {
+ return false;
+ }
+ if (!left.baseValue().equals(right.baseValue())) {
+ return false;
+ }
+ if (left.indexType() != right.indexType()) {
+ return false;
+ }
+ if (left.indexFields().size() != right.indexFields().size() ||
+ !left.indexFields().containsAll(right.indexFields())) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean equalVertex(Vertex left, Vertex right) {
+ assert left != null;
+ assert right != null;
+
+ if (!left.label().equals(right.label())) {
+ return false;
+ }
+ Map<String, Object> leftProps = left.properties();
+ Map<String, Object> rightProps = right.properties();
+ if (leftProps.size() != rightProps.size() ||
+ !leftProps.keySet().containsAll(rightProps.keySet())) {
+ return false;
+ }
+ for (String key : leftProps.keySet()) {
+ if (!leftProps.get(key).equals(rightProps.get(key)) &&
+ leftProps.get(key) != rightProps.get(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean equalEdge(Edge left, Edge right) {
+ assert left != null;
+ assert right != null;
+
+ if (!left.label().equals(right.label())) {
+ return false;
+ }
+ if (!left.sourceId().equals(right.sourceId())) {
+ return false;
+ }
+ if (!left.targetId().equals(right.targetId())) {
+ return false;
+ }
+
+ // Only compare source label when the expected `right` passed
+ if (right.sourceLabel() != null) {
+ if (!left.sourceLabel().equals(right.sourceLabel())) {
+ return false;
+ }
+ }
+ // Only compare target label when the expected `right` passed
+ if (right.targetLabel() != null) {
+ if (!left.targetLabel().equals(right.targetLabel())) {
+ return false;
+ }
+ }
+
+ Map<String, Object> leftProps = left.properties();
+ Map<String, Object> rightProps = right.properties();
+ if (leftProps.size() != rightProps.size() ||
+ !leftProps.keySet().containsAll(rightProps.keySet())) {
+ return false;
+ }
+ for (String key : leftProps.keySet()) {
+ if (!leftProps.get(key).equals(rightProps.get(key)) &&
+ leftProps.get(key) != rightProps.get(key)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static Optional<String> getLabelValue(final Object... keyValues) {
+ for (int i = 0; i < keyValues.length; i = i + 2) {
+ if (keyValues[i].equals(T.label)) {
+ return Optional.of((String) keyValues[i + 1]);
+ }
+ }
+ return Optional.empty();
+ }
+
+ public static Map<String, Object> asMap(Object... keyValues) {
+ return Utils.asPairs(keyValues).stream()
+ .collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
+ }
+
+ public static List<Pair<String, Object>> asPairs(Object... keyValues) {
+ final List<Object> list = Arrays.asList(keyValues);
+ return IntStream.range(1, list.size())
+ .filter(i -> i % 2 != 0)
+ .mapToObj(i -> Pair.of(list.get(i - 1).toString(),
+ list.get(i)))
+ .collect(Collectors.toList());
+ }
+
+ public static long date(String date) {
+ return date(date, "yyyy-MM-dd");
+ }
+
+ public static long date(String date, String pattern) {
+ return DateUtil.parse(date, pattern).getTime();
+ }
+
+ public static String formatDate(String date) {
+ return DATE_FORMAT.format(DateUtil.parse(date));
+ }
+
+ public static String formatDate(Date date) {
+ return DATE_FORMAT.format(date);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BaseUnitTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BaseUnitTest.java
new file mode 100644
index 0000000..a76856c
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BaseUnitTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import com.baidu.hugegraph.util.JsonUtil;
+
+public class BaseUnitTest {
+
+ public static <T> String serialize(T data) {
+ return JsonUtil.toJson(data);
+ }
+
+ public static <T> T deserialize(String json, Class<T> clazz) {
+ return JsonUtil.fromJson(json, clazz);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BatchElementRequestTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BatchElementRequestTest.java
new file mode 100644
index 0000000..283cffd
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/BatchElementRequestTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import static com.baidu.hugegraph.structure.graph.UpdateStrategy.INTERSECTION;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.graph.BatchEdgeRequest;
+import com.baidu.hugegraph.structure.graph.BatchVertexRequest;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.UpdateStrategy;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Whitebox;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class BatchElementRequestTest extends BaseUnitTest {
+
+ @Test
+ public void testVertexRequestBuildOK() {
+ List<Vertex> vertices = ImmutableList.of(createVertex());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of("set",
+ INTERSECTION);
+
+ BatchVertexRequest req;
+ req = new BatchVertexRequest.Builder().vertices(vertices)
+ .updatingStrategies(strategies)
+ .createIfNotExist(true)
+ .build();
+
+ Assert.assertNotNull(req);
+ Object list = Whitebox.getInternalState(req, "vertices");
+ Assert.assertEquals(vertices, list);
+ Object map = Whitebox.getInternalState(req, "updateStrategies");
+ Assert.assertEquals(strategies, map);
+ Object created = Whitebox.getInternalState(req, "createIfNotExist");
+ Assert.assertEquals(true, created);
+ }
+
+ @Test
+ public void testVertexEmptyUpdateStrategy() {
+ List<Vertex> vertices = ImmutableList.of(createVertex());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of();
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ new BatchVertexRequest.Builder().vertices(vertices)
+ .updatingStrategies(strategies)
+ .createIfNotExist(true)
+ .build();
+ });
+ }
+
+ @Test
+ public void testVertexNotSupportedUpdateParameter() {
+ List<Vertex> vertices = ImmutableList.of(createVertex());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of("set",
+ INTERSECTION);
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ new BatchVertexRequest.Builder().vertices(vertices)
+ .updatingStrategies(strategies)
+ .createIfNotExist(false)
+ .build();
+ });
+ }
+
+ @Test
+ public void testEdgeRequestBuildOK() {
+ List<Edge> edges = ImmutableList.of(createEdge());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of("set",
+ INTERSECTION);
+
+ BatchEdgeRequest req;
+ req = new BatchEdgeRequest.Builder().edges(edges)
+ .updatingStrategies(strategies)
+ .checkVertex(false)
+ .createIfNotExist(true)
+ .build();
+
+ Assert.assertNotNull(req);
+ Object list = Whitebox.getInternalState(req, "edges");
+ Assert.assertEquals(edges, list);
+ Object map = Whitebox.getInternalState(req, "updateStrategies");
+ Assert.assertEquals(strategies, map);
+ Object checked = Whitebox.getInternalState(req, "checkVertex");
+ Assert.assertEquals(false, checked);
+ Object created = Whitebox.getInternalState(req, "createIfNotExist");
+ Assert.assertEquals(true, created);
+ }
+
+ @Test
+ public void testEdgeEmptyUpdateStrategy() {
+ List<Edge> edges = ImmutableList.of(createEdge());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of();
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ new BatchEdgeRequest.Builder().edges(edges)
+ .updatingStrategies(strategies)
+ .checkVertex(false)
+ .createIfNotExist(true)
+ .build();
+ });
+ }
+
+ @Test
+ public void testEdgeNotSupportedUpdateParameter() {
+ List<Edge> edges = ImmutableList.of(createEdge());
+ Map<String, UpdateStrategy> strategies = ImmutableMap.of("set",
+ INTERSECTION);
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ new BatchEdgeRequest.Builder().edges(edges)
+ .updatingStrategies(strategies)
+ .checkVertex(false)
+ .createIfNotExist(false)
+ .build();
+ });
+ }
+
+ private static Vertex createVertex() {
+ Vertex vertex = new Vertex("object");
+ vertex.id("object:1");
+ vertex.property("name", 1);
+ vertex.property("price", 2);
+ return vertex;
+ }
+
+ private static Edge createEdge() {
+ Edge edge = new Edge("updates");
+ edge.id("object:1>updates>>object:2");
+ edge.sourceId("object:1");
+ edge.sourceLabel("object");
+ edge.targetId("object:2");
+ edge.targetLabel("object");
+ edge.property("price", 1);
+ return edge;
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/CommonUtilTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/CommonUtilTest.java
new file mode 100644
index 0000000..41c55f1
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/CommonUtilTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.CommonUtil;
+import com.google.common.collect.ImmutableMap;
+
+public class CommonUtilTest {
+
+ @Test
+ public void testCheckMapClass() {
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ CommonUtil.checkMapClass(null, Integer.class, String.class);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ CommonUtil.checkMapClass("Not map", Integer.class, String.class);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ CommonUtil.checkMapClass(ImmutableMap.of(1, "1"), null,
+ String.class);
+ });
+ Assert.assertThrows(IllegalArgumentException.class, () -> {
+ CommonUtil.checkMapClass(ImmutableMap.of(1, "1"), Integer.class,
+ null);
+ });
+
+ CommonUtil.checkMapClass(ImmutableMap.of(1, "1", 2, "2"),
+ Integer.class, String.class);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IdUtilTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IdUtilTest.java
new file mode 100644
index 0000000..6bd0fd8
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IdUtilTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.baidu.hugegraph.util.IdUtil;
+
+public class IdUtilTest {
+
+ @Test
+ public void testEscape() {
+ Assert.assertEquals("a2b2c",
+ IdUtil.escape('2', '\u0000', "a", "b", "c"));
+ Assert.assertEquals("12\u0000223",
+ IdUtil.escape('2', '\u0000', "1", "2", "3"));
+ }
+
+ @Test
+ public void testUnescape() {
+ Assert.assertArrayEquals(new String[]{"a", "b>c", "d"},
+ IdUtil.unescape("a>b/>c>d", ">", "/"));
+ Assert.assertEquals(1, IdUtil.unescape("", "", "").length);
+ Assert.assertEquals(1, IdUtil.unescape("foo", "bar", "baz").length);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IndexLabelTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IndexLabelTest.java
new file mode 100644
index 0000000..8ada3dc
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/IndexLabelTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.exception.NotSupportException;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.util.JsonUtil;
+
+public class IndexLabelTest {
+
+ @Test
+ public void testIndexLabel() {
+ IndexLabel.Builder builder = new IndexLabel.BuilderImpl("personByAge",
+ null);
+ IndexLabel indexLabel = builder.onV("person")
+ .secondary()
+ .by("age")
+ .build();
+
+ String json = "{\"name\":\"personByAge\",\"id\":0," +
+ "\"check_exist\":true,\"user_data\":{}," +
+ "\"base_type\":\"VERTEX_LABEL\"," +
+ "\"base_value\":\"person\"," +
+ "\"index_type\":\"SECONDARY\",\"fields\":[\"age\"]," +
+ "\"rebuild\":true}";
+ Assert.assertEquals(json, JsonUtil.toJson(indexLabel));
+ Assert.assertEquals(HugeType.INDEX_LABEL.string(), indexLabel.type());
+ }
+
+ @Test
+ public void testIndexLabelV49() {
+ IndexLabel.Builder builder = new IndexLabel.BuilderImpl("personByAge",
+ null);
+ IndexLabel indexLabel = builder.onV("person")
+ .secondary()
+ .by("age")
+ .build();
+
+ IndexLabel.IndexLabelV49 indexLabelV49 = indexLabel.switchV49();
+ // Without userdata
+ String json = "{\"id\":0,\"name\":\"personByAge\"," +
+ "\"check_exist\":true,\"base_type\":\"VERTEX_LABEL\"," +
+ "\"base_value\":\"person\"," +
+ "\"index_type\":\"SECONDARY\",\"fields\":[\"age\"]}";
+ Assert.assertEquals(json, JsonUtil.toJson(indexLabelV49));
+ Assert.assertEquals(HugeType.INDEX_LABEL.string(),
+ indexLabelV49.type());
+
+ Assert.assertThrows(NotSupportException.class, () -> {
+ indexLabelV49.userdata();
+ });
+ }
+
+ @Test
+ public void testIndexLabelV56() {
+ IndexLabel.Builder builder = new IndexLabel.BuilderImpl("personByAge",
+ null);
+ IndexLabel indexLabel = builder.onV("person")
+ .secondary()
+ .by("age")
+ .build();
+
+ IndexLabel.IndexLabelV56 indexLabelV56 = indexLabel.switchV56();
+
+ String json = "{\"id\":0,\"name\":\"personByAge\"," +
+ "\"check_exist\":true,\"user_data\":{}," +
+ "\"base_type\":\"VERTEX_LABEL\",\"base_value\":\"person\"," +
+ "\"index_type\":\"SECONDARY\",\"fields\":[\"age\"]}";
+ Assert.assertEquals(json, JsonUtil.toJson(indexLabelV56));
+ Assert.assertEquals(HugeType.INDEX_LABEL.string(),
+ indexLabelV56.type());
+
+ Assert.assertThrows(NotSupportException.class, () -> {
+ indexLabelV56.rebuild();
+ });
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PathSerializerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PathSerializerTest.java
new file mode 100644
index 0000000..6d772cb
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PathSerializerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.google.common.collect.ImmutableList;
+
+public class PathSerializerTest extends BaseUnitTest {
+
+ @Test
+ public void testSerializeAndDeserializePathWithVertexAndEdge() {
+ Vertex vertex = new Vertex("person");
+ vertex.id("person:marko");
+ vertex.property("name", "marko");
+ vertex.property("age", 29);
+ vertex.property("city", "Beijing");
+
+ Edge edge = new Edge("knows");
+ edge.id("person:marko>knows>>person:vadas");
+ edge.sourceId("person:marko");
+ edge.sourceLabel("person");
+ edge.targetId("person:vadas");
+ edge.targetLabel("person");
+ edge.property("date", "2016-01-10");
+ edge.property("weight", 0.5);
+
+ Path path = new Path();
+ path.labels(ImmutableList.of());
+ path.labels(ImmutableList.of());
+ path.objects(vertex);
+ path.objects(edge);
+
+ String json = serialize(path);
+ Path pathCopy = deserialize(json, Path.class);
+
+ Assert.assertEquals(2, pathCopy.objects().size());
+ Utils.assertGraphEqual(ImmutableList.of(vertex),
+ ImmutableList.of(edge),
+ path.objects());
+ }
+
+ @Test
+ public void testDeserializePathWithSimpleType() {
+ String json = "{"
+ + "\"labels\":["
+ + "[],"
+ + "[]"
+ + "],"
+ + "\"objects\":["
+ + "\"marko\","
+ + "\"lop\""
+ + "]"
+ + "}";
+
+ Path path = deserialize(json, Path.class);
+
+ Assert.assertEquals(2, path.labels().size());
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(0));
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(1));
+
+ Assert.assertEquals(2, path.objects().size());
+ Assert.assertArrayEquals(new Object[]{"marko", "lop"},
+ path.objects().toArray());
+
+ json = "{"
+ + "\"labels\":["
+ + "[],"
+ + "[]"
+ + "],"
+ + "\"objects\":["
+ + "29,"
+ + "32"
+ + "]"
+ + "}";
+
+ path = deserialize(json, Path.class);
+
+ Assert.assertEquals(2, path.objects().size());
+ Assert.assertArrayEquals(new Object[]{29, 32},
+ path.objects().toArray());
+ }
+
+ @Test
+ public void testDeserializePathWithListType() {
+ String json = "{"
+ + "\"labels\":["
+ + "[],"
+ + "[]"
+ + "],"
+ + "\"objects\":["
+ + "[\"Beijing\", \"Beijing\"],"
+ + "[\"Wuhan\", \"Hongkong\"]"
+ + "]"
+ + "}";
+
+ Path path = BaseUnitTest.deserialize(json, Path.class);
+
+ Assert.assertEquals(2, path.labels().size());
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(0));
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(1));
+
+ Assert.assertEquals(2, path.objects().size());
+ Assert.assertArrayEquals(new Object[]{
+ ImmutableList.of("Beijing", "Beijing"),
+ ImmutableList.of("Wuhan", "Hongkong")},
+ path.objects().toArray());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PropertyKeyTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PropertyKeyTest.java
new file mode 100644
index 0000000..7b13cd3
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/PropertyKeyTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.constant.Cardinality;
+import com.baidu.hugegraph.structure.constant.DataType;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.testutil.Assert;
+
+public class PropertyKeyTest {
+
+ @Test
+ public void testPropertyKey() {
+ PropertyKey.Builder builder = new PropertyKey.BuilderImpl("name",
+ null);
+ PropertyKey propertyKey = builder.dataType(DataType.INT)
+ .cardinality(Cardinality.SINGLE)
+ .userdata("min", 1)
+ .userdata("max", 100)
+ .build();
+
+ String pkString = "{name=name, cardinality=SINGLE, dataType=INT, " +
+ "aggregateType=NONE, properties=[], " +
+ "writeType=OLTP}";
+ Assert.assertEquals(pkString, propertyKey.toString());
+ Assert.assertEquals(HugeType.PROPERTY_KEY.string(), propertyKey.type());
+ Assert.assertEquals(0, propertyKey.aggregateType().code());
+ Assert.assertEquals("none", propertyKey.aggregateType().string());
+ }
+
+ @Test
+ public void testPropertyKeyV46() {
+ PropertyKey.Builder builder = new PropertyKey.BuilderImpl("name",
+ null);
+ PropertyKey propertyKey = builder.dataType(DataType.INT)
+ .cardinality(Cardinality.SINGLE)
+ .userdata("min", 1)
+ .userdata("max", 100)
+ .build();
+
+ PropertyKey.PropertyKeyV46 propertyKeyV46 = propertyKey.switchV46();
+ String pkV46String = "{name=name, cardinality=SINGLE, " +
+ "dataType=INT, properties=[]}";
+ Assert.assertEquals(pkV46String, propertyKeyV46.toString());
+ Assert.assertEquals(HugeType.PROPERTY_KEY.string(),
+ propertyKeyV46.type());
+ }
+
+ @Test
+ public void testPropertyKeyV58() {
+ PropertyKey.Builder builder = new PropertyKey.BuilderImpl("name",
+ null);
+ PropertyKey propertyKey = builder.dataType(DataType.INT)
+ .cardinality(Cardinality.SINGLE)
+ .userdata("min", 1)
+ .userdata("max", 100)
+ .build();
+
+ PropertyKey.PropertyKeyV58 propertyKeyV58 = propertyKey.switchV58();
+ String pkV58String = "{name=name, cardinality=SINGLE, " +
+ "dataType=INT, aggregateType=NONE, properties=[]}";
+ Assert.assertEquals(pkV58String, propertyKeyV58.toString());
+ Assert.assertEquals(HugeType.PROPERTY_KEY.string(),
+ propertyKeyV58.type());
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/RestResultTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/RestResultTest.java
new file mode 100644
index 0000000..9b617b2
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/RestResultTest.java
@@ -0,0 +1,1020 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.baidu.hugegraph.driver.GraphManager;
+import com.baidu.hugegraph.rest.RestResult;
+import com.baidu.hugegraph.serializer.PathDeserializer;
+import com.baidu.hugegraph.structure.constant.Cardinality;
+import com.baidu.hugegraph.structure.constant.DataType;
+import com.baidu.hugegraph.structure.constant.Frequency;
+import com.baidu.hugegraph.structure.constant.HugeType;
+import com.baidu.hugegraph.structure.constant.IdStrategy;
+import com.baidu.hugegraph.structure.constant.IndexType;
+import com.baidu.hugegraph.structure.graph.Edge;
+import com.baidu.hugegraph.structure.graph.Path;
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.structure.gremlin.Response;
+import com.baidu.hugegraph.structure.gremlin.Result;
+import com.baidu.hugegraph.structure.schema.EdgeLabel;
+import com.baidu.hugegraph.structure.schema.IndexLabel;
+import com.baidu.hugegraph.structure.schema.PropertyKey;
+import com.baidu.hugegraph.structure.schema.VertexLabel;
+import com.baidu.hugegraph.testutil.Assert;
+import com.baidu.hugegraph.testutil.Utils;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RestResultTest extends BaseUnitTest {
+
+ private jakarta.ws.rs.core.Response mockResponse;
+ private static GraphManager graphManager;
+
+ @BeforeClass
+ public static void init() {
+ graphManager = Mockito.mock(GraphManager.class);
+
+ SimpleModule module = new SimpleModule();
+ module.addDeserializer(Path.class, new PathDeserializer());
+ RestResult.registerModule(module);
+ }
+
+ public static GraphManager graph() {
+ return graphManager;
+ }
+
+ @Before
+ public void setup() {
+ // Mock caches
+ this.mockResponse = Mockito.mock(jakarta.ws.rs.core.Response.class);
+ }
+
+ @After
+ public void teardown() {
+ // pass
+ }
+
+ @Test
+ public void testReadPropertyKey() {
+ String json = "{"
+ + "\"id\": 3,"
+ + "\"data_type\": \"INT\","
+ + "\"name\": \"id\","
+ + "\"cardinality\": \"SINGLE\","
+ + "\"properties\": []"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ PropertyKey propertyKey = result.readObject(PropertyKey.class);
+
+ Assert.assertEquals("id", propertyKey.name());
+ Assert.assertEquals(DataType.INT, propertyKey.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey.cardinality());
+ Assert.assertEquals(Collections.emptySet(), propertyKey.properties());
+ }
+
+ @Test
+ public void testReadPropertyKeys() {
+ String json = "{\"propertykeys\": ["
+ + "{"
+ + "\"id\": 3,"
+ + "\"data_type\": \"TEXT\","
+ + "\"name\": \"id\","
+ + "\"cardinality\": \"SINGLE\","
+ + "\"properties\": []"
+ + "},"
+ + "{\"id\": 4,"
+ + "\"data_type\": \"FLOAT\","
+ + "\"name\": \"date\","
+ + "\"cardinality\": \"SET\","
+ + "\"properties\": []"
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<PropertyKey> propertyKeys = result.readList("propertykeys",
+ PropertyKey.class);
+ Assert.assertEquals(2, propertyKeys.size());
+ PropertyKey propertyKey1 = propertyKeys.get(0);
+ PropertyKey propertyKey2 = propertyKeys.get(1);
+
+ Assert.assertEquals("id", propertyKey1.name());
+ Assert.assertEquals(DataType.TEXT, propertyKey1.dataType());
+ Assert.assertEquals(Cardinality.SINGLE, propertyKey1.cardinality());
+ Assert.assertEquals(Collections.emptySet(), propertyKey1.properties());
+
+ Assert.assertEquals("date", propertyKey2.name());
+ Assert.assertEquals(DataType.FLOAT, propertyKey2.dataType());
+ Assert.assertEquals(Cardinality.SET, propertyKey2.cardinality());
+ Assert.assertEquals(Collections.emptySet(), propertyKey2.properties());
+ }
+
+ @Test
+ public void testReadVertexLabel() {
+ String json = "{"
+ + "\"id\": 1,"
+ + "\"primary_keys\": [\"name\"],"
+ + "\"index_labels\": [],"
+ + "\"name\": \"software\","
+ + "\"id_strategy\": \"PRIMARY_KEY\","
+ + "\"properties\": [\"price\", \"name\", \"lang\"]"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ VertexLabel vertexLabel = result.readObject(VertexLabel.class);
+
+ Assert.assertEquals("software", vertexLabel.name());
+ Assert.assertEquals(IdStrategy.PRIMARY_KEY, vertexLabel.idStrategy());
+ Assert.assertEquals(ImmutableList.of("name"),
+ vertexLabel.primaryKeys());
+ Assert.assertEquals(ImmutableSet.of("price", "name", "lang"),
+ vertexLabel.properties());
+ }
+
+ @Test
+ public void testReadVertexLabels() {
+ String json = "{\"vertexlabels\": ["
+ + "{"
+ + "\"id\": 1,"
+ + "\"primary_keys\": [\"name\"],"
+ + "\"index_labels\": [],"
+ + "\"name\": \"software\","
+ + "\"id_strategy\": \"PRIMARY_KEY\","
+ + "\"properties\": [\"price\", \"name\", \"lang\"]"
+ + "},"
+ + "{"
+ + "\"id\": 2,"
+ + "\"primary_keys\": [],"
+ + "\"index_labels\": [],"
+ + "\"name\": \"person\","
+ + "\"id_strategy\": \"CUSTOMIZE_STRING\","
+ + "\"properties\": [\"city\", \"name\", \"age\"]"
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<VertexLabel> vertexLabels = result.readList("vertexlabels",
+ VertexLabel.class);
+ Assert.assertEquals(2, vertexLabels.size());
+ VertexLabel vertexLabel1 = vertexLabels.get(0);
+ VertexLabel vertexLabel2 = vertexLabels.get(1);
+
+ Assert.assertEquals("software", vertexLabel1.name());
+ Assert.assertEquals(IdStrategy.PRIMARY_KEY, vertexLabel1.idStrategy());
+ Assert.assertEquals(ImmutableList.of("name"),
+ vertexLabel1.primaryKeys());
+ Assert.assertEquals(ImmutableSet.of("price", "name", "lang"),
+ vertexLabel1.properties());
+
+ Assert.assertEquals("person", vertexLabel2.name());
+ Assert.assertEquals(IdStrategy.CUSTOMIZE_STRING, vertexLabel2.idStrategy());
+ Assert.assertEquals(Collections.emptyList(),
+ vertexLabel2.primaryKeys());
+ Assert.assertEquals(ImmutableSet.of("city", "name", "age"),
+ vertexLabel2.properties());
+ }
+
+ @Test
+ public void testReadEdgeLabel() {
+ String json = "{"
+ + "\"id\": 2,"
+ + "\"source_label\": \"person\","
+ + "\"index_labels\": [\"createdByDate\"],"
+ + "\"name\": \"created\","
+ + "\"target_label\": \"software\","
+ + "\"sort_keys\": [],"
+ + "\"properties\": [\"date\"],"
+ + "\"frequency\": \"SINGLE\""
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ EdgeLabel edgeLabel = result.readObject(EdgeLabel.class);
+
+ Assert.assertEquals("created", edgeLabel.name());
+ Assert.assertEquals("person", edgeLabel.sourceLabel());
+ Assert.assertEquals("software", edgeLabel.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel.frequency());
+ Assert.assertEquals(Collections.emptyList(), edgeLabel.sortKeys());
+ Assert.assertEquals(ImmutableSet.of("date"), edgeLabel.properties());
+ }
+
+ @Test
+ public void testReadEdgeLabels() {
+ String json = "{\"edgelabels\": ["
+ + "{"
+ + "\"id\": 2,"
+ + "\"source_label\": \"person\","
+ + "\"index_labels\": [\"createdByDate\"],"
+ + "\"name\": \"created\","
+ + "\"target_label\": \"software\","
+ + "\"sort_keys\": [],"
+ + "\"properties\": [\"date\"],"
+ + "\"frequency\": \"SINGLE\""
+ + "},"
+ + "{\"id\": 3,"
+ + "\"source_label\": \"person\","
+ + "\"index_labels\": [],"
+ + "\"name\": \"knows\","
+ + "\"target_label\": \"person\","
+ + "\"sort_keys\": [],"
+ + "\"properties\": [\"date\", \"city\"],"
+ + "\"frequency\": \"SINGLE\""
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<EdgeLabel> edgeLabels = result.readList("edgelabels",
+ EdgeLabel.class);
+ Assert.assertEquals(2, edgeLabels.size());
+ EdgeLabel edgeLabel1 = edgeLabels.get(0);
+ EdgeLabel edgeLabel2 = edgeLabels.get(1);
+
+ Assert.assertEquals("created", edgeLabel1.name());
+ Assert.assertEquals("person", edgeLabel1.sourceLabel());
+ Assert.assertEquals("software", edgeLabel1.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel1.frequency());
+ Assert.assertEquals(Collections.emptyList(), edgeLabel1.sortKeys());
+ Assert.assertEquals(ImmutableSet.of("date"), edgeLabel1.properties());
+
+ Assert.assertEquals("knows", edgeLabel2.name());
+ Assert.assertEquals("person", edgeLabel2.sourceLabel());
+ Assert.assertEquals("person", edgeLabel2.targetLabel());
+ Assert.assertEquals(Frequency.SINGLE, edgeLabel2.frequency());
+ Assert.assertEquals(Collections.emptyList(), edgeLabel2.sortKeys());
+ Assert.assertEquals(ImmutableSet.of("date", "city"),
+ edgeLabel2.properties());
+ }
+
+ @Test
+ public void testReadIndexLabel() {
+ String json = "{"
+ + "\"id\": \"4\","
+ + "\"index_type\": \"SEARCH\","
+ + "\"base_value\": \"software\","
+ + "\"name\": \"softwareByPrice\","
+ + "\"fields\": [\"price\"],"
+ + "\"base_type\": \"VERTEX_LABEL\""
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ IndexLabel indexLabel = result.readObject(IndexLabel.class);
+
+ Assert.assertEquals("softwareByPrice", indexLabel.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel.baseType());
+ Assert.assertEquals("software", indexLabel.baseValue());
+ Assert.assertEquals(IndexType.SEARCH, indexLabel.indexType());
+ Assert.assertEquals(ImmutableList.of("price"),
+ indexLabel.indexFields());
+ }
+
+ @Test
+ public void testReadIndexLabels() {
+ String json = "{\"indexlabels\": ["
+ + "{"
+ + "\"id\": \"4\","
+ + "\"index_type\": \"SEARCH\","
+ + "\"base_value\": \"software\","
+ + "\"name\": \"softwareByPrice\","
+ + "\"fields\": [\"price\"],"
+ + "\"base_type\": \"VERTEX_LABEL\""
+ + "},"
+ + "{"
+ + "\"id\": \"4\","
+ + "\"index_type\": \"SECONDARY\","
+ + "\"base_value\": \"person\","
+ + "\"name\": \"personByName\","
+ + "\"fields\": [\"name\"],"
+ + "\"base_type\": \"VERTEX_LABEL\""
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<IndexLabel> indexLabels = result.readList("indexlabels",
+ IndexLabel.class);
+ Assert.assertEquals(2, indexLabels.size());
+ IndexLabel indexLabel1 = indexLabels.get(0);
+ IndexLabel indexLabel2 = indexLabels.get(1);
+
+ Assert.assertEquals("softwareByPrice", indexLabel1.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel1.baseType());
+ Assert.assertEquals("software", indexLabel1.baseValue());
+ Assert.assertEquals(IndexType.SEARCH, indexLabel1.indexType());
+ Assert.assertEquals(ImmutableList.of("price"),
+ indexLabel1.indexFields());
+
+ Assert.assertEquals("personByName", indexLabel2.name());
+ Assert.assertEquals(HugeType.VERTEX_LABEL, indexLabel2.baseType());
+ Assert.assertEquals("person", indexLabel2.baseValue());
+ Assert.assertEquals(IndexType.SECONDARY, indexLabel2.indexType());
+ Assert.assertEquals(ImmutableList.of("name"),
+ indexLabel2.indexFields());
+ }
+
+ @Test
+ public void testReadVertex() {
+ String json = "{"
+ + "\"id\": \"person:marko\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"name\": \"marko\""
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ Vertex vertex = result.readObject(Vertex.class);
+
+ Assert.assertEquals("person:marko", vertex.id());
+ Assert.assertEquals("person", vertex.label());
+ Assert.assertEquals(ImmutableMap.of("name", "marko"),
+ vertex.properties());
+ }
+
+ @Test
+ public void testReadVertices() {
+ String json = "{\"vertices\": ["
+ + "{"
+ + "\"id\": \"person:marko\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"city\": [\"Beijing\",\"Wuhan\",\"Beijing\"],"
+ + "\"name\": \"marko\","
+ + "\"age\": 29"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"software:lop\","
+ + "\"label\": \"software\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"price\": 328,"
+ + "\"name\": \"lop\","
+ + "\"lang\": [\"java\",\"python\",\"c++\"]"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"person:peter\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"city\": [\"Shanghai\"],"
+ + "\"name\": \"peter\","
+ + "\"age\": 29"
+ + "}"
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<Vertex> vertices = result.readList("vertices", Vertex.class);
+ Assert.assertEquals(3, vertices.size());
+ Vertex vertex1 = vertices.get(0);
+ Vertex vertex2 = vertices.get(1);
+ Vertex vertex3 = vertices.get(2);
+
+ Assert.assertEquals("person:marko", vertex1.id());
+ Assert.assertEquals("person", vertex1.label());
+ Assert.assertEquals(ImmutableMap.of(
+ "name", "marko",
+ "age", 29,
+ "city", ImmutableList.of("Beijing", "Wuhan",
+ "Beijing")
+ ),
+ vertex1.properties());
+
+ Assert.assertEquals("software:lop", vertex2.id());
+ Assert.assertEquals("software", vertex2.label());
+ Assert.assertEquals(ImmutableMap.of(
+ "name", "lop",
+ "lang", ImmutableList.of("java", "python", "c++"),
+ "price", 328),
+ vertex2.properties());
+
+ Assert.assertEquals("person:peter", vertex3.id());
+ Assert.assertEquals("person", vertex3.label());
+ Assert.assertEquals(ImmutableMap.of(
+ "name", "peter",
+ "age", 29,
+ "city", ImmutableList.of("Shanghai")),
+ vertex3.properties());
+ }
+
+ @Test
+ public void testReadEdge() {
+ String json = "{"
+ + "\"id\": \"person:peter>created>>software:lop\","
+ + "\"label\": \"created\","
+ + "\"type\": \"edge\","
+ + "\"outV\": \"person:peter\","
+ + "\"inV\": \"software:lop\","
+ + "\"outVLabel\": \"person\","
+ + "\"inVLabel\": \"software\","
+ + "\"properties\": {"
+ + "\"city\": \"Hongkong\","
+ + "\"date\": 1495036800000"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ Edge edge = result.readObject(Edge.class);
+
+ Assert.assertEquals("person:peter>created>>software:lop", edge.id());
+ Assert.assertEquals("created", edge.label());
+ Assert.assertEquals("person:peter", edge.sourceId());
+ Assert.assertEquals("software:lop", edge.targetId());
+ Assert.assertEquals("person", edge.sourceLabel());
+ Assert.assertEquals("software", edge.targetLabel());
+ Assert.assertEquals(ImmutableMap.of("city", "Hongkong",
+ "date", 1495036800000L),
+ edge.properties());
+ }
+
+ @Test
+ public void testReadEdges() {
+ String json = "{\"edges\": ["
+ + "{"
+ + "\"id\": \"person:peter>created>>software:lop\","
+ + "\"label\": \"created\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"software\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"software:lop\","
+ + "\"outV\": \"person:peter\","
+ + "\"properties\": {"
+ + "\"date\": 1495036800000,"
+ + "\"city\": \"Hongkong\""
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"person:peter>knows>>person:marko\","
+ + "\"label\": \"knows\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"person\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"person:marko\","
+ + "\"outV\": \"person:peter\","
+ + "\"properties\": {"
+ + "\"date\": 1476720000000"
+ + "}"
+ + "}"
+ + "]}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult result = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, result.status());
+ Assert.assertNull(result.headers());
+
+ List<Edge> edges = result.readList("edges", Edge.class);
+ Assert.assertEquals(2, edges.size());
+ Edge edge1 = edges.get(0);
+ Edge edge2 = edges.get(1);
+
+ Assert.assertEquals("person:peter>created>>software:lop", edge1.id());
+ Assert.assertEquals("created", edge1.label());
+ Assert.assertEquals("person:peter", edge1.sourceId());
+ Assert.assertEquals("software:lop", edge1.targetId());
+ Assert.assertEquals("person", edge1.sourceLabel());
+ Assert.assertEquals("software", edge1.targetLabel());
+ Assert.assertEquals(ImmutableMap.of("city", "Hongkong",
+ "date", 1495036800000L),
+ edge1.properties());
+
+ Assert.assertEquals("person:peter>knows>>person:marko", edge2.id());
+ Assert.assertEquals("knows", edge2.label());
+ Assert.assertEquals("person:peter", edge2.sourceId());
+ Assert.assertEquals("person:marko", edge2.targetId());
+ Assert.assertEquals("person", edge2.sourceLabel());
+ Assert.assertEquals("person", edge2.targetLabel());
+ Assert.assertEquals(ImmutableMap.of("date", 1476720000000L),
+ edge2.properties());
+ }
+
+ @Test
+ public void testReadGremlinVertices() {
+ String json = "{"
+ + "\"requestId\": \"b0fd8ead-333f-43ac-97b0-4d78784726ae\","
+ + "\"status\": {"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\": {}"
+ + "},"
+ + "\"result\": {"
+ + "\"data\": ["
+ + "{"
+ + "\"id\": \"person:marko\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"city\": [\"Beijing\",\"Wuhan\",\"Beijing\"],"
+ + "\"name\": \"marko\","
+ + "\"age\": 29"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"software:lop\","
+ + "\"label\": \"software\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"price\": 328,"
+ + "\"name\": \"lop\","
+ + "\"lang\": [\"java\",\"python\",\"c++\"]"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"person:peter\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"city\": [\"Shanghai\"],"
+ + "\"name\": \"peter\","
+ + "\"age\": 35"
+ + "}"
+ + "}"
+ + "],"
+ + "\"meta\": {}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals("b0fd8ead-333f-43ac-97b0-4d78784726ae",
+ response.requestId());
+ Assert.assertEquals(200, response.status().code());
+
+ Vertex marko = new Vertex("person");
+ marko.id("person:marko");
+ marko.property("name", "marko");
+ marko.property("city", ImmutableList.of("Beijing", "Wuhan", "Beijing"));
+ marko.property("age", 29);
+
+ Vertex lop = new Vertex("software");
+ lop.id("software:lop");
+ lop.property("name", "lop");
+ lop.property("lang", ImmutableList.of("java", "python", "c++"));
+ lop.property("price", 328);
+
+ Vertex peter = new Vertex("person");
+ peter.id("person:peter");
+ peter.property("name", "peter");
+ peter.property("city", ImmutableList.of("Shanghai"));
+ peter.property("age", 35);
+
+ List<Vertex> vertices = new ArrayList<>(3);
+ vertices.add(peter);
+ vertices.add(marko);
+ vertices.add(lop);
+
+ Iterator<Result> results = response.result().iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Assert.assertEquals(Vertex.class, result.getObject().getClass());
+ Vertex vertex = result.getVertex();
+ Assert.assertTrue(Utils.contains(vertices, vertex));
+ }
+ }
+
+ @Test
+ public void testReadGremlinEdges() {
+ String json = "{"
+ + "\"requestId\": \"cd4cfc17-1ee4-4e9e-af40-cb18b115a8dc\","
+ + "\"status\": {"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\": {}"
+ + "},"
+ + "\"result\": {"
+ + "\"data\": ["
+ + "{"
+ + "\"id\": \"person:peter>created>>software:lop\","
+ + "\"label\": \"created\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"software\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"software:lop\","
+ + "\"outV\": \"person:peter\","
+ + "\"properties\": {"
+ + "\"date\": 1490284800000,"
+ + "\"weight\": 0.2"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"person:peter>knows>>person:marko\","
+ + "\"label\": \"knows\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"person\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"person:marko\","
+ + "\"outV\": \"person:peter\","
+ + "\"properties\": {"
+ + "\"date\": 1452355200000,"
+ + "\"weight\": 0.5"
+ + "}"
+ + "}"
+ + "],"
+ + "\"meta\": {}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals(200, response.status().code());
+
+ Edge created = new Edge("created");
+ created.id("person:peter>created>>software:lop");
+ created.sourceId("person:peter");
+ created.targetId("software:lop");
+ created.sourceLabel("person");
+ created.targetLabel("software");
+ created.property("date", 1490284800000L);
+ created.property("weight", 0.2);
+
+ Edge knows = new Edge("knows");
+ knows.id("person:peter>knows>>person:marko");
+ knows.sourceId("person:peter");
+ knows.targetId("person:marko");
+ knows.sourceLabel("person");
+ knows.targetLabel("person");
+ knows.property("date", 1452355200000L);
+ knows.property("weight", 0.5);
+
+ List<Edge> edges = new ArrayList<>(2);
+ edges.add(created);
+ edges.add(knows);
+
+ Iterator<Result> results = response.result().iterator();
+ while (results.hasNext()) {
+ Result result = results.next();
+ Assert.assertEquals(Edge.class, result.getObject().getClass());
+ Edge edge = result.getEdge();
+ Assert.assertTrue(Utils.contains(edges, edge));
+ }
+ }
+
+ @Test
+ public void testReadGremlinPathWithVertexAndEdge() {
+ String json = "{"
+ + "\"requestId\": \"238c74ca-18f7-4377-b8e1-2bb3b165e5d6\","
+ + "\"status\":{"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\":{}"
+ + "},"
+ + "\"result\":{"
+ + "\"data\":["
+ + "{"
+ + "\"labels\":[[], []],"
+ + "\"objects\":["
+ + "{"
+ + "\"id\": \"person:marko\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\":{"
+ + "\"city\":\"Beijing\","
+ + "\"name\":\"marko\","
+ + "\"age\":29"
+ + "}"
+ + "},"
+ + "{"
+ + "\"id\": \"person:marko>knows>>person:vadas\","
+ + "\"label\": \"knows\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"person\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"person:vadas\","
+ + "\"outV\": \"person:marko\","
+ + "\"properties\":{"
+ + "\"date\": 1452355200000,"
+ + "\"weight\": 0.5"
+ + "}"
+ + "}"
+ + "]"
+ + "}"
+ + "],"
+ + "\"meta\":{"
+ + "}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals(200, response.status().code());
+
+ Iterator<Result> results = response.result().iterator();
+ Assert.assertTrue(results.hasNext());
+ Result result = results.next();
+ Object object = result.getObject();
+ Assert.assertEquals(Path.class, object.getClass());
+ Path path = (Path) object;
+ Assert.assertEquals(2, path.labels().size());
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(0));
+ Assert.assertEquals(ImmutableList.of(), path.labels().get(1));
+
+ Vertex vertex = new Vertex("person");
+ vertex.id("person:marko");
+ vertex.property("name", "marko");
+ vertex.property("age", 29);
+ vertex.property("city", "Beijing");
+
+ Edge edge = new Edge("knows");
+ edge.id("person:marko>knows>>person:vadas");
+ edge.sourceId("person:marko");
+ edge.sourceLabel("person");
+ edge.targetId("person:vadas");
+ edge.targetLabel("person");
+ edge.property("date", 1452355200000L);
+ edge.property("weight", 0.5);
+
+ Assert.assertEquals(2, path.objects().size());
+ Utils.assertGraphEqual(ImmutableList.of(vertex),
+ ImmutableList.of(edge),
+ path.objects());
+ }
+
+ @Test
+ public void testReadGremlinNullData() {
+ String json = "{"
+ + "\"requestId\": \"d95ac131-24b5-4140-a3ff-91b0c020764a\","
+ + "\"status\": {"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\": {}"
+ + "},"
+ + "\"result\": {"
+ + "\"data\": [null],"
+ + "\"meta\": {}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals(200, response.status().code());
+
+ Iterator<Result> results = response.result().iterator();
+ Assert.assertTrue(results.hasNext());
+ Object object = results.next();
+ Assert.assertNull(object);
+ }
+
+ @Test
+ public void testReadGremlinNullAndVertex() {
+ String json = "{"
+ + "\"requestId\": \"d95ac131-24b5-4140-a3ff-91b0c020764a\","
+ + "\"status\": {"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\": {}"
+ + "},"
+ + "\"result\": {"
+ + "\"data\": ["
+ + "null,"
+ + "{"
+ + "\"id\": \"person:marko\","
+ + "\"label\": \"person\","
+ + "\"type\": \"vertex\","
+ + "\"properties\": {"
+ + "\"city\": \"Beijing\","
+ + "\"name\": \"marko\","
+ + "\"age\": 29"
+ + "}"
+ + "}"
+ + "],"
+ + "\"meta\": {}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals(200, response.status().code());
+
+ Iterator<Result> results = response.result().iterator();
+ Assert.assertTrue(results.hasNext());
+ Result result = results.next();
+ Assert.assertNull(result);
+
+ Assert.assertTrue(results.hasNext());
+ result = results.next();
+ Assert.assertEquals(Vertex.class, result.getObject().getClass());
+
+ Vertex marko = new Vertex("person");
+ marko.id("person:marko");
+ marko.property("name", "marko");
+ marko.property("city", "Beijing");
+ marko.property("age", 29);
+ Vertex vertex = result.getVertex();
+ Assert.assertTrue(Utils.contains(ImmutableList.of(marko), vertex));
+ }
+
+ @Test
+ public void testReadGremlinEdgeAndNull() {
+ String json = "{"
+ + "\"requestId\": \"d95ac131-24b5-4140-a3ff-91b0c020764a\","
+ + "\"status\": {"
+ + "\"message\": \"\","
+ + "\"code\": 200,"
+ + "\"attributes\": {}"
+ + "},"
+ + "\"result\": {"
+ + "\"data\": ["
+ + "{"
+ + "\"id\": \"person:peter>created>>software:lop\","
+ + "\"label\": \"created\","
+ + "\"type\": \"edge\","
+ + "\"inVLabel\": \"software\","
+ + "\"outVLabel\": \"person\","
+ + "\"inV\": \"software:lop\","
+ + "\"outV\": \"person:peter\","
+ + "\"properties\": {"
+ + "\"date\": 1490284800000,"
+ + "\"weight\": 0.2"
+ + "}"
+ + "},"
+ + "null"
+ + "],"
+ + "\"meta\": {}"
+ + "}"
+ + "}";
+
+ Mockito.when(this.mockResponse.getStatus()).thenReturn(200);
+ Mockito.when(this.mockResponse.getHeaders()).thenReturn(null);
+ Mockito.when(this.mockResponse.readEntity(String.class))
+ .thenReturn(json);
+ RestResult restResult = new RestResult(this.mockResponse);
+ Assert.assertEquals(200, restResult.status());
+ Assert.assertNull(restResult.headers());
+
+ Response response = restResult.readObject(Response.class);
+ response.graphManager(graph());
+ Assert.assertEquals(200, response.status().code());
+
+ Iterator<Result> results = response.result().iterator();
+
+ Assert.assertTrue(results.hasNext());
+ Result result = results.next();
+ Assert.assertEquals(Edge.class, result.getObject().getClass());
+
+ Edge created = new Edge("created");
+ created.id("person:peter>created>>software:lop");
+ created.sourceId("person:peter");
+ created.targetId("software:lop");
+ created.sourceLabel("person");
+ created.targetLabel("software");
+ created.property("date", 1490284800000L);
+ created.property("weight", 0.2);
+ Assert.assertTrue(Utils.contains(ImmutableList.of(created),
+ result.getEdge()));
+
+ Assert.assertTrue(results.hasNext());
+ result = results.next();
+ Assert.assertNull(result);
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/SplicingIdGeneratorTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/SplicingIdGeneratorTest.java
new file mode 100644
index 0000000..bd9e231
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/SplicingIdGeneratorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.baidu.hugegraph.util.SplicingIdGenerator;
+import com.google.common.collect.ImmutableList;
+
+public class SplicingIdGeneratorTest {
+
+ @Test
+ public void testConcatIds() {
+ Assert.assertEquals("a>b>c>d",
+ SplicingIdGenerator.concat("a", "b", "c", "d"));
+ // > => `>
+ Assert.assertEquals("a>`>>c>`>",
+ SplicingIdGenerator.concat("a", ">", "c", ">"));
+ Assert.assertEquals("a>b`>c>d",
+ SplicingIdGenerator.concat("a", "b>c", "d"));
+ }
+
+ @Test
+ public void testSplitIds() {
+ Assert.assertArrayEquals(new String[]{"a", "b", "c", "d"},
+ SplicingIdGenerator.split("a>b>c>d"));
+ Assert.assertArrayEquals(new String[]{"a", ">", "c", ">"},
+ SplicingIdGenerator.split("a>`>>c>`>"));
+ Assert.assertArrayEquals(new String[]{"a", "b>c", "d"},
+ SplicingIdGenerator.split("a>b`>c>d"));
+ }
+
+ @Test
+ public void testConcatValues() {
+ Assert.assertEquals("a!1!c!d",
+ SplicingIdGenerator.concatValues("a", 1, 'c', "d"));
+ Assert.assertEquals("a!`!!1!d",
+ SplicingIdGenerator.concatValues("a", "!", 1, 'd'));
+ Assert.assertEquals("a!b`!c!d",
+ SplicingIdGenerator.concatValues("a", "b!c", "d"));
+
+ List<Object> values = ImmutableList.of("a", 1, 'c', "d");
+ Assert.assertEquals("a!1!c!d",
+ SplicingIdGenerator.concatValues(values));
+ values = ImmutableList.of("a", "!", 1, 'd');
+ Assert.assertEquals("a!`!!1!d",
+ SplicingIdGenerator.concatValues(values));
+ values = ImmutableList.of("a", "b!c", "d");
+ Assert.assertEquals("a!b`!c!d",
+ SplicingIdGenerator.concatValues(values));
+ }
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
new file mode 100644
index 0000000..dcc5417
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/UnitTestSuite.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ VertexSerializerTest.class,
+ PathSerializerTest.class,
+ RestResultTest.class,
+ BatchElementRequestTest.class,
+ PropertyKeyTest.class,
+ IndexLabelTest.class,
+ CommonUtilTest.class,
+ IdUtilTest.class,
+ SplicingIdGeneratorTest.class
+})
+public class UnitTestSuite {
+}
diff --git a/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/VertexSerializerTest.java b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/VertexSerializerTest.java
new file mode 100644
index 0000000..a232090
--- /dev/null
+++ b/hugegraph-client/src/test/java/com/baidu/hugegraph/unit/VertexSerializerTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2017 HugeGraph Authors
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.baidu.hugegraph.unit;
+
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.baidu.hugegraph.structure.graph.Vertex;
+import com.baidu.hugegraph.testutil.Assert;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class VertexSerializerTest extends BaseUnitTest {
+
+ @Test
+ public void testSerializeAndDeserializeVertex() {
+ Vertex vertex = new Vertex("person");
+ vertex.id("person:marko");
+ vertex.property("name", "marko");
+ vertex.property("age", 29);
+ vertex.property("city", "Beijing");
+
+ String json = serialize(vertex);
+ Vertex vertexCopy = deserialize(json, Vertex.class);
+
+ Assert.assertEquals("person:marko", vertexCopy.id());
+ Assert.assertEquals("person", vertexCopy.label());
+ Assert.assertEquals("vertex", vertexCopy.type());
+ Map<String, Object> props = ImmutableMap.of("name", "marko",
+ "age", 29,
+ "city", "Beijing");
+ Assert.assertEquals(props, vertexCopy.properties());
+ }
+
+ @Test
+ public void testSerializeAndDeserializeVertexWithListProp() {
+ Vertex vertex = new Vertex("person");
+ vertex.id("person:marko");
+ vertex.property("name", "marko");
+ vertex.property("age", 29);
+ vertex.property("city", ImmutableList.of("Hefei", "Wuhan"));
+
+ String json = serialize(vertex);
+ Vertex vertexCopy = deserialize(json, Vertex.class);
+
+ Assert.assertEquals("person:marko", vertexCopy.id());
+ Assert.assertEquals("person", vertexCopy.label());
+ Assert.assertEquals("vertex", vertexCopy.type());
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "marko", "age", 29,
+ "city", ImmutableList.of("Hefei", "Wuhan"));
+ Assert.assertEquals(props, vertexCopy.properties());
+ }
+
+ @Test
+ public void testSerializeAndDeserializeVertexWithSetProp() {
+ Vertex vertex = new Vertex("person");
+ vertex.id("person:marko");
+ vertex.property("name", "marko");
+ vertex.property("age", 29);
+ vertex.property("city", ImmutableSet.of("Hefei", "Wuhan", "Wuhan"));
+
+ String json = serialize(vertex);
+ Vertex vertexCopy = deserialize(json, Vertex.class);
+
+ Assert.assertEquals("person:marko", vertexCopy.id());
+ Assert.assertEquals("person", vertexCopy.label());
+ Assert.assertEquals("vertex", vertexCopy.type());
+ // TODO: Set properties should deserialize to Set instead of List
+ Map<String, Object> props = ImmutableMap.of(
+ "name", "marko", "age", 29,
+ "city", ImmutableList.of("Hefei", "Wuhan"));
+ Assert.assertEquals(props, vertexCopy.properties());
+ }
+}
diff --git a/hugegraph-client/src/test/resources/hugegraph-clone.properties b/hugegraph-client/src/test/resources/hugegraph-clone.properties
new file mode 100644
index 0000000..712b663
--- /dev/null
+++ b/hugegraph-client/src/test/resources/hugegraph-clone.properties
@@ -0,0 +1,3 @@
+store=hugegraph3
+rocksdb.data_path=./hg3
+rocksdb.wal_path=./hg3
diff --git a/hugegraph-client/src/test/resources/hugegraph-create.properties b/hugegraph-client/src/test/resources/hugegraph-create.properties
new file mode 100644
index 0000000..cc022b0
--- /dev/null
+++ b/hugegraph-client/src/test/resources/hugegraph-create.properties
@@ -0,0 +1,6 @@
+gremlin.graph=com.baidu.hugegraph.auth.HugeFactoryAuthProxy
+backend=rocksdb
+serializer=binary
+store=hugegraph2
+rocksdb.data_path=./hg2
+rocksdb.wal_path=./hg2
diff --git a/hugegraph-client/src/test/resources/hugegraph.truststore b/hugegraph-client/src/test/resources/hugegraph.truststore
new file mode 100644
index 0000000..5b0828d
--- /dev/null
+++ b/hugegraph-client/src/test/resources/hugegraph.truststore
Binary files differ