Skip to content

Commit

Permalink
improve: convert id from string to ID with type
Browse files Browse the repository at this point in the history
  • Loading branch information
diaohancai committed Jan 25, 2024
1 parent 01cde50 commit 6b42bf5
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,20 @@
package org.apache.hugegraph.computer.algorithm.path.shortest;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.hugegraph.computer.core.common.exception.ComputerException;
import org.apache.hugegraph.computer.core.config.Config;
import org.apache.hugegraph.computer.core.graph.edge.Edge;
import org.apache.hugegraph.computer.core.graph.id.Id;
import org.apache.hugegraph.computer.core.graph.id.IdCategory;
import org.apache.hugegraph.computer.core.graph.value.DoubleValue;
import org.apache.hugegraph.computer.core.graph.value.IdSet;
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.worker.Computation;
import org.apache.hugegraph.computer.core.worker.ComputationContext;
import org.apache.hugegraph.computer.core.worker.WorkerContext;
Expand All @@ -43,31 +42,36 @@ public class SingleSourceShortestPath implements Computation<SingleSourceShortes

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 =
"single_source_shortest_path.weight_property";
public static final String OPTION_DEFAULT_WEIGHT =
"single_source_shortest_path.default_weight";

/**
* id type of vertex.
* string|number|uuid
*/
// todo improve: automatic inference
private String vertexIdTypeStr;
private IdCategory vertexIdType;

/**
* source vertex id
*/
private String sourceId;
private String sourceIdStr;
private Id sourceId;

/**
* target vertex id.
* 1. single target: one vertex id
* 2. multiple target: multiple vertex ids separated by comma
* 3. all: *
*/
private String targetId;

/**
* cache of targetId
*/
private Set<String> targetIdSet;

private String targetIdStr;
private IdSet targetIdSet; // empty when targetIdStr == "*"
/**
* target quantity type
*/
Expand All @@ -89,7 +93,7 @@ public class SingleSourceShortestPath implements Computation<SingleSourceShortes
/**
* reached targets
*/
private IdSet reachedTargets;
private IdSet reachedTargets; // empty when targetIdStr == "*"

