From 0caa6885aa3722fd1f02b1b41469ee013c022492 Mon Sep 17 00:00:00 2001 From: zhangyi51 Date: Thu, 4 Jun 2020 15:26:25 +0800 Subject: [PATCH 1/3] support count api Change-Id: Ib2c83372031a8d5e5b18d7e4b1556e78f4447b35 --- pom.xml | 4 +- .../hugegraph/api/traverser/CountAPI.java | 51 ++ .../api/traverser/structure/CountRequest.java | 196 +++++++ .../hugegraph/driver/TraverserManager.java | 8 + .../structure/constant/Traverser.java | 2 + .../com/baidu/hugegraph/api/BaseApiTest.java | 3 + .../com/baidu/hugegraph/api/CountApiTest.java | 496 ++++++++++++++++++ 7 files changed, 758 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java create mode 100644 src/main/java/com/baidu/hugegraph/api/traverser/structure/CountRequest.java create mode 100644 src/test/java/com/baidu/hugegraph/api/CountApiTest.java diff --git a/pom.xml b/pom.xml index 13f6e7f7..27f77489 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.baidu.hugegraph hugegraph-client - 1.8.7 + 1.8.8 jar hugegraph-client @@ -113,7 +113,7 @@ - 1.8.7.0 + 1.8.8.0 diff --git a/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java b/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java new file mode 100644 index 00000000..4d25aeae --- /dev/null +++ b/src/main/java/com/baidu/hugegraph/api/traverser/CountAPI.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.api.traverser; + +import java.util.Map; + +import com.baidu.hugegraph.api.traverser.structure.CountRequest; +import com.baidu.hugegraph.client.RestClient; +import com.baidu.hugegraph.rest.RestResult; +import com.baidu.hugegraph.util.E; + +public class CountAPI extends TraversersAPI { + + private static final String COUNT = "count"; + + public CountAPI(RestClient client, String graph) { + super(client, graph); + } + + @Override + protected String type() { + return "count"; + } + + public long post(CountRequest request) { + this.client.checkApiVersion("0.55", "count"); + RestResult result = this.client.post(this.path(), request); + @SuppressWarnings("unchecked") + Map countMap = result.readObject(Map.class); + E.checkState(countMap.containsKey(COUNT), + "The result doesn't have key '%s'", COUNT); + return countMap.get(COUNT).longValue(); + } +} diff --git a/src/main/java/com/baidu/hugegraph/api/traverser/structure/CountRequest.java b/src/main/java/com/baidu/hugegraph/api/traverser/structure/CountRequest.java new file mode 100644 index 00000000..bad361e6 --- /dev/null +++ b/src/main/java/com/baidu/hugegraph/api/traverser/structure/CountRequest.java @@ -0,0 +1,196 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.api.traverser.structure; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.baidu.hugegraph.api.API; +import com.baidu.hugegraph.api.traverser.TraversersAPI; +import com.baidu.hugegraph.structure.constant.Direction; +import com.baidu.hugegraph.structure.constant.Traverser; +import com.baidu.hugegraph.util.E; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class CountRequest { + + @JsonProperty("source") + private Object source; + @JsonProperty("steps") + private List steps; + @JsonProperty("contains_traversed") + public boolean containsTraversed; + @JsonProperty("dedup_size") + public long dedupSize; + + private CountRequest() { + this.source = null; + this.steps = new ArrayList<>(); + this.containsTraversed = false; + this.dedupSize = Traverser.DEFAULT_DEDUP_SIZE; + } + + @Override + public String toString() { + return String.format("CountRequest{source=%s,steps=%s," + + "containsTraversed=%s,dedupSize=%s", + this.source, this.steps, this.containsTraversed, + this.dedupSize); + } + + public static class Builder { + + private CountRequest request; + private List stepBuilders; + + public Builder() { + this.request = new CountRequest(); + this.stepBuilders = new ArrayList<>(); + } + + public Builder source(Object source) { + E.checkArgumentNotNull(source, "The source can't be null"); + this.request.source = source; + return this; + } + + public Step.Builder steps() { + Step.Builder builder = new Step.Builder(); + this.stepBuilders.add(builder); + return builder; + } + + public Builder containsTraversed(boolean containsTraversed) { + this.request.containsTraversed = containsTraversed; + return this; + } + + public Builder dedupSize(long dedupSize) { + checkDedupSize(dedupSize); + this.request.dedupSize = dedupSize; + return this; + } + + public CountRequest build() { + E.checkArgumentNotNull(this.request.source, + "The source can't be null"); + for (Step.Builder builder : this.stepBuilders) { + this.request.steps.add(builder.build()); + } + E.checkArgument(this.request.steps != null && + !this.request.steps.isEmpty(), + "The steps can't be null or empty"); + checkDedupSize(this.request.dedupSize); + return this.request; + } + } + + private static void checkDedupSize(long dedupSize) { + E.checkArgument(dedupSize >= 0L || dedupSize == API.NO_LIMIT, + "The dedup size must be >= 0 or == %s, but got: %s", + API.NO_LIMIT, dedupSize); + } + + public static class Step { + + @JsonProperty("direction") + private String direction; + @JsonProperty("labels") + private List labels; + @JsonProperty("properties") + private Map properties; + @JsonProperty("degree") + private long degree; + @JsonProperty("skip_degree") + private long skipDegree; + + private Step() { + this.direction = "BOTH"; + this.labels = new ArrayList<>(); + this.properties = new HashMap<>(); + this.degree = Traverser.DEFAULT_DEGREE; + this.skipDegree = Traverser.DEFAULT_SKIP_DEGREE; + } + + @Override + public String toString() { + return String.format("Step{direction=%s,labels=%s,properties=%s," + + "degree=%s,skipDegree=%s}", + this.direction, this.labels, this.properties, + this.degree, this.skipDegree); + } + + public static class Builder { + + private Step step; + + private Builder() { + this.step = new Step(); + } + + public Builder direction(Direction direction) { + this.step.direction = direction.toString(); + return this; + } + + public Builder labels(List labels) { + this.step.labels = labels; + return this; + } + + public Builder labels(String label) { + this.step.labels.add(label); + return this; + } + + public Builder properties(Map properties) { + this.step.properties = properties; + return this; + } + + public Builder properties(String key, Object value) { + this.step.properties.put(key, value); + return this; + } + + public Builder degree(long degree) { + TraversersAPI.checkDegree(degree); + this.step.degree = degree; + return this; + } + + public Builder skipDegree(long skipDegree) { + TraversersAPI.checkSkipDegree(skipDegree, this.step.degree, + API.NO_LIMIT); + this.step.skipDegree = skipDegree; + return this; + } + + private Step build() { + TraversersAPI.checkDegree(this.step.degree); + TraversersAPI.checkSkipDegree(this.step.skipDegree, + this.step.degree, API.NO_LIMIT); + return this.step; + } + } + } +} diff --git a/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java b/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java index 25c0fd57..dcd1226b 100644 --- a/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java +++ b/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java @@ -23,6 +23,7 @@ import java.util.List; import com.baidu.hugegraph.api.traverser.AllShortestPathsAPI; +import com.baidu.hugegraph.api.traverser.CountAPI; import com.baidu.hugegraph.api.traverser.CrosspointsAPI; import com.baidu.hugegraph.api.traverser.CustomizedCrosspointsAPI; import com.baidu.hugegraph.api.traverser.CustomizedPathsAPI; @@ -41,6 +42,7 @@ import com.baidu.hugegraph.api.traverser.SingleSourceShortestPathAPI; import com.baidu.hugegraph.api.traverser.VerticesAPI; import com.baidu.hugegraph.api.traverser.WeightedShortestPathAPI; +import com.baidu.hugegraph.api.traverser.structure.CountRequest; import com.baidu.hugegraph.api.traverser.structure.CrosspointsRequest; import com.baidu.hugegraph.api.traverser.structure.CustomizedCrosspoints; import com.baidu.hugegraph.api.traverser.structure.CustomizedPaths; @@ -81,6 +83,7 @@ public class TraverserManager { private CrosspointsAPI crosspointsAPI; private KoutAPI koutAPI; private KneighborAPI kneighborAPI; + private CountAPI countAPI; private RingsAPI ringsAPI; private RaysAPI raysAPI; private CustomizedPathsAPI customizedPathsAPI; @@ -106,6 +109,7 @@ public TraverserManager(RestClient client, GraphManager graphManager) { this.crosspointsAPI = new CrosspointsAPI(client, graph); this.koutAPI = new KoutAPI(client, graph); this.kneighborAPI = new KneighborAPI(client, graph); + this.countAPI = new CountAPI(client, graph); this.ringsAPI = new RingsAPI(client, graph); this.raysAPI = new RaysAPI(client, graph); this.customizedPathsAPI = new CustomizedPathsAPI(client, graph); @@ -379,6 +383,10 @@ public List kneighbor(Object sourceId, Direction direction, degree, limit); } + public long kneighbor(CountRequest request) { + return this.countAPI.post(request); + } + public List rings(Object sourceId, int depth) { return this.rings(sourceId, Direction.BOTH, null, depth, true, DEFAULT_DEGREE, DEFAULT_CAPACITY, diff --git a/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java b/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java index 3f2e2c49..fe2a93fd 100644 --- a/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java +++ b/src/main/java/com/baidu/hugegraph/structure/constant/Traverser.java @@ -27,6 +27,8 @@ public class Traverser { public static final long DEFAULT_DEGREE = 10_000L; public static final long DEFAULT_CROSSPOINT_LIMIT = 10_000L; public static final long DEFAULT_PATHS_LIMIT = 10L; + public static final long DEFAULT_DEDUP_SIZE = 1_000_000L; + public static final long DEFAULT_SKIP_DEGREE = 100_000L; public static final long DEFAULT_SAMPLE = 100L; public static final double DEFAULT_WEIGHT = 0.0D; public static final long DEFAULT_PAGE_LIMIT = 100_000L; diff --git a/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java b/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java index c0f05d63..01fef4aa 100644 --- a/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java +++ b/src/test/java/com/baidu/hugegraph/api/BaseApiTest.java @@ -36,6 +36,7 @@ import com.baidu.hugegraph.api.schema.VertexLabelAPI; import com.baidu.hugegraph.api.task.TaskAPI; import com.baidu.hugegraph.api.traverser.AllShortestPathsAPI; +import com.baidu.hugegraph.api.traverser.CountAPI; import com.baidu.hugegraph.api.traverser.CrosspointsAPI; import com.baidu.hugegraph.api.traverser.CustomizedCrosspointsAPI; import com.baidu.hugegraph.api.traverser.CustomizedPathsAPI; @@ -82,6 +83,7 @@ public class BaseApiTest extends BaseClientTest { protected static CrosspointsAPI crosspointsAPI; protected static KoutAPI koutAPI; protected static KneighborAPI kneighborAPI; + protected static CountAPI countAPI; protected static RingsAPI ringsAPI; protected static RaysAPI raysAPI; protected static CustomizedPathsAPI customizedPathsAPI; @@ -128,6 +130,7 @@ public static void init() { crosspointsAPI = new CrosspointsAPI(client, GRAPH); koutAPI = new KoutAPI(client, GRAPH); kneighborAPI = new KneighborAPI(client, GRAPH); + countAPI = new CountAPI(client, GRAPH); ringsAPI = new RingsAPI(client, GRAPH); raysAPI = new RaysAPI(client, GRAPH); customizedPathsAPI = new CustomizedPathsAPI(client, GRAPH); diff --git a/src/test/java/com/baidu/hugegraph/api/CountApiTest.java b/src/test/java/com/baidu/hugegraph/api/CountApiTest.java new file mode 100644 index 00000000..f068aa0a --- /dev/null +++ b/src/test/java/com/baidu/hugegraph/api/CountApiTest.java @@ -0,0 +1,496 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.api; + +import org.junit.BeforeClass; +import org.junit.Test; + +import com.baidu.hugegraph.api.traverser.structure.CountRequest; +import com.baidu.hugegraph.exception.ServerException; +import com.baidu.hugegraph.structure.constant.Direction; +import com.baidu.hugegraph.structure.constant.T; +import com.baidu.hugegraph.structure.graph.Vertex; +import com.baidu.hugegraph.testutil.Assert; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +public class CountApiTest extends BaseApiTest { + + @BeforeClass + public static void initGraph() { + schema().propertyKey("time") + .asDate() + .ifNotExist() + .create(); + + schema().propertyKey("weight") + .asDouble() + .ifNotExist() + .create(); + + schema().vertexLabel("node") + .useCustomizeStringId() + .ifNotExist() + .create(); + + schema().edgeLabel("link") + .sourceLabel("node").targetLabel("node") + .properties("time") + .multiTimes().sortKeys("time") + .ifNotExist() + .create(); + + schema().edgeLabel("relateTo") + .sourceLabel("node").targetLabel("node") + .properties("weight") + .ifNotExist() + .create(); + + Vertex va = graph().addVertex(T.label, "node", T.id, "A"); + Vertex vb = graph().addVertex(T.label, "node", T.id, "B"); + Vertex vc = graph().addVertex(T.label, "node", T.id, "C"); + Vertex vd = graph().addVertex(T.label, "node", T.id, "D"); + Vertex ve = graph().addVertex(T.label, "node", T.id, "E"); + Vertex vf = graph().addVertex(T.label, "node", T.id, "F"); + Vertex vg = graph().addVertex(T.label, "node", T.id, "G"); + Vertex vh = graph().addVertex(T.label, "node", T.id, "H"); + Vertex vi = graph().addVertex(T.label, "node", T.id, "I"); + Vertex vj = graph().addVertex(T.label, "node", T.id, "J"); + Vertex vk = graph().addVertex(T.label, "node", T.id, "K"); + Vertex vl = graph().addVertex(T.label, "node", T.id, "L"); + Vertex vm = graph().addVertex(T.label, "node", T.id, "M"); + Vertex vn = graph().addVertex(T.label, "node", T.id, "N"); + Vertex vo = graph().addVertex(T.label, "node", T.id, "O"); + Vertex vp = graph().addVertex(T.label, "node", T.id, "P"); + Vertex vq = graph().addVertex(T.label, "node", T.id, "Q"); + Vertex vr = graph().addVertex(T.label, "node", T.id, "R"); + Vertex vs = graph().addVertex(T.label, "node", T.id, "S"); + Vertex vt = graph().addVertex(T.label, "node", T.id, "T"); + Vertex vu = graph().addVertex(T.label, "node", T.id, "U"); + Vertex vv = graph().addVertex(T.label, "node", T.id, "V"); + Vertex vw = graph().addVertex(T.label, "node", T.id, "W"); + Vertex vx = graph().addVertex(T.label, "node", T.id, "X"); + Vertex vy = graph().addVertex(T.label, "node", T.id, "Y"); + Vertex vz = graph().addVertex(T.label, "node", T.id, "Z"); + + /* + * + * c -----* f + * * + * / d -----* g + * / * + * / / + * b---*e -----* h + * * + * / / j *----- m + * / / + * / * + * a *---i *--- k *--- n + * . * + * . \ + * . \ + * . l *------ o + * * + * p ...* q ...* v + * ...* r ...* w + * ...* s ...* x + * ...* t ...* y + * ...* u ...* z + * + * Description: + * 1. "*" means arrow + * 2. "---" means "link" edge + * 3. "..." means "relateTo" edge + * + */ + va.addEdge("link", vb, "time", "2020-01-01"); + + vb.addEdge("link", vc, "time", "2020-01-02"); + vb.addEdge("link", vd, "time", "2020-01-03"); + vb.addEdge("link", ve, "time", "2020-01-04"); + + vc.addEdge("link", vf, "time", "2020-01-05"); + vd.addEdge("link", vg, "time", "2020-01-06"); + ve.addEdge("link", vh, "time", "2020-01-07"); + + vi.addEdge("link", va, "time", "2020-01-08"); + + vj.addEdge("link", vi, "time", "2020-01-09"); + vk.addEdge("link", vi, "time", "2020-01-10"); + vl.addEdge("link", vi, "time", "2020-01-11"); + + vm.addEdge("link", vj, "time", "2020-01-12"); + vn.addEdge("link", vk, "time", "2020-01-13"); + vo.addEdge("link", vl, "time", "2020-01-14"); + + va.addEdge("relateTo", vp, "weight", 0.0D); + + vp.addEdge("relateTo", vq, "weight", 0.1D); + vp.addEdge("relateTo", vr, "weight", 0.2D); + vp.addEdge("relateTo", vs, "weight", 0.3D); + vp.addEdge("relateTo", vt, "weight", 0.4D); + vp.addEdge("relateTo", vu, "weight", 0.5D); + + vq.addEdge("relateTo", vv, "weight", 0.6D); + vr.addEdge("relateTo", vw, "weight", 0.7D); + vs.addEdge("relateTo", vx, "weight", 0.8D); + vt.addEdge("relateTo", vy, "weight", 0.9D); + vu.addEdge("relateTo", vz, "weight", 1.0D); + } + + @Test + public void testCount() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(8L, count); + } + + @Test + public void testCountWithContainsTraversed() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(19L, count); + } + + @Test + public void testCountWithDirection() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(19L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(8L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.IN); + builder.steps().direction(Direction.IN); + builder.steps().direction(Direction.IN); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(3L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.IN); + builder.steps().direction(Direction.IN); + builder.steps().direction(Direction.IN); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(8L, count); + } + + @Test + public void testCountWithLabel() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(3L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(8L, count); + } + + @Test + public void testCountWithProperties() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(1L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.lt(\"2020-01-06\")"); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(6L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.gt(\"2020-01-03\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(1L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(true); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "P.gt(\"2020-01-03\")"); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(4L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")); + builder.steps().direction(Direction.OUT) + .labels(ImmutableList.of("link")) + .properties("time", "2020-01-07"); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(1L, count); + } + + @Test + public void testCountWithDegree() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(8L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT).degree(1); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(3L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT).degree(2); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(4L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT).degree(4); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(7L, count); + } + + @Test + public void testCountWithSkipDegree() { + CountRequest.Builder builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT); + CountRequest request = builder.build(); + + long count = countAPI.post(request); + Assert.assertEquals(8L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT).degree(3).skipDegree(5); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(3L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT).degree(2).skipDegree(3); + builder.steps().direction(Direction.OUT); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(0L, count); + + builder = new CountRequest.Builder(); + builder.source("A").containsTraversed(false); + builder.steps().direction(Direction.OUT); + builder.steps().direction(Direction.OUT).degree(3).skipDegree(4); + request = builder.build(); + + count = countAPI.post(request); + Assert.assertEquals(3L, count); + } + + @Test + public void testCountWithIllegalArgument() { + CountRequest.Builder builder = new CountRequest.Builder(); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + builder.source(null); + }, e -> { + Assert.assertContains("The source can't be null", e.getMessage()); + }); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + builder.dedupSize(-5); + }, e -> { + Assert.assertContains("The dedup size must be >= 0 or == -1, " + + "but got: ", e.getMessage()); + }); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + builder.steps().degree(0); + }, e -> { + Assert.assertContains("Degree must be > 0 or == -1, but got: ", + e.getMessage()); + }); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + builder.steps().skipDegree(-3); + }, e -> { + Assert.assertContains("The skipped degree must be >= 0, but got", + e.getMessage()); + }); + + Assert.assertThrows(IllegalArgumentException.class, () -> { + builder.steps().degree(5).skipDegree(3); + }, e -> { + Assert.assertContains("The skipped degree must be >= degree, ", + e.getMessage()); + }); + + CountRequest.Builder builder1 = new CountRequest.Builder(); + Assert.assertThrows(ServerException.class, () -> { + builder1.source("A").containsTraversed(false); + builder1.steps().properties(ImmutableMap.of("weight", 3.3D)); + countAPI.post(builder1.build()); + }, e -> { + Assert.assertContains("The properties filter condition can be " + + "set only if just set one edge label", + e.getMessage()); + }); + + CountRequest.Builder builder2 = new CountRequest.Builder(); + Assert.assertThrows(ServerException.class, () -> { + builder2.source("A").containsTraversed(false); + builder2.steps().labels(ImmutableList.of("link", "relateTo")) + .properties(ImmutableMap.of("weight", 3.3D)); + countAPI.post(builder2.build()); + }, e -> { + Assert.assertContains("The properties filter condition can be " + + "set only if just set one edge label", + e.getMessage()); + }); + + CountRequest.Builder builder3 = new CountRequest.Builder(); + builder3.source("A").containsTraversed(false); + builder3.steps().labels(ImmutableList.of("link")) + .properties(ImmutableMap.of("time", "2020-01-01")); + countAPI.post(builder3.build()); + + CountRequest.Builder builder4 = new CountRequest.Builder(); + Assert.assertThrows(ServerException.class, () -> { + builder4.source("A").containsTraversed(false); + builder4.steps().labels(ImmutableList.of("link")) + .properties(ImmutableMap.of("weight", 3.3D)); + countAPI.post(builder4.build()); + }, e -> { + Assert.assertContains("does not match sort keys of edge label", + e.getMessage()); + }); + } +} From 84ae837da381ab011612d41b2de6ad5af529d4c5 Mon Sep 17 00:00:00 2001 From: zhangyi51 Date: Thu, 18 Jun 2020 17:06:49 +0800 Subject: [PATCH 2/3] add count test into apitestsuite ` Change-Id: I2f998eb8f43776e8dfa3286e511c2860ae2652be --- src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java b/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java index 36aef5a0..a8139627 100644 --- a/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java +++ b/src/test/java/com/baidu/hugegraph/api/ApiTestSuite.java @@ -45,6 +45,7 @@ RestoreApiTest.class, TraverserApiTest.class, + CountApiTest.class, RingsRaysApiTest.class, SameNeighborsApiTest.class, JaccardSimilarityApiTest.class, From f295087f6cfe4177139eb8b9d19eae0d52a47e58 Mon Sep 17 00:00:00 2001 From: zhangyi51 Date: Thu, 18 Jun 2020 17:38:46 +0800 Subject: [PATCH 3/3] improve Change-Id: Ie1394fd82a83c869b5d468d1217637bd200cc300 --- .../hugegraph/driver/TraverserManager.java | 2 +- .../com/baidu/hugegraph/api/CountApiTest.java | 45 ++++++++++--------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java b/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java index dcd1226b..06f41852 100644 --- a/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java +++ b/src/main/java/com/baidu/hugegraph/driver/TraverserManager.java @@ -383,7 +383,7 @@ public List kneighbor(Object sourceId, Direction direction, degree, limit); } - public long kneighbor(CountRequest request) { + public long count(CountRequest request) { return this.countAPI.post(request); } diff --git a/src/test/java/com/baidu/hugegraph/api/CountApiTest.java b/src/test/java/com/baidu/hugegraph/api/CountApiTest.java index f068aa0a..5080237a 100644 --- a/src/test/java/com/baidu/hugegraph/api/CountApiTest.java +++ b/src/test/java/com/baidu/hugegraph/api/CountApiTest.java @@ -92,30 +92,31 @@ public static void initGraph() { /* * - * c -----* f - * * - * / d -----* g - * / * - * / / - * b---*e -----* h - * * - * / / j *----- m - * / / - * / * - * a *---i *--- k *--- n - * . * - * . \ - * . \ - * . l *------ o - * * - * p ...* q ...* v - * ...* r ...* w - * ...* s ...* x - * ...* t ...* y - * ...* u ...* z + * c -----> f + * ^ + * / d -----> g + * / ^ + * / / + * b ---> e -----> h + * ^ + * / j <----- m + * / / + * / / + * / < + * a <--- i <--- k <--- n + * . ^ + * . \ + * . \ + * . l <------ o + * > + * p ...> q ...> v + * ...> r ...> w + * ...> s ...> x + * ...> t ...> y + * ...> u ...> z * * Description: - * 1. "*" means arrow + * 1. ">","<","^" means arrow * 2. "---" means "link" edge * 3. "..." means "relateTo" edge *