From 8bfe4d55557e0a9918fb1b34891dea0fd7770b1c Mon Sep 17 00:00:00 2001 From: imbajin Date: Fri, 29 Oct 2021 16:58:54 +0800 Subject: [PATCH] feat(api): Support adamic-adar & resource-allocation algorithms --- .../java/com/baidu/hugegraph/api/API.java | 2 +- .../api/traversers/PredictionAPI.java | 122 ++++++++++++++++++ .../baidu/hugegraph/version/ApiVersion.java | 5 +- .../algorithm/PredictionTraverser.java | 75 +++++++++++ .../traversal/algorithm/steps/EdgeStep.java | 1 + 5 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/PredictionAPI.java create mode 100644 hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PredictionTraverser.java diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/API.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/API.java index 398f0a686d..ff51da3c9f 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/API.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/API.java @@ -45,7 +45,7 @@ public class API { - private static final Logger LOG = Log.logger(RestServer.class); + public static final Logger LOG = Log.logger(RestServer.class); public static final String CHARSET = "UTF-8"; diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/PredictionAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/PredictionAPI.java new file mode 100644 index 0000000000..728a2d8a73 --- /dev/null +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/PredictionAPI.java @@ -0,0 +1,122 @@ +/* + * Copyright 2022 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.traversers; + +import static com.baidu.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELEMENTS_LIMIT; +import static com.baidu.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE; + +import javax.inject.Singleton; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; + +import com.baidu.hugegraph.HugeGraph; +import com.baidu.hugegraph.api.API; +import com.baidu.hugegraph.api.graph.EdgeAPI; +import com.baidu.hugegraph.api.graph.VertexAPI; +import com.baidu.hugegraph.backend.id.Id; +import com.baidu.hugegraph.core.GraphManager; +import com.baidu.hugegraph.traversal.algorithm.PredictionTraverser; +import com.baidu.hugegraph.type.define.Directions; +import com.baidu.hugegraph.util.E; +import com.baidu.hugegraph.util.JsonUtil; +import com.codahale.metrics.annotation.Timed; +import com.google.common.collect.ImmutableMap; + +/** + * This API include similar prediction algorithms, now include: + * - Adamic Adar + * - Resource Allocation + * + * Could add more prediction algorithms in future + */ +@Path("graphs/{graph}/traversers/") +@Singleton +public class PredictionAPI extends API { + + @GET + @Timed + @Path("adamicadar") + @Produces(APPLICATION_JSON_WITH_CHARSET) + public String get(@Context GraphManager manager, + @PathParam("graph") String graph, + @QueryParam("vertex") String current, + @QueryParam("other") String other, + @QueryParam("direction") String direction, + @QueryParam("label") String edgeLabel, + @QueryParam("max_degree") + @DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree, + @QueryParam("limit") + @DefaultValue(DEFAULT_ELEMENTS_LIMIT) long limit) { + LOG.debug("Graph [{}] get adamic adar between '{}' and '{}' with " + + "direction {}, edge label {}, max degree '{}' and limit '{}'", + graph, current, other, direction, edgeLabel, maxDegree, + limit); + + Id sourceId = VertexAPI.checkAndParseVertexId(current); + Id targetId = VertexAPI.checkAndParseVertexId(other); + E.checkArgument(!current.equals(other), + "The source and target vertex id can't be same"); + Directions dir = Directions.convert(EdgeAPI.parseDirection(direction)); + + HugeGraph g = graph(manager, graph); + PredictionTraverser traverser = new PredictionTraverser(g); + double score = traverser.adamicAdar(sourceId, targetId, dir, + edgeLabel, maxDegree, limit); + return JsonUtil.toJson(ImmutableMap.of("adamic_adar", score)); + } + + @GET + @Timed + @Path("resourceallocation") + @Produces(APPLICATION_JSON_WITH_CHARSET) + public String create(@Context GraphManager manager, + @PathParam("graph") String graph, + @QueryParam("vertex") String current, + @QueryParam("other") String other, + @QueryParam("direction") String direction, + @QueryParam("label") String edgeLabel, + @QueryParam("max_degree") + @DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree, + @QueryParam("limit") + @DefaultValue(DEFAULT_ELEMENTS_LIMIT) long limit) { + LOG.debug("Graph [{}] get resource allocation between '{}' and '{}' " + + "with direction {}, edge label {}, max degree '{}' and " + + "limit '{}'", graph, current, other, direction, edgeLabel, + maxDegree, limit); + + Id sourceId = VertexAPI.checkAndParseVertexId(current); + Id targetId = VertexAPI.checkAndParseVertexId(other); + E.checkArgument(!current.equals(other), + "The source and target vertex id can't be same"); + Directions dir = Directions.convert(EdgeAPI.parseDirection(direction)); + + HugeGraph g = graph(manager, graph); + PredictionTraverser traverser = new PredictionTraverser(g); + double score = traverser.resourceAllocation(sourceId, targetId, dir, + edgeLabel, maxDegree, + limit); + return JsonUtil.toJson(ImmutableMap.of("resource_allocation", score)); + } +} diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java index ad1f14b76a..78870f4577 100644 --- a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java +++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java @@ -117,12 +117,13 @@ public final class ApiVersion { * [0.65] Issue-1506: Support olap property key * [0.66] Issue-1567: Support get schema RESTful API * [0.67] Issue-1065: Support dynamically add/remove graph + * [0.68] Issue-1741: Support adamic-adar & resource-allocation API */ // The second parameter of Version.of() is for IDE running without JAR - public static final Version VERSION = Version.of(ApiVersion.class, "0.67"); + public static final Version VERSION = Version.of(ApiVersion.class, "0.68"); - public static final void check() { + public static void check() { // Check version of hugegraph-core. Firstly do check from version 0.3 VersionUtil.check(CoreVersion.VERSION, "0.12", "0.13", CoreVersion.NAME); } diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PredictionTraverser.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PredictionTraverser.java new file mode 100644 index 0000000000..21b902c33e --- /dev/null +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/PredictionTraverser.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package com.baidu.hugegraph.traversal.algorithm; + +import static java.lang.Math.log; + +import java.util.Set; + +import com.baidu.hugegraph.HugeGraph; +import com.baidu.hugegraph.backend.id.Id; +import com.baidu.hugegraph.traversal.algorithm.steps.EdgeStep; +import com.baidu.hugegraph.type.define.Directions; +import com.baidu.hugegraph.util.E; +import com.google.common.collect.ImmutableList; + +public class PredictionTraverser extends OltpTraverser { + + public PredictionTraverser(HugeGraph graph) { + super(graph); + } + + public double adamicAdar(Id source, Id target, Directions dir, + String label, long degree, long limit) { + Set neighbors = checkAndGetCommonNeighbors(source, target, dir, + label, degree, limit); + EdgeStep step = label == null ? new EdgeStep(graph(), dir) : + new EdgeStep(graph(), dir, ImmutableList.of(label)); + + return neighbors.stream() + .mapToDouble(vid -> 1.0 / log(edgesCount(vid, step))) + .sum(); + } + + public double resourceAllocation(Id source, Id target, Directions dir, + String label, long degree, long limit) { + Set neighbors = checkAndGetCommonNeighbors(source, target, dir, + label, degree, limit); + EdgeStep step = label == null ? new EdgeStep(graph(), dir) : + new EdgeStep(graph(), dir, ImmutableList.of(label)); + + return neighbors.stream() + .mapToDouble(vid -> 1.0 / edgesCount(vid, step)) + .sum(); + } + + private Set checkAndGetCommonNeighbors(Id source, Id target, + Directions dir, String label, + long degree, long limit) { + E.checkNotNull(source, "source id"); + E.checkNotNull(target, "the target id"); + this.checkVertexExist(source, "source"); + this.checkVertexExist(target, "target"); + E.checkNotNull(dir, "direction"); + checkDegree(degree); + SameNeighborTraverser traverser = new SameNeighborTraverser(graph()); + return traverser.sameNeighbors(source, target, dir, label, degree, limit); + } +} diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/steps/EdgeStep.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/steps/EdgeStep.java index b1d7ad1dad..dc2bb06990 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/steps/EdgeStep.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/steps/EdgeStep.java @@ -72,6 +72,7 @@ public EdgeStep(HugeGraph g, Directions direction, List labels, public EdgeStep(HugeGraph g, Directions direction, List labels, Map properties, long degree, long skipDegree) { + E.checkNotNull(g, "The graph can't be null"); E.checkArgument(degree == NO_LIMIT || degree > 0L, "The max degree must be > 0 or == -1, but got: %s", degree);