@Override
public String category() {
Expand All @@ -103,23 +107,30 @@ public String name() {

@Override
public void init(Config config) {
this.sourceId = config.getString(OPTION_SOURCE_ID, "");
if (StringUtils.isBlank(this.sourceId)) {
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)) {
throw new ComputerException("The param '%s' must not be blank", OPTION_SOURCE_ID);
}
this.sourceId = IdUtil.parseId(this.vertexIdType, this.sourceIdStr);

this.targetId = config.getString(OPTION_TARGET_ID, "");
if (StringUtils.isBlank(this.targetId)) {
this.targetIdStr = config.getString(OPTION_TARGET_ID, "");
if (StringUtils.isBlank(this.targetIdStr)) {
throw new ComputerException("The param '%s' must not be blank", OPTION_TARGET_ID);
}

// remove spaces
this.targetId = Arrays.stream(this.targetId.split(","))
.map(e -> e.trim())
.collect(Collectors.joining(","));
// cache targetId
this.targetIdSet = new HashSet<>(Arrays.asList(this.targetId.split(",")));
this.targetIdStr = Arrays.stream(this.targetIdStr.split(","))
.map(e -> e.trim())
.collect(Collectors.joining(","));
this.targetQuantityType = this.getQuantityType();
if (this.targetQuantityType != QuantityType.ALL) {
this.targetIdSet = new IdSet();
for (String targetIdStr : this.targetIdStr.split(",")) {
targetIdSet.add(IdUtil.parseId(this.vertexIdType, targetIdStr));
}
}

this.weightProperty = config.getString(OPTION_WEIGHT_PROPERTY, "");

Expand All @@ -138,24 +149,25 @@ public void compute0(ComputationContext context, Vertex vertex) {
vertex.value(value);

// start from source vertex
if (!this.idEquals(vertex, this.sourceId)) {
if (!this.sourceId.equals(vertex.id())) {
vertex.inactivate();
return;
}
value.zeroDistance(); // source vertex

// single target && source vertex == target vertex
// single target && source == target
if (this.targetQuantityType == QuantityType.SINGLE &&
this.sourceId.equals(this.targetId)) {
LOG.debug("source vertex {} equals target vertex {}", this.sourceId, this.targetId);
this.sourceIdStr.equals(this.targetIdStr)) {
LOG.debug("source vertex {} equals target vertex {}",
this.sourceIdStr, this.targetIdStr);
vertex.inactivate();
return;
}

if (vertex.numEdges() <= 0) {
// isolated vertex
LOG.debug("source vertex {} can not reach target vertex {}",
this.sourceId, this.targetId);
this.sourceIdStr, this.targetIdStr);
vertex.inactivate();
return;
}
Expand Down Expand Up @@ -223,22 +235,15 @@ public void afterSuperstep(WorkerContext context) {
* get QuantityType by this.targetId
*/
private QuantityType getQuantityType() {
if (this.targetId.equals("*")) {
if (this.targetIdStr.equals("*")) {
return QuantityType.ALL;
} else if (this.targetId.contains(",")) {
} else if (this.targetIdStr.contains(",")) {
return QuantityType.MULTIPLE;
} else {
return QuantityType.SINGLE;
}
}

/**
* determine whether vertex.id and id are equal
*/
private boolean idEquals(Vertex vertex, String id) {
return vertex.id().value().toString().equals(id);
}

/**
* get the weight of an edge by its weight property
*/
Expand Down Expand Up @@ -267,32 +272,26 @@ private double getEdgeWeight(Edge edge) {
* determine whether vertex is one of the target
*/
private boolean isTarget(Vertex vertex) {
return this.targetIdSet.contains(vertex.id().toString());
return this.targetQuantityType != QuantityType.ALL &&
this.targetIdSet.contains(vertex.id());
}

/**
* determine whether all targets reached
*/
private boolean isAllTargetsReached(Vertex vertex) {
if (this.targetQuantityType == QuantityType.SINGLE &&
this.idEquals(vertex, this.targetId)) {
return true;
if (this.targetQuantityType == QuantityType.ALL) {
return false;
}

if (this.targetQuantityType == QuantityType.MULTIPLE) {
if (this.targetIdSet.size() == this.reachedTargets.size()) {
List<String> reachedTargets = this.reachedTargets.value()
.stream().map(Id::toString)
.collect(Collectors.toList());
for (String targetId : this.targetIdSet) {
if (!reachedTargets.contains(targetId)) {
return false;
}
if (this.targetIdSet.size() == this.reachedTargets.size()) {
for (Id targetId : this.targetIdSet.value()) {
if (!this.reachedTargets.contains(targetId)) {
return false;
}
return true;
}
return true;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES 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.graph.id;

import org.apache.commons.lang3.StringUtils;

public enum IdCategory {

NUMBER,
STRING,
UUID,
;

public static IdCategory parse(String code) {
if (StringUtils.isBlank(code)) {
return null;
}

for (IdCategory idAlias : IdCategory.values()) {
if (idAlias.name().equals(code.toUpperCase())) {
return idAlias;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ public static Id createId(IdType type) {
}
}

public static Id parseId(IdType type, Object value) {
try {
switch (type) {
case LONG:
return (Id) BYTES_ID_LONG_METHOD.invoke(null, value);
case UTF8:
return (Id) BYTES_ID_STRING_METHOD.invoke(null, value);
case UUID:
return (Id) BYTES_ID_UUID_METHOD.invoke(null, value);
default:
throw new ComputerException("Can't parse Id for %s", type.name());
}
} catch (Exception e) {
throw new ComputerException("Failed to parse Id", e);
}
}

public static Id createId() {
try {
return (Id) BYTES_ID_CONSTRUCTOR.newInstance();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES 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.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.hugegraph.computer.core.common.exception.ComputerException;
import org.apache.hugegraph.computer.core.graph.id.Id;
import org.apache.hugegraph.computer.core.graph.id.IdCategory;
import org.apache.hugegraph.computer.core.graph.id.IdFactory;
import org.apache.hugegraph.computer.core.graph.id.IdType;

public class IdUtil {

private static String UUID_REGEX = "^[0-9a-fA-F]{8}-" +
"[0-9a-fA-F]{4}-" +
"[0-9a-fA-F]{4}-" +
"[0-9a-fA-F]{4}-" +
"[0-9a-fA-F]{12}$";
private static Pattern P = Pattern.compile(UUID_REGEX);

public static Id parseId(String idStr) {
if (StringUtils.isBlank(idStr)) {
throw new ComputerException("Can't parse Id for empty string");
}

if (StringUtils.isNumeric(idStr)) {
return IdFactory.parseId(IdType.LONG, idStr);
} else if (P.matcher(idStr).matches()) {
return IdFactory.parseId(IdType.UUID, idStr);
} else {
return IdFactory.parseId(IdType.UTF8, idStr);
}
}

public static Id parseId(IdCategory idCategory, String idStr) {
if (StringUtils.isBlank(idStr)) {
throw new ComputerException("Can't parse Id for empty string");
}

if (idCategory == null) {
// automatic inference
return parseId(idStr);
}

switch (idCategory) {
case NUMBER:
return IdFactory.parseId(IdType.LONG, idStr);
case UUID:
return IdFactory.parseId(IdType.UUID, idStr);
default:
return IdFactory.parseId(IdType.UTF8, idStr);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ public String value(org.apache.hugegraph.computer.core.graph.vertex.Vertex verte

LOG.info("source vertex {} to target vertex {}, " +
"shortest path: {}, total weight: {}",
SOURCE_ID, TARGET_ID, map.get("path"), map.get("totalWeight"));
SOURCE_ID, TARGET_ID, map.get("path"), map.get("total_weight"));
Assert.assertEquals(map.get("path"), SHORTEST_PATH);
Assert.assertEquals(map.get("totalWeight"), TOTAL_WEIGHT);
Assert.assertEquals(map.get("total_weight"), TOTAL_WEIGHT);
}
return json;
}
Expand Down

0 comments on commit 6b42bf5

Please sign in to comment.