-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(algorithm): support single source shortest path algorithm #285
Changes from 4 commits
28691fd
d9c5c84
3aff95d
b6c0f4c
f5a5de9
4c37b2d
15f95f6
6630b82
9524900
2804bd7
cdaba23
125bba6
9587802
01cde50
6b42bf5
55b0ed3
2828bb7
a0a0103
c4ca8fe
174584e
aa8238d
0b1c720
6510424
d55c7c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,8 @@ | |
|
||
package org.apache.hugegraph.computer.algorithm.path.shortest; | ||
|
||
import java.util.Arrays; | ||
import java.util.Iterator; | ||
import java.util.stream.Collectors; | ||
import java.util.List; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.hugegraph.computer.core.common.exception.ComputerException; | ||
|
@@ -32,17 +31,19 @@ | |
import org.apache.hugegraph.computer.core.graph.value.Value; | ||
import org.apache.hugegraph.computer.core.graph.vertex.Vertex; | ||
import org.apache.hugegraph.computer.core.util.IdUtil; | ||
import org.apache.hugegraph.computer.core.util.JsonUtilExt; | ||
import org.apache.hugegraph.computer.core.worker.Computation; | ||
import org.apache.hugegraph.computer.core.worker.ComputationContext; | ||
import org.apache.hugegraph.computer.core.worker.WorkerContext; | ||
import org.apache.hugegraph.rest.SerializeException; | ||
import org.apache.hugegraph.util.JsonUtil; | ||
import org.apache.hugegraph.util.Log; | ||
import org.slf4j.Logger; | ||
|
||
public class SingleSourceShortestPath implements Computation<SingleSourceShortestPathValue> { | ||
|
||
private static final Logger LOG = Log.logger(SingleSourceShortestPath.class); | ||
|
||
public static final String OPTION_VERTEX_ID_TYPE = "single_source_shortest_path.vertex_id_type"; | ||
public static final String OPTION_SOURCE_ID = "single_source_shortest_path.source_id"; | ||
public static final String OPTION_TARGET_ID = "single_source_shortest_path.target_id"; | ||
public static final String OPTION_WEIGHT_PROPERTY = | ||
|
@@ -51,27 +52,20 @@ | |
"single_source_shortest_path.default_weight"; | ||
|
||
/** | ||
* id type of vertex. | ||
* string|number|uuid | ||
* source vertex id. | ||
* {"id": "", "idType": ""} | ||
*/ | ||
// todo improve: automatic inference | ||
private String vertexIdTypeStr; | ||
private IdCategory vertexIdType; | ||
|
||
/** | ||
* source vertex id | ||
*/ | ||
private String sourceIdStr; | ||
// todo improve: automatic inference idType | ||
private Id sourceId; | ||
|
||
/** | ||
* target vertex id. | ||
* 1. single target: one vertex id | ||
* 2. multiple target: multiple vertex ids separated by comma | ||
* 3. all: * | ||
* 1. single target: [{"id": "", "idType": ""}] | ||
* 2. multiple target: [{"id": "", "idType": ""}, {"id": "", "idType": ""}] | ||
* 3. all: [] | ||
*/ | ||
private String targetIdStr; | ||
private IdSet targetIdSet; // empty when targetIdStr == "*" | ||
// todo improve: automatic inference idType | ||
private IdSet targetIdSet; // empty when targetId is all | ||
/** | ||
* target quantity type | ||
*/ | ||
|
@@ -93,7 +87,7 @@ | |
/** | ||
* reached targets | ||
*/ | ||
private IdSet reachedTargets; // empty when targetIdStr == "*" | ||
private IdSet reachedTargets; // empty when targetId is all | ||
|
||
@Override | ||
public String category() { | ||
|
@@ -107,28 +101,35 @@ | |
|
||
@Override | ||
public void init(Config config) { | ||
this.vertexIdTypeStr = config.getString(OPTION_VERTEX_ID_TYPE, ""); | ||
this.vertexIdType = IdCategory.parse(this.vertexIdTypeStr); | ||
|
||
this.sourceIdStr = config.getString(OPTION_SOURCE_ID, ""); | ||
if (StringUtils.isBlank(this.sourceIdStr)) { | ||
String sourceIdStr = config.getString(OPTION_SOURCE_ID, ""); | ||
if (StringUtils.isBlank(sourceIdStr)) { | ||
throw new ComputerException("The param '%s' must not be blank", OPTION_SOURCE_ID); | ||
Check warning on line 106 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L106
|
||
} | ||
this.sourceId = IdUtil.parseId(this.vertexIdType, this.sourceIdStr); | ||
VertexInputJson sourceVertex; | ||
try { | ||
sourceVertex = JsonUtil.fromJson(sourceIdStr, VertexInputJson.class); | ||
} catch (SerializeException e) { | ||
throw new ComputerException("The param '%s' is unexpected format", OPTION_SOURCE_ID); | ||
Check warning on line 112 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L111-L112
|
||
} | ||
this.sourceId = IdUtil.parseId(IdCategory.parse(sourceVertex.getIdType()), | ||
sourceVertex.getId()); | ||
|
||
this.targetIdStr = config.getString(OPTION_TARGET_ID, ""); | ||
if (StringUtils.isBlank(this.targetIdStr)) { | ||
String targetIdStr = config.getString(OPTION_TARGET_ID, ""); | ||
if (StringUtils.isBlank(targetIdStr)) { | ||
throw new ComputerException("The param '%s' must not be blank", OPTION_TARGET_ID); | ||
Check warning on line 119 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L119
|
||
} | ||
// remove spaces | ||
this.targetIdStr = Arrays.stream(this.targetIdStr.split(",")) | ||
.map(e -> e.trim()) | ||
.collect(Collectors.joining(",")); | ||
this.targetQuantityType = this.getQuantityType(); | ||
List<VertexInputJson> targetVertices; | ||
try { | ||
targetVertices = JsonUtilExt.fromJson2List(targetIdStr, VertexInputJson.class); | ||
} catch (SerializeException e) { | ||
throw new ComputerException("The param '%s' is unexpected format", OPTION_TARGET_ID); | ||
Check warning on line 125 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L124-L125
|
||
} | ||
this.targetQuantityType = this.getQuantityType(targetVertices); | ||
if (this.targetQuantityType != QuantityType.ALL) { | ||
this.targetIdSet = new IdSet(); | ||
for (String targetIdStr : this.targetIdStr.split(",")) { | ||
targetIdSet.add(IdUtil.parseId(this.vertexIdType, targetIdStr)); | ||
for (VertexInputJson targetVertex : targetVertices) { | ||
targetIdSet.add(IdUtil.parseId(IdCategory.parse(targetVertex.getIdType()), | ||
targetVertex.getId())); | ||
} | ||
} | ||
|
||
|
@@ -136,7 +137,7 @@ | |
|
||
this.defaultWeight = config.getDouble(OPTION_DEFAULT_WEIGHT, 1); | ||
if (this.defaultWeight <= 0) { | ||
throw new ComputerException("The param '%s' must be greater than 0, " + | ||
Check warning on line 140 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L140
|
||
"actual got '%s'", | ||
OPTION_DEFAULT_WEIGHT, this.defaultWeight); | ||
} | ||
|
@@ -157,19 +158,18 @@ | |
|
||
// single target && source == target | ||
if (this.targetQuantityType == QuantityType.SINGLE && | ||
this.sourceIdStr.equals(this.targetIdStr)) { | ||
this.sourceId.equals(this.targetIdSet.value().iterator().next())) { | ||
LOG.debug("source vertex {} equals target vertex {}", | ||
this.sourceIdStr, this.targetIdStr); | ||
this.sourceId, this.targetIdSet.value().iterator().next()); | ||
vertex.inactivate(); | ||
return; | ||
Check warning on line 165 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L162-L165
|
||
} | ||
|
||
if (vertex.numEdges() <= 0) { | ||
// isolated vertex | ||
LOG.debug("source vertex {} can not reach target vertex {}", | ||
this.sourceIdStr, this.targetIdStr); | ||
LOG.debug("source vertex {} is isolated", this.sourceId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this format is more convenient for retrieving logs: |
||
vertex.inactivate(); | ||
return; | ||
Check warning on line 172 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L170-L172
|
||
} | ||
|
||
vertex.edges().forEach(edge -> { | ||
|
@@ -201,8 +201,9 @@ | |
continue; | ||
} | ||
|
||
// reach all target or nowhere to go | ||
if (this.isAllTargetsReached(vertex) || vertex.numEdges() <= 0) { | ||
// target vertex finds all targets reached or nowhere to go | ||
if ((this.isTarget(vertex) && this.isAllTargetsReached(vertex)) || | ||
vertex.numEdges() <= 0) { | ||
continue; | ||
} | ||
|
||
|
@@ -232,15 +233,15 @@ | |
} | ||
|
||
/** | ||
* get QuantityType by this.targetId | ||
* get quantityType by targetId | ||
*/ | ||
private QuantityType getQuantityType() { | ||
if (this.targetIdStr.equals("*")) { | ||
private QuantityType getQuantityType(List<VertexInputJson> targetVertices) { | ||
if (targetVertices.size() == 0) { | ||
return QuantityType.ALL; | ||
Check warning on line 240 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L240
|
||
} else if (this.targetIdStr.contains(",")) { | ||
return QuantityType.MULTIPLE; | ||
} else { | ||
} else if (targetVertices.size() == 1) { | ||
return QuantityType.SINGLE; | ||
} else { | ||
return QuantityType.MULTIPLE; | ||
Check warning on line 244 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L244
|
||
} | ||
} | ||
|
||
|
@@ -253,16 +254,16 @@ | |
Value property = edge.property(this.weightProperty); | ||
if (property != null) { | ||
if (!property.isNumber()) { | ||
throw new ComputerException("The value of %s must be a numeric value, " + | ||
Check warning on line 257 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L257
|
||
"actual got '%s'", | ||
this.weightProperty, property.string()); | ||
Check warning on line 259 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L259
|
||
} | ||
|
||
weight = ((DoubleValue) property).doubleValue(); | ||
if (weight <= 0) { | ||
throw new ComputerException("The value of %s must be greater than 0, " + | ||
Check warning on line 264 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L264
|
||
"actual got '%s'", | ||
this.weightProperty, property.string()); | ||
Check warning on line 266 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L266
|
||
} | ||
} | ||
return weight; | ||
|
@@ -281,17 +282,38 @@ | |
*/ | ||
private boolean isAllTargetsReached(Vertex vertex) { | ||
if (this.targetQuantityType == QuantityType.ALL) { | ||
return false; | ||
Check warning on line 285 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L285
|
||
} | ||
|
||
if (this.targetIdSet.size() == this.reachedTargets.size()) { | ||
for (Id targetId : this.targetIdSet.value()) { | ||
if (!this.reachedTargets.contains(targetId)) { | ||
return false; | ||
Check warning on line 291 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L291
|
||
} | ||
} | ||
return true; | ||
} | ||
return false; | ||
Check warning on line 296 in computer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java Codecov / codecov/patchcomputer-algorithm/src/main/java/org/apache/hugegraph/computer/algorithm/path/shortest/SingleSourceShortestPath.java#L296
|
||
} | ||
|
||
static class VertexInputJson { | ||
private String id; | ||
private String idType; | ||
|
||
public String getId() { | ||
return id; | ||
} | ||
|
||
public void setId(String id) { | ||
this.id = id; | ||
} | ||
|
||
public String getIdType() { | ||
return idType; | ||
} | ||
|
||
public void setIdType(String idType) { | ||
this.idType = idType; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with this | ||
* work for additional information regarding copyright ownership. The ASF | ||
* licenses this file to You under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
* WARRANTIES 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.computer.core.util; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
import org.apache.hugegraph.rest.SerializeException; | ||
|
||
import com.fasterxml.jackson.databind.JavaType; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
|
||
// todo move to org.apache.hugegraph.util.JsonUtil later | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expect format |
||
public class JsonUtilExt { | ||
|
||
private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
|
||
public static <T> List<T> fromJson2List(String json, Class<T> clazz) { | ||
try { | ||
return MAPPER.readValue(json, getCollectionType(List.class, clazz)); | ||
} catch (IOException e) { | ||
throw new SerializeException("Failed to deserialize json '%s'", | ||
Check warning on line 37 in computer-api/src/main/java/org/apache/hugegraph/computer/core/util/JsonUtilExt.java Codecov / codecov/patchcomputer-api/src/main/java/org/apache/hugegraph/computer/core/util/JsonUtilExt.java#L36-L37
|
||
e, json); | ||
} | ||
} | ||
|
||
private static JavaType getCollectionType(Class<?> collectionClass, | ||
Class<?>... elementClasses) { | ||
return MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,8 +40,8 @@ public class SingleSourceShortestPathTest extends AlgorithmTestBase { | |
public static final String EL = "road"; | ||
public static final String PROPERTY_KEY = "distance"; | ||
|
||
public static final String SOURCE_ID = "A"; | ||
public static final String TARGET_ID = "E"; | ||
public static final String SOURCE_ID = "{\"id\": \"A\", \"idType\": \"string\"}"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can use id format like this (ref
|
||
public static final String TARGET_ID = "[{\"id\": \"E\", \"idType\": \"string\"}]"; | ||
public static final String SHORTEST_PATH = "[A, C, B, E]"; | ||
public static final double TOTAL_WEIGHT = 28; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a private method
targetIdOne()
forthis.targetIdSet.value().iterator().next()
? we can also check there exist one element intargetIdOne()