diff --git a/.github/workflows/client-ci.yml b/.github/workflows/client-ci.yml index b8c19c706..7ba285c3d 100644 --- a/.github/workflows/client-ci.yml +++ b/.github/workflows/client-ci.yml @@ -1,4 +1,4 @@ -name: "hugegraph-client-ci" +name: "java-client-ci" on: workflow_dispatch: @@ -25,7 +25,7 @@ jobs: USE_STAGE: 'true' # Whether to include the stage repository. TRAVIS_DIR: hugegraph-client/assembly/travis # TODO: replace it with the (latest - n) commit id (n >= 15) - COMMIT_ID: be6ee386b9939dc6bd6fcbdf2274b8acc3a0a314 + COMMIT_ID: b52517c strategy: fail-fast: false matrix: diff --git a/.github/workflows/client-go-ci.yml b/.github/workflows/client-go-ci.yml new file mode 100644 index 000000000..b29b6304f --- /dev/null +++ b/.github/workflows/client-go-ci.yml @@ -0,0 +1,72 @@ +name: "go-client-ci" + +on: + push: + branches: + - master + - /^release-.*$/ + paths: + - hugegraph-client-go/** + - hugegraph-dist/** + - .github/workflows/** + - pom.xml + pull_request: + paths: + - hugegraph-client-go/** + - hugegraph-dist/** + - .github/workflows/** + - pom.xml + +jobs: + client-go-ci: + runs-on: ubuntu-latest + env: + USE_STAGE: 'true' # Whether to include the stage repository. + TRAVIS_DIR: hugegraph-client/assembly/travis + # TODO: replace it with the (latest - n) commit id (n >= 15) + COMMIT_ID: b52517c + strategy: + fail-fast: false + matrix: + JAVA_VERSION: ['11'] + steps: + - name: Install JDK 11 + uses: actions/setup-java@v3 + with: + java-version: ${{ matrix.JAVA_VERSION }} + distribution: 'zulu' + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Use staged maven repo + if: ${{ env.USE_STAGE == 'true' }} + run: | + cp $HOME/.m2/settings.xml /tmp/settings.xml + mv -vf .github/configs/settings.xml $HOME/.m2/settings.xml + + - name: Prepare env and service + run: | + $TRAVIS_DIR/install-hugegraph-from-source.sh $COMMIT_ID + + - name: Init Go env + uses: actions/setup-go@v2.1.3 + with: { go-version: '1.x' } + + - name: Go test + run: | + go version + sudo swapoff -a + sudo sysctl -w vm.swappiness=1 + sudo sysctl -w fs.file-max=262144 + sudo sysctl -w vm.max_map_count=262144 + cd hugegraph-client-go && make test diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9ee5a6539..1003b9e2c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,8 +13,6 @@ name: "CodeQL" on: workflow_dispatch: - push: - branches: [ master, release-* ] pull_request: # The branches below must be a subset of the branches above # branches: [ master ] # enable in all PR diff --git a/.github/workflows/hubble-ci.yml b/.github/workflows/hubble-ci.yml index ea186d40a..35016394b 100644 --- a/.github/workflows/hubble-ci.yml +++ b/.github/workflows/hubble-ci.yml @@ -1,4 +1,4 @@ -name: "hugegraph-hubble-ci" +name: "hubble-ci" on: workflow_dispatch: @@ -24,7 +24,7 @@ on: env: TRAVIS_DIR: hugegraph-hubble/hubble-dist/assembly/travis # TODO: replace it with the (latest - n) commit id (n >= 15) - COMMIT_ID: be6ee386b9939dc6bd6fcbdf2274b8acc3a0a314 + COMMIT_ID: b52517c jobs: hubble-ci: @@ -38,10 +38,6 @@ jobs: with: java-version: '11' distribution: 'adopt' - - name: Use Node.js 16 - uses: actions/setup-node@v3 - with: - node-version: '16' # we also should cache python & yarn & downloads to avoid useless work - name: Cache Maven packages uses: actions/cache@v3 @@ -51,20 +47,6 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - - name: Get Yarn path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - name: Cache Yarn packages - uses: actions/cache@v3 - # use id to check `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Cache Python packages uses: actions/cache@v3 with: diff --git a/.github/workflows/loader-ci.yml b/.github/workflows/loader-ci.yml index 3c676302b..c983eee5f 100644 --- a/.github/workflows/loader-ci.yml +++ b/.github/workflows/loader-ci.yml @@ -1,4 +1,4 @@ -name: "hugegraph-loader-ci" +name: "loader-ci" on: workflow_dispatch: @@ -27,7 +27,7 @@ jobs: TRAVIS_DIR: hugegraph-loader/assembly/travis STATIC_DIR: hugegraph-loader/assembly/static # TODO: replace it with the (latest - n) commit id (n >= 15) - COMMIT_ID: be6ee386b9939dc6bd6fcbdf2274b8acc3a0a314 + COMMIT_ID: b52517c DB_USER: root DB_PASS: root DB_DATABASE: load_test diff --git a/.github/workflows/spark-connector-ci.yml b/.github/workflows/spark-connector-ci.yml index c5b312bc2..072a70949 100644 --- a/.github/workflows/spark-connector-ci.yml +++ b/.github/workflows/spark-connector-ci.yml @@ -1,4 +1,4 @@ -name: "hugegraph-spark-connector-ci" +name: "spark-connector-ci" on: push: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3da563725..64b62dc5a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -12,7 +12,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v3 + - uses: actions/stale@v8 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' diff --git a/.github/workflows/tools-ci.yml b/.github/workflows/tools-ci.yml index f00582b2e..ac0673d84 100644 --- a/.github/workflows/tools-ci.yml +++ b/.github/workflows/tools-ci.yml @@ -1,4 +1,4 @@ -name: "hugegraph-tools-ci" +name: "tools-ci" on: workflow_dispatch: push: @@ -25,7 +25,8 @@ jobs: USE_STAGE: 'true' # Whether to include the stage repository. TRAVIS_DIR: hugegraph-tools/assembly/travis # TODO: could we use one param to unify it? or use a action template (could use one ci file) - COMMIT_ID: be6ee386b9939dc6bd6fcbdf2274b8acc3a0a314 + # TODO: replace it with the (latest - n) commit id (n >= 15) + COMMIT_ID: b52517c steps: - name: Install JDK 11 uses: actions/setup-java@v3 diff --git a/.gitignore b/.gitignore index fe63f8b6f..6909ee443 100644 --- a/.gitignore +++ b/.gitignore @@ -95,3 +95,6 @@ tree.txt # system ignore Thumbs.db + +# client-go +go.env \ No newline at end of file diff --git a/.licenserc.yaml b/.licenserc.yaml index 827be9c86..da6493d7f 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -75,6 +75,9 @@ header: # `header` section is configurations for source codes license header. - 'assembly/**' - '.github/**/*' - '**/target/*' + - 'hugegraph-client-go/go.mod' + - 'hugegraph-client-go/go.sum' + - 'hugegraph-client-go/Makefile' - 'hugegraph-client/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker' # - 'hugegraph-hubble/hubble-fe/**' comment: on-failure # on what condition license-eye will comment on the pull request, `on-failure`, `always`, `never`. diff --git a/hugegraph-client-go/Makefile b/hugegraph-client-go/Makefile new file mode 100644 index 000000000..0c49457e6 --- /dev/null +++ b/hugegraph-client-go/Makefile @@ -0,0 +1,72 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# init project path +HOMEDIR := $(shell pwd) +OUTDIR := $(HOMEDIR)/output + +# Get the go environment required for compilation +export GOENV = $(HOMEDIR)/go.env + +GO := GO111MODULE=on go +GOPATH := $(shell $(GO) env GOPATH) +GOMOD := $(GO) mod +GOBUILD := $(GO) build +GOTEST := $(GO) test -race -timeout 30s -gcflags="-N -l" +GOPKGS := $$($(GO) list ./...| grep -vE "vendor") + +# test cover files +COVPROF := $(HOMEDIR)/covprof.out # coverage profile +COVFUNC := $(HOMEDIR)/covfunc.txt # coverage profile information for each function +COVHTML := $(HOMEDIR)/covhtml.html # HTML representation of coverage profile + +# make, make all +all: prepare compile package + +set-env: + $(GO) env -w GO111MODULE=on + + +#make prepare, download dependencies +prepare: gomod + +gomod: set-env + $(GOMOD) download + +#make compile +compile: build + +build: + $(GOBUILD) -o $(HOMEDIR)/hugegraph-client-go +# make test, test your code +test: prepare test-case +test-case: + $(GOTEST) -v -cover $(GOPKGS) + +# make package +package: package-bin +package-bin: + rm -rf $(OUTDIR) + mkdir -p $(OUTDIR) + mv hugegraph-client-go $(OUTDIR)/ +# make clean +clean: + $(GO) clean + rm -rf $(OUTDIR) + rm -rf $(GOPATH)/pkg/darwin_amd64 +# avoid filename conflict and speed up build +.PHONY: all prepare compile test package clean build diff --git a/hugegraph-client-go/README.en.md b/hugegraph-client-go/README.en.md new file mode 100644 index 000000000..1b8f37f53 --- /dev/null +++ b/hugegraph-client-go/README.en.md @@ -0,0 +1,96 @@ +# go-hugegraph + +#### Introduction + +HugeGraph client SDK tool based on Go language + +#### Software Architecture + +Software Architecture Description + +#### Installation Tutorial + +```Shell + +Go get github. com/go huggraph + +``` + +#### Implement API + +| API | illustrate | +|---------|-------------------------| +| schema | Obtain the model schema | +| version | Get version information | + +#### Instructions for use + +##### 1. Initialize the client + +```Go +package main + +import "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" +import "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" + +func main() { + + clinet, err := hugegraph.NewCommonClient(hugegraph.Config{ + Host: "127.0.0.1", + Port: 8080, + Graph: "hugegraph", + Username: "", + Password: "", + Logger: &hgtransport.ColorLogger{ + Output: os.Stdout, + EnableRequestBody: true, + EnableResponseBody: true, + }, + }) + + if err != nil { + log.Fatalf("Error creating the client: %s\n", err) + } +} +``` + +##### 2. Obtain the hugegraph version + +-1. Use the SDK to obtain version information + +```Go +package main + +import ( + "fmt" + "log" +) + +func getVersion() { + + client := initClient() + res, err := client.Version() + if err != nil { + log.Fatalf("Error getting the response: %s\n", err) + } + defer res.Body.Close() + + fmt.Println(res.Versions) + fmt.Println(res.Versions.Version) +} +``` + +-2. Result Set Response Body + +```Go +package main + +type VersionResponse struct { + Versions struct { + Version string `json:"version"` // hugegraph version + Core string `json:"core"` // hugegraph core version + Gremlin string `json:"gremlin"` // hugegraph gremlin version + API string `json:"api"` // hugegraph api version + } ` json: 'versions'` +} +``` diff --git a/hugegraph-client-go/README.md b/hugegraph-client-go/README.md new file mode 100644 index 000000000..faf0cc067 --- /dev/null +++ b/hugegraph-client-go/README.md @@ -0,0 +1,94 @@ +# go-hugegraph + +#### 介绍 + +基于 Go 语言的 HugeGraph Client SDK 工具 + +#### 软件架构 + +软件架构说明 + +#### 安装教程 + +```shell +go get github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go +``` + +#### 实现 API + +| API | 说明 | +|---------|-------------| +| schema | 获取模型 schema | +| version | 获取版本信息 | + +#### 使用说明 + +##### 1.初始化客户端 + +```go +package main + +import "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" +import "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" + +func main() { + + clinet, err := hugegraph.NewCommonClient(hugegraph.Config{ + Host: "127.0.0.1", + Port: 8080, + Graph: "hugegraph", + Username: "", + Password: "", + Logger: &hgtransport.ColorLogger{ + Output: os.Stdout, + EnableRequestBody: true, + EnableResponseBody: true, + }, + }) + + if err != nil { + log.Fatalf("Error creating the client: %s\n", err) + } +} +``` + +##### 2.获取 hugegraph 版本 + +- 1.使用 SDK 获取版本信息 + +```go +package main + +import ( + "fmt" + "log" +) + +func getVersion() { + + client := initClient() + res, err := client.Version() + if err != nil { + log.Fatalf("Error getting the response: %s\n", err) + } + defer res.Body.Close() + + fmt.Println(res.Versions) + fmt.Println(res.Versions.Version) +} +``` + +- 2.返回值的结构 + +```go +package main + +type VersionResponse struct { + Versions struct { + Version string `json:"version"` // hugegraph version + Core string `json:"core"` // hugegraph core version + Gremlin string `json:"gremlin"` // hugegraph gremlin version + API string `json:"api"` // hugegraph api version + } ` json: 'versions'` +} +``` diff --git a/hugegraph-client-go/api/api.go b/hugegraph-client-go/api/api.go new file mode 100644 index 000000000..ac1ec92d7 --- /dev/null +++ b/hugegraph-client-go/api/api.go @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package api + +import ( + "net/http" + "strconv" + "time" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/version" +) + +// VERSION returns the package version as a string. +const VERSION = version.Client + +// Transport defines the interface for an API client. +type Transport interface { + Perform(*http.Request) (*http.Response, error) + GetConfig() hgtransport.Config +} + +// formatDuration converts duration to a string in the format +// accepted by Hugegraph. +func formatDuration(d time.Duration) string { + if d < time.Millisecond { + return strconv.FormatInt(int64(d), 10) + "nanos" + } + return strconv.FormatInt(int64(d)/int64(time.Millisecond), 10) + "ms" +} diff --git a/hugegraph-client-go/api/api.response.go b/hugegraph-client-go/api/api.response.go new file mode 100644 index 000000000..b8c80f1e4 --- /dev/null +++ b/hugegraph-client-go/api/api.response.go @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package api + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "strconv" + "strings" +) + +// Response represents the API response. +type Response struct { + StatusCode int + Header http.Header + Body io.ReadCloser +} + +// String returns the response as a string. +// +// The intended usage is for testing or debugging only. +func (r *Response) String() string { + var ( + out = new(bytes.Buffer) + b1 = bytes.NewBuffer([]byte{}) + b2 = bytes.NewBuffer([]byte{}) + tr io.Reader + ) + + if r != nil && r.Body != nil { + tr = io.TeeReader(r.Body, b1) + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + + } + }(r.Body) + + if _, err := io.Copy(b2, tr); err != nil { + out.WriteString(fmt.Sprintf("", err)) + return out.String() + } + defer func() { r.Body = ioutil.NopCloser(b1) }() + } + + if r != nil { + out.WriteString(fmt.Sprintf("[%d %s]", r.StatusCode, http.StatusText(r.StatusCode))) + if r.StatusCode > 0 { + out.WriteRune(' ') + } + } else { + out.WriteString("[0 ]") + } + + if r != nil && r.Body != nil { + out.ReadFrom(b2) // errcheck exclude (*bytes.Buffer).ReadFrom + } + + return out.String() +} + +// Status returns the response status as a string. +func (r *Response) Status() string { + var b strings.Builder + if r != nil { + b.WriteString(strconv.Itoa(r.StatusCode)) + b.WriteString(" ") + b.WriteString(http.StatusText(r.StatusCode)) + } + return b.String() +} + +// IsError returns true when the response status indicates failure. +func (r *Response) IsError() bool { + return r.StatusCode > 299 +} diff --git a/hugegraph-client-go/api/api.resquest.go b/hugegraph-client-go/api/api.resquest.go new file mode 100644 index 000000000..9d08acfed --- /dev/null +++ b/hugegraph-client-go/api/api.resquest.go @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package api + +import ( + "bytes" + "context" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +const ( + headerContentType = "Content-Type" +) + +var ( + headerContentTypeJSON = []string{"application/json"} +) + +// Request defines the API request. +type Request interface { + Do(ctx context.Context, transport Transport) (*Response, error) +} + +// newRequest creates an HTTP request. +func NewRequest(method, path string, params *url.Values, body io.Reader) (*http.Request, error) { + + u := &url.URL{ + Path: path, + } + if params != nil { + u.RawQuery = params.Encode() + } + r := http.Request{ + Method: method, + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + + if body != nil { + switch b := body.(type) { + case *bytes.Buffer: + r.Body = ioutil.NopCloser(body) + r.ContentLength = int64(b.Len()) + case *bytes.Reader: + r.Body = ioutil.NopCloser(body) + r.ContentLength = int64(b.Len()) + case *strings.Reader: + r.Body = ioutil.NopCloser(body) + r.ContentLength = int64(b.Len()) + default: + r.Body = ioutil.NopCloser(body) + } + } + + return &r, nil +} diff --git a/hugegraph-client-go/api/v1/api.go b/hugegraph-client-go/api/v1/api.go new file mode 100644 index 000000000..09fec0fe0 --- /dev/null +++ b/hugegraph-client-go/api/v1/api.go @@ -0,0 +1,33 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to You under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. + */ + +package v1 + +import "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + +type APIV1 struct { + Version Version + Schema Schema +} + +// New creates new API +func New(t api.Transport) *APIV1 { + return &APIV1{ + Version: newVersionFunc(t), + Schema: newSchemaFunc(t), + } +} diff --git a/hugegraph-client-go/api/v1/edge/edge.go b/hugegraph-client-go/api/v1/edge/edge.go new file mode 100644 index 000000000..46ac94956 --- /dev/null +++ b/hugegraph-client-go/api/v1/edge/edge.go @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package edge + diff --git a/hugegraph-client-go/api/v1/edge/edge_test.go b/hugegraph-client-go/api/v1/edge/edge_test.go new file mode 100644 index 000000000..d21633735 --- /dev/null +++ b/hugegraph-client-go/api/v1/edge/edge_test.go @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package edge_test + +import "testing" + +func testEdge(t *testing.T) { + // 1.create source vertex + // 2.create target vertex + // 3.create edge +} diff --git a/hugegraph-client-go/api/v1/edgelabel/edgelabel.go b/hugegraph-client-go/api/v1/edgelabel/edgelabel.go new file mode 100644 index 000000000..5a2d90a24 --- /dev/null +++ b/hugegraph-client-go/api/v1/edgelabel/edgelabel.go @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package edgelabel + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "io" + "io/ioutil" + "net/http" + "strings" +) + +type Edgelabel struct { + Create + DeleteByName + GetAll +} + +func New(t api.Transport) *Edgelabel { + return &Edgelabel{ + // Create https://hugegraph.apache.org/docs/clients/restful-api/edgelabel/#141-create-an-edgelabel + Create: newCreateFunc(t), + // DeleteByName https://hugegraph.apache.org/docs/clients/restful-api/edgelabel/#145-delete-edgelabel-by-name + DeleteByName: newDeleteByNameFunc(t), + // GetAll https://hugegraph.apache.org/docs/clients/restful-api/edgelabel/#143-get-all-edgelabels + GetAll: newGetAll(t), + } +} + +func newCreateFunc(t api.Transport) Create { + return func(o ...func(*CreateRequest)) (*CreateResponse, error) { + var r = CreateRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newDeleteByNameFunc(t api.Transport) DeleteByName { + return func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) { + var r = DeleteByNameRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newGetAll(t api.Transport) GetAll { + return func(o ...func(*GetAllRequest)) (*GetAllResponse, error) { + var r = GetAllRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type Create func(o ...func(*CreateRequest)) (*CreateResponse, error) +type DeleteByName func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) +type GetAll func(o ...func(*GetAllRequest)) (*GetAllResponse, error) + +type CreateRequest struct { + Body io.Reader + ctx context.Context + reqData CreateRequestData +} +type CreateRequestData struct { + Name string `json:"name"` + SourceLabel string `json:"source_label"` + TargetLabel string `json:"target_label"` + Frequency model.Frequency `json:"frequency"` + Properties []string `json:"properties"` + SortKeys []string `json:"sort_keys"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` +} +type CreateResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data CreateResponseData `json:"-"` +} +type CreateResponseData struct { + ID int `json:"id"` + SortKeys []string `json:"sort_keys"` + SourceLabel string `json:"source_label"` + Name string `json:"name"` + IndexNames []string `json:"index_names"` + Properties []string `json:"properties"` + TargetLabel string `json:"target_label"` + Frequency string `json:"frequency"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + } `json:"user_data"` +} + +type DeleteByNameRequest struct { + Body io.Reader + ctx context.Context + name string +} +type DeleteByNameResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data DeleteByNameResponseData `json:"-"` +} +type DeleteByNameResponseData struct { + TaskID int `json:"task_id"` +} + +type GetAllRequest struct { + Body io.Reader + ctx context.Context +} +type GetAllResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data GetAllResponseData `json:"-"` +} + +type GetAllResponseData struct { + Edgelabels []struct { + ID int `json:"id"` + Name string `json:"name"` + SourceLabel string `json:"source_label"` + TargetLabel string `json:"target_label"` + Frequency string `json:"frequency"` + SortKeys []interface{} `json:"sort_keys"` + NullableKeys []interface{} `json:"nullable_keys"` + IndexLabels []string `json:"index_labels"` + Properties []string `json:"properties"` + Status string `json:"status"` + TTL int `json:"ttl"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"edgelabels"` +} + +func (r Create) WithReqData(reqData CreateRequestData) func(request *CreateRequest) { + return func(r *CreateRequest) { + r.reqData = reqData + } +} +func (p DeleteByName) WithName(name string) func(request *DeleteByNameRequest) { + return func(r *DeleteByNameRequest) { + r.name = name + } +} + +func (r CreateRequest) Do(ctx context.Context, transport api.Transport) (*CreateResponse, error) { + + if len(r.reqData.Name) <= 0 { + return nil, errors.New("create edgeLabel must set name") + } + if len(r.reqData.SourceLabel) <= 0 { + return nil, errors.New("create edgeLabel must set source_label") + } + if len(r.reqData.TargetLabel) <= 0 { + return nil, errors.New("create edgeLabel must set target_label") + } + if len(r.reqData.Properties) <= 0 { + return nil, errors.New("create edgeLabel must set properties") + } + + byteBody, err := json.Marshal(&r.reqData) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(byteBody)) + + req, err := api.NewRequest("POST", fmt.Sprintf("/graphs/%s/schema/edgelabels", transport.GetConfig().Graph), nil, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &CreateResponse{} + respData := CreateResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r DeleteByNameRequest) Do(ctx context.Context, transport api.Transport) (*DeleteByNameResponse, error) { + + if len(r.name) <= 0 { + return nil, errors.New("delete by name ,please set name") + } + req, err := api.NewRequest("DELETE", fmt.Sprintf("/graphs/%s/schema/edgelabels/%s", transport.GetConfig().Graph, r.name), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &DeleteByNameResponse{} + respData := DeleteByNameResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (g GetAllRequest) Do(ctx context.Context, transport api.Transport) (*GetAllResponse, error) { + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema/edgelabels", transport.GetConfig().Graph), nil, nil) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &GetAllResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + fmt.Println(string(bytes)) + respData := GetAllResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} diff --git a/hugegraph-client-go/api/v1/edgelabel/edgelabel_test.go b/hugegraph-client-go/api/v1/edgelabel/edgelabel_test.go new file mode 100644 index 000000000..6709c17af --- /dev/null +++ b/hugegraph-client-go/api/v1/edgelabel/edgelabel_test.go @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package edgelabel_test + +import ( + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/edgelabel" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/propertykey" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/vertexlabel" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "math/rand" + "testing" + "time" +) + +func TestEdgelabel(t *testing.T) { + + // 1. 创建 Edgelabel + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + t.Errorf("NewDefaultCommonClient() error = %v", err) + } + + rand.Seed(time.Now().UnixNano()) + randNum := rand.Intn(999999) + pkName1 := fmt.Sprintf("tpk1-%d", randNum) + pkName2 := fmt.Sprintf("tpk2-%d", randNum) + + vertexLabelName1 := fmt.Sprintf("tvl-1%d", randNum) + vertexLabelName2 := fmt.Sprintf("tvl-2%d", randNum) + edgeLabelName := fmt.Sprintf("tel-%d", randNum) + + // 创建顶点使用的pk + createPk(client, pkName1, t) + createPk(client, pkName2, t) + + // 创建source顶点label + createVertexLabel(client, vertexLabelName1, t, pkName1, pkName1) + // 创建target顶点label + createVertexLabel(client, vertexLabelName2, t, pkName1, pkName1) + // 创建边的label + createEdgeLabel(client, edgeLabelName, t, vertexLabelName1, vertexLabelName2, []string{}, []string{}, []string{pkName2}, true) + + // 获取边的label + allEdgeLabels, err := client.EdgeLabel.GetAll() + if err != nil { + t.Errorf("client.EdgeLabel.GetAll() error = %v", err) + } + hasLabel := false + for _, edgeLabel := range allEdgeLabels.Data.Edgelabels { + if edgeLabel.Name == edgeLabelName { + hasLabel = true + break + } + } + if !hasLabel { + t.Errorf("client.EdgeLabel.GetAll() error = %v", err) + } + + // 删除边的label + client.EdgeLabel.DeleteByName( + client.EdgeLabel.DeleteByName.WithName(edgeLabelName), + ) + + client.VertexLabel.DeleteByName( + client.VertexLabel.DeleteByName.WithName(vertexLabelName1), + ) + client.VertexLabel.DeleteByName( + client.VertexLabel.DeleteByName.WithName(vertexLabelName2), + ) + + //deletePk(client, pkName1, t) + //deletePk(client, pkName2, t) +} + +func createPk(client *hugegraph.CommonClient, pkName string, t *testing.T) *propertykey.CreateResponse { + pk1 := propertykey.CreateRequestData{ + Name: pkName, + DataType: model.PropertyDataTypeInt, + Cardinality: model.PropertyCardinalitySingle, + } + + pk1Resp, err := client.Propertykey.Create( + client.Propertykey.Create.WithReqData(pk1), + ) + if err != nil { + t.Errorf("Create Propertykey error = %v", err) + } + if pk1Resp.Data.PropertyKey.Name != pk1.Name { + t.Errorf("Create Propertykey error = %v", err) + } + return pk1Resp +} + +func TestGet(t *testing.T) { + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + t.Errorf("NewDefaultCommonClient() error = %v", err) + } + client.EdgeLabel.GetAll() +} + +func createVertexLabel( + client *hugegraph.CommonClient, + vertexLabelName string, + t *testing.T, + pkName string, + pkNames ...string) *vertexlabel.CreateResponse { + createResp, err := client.VertexLabel.Create( + client.VertexLabel.Create.WithReqData( + vertexlabel.CreateRequestData{ + Name: vertexLabelName, + IDStrategy: model.IDStrategyDefault, + Properties: pkNames, + PrimaryKeys: []string{ + pkName, + }, + NullableKeys: []string{}, + EnableLabelIndex: true, + }, + ), + ) + if err != nil { + t.Errorf("Create Vertexlabel error = %v", err) + } + if createResp.Data.Name != vertexLabelName { + t.Errorf("Create Vertexlabel error = %v", err) + } + return createResp +} + +func createEdgeLabel( + client *hugegraph.CommonClient, + edgeLabelName string, + t *testing.T, + sourceLabel string, + targetLabel string, + sortKeys []string, + nullableKeys []string, + pkNames []string, + enableLabelIndex bool, +) { + // 1. 创建 Edgelabel + client.EdgeLabel.Create( + client.EdgeLabel.Create.WithReqData( + edgelabel.CreateRequestData{ + Name: edgeLabelName, + SourceLabel: sourceLabel, + TargetLabel: targetLabel, + Frequency: model.FrequencySingle, + Properties: pkNames, + SortKeys: sortKeys, + NullableKeys: nullableKeys, + EnableLabelIndex: enableLabelIndex, + }, + ), + ) +} + +func deletePk(client *hugegraph.CommonClient, name string, t *testing.T) { + // propertyKey delete + respDelete, err := client.Propertykey.DeleteByName( + client.Propertykey.DeleteByName.WithName(name), + ) + if err != nil { + t.Errorf(err.Error()) + } + if respDelete.StatusCode > 299 { + t.Errorf("delete propertyKey failed") + } +} diff --git a/hugegraph-client-go/api/v1/gremlin/gemlin.go b/hugegraph-client-go/api/v1/gremlin/gemlin.go new file mode 100644 index 000000000..5be0f42b0 --- /dev/null +++ b/hugegraph-client-go/api/v1/gremlin/gemlin.go @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +package gremlin + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + "io" + "io/ioutil" + "net/http" + url2 "net/url" + "strings" +) + +type Gremlin struct { + Get + Post +} + +func New(t api.Transport) *Gremlin { + return &Gremlin{ + Get: newGetFunc(t), + Post: newPostFunc(t), + } +} + +type Get func(o ...func(*GetRequest)) (*GetResponse, error) +type Post func(o ...func(*PostRequest)) (*PostResponse, error) + +func newGetFunc(t api.Transport) Get { + return func(o ...func(*GetRequest)) (*GetResponse, error) { + var r = GetRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newPostFunc(t api.Transport) Post { + return func(o ...func(*PostRequest)) (*PostResponse, error) { + var r = PostRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type GetRequest struct { + ctx context.Context + gremlin string + bindings map[string]string + language string + aliases map[string]string +} +type GetResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` +} +type PostRequest struct { + ctx context.Context + body io.ReadCloser + gremlin string + bindings map[string]string + language string + aliases struct { + //Graph string `json:"graph"` + //G string `json:"g"` + } +} +type PostRequestData struct { + Gremlin string `json:"gremlin"` + Bindings map[string]string `json:"bindings,omitempty"` + Language string `json:"language,omitempty"` + Aliases struct { + //Graph string `json:"graph"` + //G string `json:"g"` + } `json:"aliases,omitempty"` +} +type PostResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data *PostResponseData `json:"data"` +} +type PostResponseData struct { + RequestID string `json:"requestId,omitempty"` + Status struct { + Message string `json:"message"` + Code int `json:"code"` + Attributes struct { + } `json:"attributes"` + } `json:"status"` + Result struct { + Data interface{} `json:"data"` + Meta interface{} `json:"meta"` + } `json:"result,omitempty"` + Exception string `json:"exception,omitempty"` + Message string `json:"message,omitempty"` + Cause string `json:"cause,omitempty"` + Trace []string `json:"trace,omitempty"` +} + +func (g Get) WithGremlin(gremlin string) func(request *GetRequest) { + return func(r *GetRequest) { + r.gremlin = gremlin + } +} +func (g Post) WithGremlin(gremlin string) func(request *PostRequest) { + return func(r *PostRequest) { + r.gremlin = gremlin + } +} + +func (g GetRequest) Do(ctx context.Context, transport api.Transport) (*GetResponse, error) { + + url := "/gremlin" + params := &url2.Values{} + if len(g.gremlin) <= 0 { + return nil, errors.New("please set gremlin") + } else { + params.Add("gremlin", g.gremlin) + } + if len(g.language) > 0 { + params.Add("language", g.language) + } + + if g.aliases != nil && len(g.aliases) >= 0 { + aliasesJsonStr, err := json.Marshal(g.aliases) + if err != nil { + return nil, err + } + params.Add("aliases", string(aliasesJsonStr)) + } + + if g.bindings != nil && len(g.bindings) >= 0 { + bindingsJsonStr, err := json.Marshal(g.bindings) + if err != nil { + return nil, err + } + params.Add("bindings", string(bindingsJsonStr)) + } + + req, err := api.NewRequest("GET", url, params, nil) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + fmt.Println(string(bytes)) + + gremlinGetResponse := &GetResponse{} + gremlinGetResponse.StatusCode = res.StatusCode + return gremlinGetResponse, nil +} +func (g PostRequest) Do(ctx context.Context, transport api.Transport) (*PostResponse, error) { + + if len(g.gremlin) < 1 { + return nil, errors.New("PostRequest param error , gremlin is empty") + } + + if len(g.language) < 1 { + g.language = "gremlin-groovy" + } + + gd := &PostRequestData{ + Gremlin: g.gremlin, + Bindings: g.bindings, + Language: g.language, + Aliases: g.aliases, + } + + byteBody, err := json.Marshal(&gd) // 序列化 + + if err != nil { + return nil, err + } + + reader := strings.NewReader(string(byteBody)) + req, _ := api.NewRequest("POST", "/gremlin", nil, reader) + + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + gremlinPostResp := &PostResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + + respData := &PostResponseData{} + err = json.Unmarshal(bytes, respData) + if err != nil { + return nil, err + } + gremlinPostResp.StatusCode = res.StatusCode + gremlinPostResp.Header = res.Header + gremlinPostResp.Body = res.Body + gremlinPostResp.Data = respData + return gremlinPostResp, nil +} diff --git a/hugegraph-client-go/api/v1/gremlin/gemlin_test.go b/hugegraph-client-go/api/v1/gremlin/gemlin_test.go new file mode 100644 index 000000000..c8cddd675 --- /dev/null +++ b/hugegraph-client-go/api/v1/gremlin/gemlin_test.go @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +package gremlin_test + +import ( + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" + "log" + "testing" +) + +func TestGremlin(t *testing.T) { + + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + log.Println(err) + } + respGet, err := client.Gremlin.Get( + client.Gremlin.Get.WithGremlin("hugegraph.traversal().V().limit(3)"), + ) + if err != nil { + log.Fatalln(err) + } + if respGet.StatusCode != 200 { + t.Error("client.Gremlin.GremlinGet error ") + } + + respPost, err := client.Gremlin.Post( + client.Gremlin.Post.WithGremlin("hugegraph.traversal().V().limit(3)"), + ) + if err != nil { + log.Fatalln(err) + } + fmt.Println(respPost.Data.Result.Data) + +} diff --git a/hugegraph-client-go/api/v1/propertykey/propertykey.go b/hugegraph-client-go/api/v1/propertykey/propertykey.go new file mode 100644 index 000000000..125d9a088 --- /dev/null +++ b/hugegraph-client-go/api/v1/propertykey/propertykey.go @@ -0,0 +1,458 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to You under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. + */ + +package propertykey + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +type PropertyKey struct { + Create + DeleteByName + GetAll + GetByName + UpdateUserdata +} + +func New(t api.Transport) *PropertyKey { + return &PropertyKey{ + // Create https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#121-create-a-propertykey + Create: newCreateFunc(t), + // DeleteByName https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#125-delete-propertykey-according-to-name + DeleteByName: newDeleteByNameFunc(t), + // GetAll https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#123-get-all-propertykeys + GetAll: newGetAllFunc(t), + // GetByName https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#124-get-propertykey-according-to-name + GetByName: newGetByNameFunc(t), + // UpdateUserdata https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#122-add-or-remove-userdata-for-an-existing-propertykey + UpdateUserdata: newUpdateUserdataFunc(t), + } +} + +func newCreateFunc(t api.Transport) Create { + return func(o ...func(*CreateRequest)) (*CreateResponse, error) { + var r = CreateRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newDeleteByNameFunc(t api.Transport) DeleteByName { + return func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) { + var r = DeleteByNameRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newGetAllFunc(t api.Transport) GetAll { + return func(o ...func(*GetAllRequest)) (*GetAllResponse, error) { + var r = GetAllRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newGetByNameFunc(t api.Transport) GetByName { + return func(o ...func(*GetByNameRequest)) (*GetByNameResponse, error) { + var r = GetByNameRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newUpdateUserdataFunc(t api.Transport) UpdateUserdata { + return func(o ...func(*UpdateUserdataRequest)) (*UpdateUserdataResponse, error) { + var r = UpdateUserdataRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type Create func(o ...func(*CreateRequest)) (*CreateResponse, error) +type DeleteByName func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) +type GetAll func(o ...func(*GetAllRequest)) (*GetAllResponse, error) +type GetByName func(o ...func(*GetByNameRequest)) (*GetByNameResponse, error) +type UpdateUserdata func(o ...func(*UpdateUserdataRequest)) (*UpdateUserdataResponse, error) + +type CreateRequest struct { + Body io.Reader + ctx context.Context + reqData CreateRequestData +} +type CreateRequestData struct { + Name string `json:"name"` + DataType model.PropertyDataType `json:"data_type"` + Cardinality model.PropertyCardinality `json:"cardinality"` +} +type CreateResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data CreateResponseData `json:"versions"` +} +type CreateResponseData struct { + PropertyKey struct { + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + Cardinality string `json:"cardinality"` + AggregateType string `json:"aggregate_type"` + WriteType string `json:"write_type"` + Properties []interface{} `json:"properties"` + Status string `json:"status"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"property_key"` + TaskID int `json:"task_id"` +} + +type DeleteByNameRequest struct { + Body io.Reader + ctx context.Context + name string +} +type DeleteByNameResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data DeleteByNameResponseData `json:"versions"` +} +type DeleteByNameResponseData struct { + TaskID int `json:"task_id"` +} + +type GetAllRequest struct { + Body io.Reader + ctx context.Context +} +type GetAllResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data GetAllResponseData `json:"-"` +} +type GetAllResponseData struct { + Propertykeys []struct { + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + Cardinality string `json:"cardinality"` + Properties []interface{} `json:"properties"` + UserData struct { + } `json:"user_data"` + } `json:"propertykeys"` +} + +type GetByNameRequest struct { + Body io.Reader + ctx context.Context + name string +} +type GetByNameResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data GetByNameResponseData `json:"-"` +} +type GetByNameResponseData struct { + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + Cardinality string `json:"cardinality"` + AggregateType string `json:"aggregate_type"` + WriteType string `json:"write_type"` + Properties []interface{} `json:"properties"` + Status string `json:"status"` + UserData struct { + Min int `json:"min"` + Max int `json:"max"` + CreateTime string `json:"~create_time"` + } `json:"user_data"` +} + +type UpdateUserdataRequest struct { + Body io.Reader + ctx context.Context + reqData UpdateUserdataRequestData +} +type UpdateUserdataRequestData struct { + Action model.Action `json:"-"` + Name string `json:"name"` + UserData struct { + Min int `json:"min"` + Max int `json:"max"` + } `json:"user_data"` +} +type UpdateUserdataResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data UpdateUserdataResponseData `json:"-"` +} +type UpdateUserdataResponseData struct { + PropertyKey struct { + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + Cardinality string `json:"cardinality"` + AggregateType string `json:"aggregate_type"` + WriteType string `json:"write_type"` + Properties []interface{} `json:"properties"` + Status string `json:"status"` + UserData struct { + Min int `json:"min"` + Max int `json:"max"` + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"property_key"` + TaskID int `json:"task_id"` +} + +func (r CreateRequest) Do(ctx context.Context, transport api.Transport) (*CreateResponse, error) { + + if len(r.reqData.Name) <= 0 { + return nil, errors.New("create property must set name") + } + if len(r.reqData.DataType) <= 0 { + return nil, errors.New("create property must set dataType") + } + if len(r.reqData.Cardinality) <= 0 { + return nil, errors.New("create property must set cardinality") + } + + byteBody, err := json.Marshal(&r.reqData) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(byteBody)) + + req, err := api.NewRequest("POST", fmt.Sprintf("/graphs/%s/schema/propertykeys", transport.GetConfig().Graph), nil, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &CreateResponse{} + respData := CreateResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r DeleteByNameRequest) Do(ctx context.Context, transport api.Transport) (*DeleteByNameResponse, error) { + + if len(r.name) <= 0 { + return nil, errors.New("delete by name ,please set name") + } + req, err := api.NewRequest("DELETE", fmt.Sprintf("/graphs/%s/schema/propertykeys/%s", transport.GetConfig().Graph, r.name), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &DeleteByNameResponse{} + respData := DeleteByNameResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r GetAllRequest) Do(ctx context.Context, transport api.Transport) (*GetAllResponse, error) { + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema/propertykeys", transport.GetConfig().Graph), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &GetAllResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := GetAllResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r GetByNameRequest) Do(ctx context.Context, transport api.Transport) (*GetByNameResponse, error) { + + if len(r.name) <= 0 { + return nil, errors.New("get_by_name must set name") + } + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema/propertykeys/%s", transport.GetConfig().Graph, r.name), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &GetByNameResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := GetByNameResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r UpdateUserdataRequest) Do(ctx context.Context, transport api.Transport) (*UpdateUserdataResponse, error) { + + params := &url.Values{} + if len(r.reqData.Action) <= 0 { + return nil, errors.New("property update userdata must set action") + } else { + params.Add("action", string(r.reqData.Action)) + } + if len(r.reqData.Name) <= 0 { + return nil, errors.New("property update userdata must set name") + } + + byteBody, err := json.Marshal(&r.reqData) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(byteBody)) + + req, err := api.NewRequest("PUT", fmt.Sprintf("/graphs/%s/schema/propertykeys/%s", transport.GetConfig().Graph, r.reqData.Name), params, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &UpdateUserdataResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := UpdateUserdataResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} + +func (r Create) WithReqData(reqData CreateRequestData) func(request *CreateRequest) { + return func(r *CreateRequest) { + r.reqData = reqData + } +} +func (p DeleteByName) WithName(name string) func(request *DeleteByNameRequest) { + return func(r *DeleteByNameRequest) { + r.name = name + } +} +func (r GetByName) WithName(name string) func(request *GetByNameRequest) { + return func(r *GetByNameRequest) { + r.name = name + } +} +func (r UpdateUserdata) WithReqData(reqData UpdateUserdataRequestData) func(request *UpdateUserdataRequest) { + return func(r *UpdateUserdataRequest) { + r.reqData = reqData + } +} diff --git a/hugegraph-client-go/api/v1/propertykey/propertykey_test.go b/hugegraph-client-go/api/v1/propertykey/propertykey_test.go new file mode 100644 index 000000000..14e1e99c3 --- /dev/null +++ b/hugegraph-client-go/api/v1/propertykey/propertykey_test.go @@ -0,0 +1,120 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to You under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. + */ + +package propertykey_test + +import ( + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/propertykey" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "log" + "math/rand" + "testing" + "time" +) + +func TestPropertyKey(t *testing.T) { + + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + log.Println(err) + } + rand.Seed(time.Now().UnixNano()) + name := fmt.Sprintf("testProperty%d", rand.Intn(99999)) + + // create propertyKey + respCreate, err := client.Propertykey.Create( + client.Propertykey.Create.WithReqData( + propertykey.CreateRequestData{ + Name: name, + DataType: model.PropertyDataTypeInt, + Cardinality: model.PropertyCardinalitySingle, + }, + ), + ) + if err != nil { + t.Errorf(err.Error()) + } + if respCreate.Data.PropertyKey.Name != name || respCreate.Data.PropertyKey.ID <= 0 { + t.Errorf("create propertyKey failed") + } + + // propertyKey get all + respGetAll, err := client.Propertykey.GetAll() + if err != nil { + t.Errorf(err.Error()) + } + + hasCreated := false + for _, pk := range respGetAll.Data.Propertykeys { + if pk.Name == name { + hasCreated = true + } + } + if !hasCreated { + t.Errorf("get all propertyKey failed") + } + + // propertyKey update user_data + respUpdateUserdata, err := client.Propertykey.UpdateUserdata( + client.Propertykey.UpdateUserdata.WithReqData( + propertykey.UpdateUserdataRequestData{ + Action: model.ActionAppend, + Name: name, + UserData: struct { + Min int `json:"min"` + Max int `json:"max"` + }(struct { + Min int + Max int + }{ + Min: 1, + Max: 10, + }), + }, + ), + ) + if err != nil { + t.Errorf(err.Error()) + } + if respUpdateUserdata.Data.PropertyKey.UserData.Max != 10 { + t.Errorf("update userdata propertyKey failed") + } + + // propertyKey get by name + respGetByName, err := client.Propertykey.GetByName( + client.Propertykey.GetByName.WithName(name), + ) + if err != nil { + t.Errorf(err.Error()) + } + if respGetByName.Data.Name != name || respGetByName.Data.ID <= 0 || respGetByName.Data.UserData.Max != 10 { + t.Errorf("getByName propertyKey failed") + } + + // propertyKey delete + respDelete, err := client.Propertykey.DeleteByName( + client.Propertykey.DeleteByName.WithName(name), + ) + if err != nil { + t.Errorf(err.Error()) + } + if respDelete.StatusCode > 299 { + t.Errorf("delete propertyKey failed") + } +} diff --git a/hugegraph-client-go/api/v1/schema.go b/hugegraph-client-go/api/v1/schema.go new file mode 100644 index 000000000..ebe237ffb --- /dev/null +++ b/hugegraph-client-go/api/v1/schema.go @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package v1 + +import ( + "context" + "encoding/json" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" + "io" + "io/ioutil" + "net/http" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" +) + +// ----- API Definition ------------------------------------------------------- +// View Schema information of HugeGraph +// +// See full documentation at https://hugegraph.apache.org/docs/clients/restful-api/schema/#11-schema +func newSchemaFunc(t api.Transport) Schema { + return func(o ...func(*SchemaRequest)) (*SchemaResponse, error) { + var r = SchemaRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type Schema func(o ...func(*SchemaRequest)) (*SchemaResponse, error) + +type SchemaRequest struct { + Body io.Reader + ctx context.Context + config hgtransport.Config +} + +type SchemaResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data SchemaResponseData `json:"-"` +} + +type SchemaResponseData struct { + Propertykeys []struct { + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + Cardinality string `json:"cardinality"` + AggregateType string `json:"aggregate_type"` + WriteType string `json:"write_type"` + Properties []interface{} `json:"properties"` + Status string `json:"status"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"propertykeys"` + Vertexlabels []struct { + ID int `json:"id"` + Name string `json:"name"` + IDStrategy string `json:"id_strategy"` + PrimaryKeys []string `json:"primary_keys"` + NullableKeys []interface{} `json:"nullable_keys"` + IndexLabels []string `json:"index_labels"` + Properties []string `json:"properties"` + Status string `json:"status"` + TTL int `json:"ttl"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"vertexlabels"` + Edgelabels []struct { + ID int `json:"id"` + Name string `json:"name"` + SourceLabel string `json:"source_label"` + TargetLabel string `json:"target_label"` + Frequency string `json:"frequency"` + SortKeys []interface{} `json:"sort_keys"` + NullableKeys []interface{} `json:"nullable_keys"` + IndexLabels []string `json:"index_labels"` + Properties []string `json:"properties"` + Status string `json:"status"` + TTL int `json:"ttl"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"edgelabels"` + Indexlabels []struct { + ID int `json:"id"` + Name string `json:"name"` + BaseType string `json:"base_type"` + BaseValue string `json:"base_value"` + IndexType string `json:"index_type"` + Fields []string `json:"fields"` + Status string `json:"status"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` + } `json:"indexlabels"` +} + +func (r SchemaRequest) Do(ctx context.Context, transport api.Transport) (*SchemaResponse, error) { + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema", transport.GetConfig().Graph), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + schemaRespData := SchemaResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &schemaRespData) + if err != nil { + return nil, err + } + schemaResp := &SchemaResponse{} + schemaResp.StatusCode = res.StatusCode + schemaResp.Header = res.Header + schemaResp.Body = res.Body + schemaResp.Data = schemaRespData + return schemaResp, nil +} diff --git a/hugegraph-client-go/api/v1/schema_test.go b/hugegraph-client-go/api/v1/schema_test.go new file mode 100644 index 000000000..47daab74f --- /dev/null +++ b/hugegraph-client-go/api/v1/schema_test.go @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package v1_test + +import ( + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" + "log" + "testing" +) + +func TestSchemaRequest_Do(t *testing.T) { + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + log.Println(err) + } + schema, err := client.Schema() + if err != nil { + log.Fatalln(err) + } + fmt.Println(schema.Data) +} diff --git a/hugegraph-client-go/api/v1/version.go b/hugegraph-client-go/api/v1/version.go new file mode 100644 index 000000000..e3a55990f --- /dev/null +++ b/hugegraph-client-go/api/v1/version.go @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package v1 + +import ( + "context" + "encoding/json" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" + "io" + "io/ioutil" + "net/http" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" +) + +// ----- API Definition ------------------------------------------------------- +// View version information of HugeGraph +// +// See full documentation at https://hugegraph.apache.org/docs/clients/restful-api/other/#1111-view-version-information-of-hugegraph +func newVersionFunc(t api.Transport) Version { + return func(o ...func(*VersionRequest)) (*VersionResponse, error) { + var r = VersionRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type Version func(o ...func(*VersionRequest)) (*VersionResponse, error) + +type VersionRequest struct { + Body io.Reader + ctx context.Context + config hgtransport.Config +} + +type VersionResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Versions VersionResponseData `json:"versions"` +} + +type VersionResponseData struct { + Version string `json:"version"` // hugegraph version + Core string `json:"core"` // hugegraph core version + Gremlin string `json:"gremlin"` // hugegraph gremlin version + API string `json:"api"` // hugegraph api version +} + +func (r VersionRequest) Do(ctx context.Context, transport api.Transport) (*VersionResponse, error) { + + req, err := api.NewRequest("GET", "/versions", nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + versionResp := &VersionResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, versionResp) + if err != nil { + return nil, err + } + versionResp.StatusCode = res.StatusCode + versionResp.Header = res.Header + versionResp.Body = res.Body + return versionResp, nil +} diff --git a/hugegraph-client-go/api/v1/version_test.go b/hugegraph-client-go/api/v1/version_test.go new file mode 100644 index 000000000..623098852 --- /dev/null +++ b/hugegraph-client-go/api/v1/version_test.go @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package v1_test + +import ( + "testing" + + hugegraph "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" +) + +func TestVersionRequest_Do(t *testing.T) { + + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + t.Errorf("NewDefaultCommonClient() error = %v", err) + } + + resp, err := client.Version() + if err != nil { + t.Errorf(" client.Version() error = %v", err) + } + if resp.StatusCode != 200 { + t.Errorf(" client.Version() code error = %v", resp.StatusCode) + } +} diff --git a/hugegraph-client-go/api/v1/vertex/vertex.go b/hugegraph-client-go/api/v1/vertex/vertex.go new file mode 100644 index 000000000..0e2a3c521 --- /dev/null +++ b/hugegraph-client-go/api/v1/vertex/vertex.go @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +package vertex + +import ( + "context" + "encoding/json" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "io" + "io/ioutil" + "net/http" + url2 "net/url" + "strings" +) + +type Vertex struct { + Create + BatchCreate + UpdateProperties + BatchUpdateProperties + DeleteProperties + GetByCond + GetByID + DeleteByID +} + +func New(t api.Transport) *Vertex { + return &Vertex{ + // Create https://hugegraph.apache.org/docs/clients/restful-api/vertex/#211-create-a-vertex + Create: newCreateFunc(t), + // BatchCreate https://hugegraph.apache.org/docs/clients/restful-api/vertex/#212-create-multiple-vertices + BatchCreate: newBatchCreateFunc(t), + // UpdateProperties https://hugegraph.apache.org/docs/clients/restful-api/vertex/#213-update-vertex-properties + UpdateProperties: newUpdatePropertiesFunc(t), + } +} + +type CreateReq struct { + ctx context.Context + data model.Vertex[any] +} +type CreateResp struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data model.Vertex[any] +} +type BatchCreateReq struct { + ctx context.Context + vertices []model.Vertex[any] +} +type BatchCreateResp struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + IDs []string +} +type UpdatePropertiesReq struct { + ctx context.Context + id string + action model.Action + vertex model.Vertex[any] +} +type UpdatePropertiesResp struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data model.Vertex[any] +} + +type Create func(o ...func(*CreateReq)) (*CreateResp, error) +type BatchCreate func(o ...func(*BatchCreateReq)) (*BatchCreateResp, error) +type UpdateProperties func(o ...func(*UpdatePropertiesReq)) (*UpdatePropertiesResp, error) +type BatchUpdateProperties func(o ...func(*CreateReq)) (*CreateResp, error) +type DeleteProperties func(o ...func(*CreateReq)) (*CreateResp, error) +type GetByCond func(o ...func(*CreateReq)) (*CreateResp, error) +type GetByID func(o ...func(*CreateReq)) (*CreateResp, error) +type DeleteByID func(o ...func(*CreateReq)) (*CreateResp, error) + +func newCreateFunc(t api.Transport) Create { + return func(o ...func(*CreateReq)) (*CreateResp, error) { + var r = CreateReq{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newBatchCreateFunc(t api.Transport) BatchCreate { + return func(o ...func(*BatchCreateReq)) (*BatchCreateResp, error) { + var r = BatchCreateReq{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newUpdatePropertiesFunc(t api.Transport) UpdateProperties { + return func(o ...func(*UpdatePropertiesReq)) (*UpdatePropertiesResp, error) { + var r = UpdatePropertiesReq{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +func (c Create) WithContext(ctx context.Context) func(*CreateReq) { + return func(req *CreateReq) { + req.ctx = ctx + } +} +func (c BatchCreate) WithContext(ctx context.Context) func(req *BatchCreateReq) { + return func(req *BatchCreateReq) { + req.ctx = ctx + } +} +func (c UpdateProperties) WithContext(ctx context.Context) func(req *UpdatePropertiesReq) { + return func(req *UpdatePropertiesReq) { + req.ctx = ctx + } +} + +func (c Create) WithVertex(vertex model.Vertex[any]) func(*CreateReq) { + return func(req *CreateReq) { + req.data = vertex + } +} +func (c BatchCreate) WithVertices(vertices []model.Vertex[any]) func(*BatchCreateReq) { + return func(req *BatchCreateReq) { + req.vertices = vertices + } +} +func (c UpdateProperties) WithVertex(vertex model.Vertex[any]) func(*UpdatePropertiesReq) { + return func(req *UpdatePropertiesReq) { + req.vertex = vertex + } +} +func (c UpdateProperties) WithID(ID string) func(*UpdatePropertiesReq) { + return func(req *UpdatePropertiesReq) { + req.id = ID + } +} +func (c UpdateProperties) WithAction(action model.Action) func(*UpdatePropertiesReq) { + return func(req *UpdatePropertiesReq) { + req.action = action + } +} + +func (c CreateReq) Do(ctx context.Context, transport api.Transport) (*CreateResp, error) { + config := transport.GetConfig() + url := "" + if len(config.GraphSpace) > 0 { + url = fmt.Sprintf("/graphspaces/%s/graphs/%s/graph/vertices", config.GraphSpace, config.Graph) + } else { + url = fmt.Sprintf("/graphs/%s/graph/vertices", config.Graph) + } + + jsonData, err := json.Marshal(c.data) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(jsonData)) + + req, err := api.NewRequest("POST", url, nil, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + vertexCreateRespData := model.Vertex[any]{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &vertexCreateRespData) + if err != nil { + return nil, err + } + resp := &CreateResp{} + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = vertexCreateRespData + + return resp, nil +} +func (c BatchCreateReq) Do(ctx context.Context, transport api.Transport) (*BatchCreateResp, error) { + config := transport.GetConfig() + url := "" + if len(config.GraphSpace) > 0 { + url = fmt.Sprintf("/graphspaces/%s/graphs/%s/graph/vertices/batch", config.GraphSpace, config.Graph) + } else { + url = fmt.Sprintf("/graphs/%s/graph/vertices/batch", config.Graph) + } + + jsonData, err := json.Marshal(c.vertices) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(jsonData)) + + req, err := api.NewRequest("POST", url, nil, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + vertexCreateRespData := make([]string, 0) + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &vertexCreateRespData) + if err != nil { + return nil, err + } + resp := &BatchCreateResp{} + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.IDs = vertexCreateRespData + return resp, nil +} +func (c UpdatePropertiesReq) Do(ctx context.Context, transport api.Transport) (*UpdatePropertiesResp, error) { + config := transport.GetConfig() + url := "" + if len(config.GraphSpace) > 0 { + url = fmt.Sprintf("/graphspaces/%s/graphs/%s/graph/vertices/\"%s\"", config.GraphSpace, config.Graph, c.id) + } else { + url = fmt.Sprintf("/graphs/%s/graph/vertices/\"%s\"", config.Graph, c.id) + } + + params := &url2.Values{} + params.Add("action", string(c.action)) + + jsonData, err := json.Marshal(c.vertex) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(jsonData)) + + req, err := api.NewRequest("PUT", url, params, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + respData := model.Vertex[any]{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp := &UpdatePropertiesResp{} + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + + return resp, nil +} diff --git a/hugegraph-client-go/api/v1/vertex/vertex_test.go b/hugegraph-client-go/api/v1/vertex/vertex_test.go new file mode 100644 index 000000000..4af7fc4db --- /dev/null +++ b/hugegraph-client-go/api/v1/vertex/vertex_test.go @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package vertex_test + +import ( + "testing" +) + +type Person struct { + Name string `json:"name,omitempty"` + Age uint64 `json:"age,omitempty"` + City string `json:"city,omitempty"` +} + +func TestVertex(t *testing.T) { + + // 1.create + //client, err := hugegraph.NewDefaultCommonClient() + //ctx := context.Background() + //if err != nil { + // log.Println(err) + //} + //person := Person{ + // Name: "tom", + // Age: 18, + // City: "beijing", + //} + // + //vertex := model.Vertex[any]{ + // Label: "person", + // Properties: person, + //} + // + //// create + //respCreate, err := client.Vertex.Create( + // client.Vertex.Create.WithContext(ctx), + // client.Vertex.Create.WithVertex(vertex), + //) + //if err != nil { + // log.Println(err) + //} + //vertexID := respCreate.Data.ID + //fmt.Println(vertexID) + // + //// batchCreate + //vertices := []model.Vertex[any]{ + // { + // Label: "person", + // Properties: Person{ + // Name: "bob", + // Age: 22, + // City: "shanghai", + // }, + // }, + // { + // Label: "person", + // Properties: Person{ + // Name: "angle", + // Age: 28, + // City: "guangzhou", + // }, + // }, + //} + // + //respBatchResp, err := client.Vertex.BatchCreate( + // client.Vertex.BatchCreate.WithContext(ctx), + // client.Vertex.BatchCreate.WithVertices(vertices), + //) + //if err != nil { + // log.Println(err) + //} + //for i, datum := range respBatchResp.IDs { + // fmt.Printf("index:%d\tid:%s\n", i, datum) + //} + // + //// update properties + //updatePerson := model.Vertex[any]{ + // Label: "person", + // Properties: Person{ + // Age: 10, + // }, + //} + //respUpdate, err := client.Vertex.UpdateProperties( + // client.Vertex.UpdateProperties.WithContext(ctx), + // client.Vertex.UpdateProperties.WithVertex(updatePerson), + // client.Vertex.UpdateProperties.WithID(vertexID), + // client.Vertex.UpdateProperties.WithAction(model.ActionAppend), + //) + //if respUpdate.Data.ID != vertexID { + // t.Errorf("error") + //} + //fmt.Println(respUpdate.Data.Properties) +} + +func createVertexLabel() { + +} diff --git a/hugegraph-client-go/api/v1/vertexlabel/vertexlabel.go b/hugegraph-client-go/api/v1/vertexlabel/vertexlabel.go new file mode 100644 index 000000000..76bc3e0d3 --- /dev/null +++ b/hugegraph-client-go/api/v1/vertexlabel/vertexlabel.go @@ -0,0 +1,457 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package vertexlabel + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" +) + +type VertexLabel struct { + Create + DeleteByName + GetAll + GetByName + UpdateUserdata +} + +func New(t api.Transport) *VertexLabel { + return &VertexLabel{ + // Create https://hugegraph.apache.org/docs/clients/restful-api/vertexlabel/#131-create-a-vertexlabel + Create: newCreateFunc(t), + // DeleteByName https://hugegraph.apache.org/docs/clients/restful-api/vertexlabel/#135-delete-vertexlabel-by-name + DeleteByName: newDeleteByNameFunc(t), + // GetAll https://hugegraph.apache.org/docs/clients/restful-api/vertexlabel/#133-get-all-vertexlabels + GetAll: newGetAllFunc(t), + // GetByName https://hugegraph.apache.org/docs/clients/restful-api/vertexlabel/#134-get-vertexlabel-by-name + GetByName: newGetByNameFunc(t), + // UpdateUserdata https://hugegraph.apache.org/docs/clients/restful-api/propertykey/#122-add-or-remove-userdata-for-an-existing-propertykey + UpdateUserdata: newUpdateUserdataFunc(t), + } +} + +type Create func(o ...func(*CreateRequest)) (*CreateResponse, error) +type DeleteByName func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) +type GetAll func(o ...func(*GetAllRequest)) (*GetAllResponse, error) +type GetByName func(o ...func(*GetByNameRequest)) (*GetByNameResponse, error) +type UpdateUserdata func(o ...func(*UpdateUserdataRequest)) (*UpdateUserdataResponse, error) + +func newCreateFunc(t api.Transport) Create { + return func(o ...func(*CreateRequest)) (*CreateResponse, error) { + var r = CreateRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newDeleteByNameFunc(t api.Transport) DeleteByName { + return func(o ...func(*DeleteByNameRequest)) (*DeleteByNameResponse, error) { + var r = DeleteByNameRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newGetAllFunc(t api.Transport) GetAll { + return func(o ...func(*GetAllRequest)) (*GetAllResponse, error) { + var r = GetAllRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newGetByNameFunc(t api.Transport) GetByName { + return func(o ...func(*GetByNameRequest)) (*GetByNameResponse, error) { + var r = GetByNameRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} +func newUpdateUserdataFunc(t api.Transport) UpdateUserdata { + return func(o ...func(*UpdateUserdataRequest)) (*UpdateUserdataResponse, error) { + var r = UpdateUserdataRequest{} + for _, f := range o { + f(&r) + } + return r.Do(r.ctx, t) + } +} + +type CreateRequest struct { + Body io.Reader + ctx context.Context + reqData CreateRequestData +} +type CreateRequestData struct { + Name string `json:"name"` + IDStrategy model.IDStrategy `json:"id_strategy"` + Properties []string `json:"properties"` + PrimaryKeys []string `json:"primary_keys"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` +} +type CreateResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data CreateResponseData `json:"-"` +} +type CreateResponseData struct { + ID int `json:"id"` + PrimaryKeys []string `json:"primary_keys"` + IDStrategy model.IDStrategy `json:"id_strategy"` + Name string `json:"name"` + IndexNames []string `json:"index_names"` + Properties []string `json:"properties"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + } `json:"user_data"` +} + +type DeleteByNameRequest struct { + Body io.Reader + ctx context.Context + name string +} +type DeleteByNameResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + DeleteByNames DeleteByNameResponseData `json:"versions"` +} +type DeleteByNameResponseData struct { + TaskID int `json:"task_id"` +} + +type GetAllRequest struct { + Body io.Reader + ctx context.Context +} +type GetAllResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data GetAllResponseData `json:"-"` +} +type GetAllResponseData struct { + Vertexlabels []struct { + ID int `json:"id"` + PrimaryKeys []string `json:"primary_keys"` + IDStrategy string `json:"id_strategy"` + Name string `json:"name"` + IndexNames []interface{} `json:"index_names"` + Properties []string `json:"properties"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + Super string `json:"super"` + } `json:"user_data,omitempty"` + } `json:"vertexlabels"` +} + +type GetByNameRequest struct { + Body io.Reader + ctx context.Context + name string +} +type GetByNameResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data GetByNameResponseData `json:"-"` +} +type GetByNameResponseData struct { + ID int `json:"id"` + Name string `json:"name"` + IDStrategy string `json:"id_strategy"` + PrimaryKeys []string `json:"primary_keys"` + NullableKeys []interface{} `json:"nullable_keys"` + IndexLabels []string `json:"index_labels"` + Properties []string `json:"properties"` + Status string `json:"status"` + TTL int `json:"ttl"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + CreateTime string `json:"~create_time"` + } `json:"user_data"` +} + +type UpdateUserdataRequest struct { + Body io.Reader + ctx context.Context + reqData UpdateUserdataRequestData +} + + +type UpdateUserdataRequestData struct { + Action model.Action `json:"-"` + Name string `json:"name"` + Properties []string `json:"properties"` + NullableKeys []string `json:"nullable_keys"` + UserData struct { + Super string `json:"super"` + } `json:"user_data"` +} +type UpdateUserdataResponse struct { + StatusCode int `json:"-"` + Header http.Header `json:"-"` + Body io.ReadCloser `json:"-"` + Data UpdateUserdataResponseData `json:"-"` +} +type UpdateUserdataResponseData struct { + ID int `json:"id"` + PrimaryKeys []string `json:"primary_keys"` + IDStrategy string `json:"id_strategy"` + Name string `json:"name"` + IndexNames []interface{} `json:"index_names"` + Properties []string `json:"properties"` + NullableKeys []string `json:"nullable_keys"` + EnableLabelIndex bool `json:"enable_label_index"` + UserData struct { + Super string `json:"super"` + } `json:"user_data"` +} + +func (r CreateRequest) Do(ctx context.Context, transport api.Transport) (*CreateResponse, error) { + + if len(r.reqData.Name) <= 0 { + return nil, errors.New("create property must set name") + } + if len(r.reqData.IDStrategy) <= 0 { + return nil, errors.New("create property must set id_strategy") + } + if len(r.reqData.Properties) <= 0 { + return nil, errors.New("create property must set properties") + } + + byteBody, err := json.Marshal(&r.reqData) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(byteBody)) + + req, err := api.NewRequest("POST", fmt.Sprintf("/graphs/%s/schema/vertexlabels", transport.GetConfig().Graph), nil, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &CreateResponse{} + respData := CreateResponseData{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r DeleteByNameRequest) Do(ctx context.Context, transport api.Transport) (*DeleteByNameResponse, error) { + + if len(r.name) <= 0 { + return nil, errors.New("delete by name ,please set name") + } + req, err := api.NewRequest("DELETE", fmt.Sprintf("/graphs/%s/schema/vertexlabels/%s", transport.GetConfig().Graph, r.name), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + versionResp := &DeleteByNameResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + err = json.Unmarshal(bytes, versionResp) + if err != nil { + return nil, err + } + versionResp.StatusCode = res.StatusCode + versionResp.Header = res.Header + versionResp.Body = res.Body + return versionResp, nil +} +func (r GetAllRequest) Do(ctx context.Context, transport api.Transport) (*GetAllResponse, error) { + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema/vertexlabels", transport.GetConfig().Graph), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &GetAllResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := GetAllResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r GetByNameRequest) Do(ctx context.Context, transport api.Transport) (*GetByNameResponse, error) { + + if len(r.name) <= 0 { + return nil, errors.New("GetByNameRequest must set name") + } + + req, err := api.NewRequest("GET", fmt.Sprintf("/graphs/%s/schema/vertexlabels/%s", transport.GetConfig().Graph, r.name), nil, r.Body) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &GetByNameResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := GetByNameResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} +func (r UpdateUserdataRequest) Do(ctx context.Context, transport api.Transport) (*UpdateUserdataResponse, error) { + params := &url.Values{} + if len(r.reqData.Action) <= 0 { + return nil, errors.New("property update userdata must set action") + } else { + params.Add("action", string(r.reqData.Action)) + } + if len(r.reqData.Name) <= 0 { + return nil, errors.New("property update userdata must set name") + } + + byteBody, err := json.Marshal(&r.reqData) + if err != nil { + return nil, err + } + reader := strings.NewReader(string(byteBody)) + + req, err := api.NewRequest("PUT", fmt.Sprintf("/graphs/%s/schema/vertexlabels/%s", transport.GetConfig().Graph, r.reqData.Name), params, reader) + if err != nil { + return nil, err + } + if ctx != nil { + req = req.WithContext(ctx) + } + + res, err := transport.Perform(req) + if err != nil { + return nil, err + } + + resp := &UpdateUserdataResponse{} + bytes, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + respData := UpdateUserdataResponseData{} + err = json.Unmarshal(bytes, &respData) + if err != nil { + return nil, err + } + resp.StatusCode = res.StatusCode + resp.Header = res.Header + resp.Body = res.Body + resp.Data = respData + return resp, nil +} + +func (r Create) WithReqData(reqData CreateRequestData) func(request *CreateRequest) { + return func(r *CreateRequest) { + r.reqData = reqData + } +} +func (p DeleteByName) WithName(name string) func(request *DeleteByNameRequest) { + return func(r *DeleteByNameRequest) { + r.name = name + } +} +func (v GetByName) WithName(name string) func(r *GetByNameRequest) { + return func(r *GetByNameRequest) { + r.name = name + } +} +func (r UpdateUserdata) WithReqData(reqData UpdateUserdataRequestData) func(request *UpdateUserdataRequest) { + return func(r *UpdateUserdataRequest) { + r.reqData = reqData + } +} diff --git a/hugegraph-client-go/api/v1/vertexlabel/vertexlabel_test.go b/hugegraph-client-go/api/v1/vertexlabel/vertexlabel_test.go new file mode 100644 index 000000000..5289434d9 --- /dev/null +++ b/hugegraph-client-go/api/v1/vertexlabel/vertexlabel_test.go @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package vertexlabel_test + +import ( + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/propertykey" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/vertexlabel" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/model" + "math/rand" + "testing" + "time" +) + +func TestVertexLabel(t *testing.T) { + client, err := hugegraph.NewDefaultCommonClient() + if err != nil { + t.Errorf("NewDefaultCommonClient() error = %v", err) + } + + rand.Seed(time.Now().UnixNano()) + randNum := rand.Intn(999999) + pkName := fmt.Sprintf("testPropertyKey%d", randNum) + vertexLabelName := fmt.Sprintf("testVertexLabel%d", randNum) + + // create propertykey + createPk(client, pkName, t) + // create vertexlabel + createVertexLabel(client, vertexLabelName, t, pkName, pkName) + // get all + getAll(client, vertexLabelName, t) + // update vertexlabel userdata + //updateUserData(client, vertexLabelName, pkName, t) + // get by name + getByName(client, vertexLabelName, t) + // delete vertexlabel + deleteByName(client, vertexLabelName, t) +} + +func createPk(client *hugegraph.CommonClient, pkName string, t *testing.T) *propertykey.CreateResponse { + pk1 := propertykey.CreateRequestData{ + Name: pkName, + DataType: model.PropertyDataTypeInt, + Cardinality: model.PropertyCardinalitySingle, + } + + pk1Resp, err := client.Propertykey.Create( + client.Propertykey.Create.WithReqData(pk1), + ) + if err != nil { + t.Errorf("Create Propertykey error = %v", err) + } + if pk1Resp.Data.PropertyKey.Name != pk1.Name { + t.Errorf("Create Propertykey error = %v", err) + } + + return pk1Resp +} + +func createVertexLabel( + client *hugegraph.CommonClient, + vertexLabelName string, + t *testing.T, + pkName string, + pkNames ...string) *vertexlabel.CreateResponse { + createResp, err := client.VertexLabel.Create( + client.VertexLabel.Create.WithReqData( + vertexlabel.CreateRequestData{ + Name: vertexLabelName, + IDStrategy: model.IDStrategyDefault, + Properties: pkNames, + PrimaryKeys: []string{ + pkName, + }, + NullableKeys: []string{}, + EnableLabelIndex: true, + }, + ), + ) + if err != nil { + t.Errorf("Create Vertexlabel error = %v", err) + } + if createResp.Data.Name != vertexLabelName { + t.Errorf("Create Vertexlabel error = %v", err) + } + return createResp +} + +func updateUserData(client *hugegraph.CommonClient, vertexLabelName string, pkName string, t *testing.T) *vertexlabel.UpdateUserdataResponse { + respUpdateUserData, err := client.VertexLabel.UpdateUserdata( + client.VertexLabel.UpdateUserdata.WithReqData( + vertexlabel.UpdateUserdataRequestData{ + Action: model.ActionAppend, + Name: vertexLabelName, + Properties: []string{pkName}, + NullableKeys: []string{pkName}, + UserData: struct { + Super string `json:"super"` + }{ + Super: "animal", + }, + }, + ), + ) + if err != nil { + t.Errorf("updateUserData Vertexlabel error = %v", err) + } + //if respUpdateUserData.Data.Name != vertexLabelName { + // t.Errorf("updateUserData Vertexlabel error = %v", err) + //} + return respUpdateUserData +} + +func getByName(client *hugegraph.CommonClient, vertexLabelName string, t *testing.T) { + respGetByName, err := client.VertexLabel.GetByName( + client.VertexLabel.GetByName.WithName(vertexLabelName), + ) + if err != nil { + t.Errorf("getbyname Vertexlabel error = %v", err) + } + if respGetByName.Data.Name != vertexLabelName { + t.Errorf("getbyname Vertexlabel error") + } +} + +func getAll(client *hugegraph.CommonClient, vertexLabelName string, t *testing.T) { + respGetAll, err := client.VertexLabel.GetAll() + if err != nil { + t.Errorf("getAll Vertexlabel error = %v", err) + } + name := "" + for _, s := range respGetAll.Data.Vertexlabels { + if s.Name == vertexLabelName { + name = s.Name + } + } + if name != vertexLabelName { + t.Errorf("getAll Vertexlabel error") + } +} + +func deleteByName(client *hugegraph.CommonClient, vertexLabelName string, t *testing.T) { + respDeleteByName, err := client.VertexLabel.DeleteByName( + client.VertexLabel.DeleteByName.WithName(vertexLabelName), + ) + if err != nil { + t.Errorf("deletebyname Vertexlabel error = %v", err) + } + if respDeleteByName.StatusCode > 299 { + t.Errorf("deletebyname Vertexlabel error") + } +} diff --git a/hugegraph-client-go/go.mod b/hugegraph-client-go/go.mod new file mode 100644 index 000000000..d45b4200e --- /dev/null +++ b/hugegraph-client-go/go.mod @@ -0,0 +1,4 @@ +module github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go + +go 1.19 + diff --git a/hugegraph-client-go/go.sum b/hugegraph-client-go/go.sum new file mode 100644 index 000000000..b156c04ca --- /dev/null +++ b/hugegraph-client-go/go.sum @@ -0,0 +1,16 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/ianlancetaylor/demangle v0.0.0-20211126204342-3ad08eb09c01 h1:+0qIm4/XbPn2PYkj6QM6CX/FJN5DGvFOaMkSyB1xuh8= +github.com/ianlancetaylor/demangle v0.0.0-20211126204342-3ad08eb09c01/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hugegraph-client-go/hgtransport/hgtransport.go b/hugegraph-client-go/hgtransport/hgtransport.go new file mode 100644 index 000000000..8c669c8be --- /dev/null +++ b/hugegraph-client-go/hgtransport/hgtransport.go @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package hgtransport + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "runtime" + "strings" + "time" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/internal/version" +) + +// Version returns the package version as a string. +const Version = version.Client + +var ( + userAgent string + reGoVersion = regexp.MustCompile(`go(\d+\.\d+\..+)`) +) + +func init() { + userAgent = initUserAgent() +} + +// Interface defines the interface for HTTP client. +type Interface interface { + Perform(*http.Request) (*http.Response, error) + GetConfig() Config +} + +// Config represents the configuration of HTTP client. +type Config struct { + URL *url.URL + Username string + Password string + GraphSpace string + Graph string + Transport http.RoundTripper + Logger Logger +} + +// Client represents the HTTP client. +type Client struct { + url *url.URL + username string + password string + graphspace string + graph string + transport http.RoundTripper + logger Logger +} + +// New creates new HTTP client. +// +// http.DefaultTransport will be used if no transport is passed in the configuration. +func New(cfg Config) *Client { + if cfg.Transport == nil { + cfg.Transport = http.DefaultTransport + } + + return &Client{ + url: cfg.URL, + username: cfg.Username, + password: cfg.Password, + graphspace: cfg.GraphSpace, + graph: cfg.Graph, + transport: cfg.Transport, + logger: cfg.Logger, + } +} + +func (c *Client) GetConfig() Config { + return Config{ + URL: c.url, + Username: c.username, + Password: c.password, + GraphSpace: c.graphspace, + Graph: c.graph, + Transport: c.transport, + Logger: nil, + } +} + +// Perform executes the request and returns a response or error. +func (c *Client) Perform(req *http.Request) (*http.Response, error) { + + u := c.url + c.setURL(u, req) + c.setUserAgent(req) + c.setHost(req) + c.setContentTypeJSON(req) + + if _, ok := req.Header["Authorization"]; !ok { + c.setBasicAuth(u, req) + } + + var dupReqBody *bytes.Buffer + if c.logger != nil && c.logger.RequestBodyEnabled() { + if req.Body != nil && req.Body != http.NoBody { + dupReqBody = bytes.NewBuffer(make([]byte, 0, int(req.ContentLength))) + dupReqBody.ReadFrom(req.Body) + req.Body = ioutil.NopCloser(bytes.NewBuffer(dupReqBody.Bytes())) + } + } + start := time.Now().UTC() + res, err := c.transport.RoundTrip(req) + dur := time.Since(start) + + if c.logger == nil { + return res, err + } + + var dupRes http.Response + if res != nil { + dupRes = *res + } + if c.logger.RequestBodyEnabled() { + if req.Body != nil && req.Body != http.NoBody { + req.Body = ioutil.NopCloser(dupReqBody) + } + } + if c.logger.ResponseBodyEnabled() { + if res != nil && res.Body != nil && res.Body != http.NoBody { + b1, b2, _ := duplicateBody(res.Body) + dupRes.Body = b1 + res.Body = b2 + } + } + c.logger.LogRoundTrip(req, &dupRes, err, start, dur) // errcheck exclude + + return res, err +} + +func (c *Client) setURL(u *url.URL, req *http.Request) *http.Request { + req.URL.Scheme = u.Scheme + req.URL.Host = u.Host + + if u.Path != "" { + var b strings.Builder + b.Grow(len(u.Path) + len(req.URL.Path)) + b.WriteString(u.Path) + b.WriteString(req.URL.Path) + req.URL.Path = b.String() + } + + return req +} + +func (c *Client) setBasicAuth(u *url.URL, req *http.Request) *http.Request { + if u.User != nil { + password, _ := u.User.Password() + req.SetBasicAuth(u.User.Username(), password) + return req + } + + if c.username != "" && c.password != "" { + req.SetBasicAuth(c.username, c.password) + return req + } + + return req +} + +func (c *Client) setUserAgent(req *http.Request) *http.Request { + req.Header.Set("User-Agent", userAgent) + return req +} + +func (c *Client) setHost(req *http.Request) *http.Request { + req.Header.Set("Host", req.URL.Host) + return req +} + +func (c *Client) setContentTypeJSON(req *http.Request) *http.Request { + req.Header.Set("Content-Type", "application/json;charset=UTF-8") + return req +} + +func initUserAgent() string { + var b strings.Builder + + b.WriteString("go-hugegraph") + b.WriteRune('/') + b.WriteString(Version) + b.WriteRune(' ') + b.WriteRune('(') + b.WriteString(runtime.GOOS) + b.WriteRune(' ') + b.WriteString(runtime.GOARCH) + b.WriteString("; ") + b.WriteString("Go ") + if v := reGoVersion.ReplaceAllString(runtime.Version(), "$1"); v != "" { + b.WriteString(v) + } else { + b.WriteString(runtime.Version()) + } + b.WriteRune(')') + + return b.String() +} diff --git a/hugegraph-client-go/hgtransport/logger.go b/hugegraph-client-go/hgtransport/logger.go new file mode 100644 index 000000000..306a77661 --- /dev/null +++ b/hugegraph-client-go/hgtransport/logger.go @@ -0,0 +1,396 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package hgtransport + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +// Logger defines an interface for logging request and response. +type Logger interface { + // LogRoundTrip should not modify the request or response, except for consuming and closing the body. + // Implementations have to check for nil values in request and response. + LogRoundTrip(*http.Request, *http.Response, error, time.Time, time.Duration) error + // RequestBodyEnabled makes the client pass a copy of request body to the logger. + RequestBodyEnabled() bool + // ResponseBodyEnabled makes the client pass a copy of response body to the logger. + ResponseBodyEnabled() bool +} + +// TextLogger prints the log message in plain text. +type TextLogger struct { + Output io.Writer + EnableRequestBody bool + EnableResponseBody bool +} + +// ColorLogger prints the log message in a terminal-optimized plain text. +type ColorLogger struct { + Output io.Writer + EnableRequestBody bool + EnableResponseBody bool +} + +// CurlLogger prints the log message as a runnable curl command. +type CurlLogger struct { + Output io.Writer + EnableRequestBody bool + EnableResponseBody bool +} + +// JSONLogger prints the log message as JSON. +type JSONLogger struct { + Output io.Writer + EnableRequestBody bool + EnableResponseBody bool +} + +// LogRoundTrip prints the information about request and response. +func (l *TextLogger) LogRoundTrip(req *http.Request, res *http.Response, err error, start time.Time, dur time.Duration) error { + fmt.Fprintf(l.Output, "%s %s %s [status:%d request:%s]\n", + start.Format(time.RFC3339), + req.Method, + req.URL.String(), + resStatusCode(res), + dur.Truncate(time.Millisecond), + ) + if l.RequestBodyEnabled() && req != nil && req.Body != nil && req.Body != http.NoBody { + var buf bytes.Buffer + buf.ReadFrom(req.Body) + logBodyAsText(l.Output, &buf, ">") + } + if l.ResponseBodyEnabled() && res != nil && res.Body != nil && res.Body != http.NoBody { + defer res.Body.Close() + var buf bytes.Buffer + buf.ReadFrom(res.Body) + logBodyAsText(l.Output, &buf, "<") + } + if err != nil { + fmt.Fprintf(l.Output, "! ERROR: %v\n", err) + } + return nil +} + +// RequestBodyEnabled returns true when the request body should be logged. +func (l *TextLogger) RequestBodyEnabled() bool { return l.EnableRequestBody } + +// ResponseBodyEnabled returns true when the response body should be logged. +func (l *TextLogger) ResponseBodyEnabled() bool { return l.EnableResponseBody } + +// LogRoundTrip prints the information about request and response. +func (l *ColorLogger) LogRoundTrip(req *http.Request, res *http.Response, err error, start time.Time, dur time.Duration) error { + query, _ := url.QueryUnescape(req.URL.RawQuery) + if query != "" { + query = "?" + query + } + + var ( + status string + color string + ) + + status = res.Status + switch { + case res.StatusCode > 0 && res.StatusCode < 300: + color = "\x1b[32m" + case res.StatusCode > 299 && res.StatusCode < 500: + color = "\x1b[33m" + case res.StatusCode > 499: + color = "\x1b[31m" + default: + status = "ERROR" + color = "\x1b[31;4m" + } + + fmt.Fprintf(l.Output, "%6s \x1b[1;4m%s://%s%s\x1b[0m%s %s%s\x1b[0m \x1b[2m%s\x1b[0m\n", + req.Method, + req.URL.Scheme, + req.URL.Host, + req.URL.Path, + query, + color, + status, + dur.Truncate(time.Millisecond), + ) + + if l.RequestBodyEnabled() && req != nil && req.Body != nil && req.Body != http.NoBody { + var buf bytes.Buffer + buf.ReadFrom(req.Body) + fmt.Fprint(l.Output, "\x1b[2m") + logBodyAsText(l.Output, &buf, " »") + fmt.Fprint(l.Output, "\x1b[0m") + } + + if l.ResponseBodyEnabled() && res != nil && res.Body != nil && res.Body != http.NoBody { + defer res.Body.Close() + var buf bytes.Buffer + buf.ReadFrom(res.Body) + fmt.Fprint(l.Output, "\x1b[2m") + logBodyAsText(l.Output, &buf, " «") + fmt.Fprint(l.Output, "\x1b[0m") + } + + if err != nil { + fmt.Fprintf(l.Output, "\x1b[31;1m» ERROR \x1b[31m%v\x1b[0m\n", err) + } + + if l.RequestBodyEnabled() || l.ResponseBodyEnabled() { + fmt.Fprintf(l.Output, "\x1b[2m%s\x1b[0m\n", strings.Repeat("─", 80)) + } + return nil +} + +// RequestBodyEnabled returns true when the request body should be logged. +func (l *ColorLogger) RequestBodyEnabled() bool { return l.EnableRequestBody } + +// ResponseBodyEnabled returns true when the response body should be logged. +func (l *ColorLogger) ResponseBodyEnabled() bool { return l.EnableResponseBody } + +// LogRoundTrip prints the information about request and response. +func (l *CurlLogger) LogRoundTrip(req *http.Request, res *http.Response, err error, start time.Time, dur time.Duration) error { + var b bytes.Buffer + + var query string + qvalues := url.Values{} + for k, v := range req.URL.Query() { + if k == "pretty" { + continue + } + for _, qv := range v { + qvalues.Add(k, qv) + } + } + if len(qvalues) > 0 { + query = qvalues.Encode() + } + + b.WriteString(`curl`) + if req.Method == "HEAD" { + b.WriteString(" --head") + } else { + fmt.Fprintf(&b, " -X %s", req.Method) + } + + if len(req.Header) > 0 { + for k, vv := range req.Header { + if k == "Authorization" || k == "User-Agent" { + continue + } + v := strings.Join(vv, ",") + b.WriteString(fmt.Sprintf(" -H '%s: %s'", k, v)) + } + } + + b.WriteString(" 'http://localhost:9200") + b.WriteString(req.URL.Path) + b.WriteString("?pretty") + if query != "" { + fmt.Fprintf(&b, "&%s", query) + } + b.WriteString("'") + + if req != nil && req.Body != nil && req.Body != http.NoBody { + var buf bytes.Buffer + buf.ReadFrom(req.Body) + + b.Grow(buf.Len()) + b.WriteString(" -d \\\n'") + json.Indent(&b, buf.Bytes(), "", " ") + b.WriteString("'") + } + + b.WriteRune('\n') + + var status string + status = res.Status + + fmt.Fprintf(&b, "# => %s [%s] %s\n", start.UTC().Format(time.RFC3339), status, dur.Truncate(time.Millisecond)) + if l.ResponseBodyEnabled() && res != nil && res.Body != nil && res.Body != http.NoBody { + var buf bytes.Buffer + buf.ReadFrom(res.Body) + + b.Grow(buf.Len()) + b.WriteString("# ") + json.Indent(&b, buf.Bytes(), "# ", " ") + } + + b.WriteString("\n") + if l.ResponseBodyEnabled() && res != nil && res.Body != nil && res.Body != http.NoBody { + b.WriteString("\n") + } + + b.WriteTo(l.Output) + + return nil +} + +// RequestBodyEnabled returns true when the request body should be logged. +func (l *CurlLogger) RequestBodyEnabled() bool { return l.EnableRequestBody } + +// ResponseBodyEnabled returns true when the response body should be logged. +func (l *CurlLogger) ResponseBodyEnabled() bool { return l.EnableResponseBody } + +// LogRoundTrip prints the information about request and response. +func (l *JSONLogger) LogRoundTrip(req *http.Request, res *http.Response, err error, start time.Time, dur time.Duration) error { + // https://github.com/elastic/ecs/blob/master/schemas/http.yml + // + // TODO(karmi): Research performance optimization of using sync.Pool + + bsize := 200 + var b = bytes.NewBuffer(make([]byte, 0, bsize)) + var v = make([]byte, 0, bsize) + + appendTime := func(t time.Time) { + v = v[:0] + v = t.AppendFormat(v, time.RFC3339) + b.Write(v) + } + + appendQuote := func(s string) { + v = v[:0] + v = strconv.AppendQuote(v, s) + b.Write(v) + } + + appendInt := func(i int64) { + v = v[:0] + v = strconv.AppendInt(v, i, 10) + b.Write(v) + } + + port := req.URL.Port() + + b.WriteRune('{') + // -- Timestamp + b.WriteString(`"@timestamp":"`) + appendTime(start.UTC()) + b.WriteRune('"') + // -- Event + b.WriteString(`,"event":{`) + b.WriteString(`"duration":`) + appendInt(dur.Nanoseconds()) + b.WriteRune('}') + // -- URL + b.WriteString(`,"url":{`) + b.WriteString(`"scheme":`) + appendQuote(req.URL.Scheme) + b.WriteString(`,"domain":`) + appendQuote(req.URL.Hostname()) + if port != "" { + b.WriteString(`,"port":`) + b.WriteString(port) + } + b.WriteString(`,"path":`) + appendQuote(req.URL.Path) + b.WriteString(`,"query":`) + appendQuote(req.URL.RawQuery) + b.WriteRune('}') // Close "url" + // -- HTTP + b.WriteString(`,"http":`) + // ---- Request + b.WriteString(`{"request":{`) + b.WriteString(`"method":`) + appendQuote(req.Method) + if l.RequestBodyEnabled() && req != nil && req.Body != nil && req.Body != http.NoBody { + var buf bytes.Buffer + buf.ReadFrom(req.Body) + + b.Grow(buf.Len() + 8) + b.WriteString(`,"body":`) + appendQuote(buf.String()) + } + b.WriteRune('}') // Close "http.request" + // ---- Response + b.WriteString(`,"response":{`) + b.WriteString(`"status_code":`) + appendInt(int64(resStatusCode(res))) + if l.ResponseBodyEnabled() && res != nil && res.Body != nil && res.Body != http.NoBody { + defer res.Body.Close() + var buf bytes.Buffer + buf.ReadFrom(res.Body) + + b.Grow(buf.Len() + 8) + b.WriteString(`,"body":`) + appendQuote(buf.String()) + } + b.WriteRune('}') // Close "http.response" + b.WriteRune('}') // Close "http" + // -- Error + if err != nil { + b.WriteString(`,"error":{"message":`) + appendQuote(err.Error()) + b.WriteRune('}') // Close "error" + } + b.WriteRune('}') + b.WriteRune('\n') + b.WriteTo(l.Output) + + return nil +} + +// RequestBodyEnabled returns true when the request body should be logged. +func (l *JSONLogger) RequestBodyEnabled() bool { return l.EnableRequestBody } + +// ResponseBodyEnabled returns true when the response body should be logged. +func (l *JSONLogger) ResponseBodyEnabled() bool { return l.EnableResponseBody } + +func logBodyAsText(dst io.Writer, body io.Reader, prefix string) { + scanner := bufio.NewScanner(body) + for scanner.Scan() { + s := scanner.Text() + if s != "" { + fmt.Fprintf(dst, "%s %s\n", prefix, s) + } + } +} + +func duplicateBody(body io.ReadCloser) (io.ReadCloser, io.ReadCloser, error) { + var ( + b1 bytes.Buffer + b2 bytes.Buffer + tr = io.TeeReader(body, &b2) + ) + _, err := b1.ReadFrom(tr) + if err != nil { + return ioutil.NopCloser(io.MultiReader(&b1, errorReader{err: err})), ioutil.NopCloser(io.MultiReader(&b2, errorReader{err: err})), err + } + defer func() { body.Close() }() + + return ioutil.NopCloser(&b1), ioutil.NopCloser(&b2), nil +} + +func resStatusCode(res *http.Response) int { + if res == nil { + return -1 + } + return res.StatusCode +} + +type errorReader struct{ err error } + +func (r errorReader) Read(p []byte) (int, error) { return 0, r.err } diff --git a/hugegraph-client-go/hugegraph.go b/hugegraph-client-go/hugegraph.go new file mode 100644 index 000000000..910f836bd --- /dev/null +++ b/hugegraph-client-go/hugegraph.go @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package hugegraph + +import ( + "errors" + "fmt" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/edgelabel" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/gremlin" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/propertykey" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/vertex" + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/api/v1/vertexlabel" + "net" + "net/http" + "net/url" + "os" + + "github.com/apache/incubator-hugegraph-toolchain/hugegraph-client-go/hgtransport" +) + +// Config 配置类型 +type Config struct { + Host string // hugegraph Server host + Port int // hugegraph Server api port + GraphSpace string // graphSpace is a feature supported feature. if you not set graphSpace, please set empty string + Graph string // this name is configured in the hugegraph + Username string // server username .if you not set username ,please set empty string + Password string // server password .if you not set password ,please set empty string + Transport http.RoundTripper // The HTTP transport object. + Logger hgtransport.Logger // The logger object. +} + +type CommonClient struct { + Vertex *vertex.Vertex + Gremlin *gremlin.Gremlin + Propertykey *propertykey.PropertyKey + VertexLabel *vertexlabel.VertexLabel + EdgeLabel *edgelabel.Edgelabel + *v1.APIV1 + Transport hgtransport.Interface + Graph string + GraphSpace string +} + +func NewDefaultCommonClient() (*CommonClient, error) { + return NewCommonClient(Config{ + Host: "127.0.0.1", + Port: 8080, + GraphSpace: "", + Graph: "hugegraph", + Username: "admin", + Password: "pa", + Logger: &hgtransport.ColorLogger{ + Output: os.Stdout, + EnableRequestBody: true, + EnableResponseBody: true, + }, + }) +} + +func NewCommonClient(cfg Config) (*CommonClient, error) { + + if len(cfg.Host) < 3 { + return nil, errors.New("cannot create client: host length error") + } + address := net.ParseIP(cfg.Host) + if address == nil { + return nil, errors.New("cannot create client: host is format error") + } + if cfg.Port < 1 || cfg.Port > 65535 { + return nil, errors.New("cannot create client: port is error") + } + + tp := hgtransport.New(hgtransport.Config{ + URL: &url.URL{ + Host: fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), + Scheme: "http", + }, + Username: cfg.Username, + Password: cfg.Password, + Graph: cfg.Graph, + Transport: cfg.Transport, + Logger: cfg.Logger, + }) + + return &CommonClient{ + Vertex: vertex.New(tp), + Gremlin: gremlin.New(tp), + Propertykey: propertykey.New(tp), + VertexLabel: vertexlabel.New(tp), + EdgeLabel: edgelabel.New(tp), + APIV1: v1.New(tp), + Transport: tp, + Graph: cfg.Graph, + GraphSpace: cfg.GraphSpace, + }, nil +} diff --git a/hugegraph-client-go/hugegraph_test.go b/hugegraph-client-go/hugegraph_test.go new file mode 100644 index 000000000..b58a6a17f --- /dev/null +++ b/hugegraph-client-go/hugegraph_test.go @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package hugegraph + +import ( + "testing" +) + +func TestNewDefaultCommonClient(t *testing.T) { + tests := []struct { + name string + wantErr bool + }{ + { + name: "test", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewDefaultCommonClient() + if (err != nil) != tt.wantErr { + t.Errorf("NewDefaultCommonClient() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/hugegraph-client-go/internal/model/model.go b/hugegraph-client-go/internal/model/model.go new file mode 100644 index 000000000..51fdd5dd9 --- /dev/null +++ b/hugegraph-client-go/internal/model/model.go @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package model + +const ( + PropertyDataTypeInt PropertyDataType = "INT" // data_type int + + PropertyCardinalitySingle PropertyCardinality = "SINGLE" // cardinality single + + ActionAppend Action = "append" // append action + ActionEliminate Action = "eliminate" // eliminate(remove) action + + IDStrategyDefault IDStrategy = "DEFAULT" // default id_strategy,The default strategy is primary key ID. + + FrequencySingle Frequency = "SINGLE" // single frequency +) + +type PropertyDataType string +type PropertyCardinality string +type Action string +type IDStrategy string +type Frequency string + +// Vertex models that support generic types +type Vertex[T any] struct { + ID string `json:"id,omitempty"` + Label string `json:"label"` + Typ string `json:"type,omitempty"` + Properties T `json:"properties"` +} + +// Edge models that support generic types +type Edge[T any] struct { + ID string `json:"id,omitempty"` + Label string `json:"label"` + Typ string `json:"type,omitempty"` + OutV string `json:"outV"` + OutVLabel string `json:"outVLabel"` + InV string `json:"inV"` + InVLabel string `json:"inVLabel"` + Properties T `json:"properties"` +} diff --git a/hugegraph-client-go/internal/version/version.go b/hugegraph-client-go/internal/version/version.go new file mode 100644 index 000000000..05e3e2b7c --- /dev/null +++ b/hugegraph-client-go/internal/version/version.go @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package version + +// Client returns the client version as a string. +const Client = "1.2.0" diff --git a/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh b/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh index 2c9ea319e..3dc3dcdf9 100755 --- a/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh +++ b/hugegraph-client/assembly/travis/install-hugegraph-from-source.sh @@ -32,9 +32,10 @@ git checkout "${COMMIT_ID}" mvn package -DskipTests -Dmaven.javadoc.skip=true -ntp # TODO: lack incubator after apache package release (update it later) +cd hugegraph-server TAR=$(echo apache-hugegraph-*.tar.gz) -tar zxf "${TAR}" -C ../ -cd ../ +tar zxf "${TAR}" -C ../../ +cd ../../ rm -rf "${GIT_DIR}" # TODO: lack incubator after apache package release (update it later) HTTP_SERVER_DIR=$(echo apache-hugegraph-*.*) diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java b/hugegraph-client/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java index a4fd226df..fec52283d 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/api/metrics/MetricsAPI.java @@ -17,6 +17,7 @@ package org.apache.hugegraph.api.metrics; +import java.util.HashMap; import java.util.Map; import org.apache.hugegraph.util.CommonUtil; @@ -27,6 +28,8 @@ public class MetricsAPI extends API { + public static final String STATISTICS_PATH = "/statistics"; + public MetricsAPI(RestClient client) { super(client); this.path(this.type()); @@ -65,12 +68,39 @@ public Map backend(String graph) { @SuppressWarnings("unchecked") public Map> all() { + Map params = new HashMap<>(); + params.put("type", "json"); + RestResult result = this.client.get(this.path(), params); + 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>) map; + } + + @SuppressWarnings("unchecked") + public String allWithPromFormat() { RestResult result = this.client.get(this.path()); + return result.content(); + } + + public Map> statistics() { + Map params = new HashMap<>(); + params.put("type", "json"); + RestResult result = this.client.get(this.path() + STATISTICS_PATH, params); 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>) map; } + + @SuppressWarnings("unchecked") + public String statisticsWithPromFormat() { + RestResult result = this.client.get(this.path() + STATISTICS_PATH); + return result.content(); + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPI.java b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPI.java new file mode 100644 index 000000000..a62a7972c --- /dev/null +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPI.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.api.traverser; + + +import org.apache.hugegraph.api.graph.GraphAPI; +import org.apache.hugegraph.client.RestClient; +import org.apache.hugegraph.rest.RestResult; +import org.apache.hugegraph.structure.graph.Edge; +import org.apache.logging.log4j.util.Strings; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class EdgeExistenceAPI extends TraversersAPI { + + public EdgeExistenceAPI(RestClient client, String graph) { + super(client, graph); + } + + @Override + protected String type() { + return "edgeexist"; + } + + public List get(Object sourceId, Object targetId, String edgeLabel, + String sortValues, int limit) { + this.client.checkApiVersion("0.70", "count"); + String source = GraphAPI.formatVertexId(sourceId, false); + String target = GraphAPI.formatVertexId(targetId, false); + + Map params = new LinkedHashMap<>(); + params.put("source", source); + params.put("target", target); + params.put("label", edgeLabel); + if (!Strings.isEmpty(sortValues)) { + params.put("sort_values", sortValues); + } + params.put("limit", limit); + RestResult result = this.client.get(this.path(), params); + return result.readList("edges", Edge.class); + } +} diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/JaccardSimilarityAPI.java b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/JaccardSimilarityAPI.java index 80fe8b486..f87958bb0 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/JaccardSimilarityAPI.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/JaccardSimilarityAPI.java @@ -26,6 +26,7 @@ import org.apache.hugegraph.rest.RestResult; import org.apache.hugegraph.structure.constant.Direction; +import org.apache.hugegraph.structure.traverser.JaccardSimilarity; import org.apache.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest; import org.apache.hugegraph.util.E; @@ -64,9 +65,9 @@ public double get(Object vertexId, Object otherId, Direction direction, } @SuppressWarnings("unchecked") - public Map post(SingleSourceJaccardSimilarityRequest request) { + public JaccardSimilarity post(SingleSourceJaccardSimilarityRequest request) { this.client.checkApiVersion("0.58", "jaccard similar"); RestResult result = this.client.post(this.path(), request); - return result.readObject(Map.class); + return result.readObject(JaccardSimilarity.class); } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathAPI.java b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathAPI.java index a1930fc31..31ee2dbd5 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathAPI.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathAPI.java @@ -40,7 +40,7 @@ protected String type() { public WeightedPaths get(Object sourceId, Direction direction, String label, String weight, long degree, long skipDegree, - long capacity, int limit, boolean withVertex) { + long capacity, int limit, boolean withVertex, boolean withEdge) { this.client.checkApiVersion("0.51", "single source shortest path"); String source = GraphAPI.formatVertexId(sourceId, false); @@ -60,6 +60,7 @@ public WeightedPaths get(Object sourceId, Direction direction, String label, params.put("capacity", capacity); params.put("limit", limit); params.put("with_vertex", withVertex); + params.put("with_edge", withEdge); RestResult result = this.client.get(this.path(), params); return result.readObject(WeightedPaths.class); } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/WeightedShortestPathAPI.java b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/WeightedShortestPathAPI.java index 7cf2a3b25..aaf043f95 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/WeightedShortestPathAPI.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/api/traverser/WeightedShortestPathAPI.java @@ -41,7 +41,7 @@ protected String type() { public WeightedPath get(Object sourceId, Object targetId, Direction direction, String label, String weight, long degree, long skipDegree, - long capacity, boolean withVertex) { + long capacity, boolean withVertex, boolean withEdge) { this.client.checkApiVersion("0.51", "weighted shortest path"); String source = GraphAPI.formatVertexId(sourceId, false); String target = GraphAPI.formatVertexId(targetId, false); @@ -61,6 +61,7 @@ public WeightedPath get(Object sourceId, Object targetId, params.put("skip_degree", skipDegree); params.put("capacity", capacity); params.put("with_vertex", withVertex); + params.put("with_edge", withEdge); RestResult result = this.client.get(this.path(), params); return result.readObject(WeightedPath.class); } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/client/RestClient.java b/hugegraph-client/src/main/java/org/apache/hugegraph/client/RestClient.java index a63e1f330..1081aa71c 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/client/RestClient.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/client/RestClient.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.databind.module.SimpleModule; - public class RestClient extends AbstractRestClient { private static final int SECOND = 1000; diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/driver/MetricsManager.java b/hugegraph-client/src/main/java/org/apache/hugegraph/driver/MetricsManager.java index df6facb12..b3ff5c08f 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/driver/MetricsManager.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/driver/MetricsManager.java @@ -48,4 +48,16 @@ public Map> system() { public Map> all() { return this.metricsAPI.all(); } + + public String allWithPromFormat() { + return this.metricsAPI.allWithPromFormat(); + } + + public Map> statistics() { + return this.metricsAPI.statistics(); + } + + public String statisticsWithPromFormat() { + return this.metricsAPI.statisticsWithPromFormat(); + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/driver/TraverserManager.java b/hugegraph-client/src/main/java/org/apache/hugegraph/driver/TraverserManager.java index 735046a7f..580813426 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/driver/TraverserManager.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/driver/TraverserManager.java @@ -19,10 +19,7 @@ import java.util.Iterator; import java.util.List; -import java.util.Map; -import org.apache.hugegraph.structure.constant.Direction; -import org.apache.hugegraph.structure.constant.Traverser; import org.apache.hugegraph.api.traverser.AllShortestPathsAPI; import org.apache.hugegraph.api.traverser.CountAPI; import org.apache.hugegraph.api.traverser.CrosspointsAPI; @@ -45,7 +42,10 @@ import org.apache.hugegraph.api.traverser.TemplatePathsAPI; import org.apache.hugegraph.api.traverser.VerticesAPI; import org.apache.hugegraph.api.traverser.WeightedShortestPathAPI; +import org.apache.hugegraph.api.traverser.EdgeExistenceAPI; import org.apache.hugegraph.client.RestClient; +import org.apache.hugegraph.structure.constant.Direction; +import org.apache.hugegraph.structure.constant.Traverser; import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Edges; import org.apache.hugegraph.structure.graph.GraphIterator; @@ -56,18 +56,19 @@ import org.apache.hugegraph.structure.traverser.CountRequest; import org.apache.hugegraph.structure.traverser.CrosspointsRequest; import org.apache.hugegraph.structure.traverser.CustomizedCrosspoints; -import org.apache.hugegraph.structure.traverser.MultiNodeShortestPathRequest; -import org.apache.hugegraph.structure.traverser.PathsWithVertices; +import org.apache.hugegraph.structure.traverser.CustomizedPathsRequest; import org.apache.hugegraph.structure.traverser.FusiformSimilarity; import org.apache.hugegraph.structure.traverser.FusiformSimilarityRequest; -import org.apache.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest; +import org.apache.hugegraph.structure.traverser.JaccardSimilarity; import org.apache.hugegraph.structure.traverser.Kneighbor; import org.apache.hugegraph.structure.traverser.KneighborRequest; import org.apache.hugegraph.structure.traverser.Kout; import org.apache.hugegraph.structure.traverser.KoutRequest; -import org.apache.hugegraph.structure.traverser.CustomizedPathsRequest; +import org.apache.hugegraph.structure.traverser.MultiNodeShortestPathRequest; import org.apache.hugegraph.structure.traverser.PathsRequest; +import org.apache.hugegraph.structure.traverser.PathsWithVertices; import org.apache.hugegraph.structure.traverser.Ranks; +import org.apache.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest; import org.apache.hugegraph.structure.traverser.TemplatePathsRequest; import org.apache.hugegraph.structure.traverser.WeightedPath; import org.apache.hugegraph.structure.traverser.WeightedPaths; @@ -76,28 +77,29 @@ 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; + private final JaccardSimilarityAPI jaccardSimilarityAPI; + private final SameNeighborsAPI sameNeighborsAPI; + private final ShortestPathAPI shortestPathAPI; + private final AllShortestPathsAPI allShortestPathsAPI; + private final SingleSourceShortestPathAPI singleSourceShortestPathAPI; + private final WeightedShortestPathAPI weightedShortestPathAPI; + private final MultiNodeShortestPathAPI multiNodeShortestPathAPI; + private final PathsAPI pathsAPI; + private final CrosspointsAPI crosspointsAPI; + private final KoutAPI koutAPI; + private final KneighborAPI kneighborAPI; + private final CountAPI countAPI; + private final RingsAPI ringsAPI; + private final RaysAPI raysAPI; + private final CustomizedPathsAPI customizedPathsAPI; + private final CustomizedCrosspointsAPI customizedCrosspointsAPI; + private final TemplatePathsAPI templatePathsAPI; + private final FusiformSimilarityAPI fusiformSimilarityAPI; + private final NeighborRankAPI neighborRankAPI; + private final PersonalRankAPI personalRankAPI; + private final VerticesAPI verticesAPI; + private final EdgesAPI edgesAPI; + private final EdgeExistenceAPI edgeExistenceAPI; public TraverserManager(RestClient client, GraphManager graphManager) { this.graphManager = graphManager; @@ -124,6 +126,7 @@ public TraverserManager(RestClient client, GraphManager graphManager) { this.personalRankAPI = new PersonalRankAPI(client, graph); this.verticesAPI = new VerticesAPI(client, graph); this.edgesAPI = new EdgesAPI(client, graph); + this.edgeExistenceAPI = new EdgeExistenceAPI(client, graph); } public double jaccardSimilarity(Object vertexId, Object otherId) { @@ -143,7 +146,7 @@ public double jaccardSimilarity(Object vertexId, Object otherId, label, degree); } - public Map jaccardSimilarity(SingleSourceJaccardSimilarityRequest request) { + public JaccardSimilarity jaccardSimilarity(SingleSourceJaccardSimilarityRequest request) { return this.jaccardSimilarityAPI.post(request); } @@ -241,19 +244,20 @@ public List allShortestPaths(Object sourceId, Object targetId, public WeightedPaths singleSourceShortestPath(Object sourceId, String weight, - boolean withVertex) { + boolean withVertex, + boolean withEdge) { return this.singleSourceShortestPath(sourceId, Direction.BOTH, null, - weight, withVertex); + weight, withVertex, withEdge); } public WeightedPaths singleSourceShortestPath(Object sourceId, Direction direction, String label, String weight, - boolean withVertex) { + boolean withVertex, boolean withEdge) { return this.singleSourceShortestPath(sourceId, direction, label, weight, Traverser.DEFAULT_MAX_DEGREE, 0L, Traverser.DEFAULT_CAPACITY, - Traverser.DEFAULT_PATHS_LIMIT, withVertex); + Traverser.DEFAULT_PATHS_LIMIT, withVertex, withEdge); } public WeightedPaths singleSourceShortestPath(Object sourceId, @@ -261,35 +265,36 @@ public WeightedPaths singleSourceShortestPath(Object sourceId, String label, String weight, long degree, long skipDegree, long capacity, int limit, - boolean withVertex) { + boolean withVertex, boolean withEdge) { return this.singleSourceShortestPathAPI.get(sourceId, direction, label, weight, degree, skipDegree, capacity, limit, - withVertex); + withVertex, withEdge); } public WeightedPath weightedShortestPath(Object sourceId, Object targetId, - String weight, boolean withVertex) { + String weight, boolean withVertex, boolean withEdge) { return this.weightedShortestPath(sourceId, targetId, Direction.BOTH, - null, weight, withVertex); + null, weight, withVertex, withEdge); } public WeightedPath weightedShortestPath(Object sourceId, Object targetId, Direction direction, String label, String weight, - boolean withVertex) { + boolean withVertex, + boolean withEdge) { return this.weightedShortestPath(sourceId, targetId, direction, label, weight, Traverser.DEFAULT_MAX_DEGREE, 0L, - Traverser.DEFAULT_CAPACITY, withVertex); + Traverser.DEFAULT_CAPACITY, withVertex, withEdge); } public WeightedPath weightedShortestPath(Object sourceId, Object targetId, Direction direction, String label, String weight, long degree, long skipDegree, - long capacity, boolean withVertex) { + long capacity, boolean withVertex, boolean withEdge) { return this.weightedShortestPathAPI.get(sourceId, targetId, direction, label, weight, - degree, skipDegree, capacity, withVertex); + degree, skipDegree, capacity, withVertex, withEdge); } public PathsWithVertices multiNodeShortestPath(MultiNodeShortestPathRequest request) { @@ -538,4 +543,17 @@ public Iterator iteratorEdges(Shard shard, int sizePerPage) { return this.edges(shard, page, sizePerPage); }); } + + public List edgeExistence(Object sourceId, Object targetId, String edgeLabel, + String sortValues, int limit) { + return this.edgeExistenceAPI.get(sourceId, targetId, edgeLabel, sortValues, limit); + } + + public List edgeExistence(Object sourceId, Object targetId) { + return this.edgeExistenceAPI.get(sourceId, targetId, "", "", -1); + } + + public List edgeExistence(Object sourceId, Object targetId, String edgeLabel) { + return this.edgeExistenceAPI.get(sourceId, targetId, edgeLabel, null, -1); + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/constant/Traverser.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/constant/Traverser.java index 3b1aaea8b..cc2398d93 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/constant/Traverser.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/constant/Traverser.java @@ -33,4 +33,6 @@ public class Traverser { public static final double DEFAULT_ALPHA = 0.9; public static final int DEFAULT_MAX_TOP = 1000; public static final int DEFAULT_MAX_DEPTH = 5000; + public static final String TRAVERSE_MODE_BFS = "breadth_first_search"; + public static final String TRAVERSE_MODE_DFS = "depth_first_search"; } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/ApiMeasure.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/ApiMeasure.java new file mode 100644 index 000000000..c5037b557 --- /dev/null +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/ApiMeasure.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.structure.traverser; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ApiMeasure { + + @JsonProperty("edge_iterations") + private Long edgeIters; + @JsonProperty("vertice_iterations") + private Long verticeIters; + @JsonProperty("cost(ns)") + private Long totalTime; + + public Long edgeIters() { + return this.edgeIters; + } + + public Long verticeIters() { + return this.verticeIters; + } + + public Long totalTime() { + return this.totalTime; + } +} diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CrosspointsRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CrosspointsRequest.java index 7a39fe553..7371e95b7 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CrosspointsRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CrosspointsRequest.java @@ -43,6 +43,8 @@ public class CrosspointsRequest { private boolean withPath; @JsonProperty("with_vertex") private boolean withVertex; + @JsonProperty("with_edge") + private boolean withEdge; private CrosspointsRequest() { this.sources = null; @@ -51,6 +53,7 @@ private CrosspointsRequest() { this.limit = Traverser.DEFAULT_CROSSPOINT_LIMIT; this.withPath = false; this.withVertex = false; + this.withEdge = false; } public static Builder builder() { @@ -61,9 +64,10 @@ public static Builder builder() { public String toString() { return String.format("CrosspointsRequest{sourceVertex=%s," + "pathPatterns=%s,capacity=%s,limit=%s," + - "withPath=%s,withVertex=%s}", + "withPath=%s,withVertex=%s,withEdge=%s}", this.sources, this.pathPatterns, this.capacity, - this.limit, this.withPath, this.withVertex); + this.limit, this.withPath, this.withVertex, + this.withEdge); } public static class Builder { @@ -110,6 +114,11 @@ public Builder withVertex(boolean withVertex) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public CrosspointsRequest build() { this.request.sources = this.sourcesBuilder.build(); for (PathPattern.Builder builder : this.pathPatternBuilders) { diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedCrosspoints.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedCrosspoints.java index 80d4d1a2a..568d5983b 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedCrosspoints.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedCrosspoints.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Path; import org.apache.hugegraph.structure.graph.Vertex; @@ -33,6 +34,10 @@ public class CustomizedCrosspoints { private List paths; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public List crosspoints() { return this.crosspoints; @@ -45,4 +50,12 @@ public List paths() { public Set vertices() { return this.vertices; } + + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedPathsRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedPathsRequest.java index de41abab2..b238286e3 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedPathsRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/CustomizedPathsRequest.java @@ -45,6 +45,8 @@ public class CustomizedPathsRequest { private int limit; @JsonProperty("with_vertex") private boolean withVertex; + @JsonProperty("with_edge") + private boolean withEdge; private CustomizedPathsRequest() { this.sources = null; @@ -53,6 +55,7 @@ private CustomizedPathsRequest() { this.capacity = Traverser.DEFAULT_CAPACITY; this.limit = Traverser.DEFAULT_PATHS_LIMIT; this.withVertex = false; + this.withEdge = false; } public static Builder builder() { @@ -63,9 +66,9 @@ public static Builder builder() { public String toString() { return String.format("CustomizedPathsRequest{sourceVertex=%s,steps=%s," + "sortBy=%s,capacity=%s,limit=%s," + - "withVertex=%s}", this.sources, this.steps, + "withVertex=%s,withEdge=%s}", this.sources, this.steps, this.sortBy, this.capacity, this.limit, - this.withVertex); + this.withVertex, this.withEdge); } public static class Builder { @@ -112,6 +115,11 @@ public Builder withVertex(boolean withVertex) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public CustomizedPathsRequest build() { this.request.sources = this.sourcesBuilder.build(); for (Step.Builder builder : this.stepBuilders) { diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/FusiformSimilarity.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/FusiformSimilarity.java index d0b0925da..f354ff1da 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/FusiformSimilarity.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/FusiformSimilarity.java @@ -30,6 +30,8 @@ public class FusiformSimilarity { private Map> similarsMap; @JsonProperty("vertices") private Set vertices; + @JsonProperty("measure") + private ApiMeasure measure; public Map> similarsMap() { return this.similarsMap; @@ -39,6 +41,10 @@ public Set vertices() { return this.vertices; } + public ApiMeasure measure() { + return this.measure; + } + public int size() { return this.similarsMap.size(); } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/JaccardSimilarity.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/JaccardSimilarity.java new file mode 100644 index 000000000..428598cc3 --- /dev/null +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/JaccardSimilarity.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.structure.traverser; + +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class JaccardSimilarity { + + @JsonProperty("jaccard_similarity") + private Map similarsMap; + @JsonProperty + private ApiMeasure measure; + + public Map similarsMap() { + return this.similarsMap; + } + + public ApiMeasure measure() { + return this.measure; + } + + public int size() { + return this.similarsMap.size(); + } + + public Set keySet() { + return this.similarsMap.keySet(); + } + + public Double get(Object key) { + return this.similarsMap.get(key); + } +} diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kneighbor.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kneighbor.java index 7244eacd2..df132b1fd 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kneighbor.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kneighbor.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Path; import org.apache.hugegraph.structure.graph.Vertex; @@ -35,6 +36,10 @@ public class Kneighbor { private List paths; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public int size() { return this.size; @@ -51,4 +56,12 @@ public List paths() { public Set vertices() { return this.vertices; } + + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KneighborRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KneighborRequest.java index ea03fc8e8..4c9bd580f 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KneighborRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KneighborRequest.java @@ -27,8 +27,8 @@ public class KneighborRequest { @JsonProperty("source") private Object source; - @JsonProperty("step") - public EdgeStep step; + @JsonProperty("steps") + public VESteps steps; @JsonProperty("max_depth") public int maxDepth; @JsonProperty("count_only") @@ -39,15 +39,18 @@ public class KneighborRequest { public boolean withVertex; @JsonProperty("with_path") public boolean withPath; + @JsonProperty("with_edge") + public boolean withEdge; private KneighborRequest() { this.source = null; - this.step = null; + this.steps = null; this.maxDepth = Traverser.DEFAULT_MAX_DEPTH; this.countOnly = false; this.limit = Traverser.DEFAULT_PATHS_LIMIT; this.withVertex = false; this.withPath = false; + this.withEdge = false; } public static Builder builder() { @@ -56,21 +59,21 @@ public static Builder 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); + return String.format("KneighborRequest{source=%s,steps=%s,maxDepth=%s" + + "countOnly=%s,limit=%s,withVertex=%s,withPath=%s,withEdge=%s}", + this.source, this.steps, this.maxDepth, + this.countOnly, this.limit, this.withVertex, + this.withPath, this.withEdge); } public static class Builder { private KneighborRequest request; - private EdgeStep.Builder stepBuilder; + private VESteps.Builder stepBuilder; private Builder() { this.request = new KneighborRequest(); - this.stepBuilder = EdgeStep.builder(); + this.stepBuilder = VESteps.builder(); } public Builder source(Object source) { @@ -79,8 +82,8 @@ public Builder source(Object source) { return this; } - public EdgeStep.Builder step() { - EdgeStep.Builder builder = EdgeStep.builder(); + public VESteps.Builder steps() { + VESteps.Builder builder = VESteps.builder(); this.stepBuilder = builder; return builder; } @@ -107,6 +110,11 @@ public Builder withVertex(boolean withVertex) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public Builder withPath(boolean withPath) { this.request.withPath = withPath; return this; @@ -114,8 +122,8 @@ public Builder withPath(boolean withPath) { 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"); + this.request.steps = this.stepBuilder.build(); + E.checkNotNull(this.request.steps, "steps"); TraversersAPI.checkPositive(this.request.maxDepth, "max depth"); TraversersAPI.checkLimit(this.request.limit); if (this.request.countOnly) { diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kout.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kout.java index 966c4d6bc..7e19b9b42 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kout.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/Kout.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Path; import org.apache.hugegraph.structure.graph.Vertex; @@ -35,6 +36,10 @@ public class Kout { private List paths; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public int size() { return this.size; @@ -51,4 +56,12 @@ public List paths() { public Set vertices() { return this.vertices; } + + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } } diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KoutRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KoutRequest.java index b5c859d9c..97795193b 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KoutRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/KoutRequest.java @@ -27,8 +27,8 @@ public class KoutRequest { @JsonProperty("source") private Object source; - @JsonProperty("step") - public EdgeStep step; + @JsonProperty("steps") + public VESteps steps; @JsonProperty("max_depth") public int maxDepth; @JsonProperty("nearest") @@ -43,10 +43,14 @@ public class KoutRequest { public boolean withVertex; @JsonProperty("with_path") public boolean withPath; + @JsonProperty("with_edge") + public boolean withEdge; + @JsonProperty("traverse_mode") + public String traverseMode; private KoutRequest() { this.source = null; - this.step = null; + this.steps = null; this.maxDepth = Traverser.DEFAULT_MAX_DEPTH; this.nearest = true; this.countOnly = false; @@ -54,6 +58,8 @@ private KoutRequest() { this.limit = Traverser.DEFAULT_PATHS_LIMIT; this.withVertex = false; this.withPath = false; + this.withEdge = false; + this.traverseMode = Traverser.TRAVERSE_MODE_BFS; } public static Builder builder() { @@ -62,22 +68,24 @@ public static Builder builder() { @Override public String toString() { - return String.format("KoutRequest{source=%s,step=%s,maxDepth=%s" + + return String.format("KoutRequest{source=%s,steps=%s,maxDepth=%s" + "nearest=%s,countOnly=%s,capacity=%s," + - "limit=%s,withVertex=%s,withPath=%s}", - this.source, this.step, this.maxDepth, + "limit=%s,withVertex=%s,withPath=%s," + + "withEdge=%s,traverseMode=%s}", + this.source, this.steps, this.maxDepth, this.nearest, this.countOnly, this.capacity, - this.limit, this.withVertex, this.withPath); + this.limit, this.withVertex, this.withPath, + this.withEdge, this.traverseMode); } public static class Builder { private final KoutRequest request; - private EdgeStep.Builder stepBuilder; + private VESteps.Builder stepBuilder; private Builder() { this.request = new KoutRequest(); - this.stepBuilder = EdgeStep.builder(); + this.stepBuilder = VESteps.builder(); } public Builder source(Object source) { @@ -86,8 +94,8 @@ public Builder source(Object source) { return this; } - public EdgeStep.Builder step() { - EdgeStep.Builder builder = EdgeStep.builder(); + public VESteps.Builder steps() { + VESteps.Builder builder = VESteps.builder(); this.stepBuilder = builder; return builder; } @@ -130,17 +138,22 @@ public Builder withPath(boolean withPath) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + 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"); + this.request.steps = this.stepBuilder.build(); + E.checkNotNull(this.request.steps, "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 " + + !this.request.withPath && !this.request.withEdge, + "Can't return vertex or path or edge " + "when count only is true"); } return this.request; diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java index 4053d5286..915d5b0d8 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/MultiNodeShortestPathRequest.java @@ -35,6 +35,8 @@ public class MultiNodeShortestPathRequest { public long capacity; @JsonProperty("with_vertex") public boolean withVertex; + @JsonProperty("with_edge") + public boolean withEdge; private MultiNodeShortestPathRequest() { this.vertices = null; @@ -42,6 +44,7 @@ private MultiNodeShortestPathRequest() { this.maxDepth = Traverser.DEFAULT_MAX_DEPTH; this.capacity = Traverser.DEFAULT_CAPACITY; this.withVertex = false; + this.withEdge = false; } public static Builder builder() { @@ -51,9 +54,9 @@ public static Builder builder() { @Override public String toString() { return String.format("MultiNodeShortestPathRequest{vertices=%s," + - "step=%s,maxDepth=%s,capacity=%s,withVertex=%s}", + "step=%s,maxDepth=%s,capacity=%s,withVertex=%s,withEdge=%s}", this.vertices, this.step, this.maxDepth, - this.capacity, this.withVertex); + this.capacity, this.withVertex, this.withEdge); } public static class Builder { @@ -95,6 +98,11 @@ public Builder withVertex(boolean withVertex) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public MultiNodeShortestPathRequest build() { this.request.vertices = this.verticesBuilder.build(); E.checkArgument(this.request.vertices != null, diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsRequest.java index 24b70f684..11e361041 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsRequest.java @@ -41,6 +41,8 @@ public class PathsRequest { public int limit = Traverser.DEFAULT_LIMIT; @JsonProperty("with_vertex") public boolean withVertex = false; + @JsonProperty("with_edge") + public boolean withEdge = false; public static Builder builder() { return new Builder(); @@ -50,10 +52,10 @@ public static Builder builder() { public String toString() { return String.format("PathRequest{sources=%s,targets=%s,step=%s," + "maxDepth=%s,nearest=%s,capacity=%s," + - "limit=%s,withVertex=%s}", + "limit=%s,withVertex=%s,withEdge=%s}", this.sources, this.targets, this.step, this.depth, this.nearest, this.capacity, - this.limit, this.withVertex); + this.limit, this.withVertex, this.withEdge); } public static class Builder { @@ -112,6 +114,11 @@ public PathsRequest.Builder withVertex(boolean withVertex) { return this; } + public PathsRequest.Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public PathsRequest build() { this.request.sources = this.sourcesBuilder.build(); E.checkArgument(this.request.sources != null, diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsWithVertices.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsWithVertices.java index d1800f7ad..5ff3a9287 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsWithVertices.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/PathsWithVertices.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Vertex; import com.fasterxml.jackson.annotation.JsonProperty; @@ -30,6 +31,10 @@ public class PathsWithVertices { private List paths; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public List paths() { return this.paths; @@ -39,6 +44,14 @@ public Set vertices() { return this.vertices; } + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } + public static class Paths { @JsonProperty diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/TemplatePathsRequest.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/TemplatePathsRequest.java index 77b50902d..fad2d3090 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/TemplatePathsRequest.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/TemplatePathsRequest.java @@ -28,10 +28,6 @@ public class TemplatePathsRequest { - @JsonProperty("sources") - private VerticesArgs sources; - @JsonProperty("targets") - private VerticesArgs targets; @JsonProperty("steps") public List steps; @JsonProperty("with_ring") @@ -42,6 +38,12 @@ public class TemplatePathsRequest { public int limit; @JsonProperty("with_vertex") public boolean withVertex; + @JsonProperty("with_edge") + public boolean withEdge; + @JsonProperty("sources") + private VerticesArgs sources; + @JsonProperty("targets") + private VerticesArgs targets; private TemplatePathsRequest() { this.sources = null; @@ -51,6 +53,7 @@ private TemplatePathsRequest() { this.capacity = Traverser.DEFAULT_CAPACITY; this.limit = Traverser.DEFAULT_PATHS_LIMIT; this.withVertex = false; + this.withEdge = false; } public static Builder builder() { @@ -61,9 +64,10 @@ public static Builder builder() { 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); + "withVertex=%s,withEdge=%s}", + this.sources, this.targets, this.steps, + this.withRing, this.capacity, this.limit, + this.withVertex, this.withEdge); } public static class Builder { @@ -116,6 +120,11 @@ public Builder withVertex(boolean withVertex) { return this; } + public Builder withEdge(boolean withEdge) { + this.request.withEdge = withEdge; + return this; + } + public TemplatePathsRequest build() { this.request.sources = this.sourcesBuilder.build(); E.checkArgument(this.request.sources != null, diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VEStep.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VEStep.java new file mode 100644 index 000000000..65c1baf25 --- /dev/null +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VEStep.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.structure.traverser; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class VEStep { + + @JsonProperty("label") + public String label; + + @JsonProperty("properties") + public Map properties; + + protected VEStep() { + this.properties = new HashMap<>(); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public String toString() { + return String.format("VEStepEntity{label=%s,properties=%s}", + this.label, this.properties); + } + + public static class Builder { + + protected VEStep veStep; + + private Builder() { + this.veStep = new VEStep(); + } + + public VEStep.Builder label(String label) { + this.veStep.label = label; + return this; + } + + public VEStep.Builder properties(Map properties) { + this.veStep.properties = properties; + return this; + } + + public VEStep.Builder properties(String key, Object value) { + this.veStep.properties.put(key, value); + return this; + } + + public VEStep build() { + return this.veStep; + } + + } +} diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VESteps.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VESteps.java new file mode 100644 index 000000000..bd13c5b7c --- /dev/null +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/VESteps.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.structure.traverser; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.hugegraph.api.API; +import org.apache.hugegraph.api.traverser.TraversersAPI; +import org.apache.hugegraph.structure.constant.Direction; +import org.apache.hugegraph.structure.constant.Traverser; + +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class VESteps { + + @JsonProperty("direction") + public Direction direction; + @JsonAlias("degree") + @JsonProperty("max_degree") + public long maxDegree; + @JsonProperty("skip_degree") + public long skipDegree; + @JsonProperty("vertex_steps") + public List vSteps; + @JsonProperty("edge_steps") + public List eSteps; + + protected VESteps() { + this.direction = Direction.BOTH; + this.maxDegree = Traverser.DEFAULT_MAX_DEGREE; + this.skipDegree = Traverser.DEFAULT_SKIP_DEGREE; + this.vSteps = new ArrayList<>(); + this.eSteps = new ArrayList<>(); + } + + public static Builder builder() { + return new Builder(); + } + + @Override + public String toString() { + return String.format("Steps{direction=%s,maxDegree=%s," + + "skipDegree=%s,vSteps=%s,eSteps=%s}", + this.direction, this.maxDegree, + this.skipDegree, this.vSteps, this.eSteps); + } + + public static class Builder { + + protected VESteps steps; + + private Builder() { + this.steps = new VESteps(); + } + + public VESteps.Builder direction(Direction direction) { + this.steps.direction = direction; + return this; + } + + public VESteps.Builder vSteps(List vSteps) { + this.steps.vSteps = vSteps; + return this; + } + + public VESteps.Builder addVStep(String label, Map properties) { + VEStep vStep = VEStep.builder() + .label(label) + .properties(properties) + .build(); + this.steps.vSteps.add(vStep); + return this; + } + + public VESteps.Builder addVStep(String label) { + return this.addVStep(label, Collections.emptyMap()); + } + + public VESteps.Builder eSteps(List eSteps) { + this.steps.eSteps = eSteps; + return this; + } + + public VESteps.Builder addEStep(String label, Map properties) { + VEStep eStep = VEStep.builder() + .label(label) + .properties(properties) + .build(); + this.steps.eSteps.add(eStep); + return this; + } + + public VESteps.Builder addEStep(String label) { + return this.addEStep(label, Collections.emptyMap()); + } + + public VESteps.Builder degree(long degree) { + TraversersAPI.checkDegree(degree); + this.steps.maxDegree = degree; + return this; + } + + public VESteps.Builder skipDegree(long skipDegree) { + TraversersAPI.checkSkipDegree(skipDegree, this.steps.maxDegree, + API.NO_LIMIT); + this.steps.skipDegree = skipDegree; + return this; + } + + public VESteps build() { + TraversersAPI.checkDegree(this.steps.maxDegree); + TraversersAPI.checkSkipDegree(this.steps.skipDegree, + this.steps.maxDegree, API.NO_LIMIT); + return this.steps; + } + } +} diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPath.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPath.java index d16862111..a6fd28b03 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPath.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPath.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Vertex; import com.fasterxml.jackson.annotation.JsonProperty; @@ -30,6 +31,10 @@ public class WeightedPath { private Path path; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public Path path() { return this.path; @@ -39,6 +44,14 @@ public Set vertices() { return this.vertices; } + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } + public static class Path { @JsonProperty diff --git a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPaths.java b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPaths.java index 1870de51b..29ae2c36b 100644 --- a/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPaths.java +++ b/hugegraph-client/src/main/java/org/apache/hugegraph/structure/traverser/WeightedPaths.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; +import org.apache.hugegraph.structure.graph.Edge; import org.apache.hugegraph.structure.graph.Vertex; import com.fasterxml.jackson.annotation.JsonProperty; @@ -30,6 +31,10 @@ public class WeightedPaths { private Map paths; @JsonProperty private Set vertices; + @JsonProperty + private Set edges; + @JsonProperty + private ApiMeasure measure; public Map paths() { return this.paths; @@ -38,4 +43,12 @@ public Map paths() { public Set vertices() { return this.vertices; } + + public Set edges() { + return this.edges; + } + + public ApiMeasure measure() { + return this.measure; + } } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/ApiTestSuite.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/ApiTestSuite.java index 6fdb3c537..c9edba73f 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/ApiTestSuite.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/ApiTestSuite.java @@ -44,6 +44,7 @@ import org.apache.hugegraph.api.traverser.SingleSourceShortestPathApiTest; import org.apache.hugegraph.api.traverser.TemplatePathsApiTest; import org.apache.hugegraph.api.traverser.WeightedShortestPathApiTest; +import org.apache.hugegraph.api.traverser.EdgeExistenceAPITest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -84,6 +85,7 @@ FusiformSimilarityApiTest.class, NeighborRankApiTest.class, PersonalRankApiTest.class, + EdgeExistenceAPITest.class, TargetApiTest.class, GroupApiTest.class, diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/BaseApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/BaseApiTest.java index 37e88622e..c6570149b 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/BaseApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/BaseApiTest.java @@ -120,25 +120,25 @@ protected static void clearData() { indexLabelAPI.list().forEach(indexLabel -> { ilTaskIds.add(indexLabelAPI.delete(indexLabel.name())); }); - ilTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId)); + ilTaskIds.forEach(BaseApiTest::waitUntilTaskCompleted); List elTaskIds = new ArrayList<>(); edgeLabelAPI.list().forEach(edgeLabel -> { elTaskIds.add(edgeLabelAPI.delete(edgeLabel.name())); }); - elTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId)); + elTaskIds.forEach(BaseApiTest::waitUntilTaskCompleted); List vlTaskIds = new ArrayList<>(); vertexLabelAPI.list().forEach(vertexLabel -> { vlTaskIds.add(vertexLabelAPI.delete(vertexLabel.name())); }); - vlTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId)); + vlTaskIds.forEach(BaseApiTest::waitUntilTaskCompleted); List pkTaskIds = new ArrayList<>(); propertyKeyAPI.list().forEach(propertyKey -> { pkTaskIds.add(propertyKeyAPI.delete(propertyKey.name())); }); - pkTaskIds.forEach(taskId -> waitUntilTaskCompleted(taskId)); + pkTaskIds.forEach(BaseApiTest::waitUntilTaskCompleted); // Clear system taskAPI.list(null, -1).forEach(task -> { diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/auth/TokenApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/auth/TokenApiTest.java index 8ac29e144..43c3985cd 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/auth/TokenApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/auth/TokenApiTest.java @@ -75,8 +75,7 @@ public void testVerify() { Assert.assertThrows(ServerException.class, () -> { tokenAPI.verifyToken(); }, e -> { - Assert.assertContains("Only HTTP Bearer authentication is supported", - e.getMessage()); + Assert.assertContains("Only HTTP Bearer authentication is supported", e.getMessage()); }); String token = result.token(); @@ -91,15 +90,14 @@ public void testVerify() { Assert.assertThrows(ServerException.class, () -> { tokenAPI.verifyToken(); }, e -> { - Assert.assertContains("Invalid token", e.getMessage()); + Assert.assertContains("Authentication failed", 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()); + Assert.assertContains("Only HTTP Bearer authentication is supported", e.getMessage()); }); client2.setAuthContext("Bearer " + token); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CommonTraverserApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CommonTraverserApiTest.java index 470a1112b..766008dbd 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CommonTraverserApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CommonTraverserApiTest.java @@ -99,11 +99,11 @@ public void testCustomizedCrosspoints() { 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); + .labels("created").degree(-1); + builder.withPath(true).withVertex(true).withEdge(true).capacity(-1).limit(-1); CustomizedCrosspoints customizedCrosspoints = - customizedCrosspointsAPI.post(builder.build()); + customizedCrosspointsAPI.post(builder.build()); List crosspoints = customizedCrosspoints.crosspoints(); Assert.assertEquals(1, crosspoints.size()); Assert.assertEquals(joshId, crosspoints.get(0)); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CountApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CountApiTest.java index 33412c2c5..50a721587 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CountApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CountApiTest.java @@ -24,6 +24,7 @@ import org.apache.hugegraph.structure.traverser.CountRequest; import org.apache.hugegraph.testutil.Assert; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.ImmutableList; @@ -253,6 +254,7 @@ public void testCountWithLabel() { } @Test + @Ignore public void testCountWithProperties() { CountRequest.Builder builder = CountRequest.builder(); builder.source("A").containsTraversed(false); @@ -427,29 +429,26 @@ public void testCountWithIllegalArgument() { Assert.assertThrows(IllegalArgumentException.class, () -> { builder.dedupSize(-5); }, e -> { - Assert.assertContains("The dedup size must be >= 0 or == -1, " + - "but got: ", e.getMessage()); + 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.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.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()); + Assert.assertContains("The skipped degree must be >= max degree", e.getMessage()); }); CountRequest.Builder builder1 = CountRequest.builder(); @@ -459,8 +458,7 @@ public void testCountWithIllegalArgument() { countAPI.post(builder1.build()); }, e -> { Assert.assertContains("The properties filter condition can be " + - "set only if just set one edge label", - e.getMessage()); + "set only if just set one edge label", e.getMessage()); }); CountRequest.Builder builder2 = CountRequest.builder(); @@ -471,8 +469,7 @@ public void testCountWithIllegalArgument() { countAPI.post(builder2.build()); }, e -> { Assert.assertContains("The properties filter condition can be " + - "set only if just set one edge label", - e.getMessage()); + "set only if just set one edge label", e.getMessage()); }); CountRequest.Builder builder3 = CountRequest.builder(); @@ -488,8 +485,7 @@ public void testCountWithIllegalArgument() { .properties(ImmutableMap.of("weight", 3.3D)); countAPI.post(builder4.build()); }, e -> { - Assert.assertContains("does not match sort keys of edge label", - e.getMessage()); + Assert.assertContains("does not match sort keys of edge label", e.getMessage()); }); } } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CustomizedPathsApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CustomizedPathsApiTest.java index f94358553..2a621d148 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CustomizedPathsApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/CustomizedPathsApiTest.java @@ -73,7 +73,7 @@ public void testCustomizedPathsSourceLabelProperty() { .weightBy("weight").degree(-1); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1); - builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(-1).limit(-1); CustomizedPathsRequest request = builder.build(); @@ -111,7 +111,7 @@ public void testCustomizedPathsSourceIds() { builder.sources().ids(markoId, peterId); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1); - builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(-1).limit(-1); CustomizedPathsRequest request = builder.build(); @@ -152,7 +152,7 @@ public void testCustomizedPathsSourceLabelPropertyMultiValue() { 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) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(-1).limit(-1); CustomizedPathsRequest request = builder.build(); @@ -195,7 +195,7 @@ public void testCustomizedPathsWithSample() { .weightBy("weight").degree(-1); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1).sample(1); - builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(-1).limit(-1); CustomizedPathsRequest request = builder.build(); @@ -236,7 +236,7 @@ public void testCustomizedPathsWithDecr() { .weightBy("weight").degree(-1); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1); - builder.sortBy(CustomizedPathsRequest.SortBy.DECR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.DECR).withVertex(true).withEdge(true) .capacity(-1).limit(-1); CustomizedPathsRequest request = builder.build(); @@ -277,7 +277,7 @@ public void testCustomizedPathsWithLimit() { .weightBy("weight").degree(-1); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1); - builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(-1).limit(1); CustomizedPathsRequest request = builder.build(); @@ -315,7 +315,7 @@ public void testCustomizedPathsWithCapacity() { .weightBy("weight").degree(-1); builder.steps().direction(Direction.OUT).labels("created1") .weightBy("weight").degree(-1); - builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true) + builder.sortBy(CustomizedPathsRequest.SortBy.INCR).withVertex(true).withEdge(true) .capacity(1).limit(-1); CustomizedPathsRequest request = builder.build(); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPITest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPITest.java new file mode 100644 index 000000000..ff46066c0 --- /dev/null +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/EdgeExistenceAPITest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.api.traverser; + + +import org.apache.hugegraph.api.BaseApiTest; +import org.apache.hugegraph.structure.graph.Edge; +import org.apache.hugegraph.testutil.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.List; + +public class EdgeExistenceAPITest extends TraverserApiTest { + + @BeforeClass + public static void prepareSchemaAndGraph() { + BaseApiTest.initPropertyKey(); + BaseApiTest.initVertexLabel(); + BaseApiTest.initEdgeLabel(); + BaseApiTest.initIndexLabel(); + BaseApiTest.initVertex(); + BaseApiTest.initEdge(); + } + + @Test + public void testEdgeExistenceGet() { + Object markoId = getVertexId("person", "name", "marko"); + Object vadasId = getVertexId("person", "name", "vadas"); + + List edges = edgeExistenceAPI.get(markoId, vadasId, "", "", 100); + Assert.assertEquals(1, edges.size()); + + String sortValues = edges.get(0).name(); + edges = edgeExistenceAPI.get(markoId, vadasId, "knows", sortValues, 100); + String id = edges.get(0).id(); + + Assert.assertEquals(1, edges.size()); + Assert.assertTrue(id.contains("marko") && id.contains("vadas") && id.contains(sortValues)); + + Object lopId = getVertexId("software", "name", "lop"); + + edges = edgeExistenceAPI.get(lopId, vadasId, "knows", "", 100); + Assert.assertEquals(0, edges.size()); + + Object joshId = getVertexId("person", "name", "josh"); + Object rippleId = getVertexId("software", "name", "ripple"); + + edges = edgeExistenceAPI.get(joshId, rippleId, "created", "", 100); + Assert.assertEquals(1, edges.size()); + + edges = edgeExistenceAPI.get(joshId, rippleId, "created", "", 0); + Assert.assertEquals(0, edges.size()); + } +} diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/FusiformSimilarityApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/FusiformSimilarityApiTest.java index 8cee1ea02..82bda4f81 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/FusiformSimilarityApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/FusiformSimilarityApiTest.java @@ -146,7 +146,7 @@ public void testFusiformSimilarity() { 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); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); @@ -176,7 +176,7 @@ public void testFusiformSimilarityLessThanMinEdgeCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -194,7 +194,7 @@ public void testFusiformSimilarityLessThanMinEdgeCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -203,7 +203,7 @@ public void testFusiformSimilarityLessThanMinEdgeCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(0, results.size()); @@ -212,7 +212,7 @@ public void testFusiformSimilarityLessThanMinEdgeCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -229,7 +229,7 @@ public void testFusiformSimilarityAlpha() { 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); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); @@ -246,7 +246,7 @@ public void testFusiformSimilarityAlpha() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -283,7 +283,7 @@ public void testFusiformSimilarityMinSimilars() { builder.label("read").direction(Direction.OUT).minNeighbors(8) .alpha(0.75D).minSimilars(2).groupProperty("city") .minGroups(2); - builder.capacity(-1).limit(-1); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); @@ -301,7 +301,7 @@ public void testFusiformSimilarityMinSimilars() { builder.label("read").direction(Direction.OUT).minNeighbors(8) .alpha(0.75D).minSimilars(1).groupProperty("city") .minGroups(2); - builder.capacity(-1).limit(-1); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); @@ -327,7 +327,7 @@ public void testFusiformSimilarityTop() { 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); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); @@ -344,7 +344,7 @@ public void testFusiformSimilarityTop() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -369,7 +369,7 @@ public void testFusiformSimilarityMinGroupCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); @@ -386,7 +386,7 @@ public void testFusiformSimilarityMinGroupCount() { 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); + builder.capacity(-1).limit(-1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(0, results.size()); @@ -399,7 +399,7 @@ public void testFusiformSimilarityCapacity() { 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); + builder.capacity(10).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); Assert.assertThrows(ServerException.class, () -> { @@ -422,7 +422,7 @@ public void testFusiformSimilarityLimit() { builder.sources().ids(p1, p2, p3); builder.label("read").direction(Direction.OUT).minNeighbors(5) .alpha(0.8D).top(2); - builder.capacity(-1).limit(-1); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); Assert.assertEquals(3, results.size()); @@ -431,7 +431,7 @@ public void testFusiformSimilarityLimit() { builder.sources().ids(p1, p2, p3); builder.label("read").direction(Direction.OUT).minNeighbors(5) .alpha(0.8D).top(2); - builder.capacity(-1).limit(2); + builder.capacity(-1).limit(2).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(2, results.size()); @@ -440,7 +440,7 @@ public void testFusiformSimilarityLimit() { builder.sources().ids(p1, p2, p3); builder.label("read").direction(Direction.OUT).minNeighbors(5) .alpha(0.8D).top(2); - builder.capacity(-1).limit(1); + builder.capacity(-1).limit(1).withVertex(true); request = builder.build(); results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -461,7 +461,7 @@ public void testFusiformSimilarityWithMultiTimesEdges() { builder.sources().ids(id1, id2, id3); builder.label("write").direction(Direction.OUT).minNeighbors(3) .alpha(0.666D); - builder.capacity(-1).limit(-1); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); Assert.assertEquals(1, results.size()); @@ -484,7 +484,7 @@ public void testFusiformSimilarityWithoutEdgeLabel() { builder.sources().ids(p1, p2, p3); builder.direction(Direction.OUT).minNeighbors(8) .alpha(0.875D); - builder.capacity(-1).limit(-1); + builder.capacity(-1).limit(-1).withVertex(true); FusiformSimilarityRequest request = builder.build(); FusiformSimilarity results = fusiformSimilarityAPI.post(request); @@ -565,66 +565,6 @@ public void testFusiformSimilarityWithIntermediaryAndVertex() { 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> entry = results.first(); - Assert.assertEquals(p1Id, entry.getKey()); - - Assert.assertEquals(2, entry.getValue().size()); - Set similars = entry.getValue(); - Set p2Inter = ImmutableSet.of(b2Id, b3Id, b4Id, b5Id, - b6Id, b7Id, b8Id, b9Id); - Set 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 vertices = ImmutableSet.of(); - Assert.assertEquals(vertices, results.vertices()); - } - @Test public void testFusiformSimilarityWithoutIntermediaryWithVertex() { Vertex p1 = getVertex("person", "name", "p1"); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/JaccardSimilarityApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/JaccardSimilarityApiTest.java index 27a9caa2e..1b9580813 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/JaccardSimilarityApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/JaccardSimilarityApiTest.java @@ -17,12 +17,12 @@ package org.apache.hugegraph.api.traverser; -import java.util.Map; import java.util.Set; import org.apache.hugegraph.structure.constant.Direction; import org.apache.hugegraph.structure.constant.T; import org.apache.hugegraph.structure.graph.Vertex; +import org.apache.hugegraph.structure.traverser.JaccardSimilarity; import org.apache.hugegraph.structure.traverser.SingleSourceJaccardSimilarityRequest; import org.apache.hugegraph.testutil.Assert; import org.junit.BeforeClass; @@ -85,11 +85,11 @@ public void testJaccardSimilarity() { @Test public void testJaccardSimilarityWithDirection() { double jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT, - null, -1); + null, -1); Assert.assertEquals(0.5, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN, - null, -1); + null, -1); Assert.assertEquals(0.5, jaccard, Double.MIN_VALUE); } @@ -104,19 +104,19 @@ public void testJaccardSimilarityWithLabel() { Assert.assertEquals(0.3333333333333333D, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN, - "link", -1); + "link", -1); Assert.assertEquals(0.3333333333333333D, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.BOTH, - "relateTo", -1); + "relateTo", -1); Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT, - "relateTo", -1); + "relateTo", -1); Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.IN, - "relateTo", -1); + "relateTo", -1); Assert.assertEquals(1.0D, jaccard, Double.MIN_VALUE); } @@ -127,7 +127,7 @@ public void testJaccardSimilarityWithDegree() { Assert.assertEquals(0.5D, jaccard, Double.MIN_VALUE); jaccard = jaccardSimilarityAPI.get(1, 2, Direction.OUT, - null, 1); + null, 1); Assert.assertEquals(1D, jaccard, Double.MIN_VALUE); } @@ -138,7 +138,7 @@ public void testJaccardSimilar() { builder.vertex(4); builder.step().direction(Direction.BOTH); SingleSourceJaccardSimilarityRequest request = builder.build(); - Map results = jaccardSimilarityAPI.post(request); + JaccardSimilarity results = jaccardSimilarityAPI.post(request); Assert.assertEquals(9, results.size()); Set expected = ImmutableSet.of("1", "2", "3", "5", "6", @@ -164,7 +164,7 @@ public void testJaccardSimilarWithTop() { builder.step().direction(Direction.BOTH); builder.top(5); SingleSourceJaccardSimilarityRequest request = builder.build(); - Map results = jaccardSimilarityAPI.post(request); + JaccardSimilarity results = jaccardSimilarityAPI.post(request); Assert.assertEquals(5, results.size()); Set expected = ImmutableSet.of("3", "5", "6", "7", "8"); @@ -184,7 +184,7 @@ public void testJaccardSimilarWithLabel() { builder.vertex(4); builder.step().direction(Direction.BOTH).labels("link"); SingleSourceJaccardSimilarityRequest request = builder.build(); - Map results = jaccardSimilarityAPI.post(request); + JaccardSimilarity results = jaccardSimilarityAPI.post(request); Assert.assertEquals(7, results.size()); Set expected = ImmutableSet.of("3", "7", "8", "9", @@ -207,7 +207,7 @@ public void testJaccardSimilarWithDirection() { builder.vertex(4); builder.step().direction(Direction.OUT); SingleSourceJaccardSimilarityRequest request = builder.build(); - Map results = jaccardSimilarityAPI.post(request); + JaccardSimilarity results = jaccardSimilarityAPI.post(request); Assert.assertEquals(6, results.size()); Set expected = ImmutableSet.of("1", "2", "3", diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KneighborApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KneighborApiTest.java index 5d4338d8f..94ffcb963 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KneighborApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KneighborApiTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; public class KneighborApiTest extends TraverserApiTest { @@ -53,7 +54,7 @@ public void testKneighborGet() { long softwareId = vertexLabelAPI.get("software").id(); List vertices = kneighborAPI.get(markoId, Direction.OUT, - null, 2, -1L, -1); + null, 2, -1L, 100000); Assert.assertEquals(4, vertices.size()); Assert.assertTrue(vertices.contains(softwareId + ":lop")); Assert.assertTrue(vertices.contains(softwareId + ":ripple")); @@ -72,8 +73,10 @@ public void testKneighborPost() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); + builder.withVertex(true); + ; KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -84,8 +87,9 @@ public void testKneighborPost() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -106,9 +110,9 @@ public void testKneighborPostWithPath() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); - builder.withPath(true); + builder.withPath(true).withVertex(true).withEdge(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -128,9 +132,9 @@ public void testKneighborPostWithPath() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); - builder.withPath(true); + builder.withPath(true).withVertex(true).withEdge(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -162,10 +166,10 @@ public void testKneighborPostWithVertex() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.withPath(false); - builder.withVertex(true); + builder.withVertex(true).withEdge(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -181,7 +185,7 @@ public void testKneighborPostWithVertex() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.withPath(false); builder.withVertex(true); @@ -201,10 +205,10 @@ public void testKneighborPostWithVertex() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.withPath(true); - builder.withVertex(true); + builder.withVertex(true).withEdge(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -229,10 +233,10 @@ public void testKneighborPostWithVertex() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.withPath(true); - builder.withVertex(true); + builder.withVertex(true).withEdge(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -269,8 +273,9 @@ public void testKneighborPostWithLabel() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("created"); + builder.steps().direction(Direction.BOTH).addEStep("created"); builder.maxDepth(1); + builder.withVertex(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -281,8 +286,9 @@ public void testKneighborPostWithLabel() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("created"); + builder.steps().direction(Direction.BOTH).addEStep("created"); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -293,8 +299,9 @@ public void testKneighborPostWithLabel() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("knows"); + builder.steps().direction(Direction.BOTH).addEStep("knows"); builder.maxDepth(1); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -305,8 +312,9 @@ public void testKneighborPostWithLabel() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("knows"); + builder.steps().direction(Direction.BOTH).addEStep("knows"); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -326,8 +334,9 @@ public void testKneighborPostWithDirection() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); builder.maxDepth(1); + builder.withVertex(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -338,8 +347,9 @@ public void testKneighborPostWithDirection() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -359,9 +369,11 @@ public void testKneighborPostWithProperties() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); builder.maxDepth(1); + builder.withVertex(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -372,9 +384,11 @@ public void testKneighborPostWithProperties() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -385,9 +399,11 @@ public void testKneighborPostWithProperties() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); builder.maxDepth(3); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -409,9 +425,10 @@ public void testKneighborPostWithLimit() { // 1 depth with in&out edges KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.limit(3); + builder.withVertex(true); KneighborRequest request = builder.build(); Kneighbor kneighborResult = kneighborAPI.post(request); @@ -423,9 +440,10 @@ public void testKneighborPostWithLimit() { // 2 depth with out edges builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); builder.maxDepth(2); builder.limit(5); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -437,9 +455,10 @@ public void testKneighborPostWithLimit() { // 2 depth with in&out edges builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.limit(5); + builder.withVertex(true); request = builder.build(); kneighborResult = kneighborAPI.post(request); @@ -455,7 +474,7 @@ public void testKneighborPostWithCountOnly() { KneighborRequest.Builder builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); KneighborRequest request = builder.build(); @@ -469,7 +488,7 @@ public void testKneighborPostWithCountOnly() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.countOnly(true); request = builder.build(); @@ -483,25 +502,25 @@ public void testKneighborPostWithCountOnly() { builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); builder.withPath(true); KneighborRequest.Builder finalBuilder = builder; - Assert.assertThrows(IllegalArgumentException.class, ()-> { + Assert.assertThrows(IllegalArgumentException.class, () -> { finalBuilder.build(); }); builder = KneighborRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); builder.withVertex(true); KneighborRequest.Builder finalBuilder1 = builder; - Assert.assertThrows(IllegalArgumentException.class, ()-> { + Assert.assertThrows(IllegalArgumentException.class, () -> { finalBuilder1.build(); }); } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KoutApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KoutApiTest.java index 87de1007d..d07bc32fd 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KoutApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/KoutApiTest.java @@ -32,6 +32,7 @@ import org.junit.Test; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; public class KoutApiTest extends TraverserApiTest { @@ -121,7 +122,7 @@ public void testKoutGetBothAllWithCapacityNoLimit() { Assert.assertThrows(ServerException.class, () -> { koutAPI.get(markoId, Direction.BOTH, null, - 2, false, -1L, 1L, -1); + 2, false, -1L, 1L, -1); }, e -> { String expect = "Capacity can't be less than limit, " + "but got capacity '1' and limit '-1'"; @@ -140,8 +141,9 @@ public void testKoutPost() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -152,8 +154,9 @@ public void testKoutPost() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -174,9 +177,10 @@ public void testKoutPostWithNearest() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.nearest(false); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -187,9 +191,10 @@ public void testKoutPostWithNearest() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.nearest(false); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -210,9 +215,9 @@ public void testKoutPostWithPath() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); - builder.withPath(true); + builder.withPath(true).withVertex(true).withEdge(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -232,9 +237,9 @@ public void testKoutPostWithPath() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); - builder.withPath(true); + builder.withPath(true).withVertex(true).withEdge(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -263,7 +268,7 @@ public void testKoutPostWithVertex() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.withPath(false); builder.withVertex(true); @@ -282,7 +287,7 @@ public void testKoutPostWithVertex() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.withPath(false); builder.withVertex(true); @@ -301,10 +306,11 @@ public void testKoutPostWithVertex() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.withPath(true); builder.withVertex(true); + builder.withEdge(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -329,10 +335,11 @@ public void testKoutPostWithVertex() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.withPath(true); builder.withVertex(true); + builder.withEdge(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -366,8 +373,9 @@ public void testKoutPostWithSingleLabel() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("created"); + builder.steps().direction(Direction.BOTH).addEStep("created"); builder.maxDepth(1); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -378,8 +386,9 @@ public void testKoutPostWithSingleLabel() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("created"); + builder.steps().direction(Direction.BOTH).addEStep("created"); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -390,8 +399,9 @@ public void testKoutPostWithSingleLabel() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("knows"); + builder.steps().direction(Direction.BOTH).addEStep("knows"); builder.maxDepth(1); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -402,8 +412,9 @@ public void testKoutPostWithSingleLabel() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH).labels("knows"); + builder.steps().direction(Direction.BOTH).addEStep("knows"); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -422,9 +433,11 @@ public void testKoutPostWithMultiLabels() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .labels("knows").labels("created"); + builder.steps().direction(Direction.BOTH) + .addEStep("knows") + .addEStep("created"); builder.maxDepth(1); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -432,12 +445,14 @@ public void testKoutPostWithMultiLabels() { Assert.assertEquals(3, koutResult.size()); Set 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.steps().direction(Direction.BOTH) + .addEStep("knows") + .addEStep("created"); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -457,8 +472,9 @@ public void testKoutPostWithDirection() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); builder.maxDepth(1); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -469,8 +485,9 @@ public void testKoutPostWithDirection() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -490,9 +507,11 @@ public void testKoutPostWithProperties() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); builder.maxDepth(1); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -503,9 +522,12 @@ public void testKoutPostWithProperties() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); + builder.maxDepth(2); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -516,9 +538,11 @@ public void testKoutPostWithProperties() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH) - .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); + builder.steps() + .direction(Direction.BOTH) + .addEStep("created", ImmutableMap.of("date", "P.gt(\"2014-01-01 00:00:00\")")); builder.maxDepth(3); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -539,9 +563,10 @@ public void testKoutPostWithLimit() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.limit(2); + builder.withVertex(true); KoutRequest request = builder.build(); Kout koutResult = koutAPI.post(request); @@ -552,9 +577,10 @@ public void testKoutPostWithLimit() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.limit(1); + builder.withVertex(true); request = builder.build(); koutResult = koutAPI.post(request); @@ -570,7 +596,7 @@ public void testKoutPostWithCountOnly() { KoutRequest.Builder builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); KoutRequest request = builder.build(); @@ -584,7 +610,7 @@ public void testKoutPostWithCountOnly() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(2); builder.countOnly(true); request = builder.build(); @@ -598,25 +624,25 @@ public void testKoutPostWithCountOnly() { builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); builder.withPath(true); KoutRequest.Builder finalBuilder = builder; - Assert.assertThrows(IllegalArgumentException.class, ()-> { + Assert.assertThrows(IllegalArgumentException.class, () -> { finalBuilder.build(); }); builder = KoutRequest.builder(); builder.source(markoId); - builder.step().direction(Direction.BOTH); + builder.steps().direction(Direction.BOTH); builder.maxDepth(1); builder.countOnly(true); builder.withVertex(true); KoutRequest.Builder finalBuilder1 = builder; - Assert.assertThrows(IllegalArgumentException.class, ()-> { + Assert.assertThrows(IllegalArgumentException.class, () -> { finalBuilder1.build(); }); } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java index c0eca409c..20e68f6e9 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/MultiNodeShortestPathApiTest.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.hugegraph.api.BaseApiTest; @@ -45,6 +46,16 @@ public static void prepareSchemaAndGraph() { BaseApiTest.initEdge(); } + private static boolean contains(List> expected, + PathsWithVertices.Paths path) { + List objects = path.objects(); + if (expected.contains(objects)) { + return true; + } + Collections.reverse(objects); + return expected.contains(objects); + } + @Test public void testMultiNodeShortestPath() { Object markoId = getVertexId("person", "name", "marko"); @@ -59,6 +70,7 @@ public void testMultiNodeShortestPath() { builder.vertices().ids(markoId, rippleId, joshId, lopId, vadasId, peterId); builder.step().direction(Direction.BOTH); + builder.withVertex(true).withEdge(true); MultiNodeShortestPathRequest request = builder.build(); PathsWithVertices pathsWithVertices = @@ -103,6 +115,7 @@ public void testMultiNodeShortestPathWithMaxDepth() { lopId, vadasId, peterId); builder.step().direction(Direction.BOTH); builder.maxDepth(2); + builder.withVertex(true).withEdge(true); MultiNodeShortestPathRequest request = builder.build(); PathsWithVertices pathsWithVertices = @@ -133,6 +146,7 @@ public void testMultiNodeShortestPathWithMaxDepth() { lopId, vadasId, peterId); builder.step().direction(Direction.BOTH); builder.maxDepth(1); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = multiNodeShortestPathAPI.post(request); @@ -166,7 +180,7 @@ public void testMultiNodeShortestPathWithVertex() { builder.vertices().ids(markoId, rippleId, joshId, lopId, vadasId, peterId); builder.step().direction(Direction.BOTH); - builder.withVertex(true); + builder.withVertex(true).withEdge(true); MultiNodeShortestPathRequest request = builder.build(); PathsWithVertices pathsWithVertices = @@ -203,14 +217,4 @@ public void testMultiNodeShortestPathWithVertex() { Assert.assertTrue(vertexIds.contains(vertex.id())); } } - - private static boolean contains(List> expected, - PathsWithVertices.Paths path) { - List objects = path.objects(); - if (expected.contains(objects)) { - return true; - } - Collections.reverse(objects); - return expected.contains(objects); - } } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/PathsApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/PathsApiTest.java index af6f09ef2..fc9057e6c 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/PathsApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/PathsApiTest.java @@ -102,6 +102,7 @@ public void testPathsPost() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -129,7 +130,7 @@ public void testPathsPostWithVertex() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH); builder.maxDepth(3); - builder.withVertex(true); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -165,6 +166,7 @@ public void testPathsPostWithLabel() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -184,6 +186,7 @@ public void testPathsPostWithLabel() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH).labels("created"); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = pathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -225,6 +228,7 @@ public void testPathsPostWithNearest() { builder.targets().ids(jimId); builder.step().direction(Direction.BOTH); builder.maxDepth(6); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -247,6 +251,7 @@ public void testPathsPostWithNearest() { builder.step().direction(Direction.BOTH); builder.nearest(true); builder.maxDepth(6); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = pathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -272,6 +277,7 @@ public void testPathsPostWithProperties() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -292,6 +298,7 @@ public void testPathsPostWithProperties() { builder.step().direction(Direction.BOTH) .properties("date", "P.gt(\"2014-01-01 00:00:00\")"); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = pathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -316,6 +323,7 @@ public void testPathsWithLimit() { builder.targets().ids(rippleId); builder.step().direction(Direction.BOTH); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); PathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = pathsAPI.post(request); @@ -336,6 +344,7 @@ public void testPathsWithLimit() { builder.step().direction(Direction.BOTH); builder.limit(1); builder.maxDepth(3); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = pathsAPI.post(request); paths = pathsWithVertices.paths(); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java index bbef97985..1dd9ae028 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/SingleSourceShortestPathApiTest.java @@ -126,8 +126,8 @@ public static void initShortestPathGraph() { @Test public void testSingleSourceShortestPath() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.BOTH, null, "weight", - -1, 0, -1, -1, false); + "A", Direction.BOTH, null, "weight", + -1, 0, -1, -1, true, true); Assert.assertEquals(18, weightedPaths.paths().size()); WeightedPath.Path path = weightedPaths.paths().get("B"); @@ -216,8 +216,8 @@ public void testSingleSourceShortestPath() { @Test public void testSingleSourceShortestPathWithLabel() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.BOTH, "link", "weight", - -1, 0, -1, -1, false); + "A", Direction.BOTH, "link", "weight", + -1, 0, -1, -1, true, true); Assert.assertEquals(12, weightedPaths.paths().size()); WeightedPath.Path path = weightedPaths.paths().get("B"); @@ -276,8 +276,8 @@ public void testSingleSourceShortestPathWithLabel() { path.vertices()); weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.BOTH, "relateTo", "weight", - -1, 0, -1, -1, false); + "A", Direction.BOTH, "relateTo", "weight", + -1, 0, -1, -1, true, true); Assert.assertEquals(7, weightedPaths.paths().size()); @@ -319,8 +319,8 @@ public void testSingleSourceShortestPathWithLabel() { @Test public void testSingleSourceShortestPathWithDirection() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.OUT, null, "weight", - -1, 0, -1, -1, false); + "A", Direction.OUT, null, "weight", + -1, 0, -1, -1, true, true); Assert.assertEquals(17, weightedPaths.paths().size()); @@ -411,8 +411,8 @@ public void testSingleSourceShortestPathWithDirection() { @Test public void testSingleSourceShortestPathWithDegree() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.OUT, null, "weight", - 1, 0, -1, -1, false); + "A", Direction.OUT, null, "weight", + 1, 0, -1, -1, true, true); Assert.assertEquals(4, weightedPaths.paths().size()); WeightedPath.Path path = weightedPaths.paths().get("B"); @@ -439,8 +439,8 @@ public void testSingleSourceShortestPathWithDegree() { @Test public void testSingleSourceShortestPathWithLimit() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.BOTH, null, "weight", - -1, 0, -1, 11, false); + "A", Direction.BOTH, null, "weight", + -1, 0, -1, 11, true, true); Assert.assertEquals(11, weightedPaths.paths().size()); WeightedPath.Path path = weightedPaths.paths().get("B"); @@ -497,8 +497,8 @@ public void testSingleSourceShortestPathWithLimit() { @Test public void testSingleSourceShortestPathWithVertex() { WeightedPaths weightedPaths = singleSourceShortestPathAPI.get( - "A", Direction.BOTH, null, "weight", - -1, 0, -1, -1, true); + "A", Direction.BOTH, null, "weight", + -1, 0, -1, -1, true, true); Assert.assertEquals(18, weightedPaths.paths().size()); Assert.assertEquals(19, weightedPaths.vertices().size()); } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TemplatePathsApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TemplatePathsApiTest.java index f5739fe01..e9cd4caf8 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TemplatePathsApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TemplatePathsApiTest.java @@ -108,6 +108,7 @@ public void testTemplatePaths() { builder.steps().direction(Direction.OUT).maxTimes(3); builder.steps().direction(Direction.OUT).maxTimes(3); builder.steps().direction(Direction.IN).maxTimes(3); + builder.withVertex(true).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -130,7 +131,7 @@ public void testTemplatePathsWithVertex() { 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); + builder.withVertex(true).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -155,6 +156,7 @@ public void testTemplatePathsWithLabel() { 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.withVertex(true).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -174,6 +176,7 @@ public void testTemplatePathsWithLabel() { 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).withEdge(true); request = builder.build(); pathsWithVertices = templatePathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -194,6 +197,7 @@ public void testTemplatePathsWithRing() { 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).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -212,6 +216,7 @@ public void testTemplatePathsWithRing() { builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3); builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3); builder.withRing(true); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = templatePathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -236,6 +241,7 @@ public void testTemplatePathsWithProperties() { builder.steps().direction(Direction.OUT).labels("relateTo").maxTimes(3); builder.steps().direction(Direction.IN).labels("relateTo").maxTimes(3); builder.withRing(true); + builder.withVertex(true).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -260,6 +266,7 @@ public void testTemplatePathsWithProperties() { builder.steps().direction(Direction.IN).labels("relateTo") .properties("weight", "P.gt(0.4)").maxTimes(3); builder.withRing(true); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = templatePathsAPI.post(request); paths = pathsWithVertices.paths(); @@ -280,6 +287,7 @@ public void testTemplatePathsWithLimit() { 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.withVertex(true).withEdge(true); TemplatePathsRequest request = builder.build(); PathsWithVertices pathsWithVertices = templatePathsAPI.post(request); List paths = pathsWithVertices.paths(); @@ -300,6 +308,7 @@ public void testTemplatePathsWithLimit() { builder.steps().direction(Direction.OUT).labels("link").maxTimes(3); builder.steps().direction(Direction.IN).labels("link").maxTimes(3); builder.limit(2); + builder.withVertex(true).withEdge(true); request = builder.build(); pathsWithVertices = templatePathsAPI.post(request); paths = pathsWithVertices.paths(); diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TraverserApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TraverserApiTest.java index 2a02dd10d..f6ab77156 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TraverserApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/TraverserApiTest.java @@ -49,6 +49,7 @@ public class TraverserApiTest extends BaseApiTest { protected static VerticesAPI verticesAPI; protected static EdgesAPI edgesAPI; + protected static EdgeExistenceAPI edgeExistenceAPI; @BeforeClass public static void init() { @@ -82,5 +83,7 @@ public static void init() { verticesAPI = new VerticesAPI(client, GRAPH); edgesAPI = new EdgesAPI(client, GRAPH); + + edgeExistenceAPI = new EdgeExistenceAPI(client, GRAPH); } } diff --git a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/WeightedShortestPathApiTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/WeightedShortestPathApiTest.java index ffa4b1617..425c49e20 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/WeightedShortestPathApiTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/api/traverser/WeightedShortestPathApiTest.java @@ -127,9 +127,10 @@ public static void initShortestPathGraph() { @Test public void testWeightedShortestPath() { WeightedPath weightedPath = weightedShortestPathAPI.get( - "A", "Z", Direction.BOTH, null, - "weight", -1, 0, -1, false); - Assert.assertTrue(weightedPath.vertices().isEmpty()); + "A", "Z", Direction.BOTH, null, + "weight", -1, 0, -1, true, true); + Assert.assertFalse(weightedPath.vertices().isEmpty()); + Assert.assertFalse(weightedPath.edges().isEmpty()); Assert.assertEquals(0.5D, weightedPath.path().weight(), Double.MIN_VALUE); Assert.assertEquals(ImmutableList.of("A", "H", "I", "J", "Z"), @@ -139,18 +140,20 @@ public void testWeightedShortestPath() { @Test public void testWeightedShortestPathWithLabel() { WeightedPath weightedPath = weightedShortestPathAPI.get( - "A", "Z", Direction.BOTH, "link", - "weight", -1, 0, -1, false); - Assert.assertTrue(weightedPath.vertices().isEmpty()); + "A", "Z", Direction.BOTH, "link", + "weight", -1, 0, -1, true, true); + Assert.assertFalse(weightedPath.vertices().isEmpty()); + Assert.assertFalse(weightedPath.edges().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()); + "A", "Z", Direction.BOTH, "relateTo", + "weight", -1, 0, -1, true, true); + Assert.assertFalse(weightedPath.vertices().isEmpty()); + Assert.assertFalse(weightedPath.edges().isEmpty()); Assert.assertEquals(6.3999999999999995D, weightedPath.path().weight(), Double.MIN_VALUE); Assert.assertEquals(ImmutableList.of("A", "Q", "R", "Z"), @@ -160,9 +163,10 @@ public void testWeightedShortestPathWithLabel() { @Test public void testWeightedShortestPathWithDirection() { WeightedPath weightedPath = weightedShortestPathAPI.get( - "A", "Z", Direction.OUT, null, - "weight", -1, 0, -1, false); - Assert.assertTrue(weightedPath.vertices().isEmpty()); + "A", "Z", Direction.OUT, null, + "weight", -1, 0, -1, true, true); + Assert.assertFalse(weightedPath.vertices().isEmpty()); + Assert.assertFalse(weightedPath.edges().isEmpty()); Assert.assertEquals(2.0D, weightedPath.path().weight(), Double.MIN_VALUE); Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"), @@ -172,9 +176,10 @@ public void testWeightedShortestPathWithDirection() { @Test public void testWeightedShortestPathWithDegree() { WeightedPath weightedPath = weightedShortestPathAPI.get( - "A", "Z", Direction.OUT, null, - "weight", 1L, 0L, -1L, false); - Assert.assertTrue(weightedPath.vertices().isEmpty()); + "A", "Z", Direction.OUT, null, + "weight", 1L, 0L, -1L, true, true); + Assert.assertFalse(weightedPath.vertices().isEmpty()); + Assert.assertFalse(weightedPath.edges().isEmpty()); Assert.assertEquals(2.0D, weightedPath.path().weight(), Double.MIN_VALUE); Assert.assertEquals(ImmutableList.of("A", "B", "C", "D", "Z"), @@ -184,9 +189,10 @@ public void testWeightedShortestPathWithDegree() { @Test public void testWeightedShortestPathWithVertex() { WeightedPath weightedPath = weightedShortestPathAPI.get( - "A", "Z", Direction.BOTH, null, "weight", - -1, 0, -1, true); + "A", "Z", Direction.BOTH, null, "weight", + -1, 0, -1, true, true); Assert.assertEquals(5, weightedPath.vertices().size()); + Assert.assertFalse(weightedPath.edges().isEmpty()); List 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/org/apache/hugegraph/functional/MetricsManagerTest.java b/hugegraph-client/src/test/java/org/apache/hugegraph/functional/MetricsManagerTest.java index 72d7732cb..9b4351781 100644 --- a/hugegraph-client/src/test/java/org/apache/hugegraph/functional/MetricsManagerTest.java +++ b/hugegraph-client/src/test/java/org/apache/hugegraph/functional/MetricsManagerTest.java @@ -50,5 +50,17 @@ public void testAllMetrics() { Assert.assertEquals(ImmutableSet.of("gauges", "counters", "histograms", "meters", "timers"), results.keySet()); + // prom format + String resultsProm = metrics().allWithPromFormat(); + Assert.assertContains("# HELP hugegraph_info", resultsProm); + } + + @Test + public void testStatisticsMetrics() { + Map> results = metrics().statistics(); + Assert.assertNotNull(results); + // prom format + String resultsProm = metrics().statisticsWithPromFormat(); + Assert.assertContains("# HELP hugegraph_info", resultsProm); } } diff --git a/hugegraph-dist/release-docs/licenses/LICENSE-frontend-maven-plugin.txt b/hugegraph-dist/release-docs/licenses/LICENSE-frontend-maven-plugin.txt new file mode 100644 index 000000000..8dada3eda --- /dev/null +++ b/hugegraph-dist/release-docs/licenses/LICENSE-frontend-maven-plugin.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/hugegraph-dist/scripts/dependency/known-dependencies.txt b/hugegraph-dist/scripts/dependency/known-dependencies.txt index 83add0d23..b47b3aa46 100644 --- a/hugegraph-dist/scripts/dependency/known-dependencies.txt +++ b/hugegraph-dist/scripts/dependency/known-dependencies.txt @@ -147,10 +147,10 @@ javax.annotation-api-1.3.2.jar javax.el-3.0.0.jar javax.el-3.0.1-b12.jar javax.json-1.0.jar -javax.servlet.jsp-2.3.2.jar -javax.servlet.jsp-api-2.3.1.jar javax.servlet-api-3.1.0.jar javax.servlet-api-4.0.1.jar +javax.servlet.jsp-2.3.2.jar +javax.servlet.jsp-api-2.3.1.jar javolution-5.5.1.jar jaxb-api-2.2.11.jar jaxb-api-2.3.1.jar diff --git a/hugegraph-hubble/hubble-dist/assembly/travis/download-hugegraph.sh b/hugegraph-hubble/hubble-dist/assembly/travis/download-hugegraph.sh index 1a08376dd..ac4cf585c 100755 --- a/hugegraph-hubble/hubble-dist/assembly/travis/download-hugegraph.sh +++ b/hugegraph-hubble/hubble-dist/assembly/travis/download-hugegraph.sh @@ -32,7 +32,8 @@ git checkout "${COMMIT_ID}" mvn package -DskipTests -Dmaven.javadoc.skip=true -ntp # TODO: lack incubator after apache package release (update it later) +cd hugegraph-server TAR=$(echo apache-hugegraph-*.tar.gz) -cp apache-hugegraph-*.tar.gz ../ -cd ../ +cp apache-hugegraph-*.tar.gz ../../ +cd ../../ rm -rf "${GIT_DIR}" diff --git a/hugegraph-hubble/hubble-dist/pom.xml b/hugegraph-hubble/hubble-dist/pom.xml index 3db9ddd93..93b069052 100644 --- a/hugegraph-hubble/hubble-dist/pom.xml +++ b/hugegraph-hubble/hubble-dist/pom.xml @@ -67,6 +67,49 @@ + + com.github.eirslett + frontend-maven-plugin + 1.14.2 + + + target + ../hubble-fe + + + + + install node and yarn + + install-node-and-yarn + + + v16.16.0 + v1.22.15 + + + + + yarn install + + yarn + + + install + + + + + yarn build + + yarn + + + build + + + + maven-antrun-plugin @@ -81,8 +124,6 @@ cd ${hubble-fe.dir} || exit 1 export CI=false - yarn install --network-timeout 600000 && yarn build || exit 1 - echo -e "Hubble-FE build successfully.\n" cd ${top.level.dir} && pwd rm -rf ${final.name}/ui diff --git a/hugegraph-hubble/hubble-fe/package.json b/hugegraph-hubble/hubble-fe/package.json index 21a258f11..50ade67cd 100644 --- a/hugegraph-hubble/hubble-fe/package.json +++ b/hugegraph-hubble/hubble-fe/package.json @@ -49,7 +49,7 @@ }, "scripts": { "start": "react-app-rewired start", - "build": "react-app-rewired build && npm run license", + "build": "CI=false && react-app-rewired build && npm run license", "test": "react-app-rewired test", "license": "node add-license.js" }, diff --git a/hugegraph-hubble/hubble-fe/yarn.lock b/hugegraph-hubble/hubble-fe/yarn.lock index 4a2b407a8..4e1c27aaf 100644 --- a/hugegraph-hubble/hubble-fe/yarn.lock +++ b/hugegraph-hubble/hubble-fe/yarn.lock @@ -2702,9 +2702,9 @@ async-validator@^4.0.2: integrity sha1-A0oP0hA6ay6/AQ2nUYO+wpkkev4= async@^2.6.2: - version "2.6.3" - resolved "https://registry.npmmirror.com/async/download/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" @@ -11283,24 +11283,29 @@ selfsigned@^1.10.7: node-forge "^0.10.0" "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.nlark.com/semver/download/semver-5.7.1.tgz?cache=0&sync_timestamp=1627487511390&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha1-qVT5Ma66UI0we78Gnv8MAclhFvc= + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@6.3.0, semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@6.3.0: version "6.3.0" - resolved "https://registry.nlark.com/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1627487511390&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0= + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@7.0.0: version "7.0.0" - resolved "https://registry.nlark.com/semver/download/semver-7.0.0.tgz?cache=0&sync_timestamp=1627487511390&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha1-XzyjV2HkfgWyBsba/yz4FPAxa44= + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.3.2, semver@^7.3.4: - version "7.3.5" - resolved "https://registry.nlark.com/semver/download/semver-7.3.5.tgz?cache=0&sync_timestamp=1627487511390&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fsemver%2Fdownload%2Fsemver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha1-C2Ich5NI2JmOSw5L6Us/EuYBjvc= + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" diff --git a/hugegraph-loader/assembly/travis/install-hugegraph-from-source.sh b/hugegraph-loader/assembly/travis/install-hugegraph-from-source.sh index 202f45691..61ea1c04f 100755 --- a/hugegraph-loader/assembly/travis/install-hugegraph-from-source.sh +++ b/hugegraph-loader/assembly/travis/install-hugegraph-from-source.sh @@ -29,8 +29,9 @@ cd hugegraph git checkout "${COMMIT_ID}" mvn package -DskipTests -Dmaven.javadoc.skip=true -ntp # TODO: lack incubator after apache package release (update it later) -mv apache-hugegraph-*.tar.gz ../ -cd ../ +cd hugegraph-server +mv apache-hugegraph-*.tar.gz ../../ +cd ../../ rm -rf hugegraph tar zxf apache-hugegraph-*.tar.gz diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/direct/loader/HBaseDirectLoader.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/direct/loader/HBaseDirectLoader.java index 7456e9271..fd1a02369 100644 --- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/direct/loader/HBaseDirectLoader.java +++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/direct/loader/HBaseDirectLoader.java @@ -74,10 +74,10 @@ public String getTableName() { String tableName = null; if (struct.edges().size() > 0) { - tableName = this.loadOptions.edgeTablename; + tableName = this.loadOptions.edgeTableName; } else if (struct.vertices().size() > 0) { - tableName = this.loadOptions.vertexTablename; + tableName = this.loadOptions.vertexTableName; } return tableName; diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/executor/LoadOptions.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/executor/LoadOptions.java index fb2a7d9dc..d2a8a4546 100644 --- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/executor/LoadOptions.java +++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/executor/LoadOptions.java @@ -209,28 +209,34 @@ public class LoadOptions implements Serializable { description = "Sink to different storage") public boolean sinkType = true; + @Parameter(names = {"--vertex-partitions"}, arity = 1, + validateWith = {PositiveValidator.class}, + description = "The number of partitions of the HBase vertex table") + public int vertexPartitions = 64; + @Parameter(names = {"--edge-partitions"}, arity = 1, + validateWith = {PositiveValidator.class}, description = "The number of partitions of the HBase edge table") public int edgePartitions = 64; - @Parameter(names = {"--vertex-partitions"}, arity = 1, - description = "The number of partitions of the HBase vertex table") - public int vertexPartitions = 64; + @Parameter(names = {"--vertex-table-name"}, arity = 1, + description = "HBase vertex table name") + public String vertexTableName; - @Parameter(names = {"edgeTablename"}, arity = 1, - description = "edgeTablename") - public String edgeTablename; - @Parameter(names = {"vertexTablename"}, arity = 1, - description = "vertexTablename") - public String vertexTablename; - @Parameter(names = {"hbaseZKQuorum"}, arity = 1, - description = "hbaseZKQuorum") + @Parameter(names = {"--edge-table-name"}, arity = 1, + description = "HBase edge table name") + public String edgeTableName; + + @Parameter(names = {"--hbase-zk-quorum"}, arity = 1, + description = "HBase zookeeper quorum") public String hbaseZKQuorum; - @Parameter(names = {"hbaseZKPort"}, arity = 1, - description = "hbaseZKPort") + + @Parameter(names = {"--hbase-zk-port"}, arity = 1, + description = "HBase zookeeper port") public String hbaseZKPort; - @Parameter(names = {"hbaseZKParent"}, arity = 1, - description = "hbaseZKParent") + + @Parameter(names = {"--hbase-zk-parent"}, arity = 1, + description = "HBase zookeeper parent") public String hbaseZKParent; public String workModeString() { @@ -292,8 +298,8 @@ public static LoadOptions parseOptions(String[] args) { public void copyBackendStoreInfo (BackendStoreInfo backendStoreInfo) { E.checkArgument(null != backendStoreInfo, "The backendStoreInfo can't be null"); - this.edgeTablename = backendStoreInfo.getEdgeTablename(); - this.vertexTablename = backendStoreInfo.getVertexTablename(); + this.edgeTableName = backendStoreInfo.getEdgeTablename(); + this.vertexTableName = backendStoreInfo.getVertexTablename(); this.hbaseZKParent = backendStoreInfo.getHbaseZKParent(); this.hbaseZKPort = backendStoreInfo.getHbaseZKPort(); this.hbaseZKQuorum = backendStoreInfo.getHbaseZKQuorum(); diff --git a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/spark/HugeGraphSparkLoader.java b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/spark/HugeGraphSparkLoader.java index 549f9e1c5..61cf3136c 100644 --- a/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/spark/HugeGraphSparkLoader.java +++ b/hugegraph-loader/src/main/java/org/apache/hugegraph/loader/spark/HugeGraphSparkLoader.java @@ -293,11 +293,11 @@ private void parse(Row row, Map.Entry> builde case FILE: case HDFS: FileSource fileSource = struct.input().asFileSource(); - String delimiter = fileSource.delimiter() ; + String delimiter = fileSource.delimiter(); if (Optional.ofNullable(delimiter).isPresent()) { elements = builder.build(fileSource.header(), row.mkString(delimiter).split(delimiter)); - }else { + } else { elements = builder.build(row); } break; diff --git a/hugegraph-tools/assembly/travis/install-hugegraph-from-source.sh b/hugegraph-tools/assembly/travis/install-hugegraph-from-source.sh index 45de8924c..0987dd739 100755 --- a/hugegraph-tools/assembly/travis/install-hugegraph-from-source.sh +++ b/hugegraph-tools/assembly/travis/install-hugegraph-from-source.sh @@ -31,9 +31,10 @@ git checkout "${COMMIT_ID}" mvn package -DskipTests -Dmaven.javadoc.skip=true -ntp # TODO: lack incubator after apache package release (update it later) +cd hugegraph-server TAR=$(echo apache-hugegraph-*.tar.gz) -tar zxf "${TAR}" -C ../ -cd ../ +tar zxf "${TAR}" -C ../../ +cd ../../ rm -rf "${GIT_DIR}" # TODO: lack incubator after apache package release (update it later) HTTP_SERVER_DIR=$(echo apache-hugegraph-*.*) diff --git a/pom.xml b/pom.xml index ed3aa7c87..daa21e32e 100644 --- a/pom.xml +++ b/pom.xml @@ -506,6 +506,9 @@ .repository/** **/.flattened-pom.xml + + **/hugegraph-client-go/go.sum + **/hugegraph-client-go/go.mod true