SPARQL 如何找到通过 RDF 图中多个节点的最短路径
SPARQL How can I find the shortest path through multiple nodes in a RDF graph
如何使用sparql设置多个节点并求出通过每个节点的最短路径?可以使用Cypher实现这个功能。但是,我不知道如何通过Sparql实现它,以便我可以在Jena中查询路径。
非常感谢您的热心帮助!最后我修改了jena(findShortestPath)的javaapi函数来实现这个功能
首先,为了实现任意两个节点之间的最短路径查询(不考虑关系方向),我修改了jena的javaapi函数(findShortestPath ) 如下。
private JSONObject findShortestPath(Model m, Resource start, Resource end) {
int shortestLength = 20;
JSONObject pathResult = new JSONObject();
pathResult.put("length", shortestLength);
pathResult.put("paths", new JSONArray());
List<OntTools.Path> bfs = new LinkedList<>();
ArrayList<ExtendedIterator> nodeStatementIter = new ArrayList<>();
nodeStatementIter.add(m.listStatements(start, null, (RDFNode)null));
nodeStatementIter.add(m.listStatements(null, null, start));
for (var iter : nodeStatementIter) {
while(iter.hasNext()) {
Statement statement = (Statement) iter.next();
if (! statement.getObject().isLiteral())
bfs.add((new OntTools.Path()).append(statement));
}
}
nodeStatementIter.clear();
HashSet<Resource> passedNodes = new HashSet<>();
passedNodes.add(start);
while(!bfs.isEmpty()) {
OntTools.Path candidate = bfs.remove(0);
if (candidate.size() > shortestLength) {
break;
}
Resource subject = candidate.getStatement(candidate.size() - 1).getSubject();
Resource object = candidate.getStatement(candidate.size() - 1).getObject().isResource() ? candidate.getStatement(candidate.size() - 1).getObject().asResource() : null;
ArrayList<Resource> resources = new ArrayList<>();
resources.add(subject);
if (object != null) {
resources.add(object);
}
if (resources.contains(end)) {
// solution = candidate;
shortestLength = candidate.size();
pathResult.put("length", shortestLength);
pathResult.getJSONArray("paths").add(pathToTriples(candidate));
} else {
for (Resource resource : resources) {
if (! passedNodes.contains(resource)) {
nodeStatementIter.add(m.listStatements(resource, null, (RDFNode)null));
nodeStatementIter.add(m.listStatements(null, null, resource));
passedNodes.add(resource);
for (var iter : nodeStatementIter) {
while(iter.hasNext()) {
// Statement link = (Statement)iter.next();
Statement statement = (Statement) iter.next();
if (!candidate.contains(statement))
bfs.add(candidate.append(statement));
}
}
nodeStatementIter.clear();
}
}
}
}
if (pathResult.getJSONArray("paths").size() == 0) {
pathResult.put("length", Integer.MAX_VALUE);
}
return pathResult;
}
在这个函数中,另一个函数用于将路径转换为三元组
private ArrayList<ArrayList<String>> pathToTriples(OntTools.Path path) {
ListIterator<Statement> statementListIterator = path.listIterator();
ArrayList<ArrayList<String>> statements = new ArrayList<>();
while (statementListIterator.hasNext()) {
Statement next = statementListIterator.next();
statements.add(new ArrayList<>(){{
add(next.getSubject().toString());
add(next.getPredicate().toString());
add(next.getObject().toString());
}});
}
return statements;
}
最后,为了实现通过多个指定节点的最短路径查询,我列举了节点序列的可能组合。然后,计算每个组合每两个节点之间的最短路径之和,select所有总长度最短的组合作为最终结果。
public JSONObject findShortestPathBetweenMultipleNodes(Model m, List<Resource> nodes) {
ArrayList<ArrayList<JSONArray>> nodePairPathMatrix = new ArrayList<>();
ArrayList<ArrayList<Integer>> nodePairLengthMatrix = new ArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
nodePairPathMatrix.add(new ArrayList<>(){{
for (int j = 0; j < nodes.size(); j++) {
add(null);
}
}});
nodePairLengthMatrix.add(new ArrayList<>(){{
for (int j = 0; j < nodes.size(); j++) {
add(Integer.MAX_VALUE);
}
}});
}
for (int i = 0; i < nodes.size()-1; i++) {
for (int j = i+1; j < nodes.size(); j++) {
Resource source = nodes.get(i);
Resource target = nodes.get(j);
JSONObject shortestPath = findShortestPath(m, source, target);
JSONArray paths = shortestPath.getJSONArray("paths");
nodePairPathMatrix.get(i).set(j, paths);
nodePairPathMatrix.get(j).set(i, paths);
nodePairLengthMatrix.get(i).set(j, shortestPath.getInteger("length"));
nodePairLengthMatrix.get(j).set(i, shortestPath.getInteger("length"));
}
}
ArrayList<ArrayList<Integer>> permutationsOfNodes = permutationByMaxNum(nodes.size());
Integer minLength = Integer.MAX_VALUE;
ArrayList<ArrayList<JSONArray>> nodePairPathListWithMinLength = new ArrayList<>();
for (var permutation : permutationsOfNodes){
Integer length = 0;
ArrayList<JSONArray> nodePairPathList = new ArrayList<>();
for (int permutationItemIndex = 1; permutationItemIndex < permutation.size(); permutationItemIndex++) {
int end = permutation.get(permutationItemIndex);
int start = permutation.get(permutationItemIndex-1);
Integer curNodePairLength = nodePairLengthMatrix.get(start).get(end);
if (curNodePairLength.equals(Integer.MAX_VALUE) || nodePairLengthMatrix.get(start).get(end).equals(Integer.MAX_VALUE)){
length = Integer.MAX_VALUE;
break;
}
length += nodePairLengthMatrix.get(start).get(end);
nodePairPathList.add(nodePairPathMatrix.get(start).get(end));
}
if (length < minLength) {
minLength = length;
nodePairPathListWithMinLength.clear();
nodePairPathListWithMinLength.add(nodePairPathList);
} else if (length.equals(minLength)) {
nodePairPathListWithMinLength.add(nodePairPathList);
}
}
Set<List<String>> tripleResults = new HashSet<>();
for (var equalTotalLengthNodePairPathListWithMinLength : nodePairPathListWithMinLength) {
for (var nodePairPathList : equalTotalLengthNodePairPathListWithMinLength) {
for (int i = 0; i < nodePairPathList.size(); i++) {
JSONArray nodePairPathWithEqualLength = nodePairPathList.getJSONArray(i);
for (int j = 0; j < nodePairPathWithEqualLength.size(); j++) {
JSONArray nodePairPath = nodePairPathWithEqualLength.getJSONArray(j);
List<String> triple = nodePairPath.toJavaList(String.class);
tripleResults.add(triple);
}
}
}
}
JSONObject result = new JSONObject();
result.put("triples", tripleResults);
result.put("length", minLength);
return result;
}
节点的排列由以下函数生成。
private ArrayList<ArrayList<Integer>> permutationByMaxNum (int maxNum) {
Stack<Integer> stack = new Stack<>();
ArrayList<ArrayList<Integer>> results = new ArrayList<>();
ArrayList<Integer> sourceList = new ArrayList<>();
for (int i = 0; i < maxNum; i++) {
sourceList.add(i);
}
permutateFunction(results, sourceList, maxNum, 0, stack);
return results;
}
private static void permutateFunction(ArrayList<ArrayList<Integer>> results, ArrayList<Integer> sourceList, int targetNumber, int curNumber, Stack<Integer> stack) {
if(curNumber == targetNumber) {
// System.out.println(stack);
results.add(new ArrayList<>(stack));
return;
}
for (int j : sourceList) {
if (!stack.contains(j)) {
stack.add(j);
permutateFunction(results, sourceList, targetNumber, curNumber + 1, stack);
stack.pop();
}
}
}
如何使用sparql设置多个节点并求出通过每个节点的最短路径?可以使用Cypher实现这个功能。但是,我不知道如何通过Sparql实现它,以便我可以在Jena中查询路径。
非常感谢您的热心帮助!最后我修改了jena(findShortestPath)的javaapi函数来实现这个功能
首先,为了实现任意两个节点之间的最短路径查询(不考虑关系方向),我修改了jena的javaapi函数(findShortestPath ) 如下。
private JSONObject findShortestPath(Model m, Resource start, Resource end) {
int shortestLength = 20;
JSONObject pathResult = new JSONObject();
pathResult.put("length", shortestLength);
pathResult.put("paths", new JSONArray());
List<OntTools.Path> bfs = new LinkedList<>();
ArrayList<ExtendedIterator> nodeStatementIter = new ArrayList<>();
nodeStatementIter.add(m.listStatements(start, null, (RDFNode)null));
nodeStatementIter.add(m.listStatements(null, null, start));
for (var iter : nodeStatementIter) {
while(iter.hasNext()) {
Statement statement = (Statement) iter.next();
if (! statement.getObject().isLiteral())
bfs.add((new OntTools.Path()).append(statement));
}
}
nodeStatementIter.clear();
HashSet<Resource> passedNodes = new HashSet<>();
passedNodes.add(start);
while(!bfs.isEmpty()) {
OntTools.Path candidate = bfs.remove(0);
if (candidate.size() > shortestLength) {
break;
}
Resource subject = candidate.getStatement(candidate.size() - 1).getSubject();
Resource object = candidate.getStatement(candidate.size() - 1).getObject().isResource() ? candidate.getStatement(candidate.size() - 1).getObject().asResource() : null;
ArrayList<Resource> resources = new ArrayList<>();
resources.add(subject);
if (object != null) {
resources.add(object);
}
if (resources.contains(end)) {
// solution = candidate;
shortestLength = candidate.size();
pathResult.put("length", shortestLength);
pathResult.getJSONArray("paths").add(pathToTriples(candidate));
} else {
for (Resource resource : resources) {
if (! passedNodes.contains(resource)) {
nodeStatementIter.add(m.listStatements(resource, null, (RDFNode)null));
nodeStatementIter.add(m.listStatements(null, null, resource));
passedNodes.add(resource);
for (var iter : nodeStatementIter) {
while(iter.hasNext()) {
// Statement link = (Statement)iter.next();
Statement statement = (Statement) iter.next();
if (!candidate.contains(statement))
bfs.add(candidate.append(statement));
}
}
nodeStatementIter.clear();
}
}
}
}
if (pathResult.getJSONArray("paths").size() == 0) {
pathResult.put("length", Integer.MAX_VALUE);
}
return pathResult;
}
在这个函数中,另一个函数用于将路径转换为三元组
private ArrayList<ArrayList<String>> pathToTriples(OntTools.Path path) {
ListIterator<Statement> statementListIterator = path.listIterator();
ArrayList<ArrayList<String>> statements = new ArrayList<>();
while (statementListIterator.hasNext()) {
Statement next = statementListIterator.next();
statements.add(new ArrayList<>(){{
add(next.getSubject().toString());
add(next.getPredicate().toString());
add(next.getObject().toString());
}});
}
return statements;
}
最后,为了实现通过多个指定节点的最短路径查询,我列举了节点序列的可能组合。然后,计算每个组合每两个节点之间的最短路径之和,select所有总长度最短的组合作为最终结果。
public JSONObject findShortestPathBetweenMultipleNodes(Model m, List<Resource> nodes) {
ArrayList<ArrayList<JSONArray>> nodePairPathMatrix = new ArrayList<>();
ArrayList<ArrayList<Integer>> nodePairLengthMatrix = new ArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
nodePairPathMatrix.add(new ArrayList<>(){{
for (int j = 0; j < nodes.size(); j++) {
add(null);
}
}});
nodePairLengthMatrix.add(new ArrayList<>(){{
for (int j = 0; j < nodes.size(); j++) {
add(Integer.MAX_VALUE);
}
}});
}
for (int i = 0; i < nodes.size()-1; i++) {
for (int j = i+1; j < nodes.size(); j++) {
Resource source = nodes.get(i);
Resource target = nodes.get(j);
JSONObject shortestPath = findShortestPath(m, source, target);
JSONArray paths = shortestPath.getJSONArray("paths");
nodePairPathMatrix.get(i).set(j, paths);
nodePairPathMatrix.get(j).set(i, paths);
nodePairLengthMatrix.get(i).set(j, shortestPath.getInteger("length"));
nodePairLengthMatrix.get(j).set(i, shortestPath.getInteger("length"));
}
}
ArrayList<ArrayList<Integer>> permutationsOfNodes = permutationByMaxNum(nodes.size());
Integer minLength = Integer.MAX_VALUE;
ArrayList<ArrayList<JSONArray>> nodePairPathListWithMinLength = new ArrayList<>();
for (var permutation : permutationsOfNodes){
Integer length = 0;
ArrayList<JSONArray> nodePairPathList = new ArrayList<>();
for (int permutationItemIndex = 1; permutationItemIndex < permutation.size(); permutationItemIndex++) {
int end = permutation.get(permutationItemIndex);
int start = permutation.get(permutationItemIndex-1);
Integer curNodePairLength = nodePairLengthMatrix.get(start).get(end);
if (curNodePairLength.equals(Integer.MAX_VALUE) || nodePairLengthMatrix.get(start).get(end).equals(Integer.MAX_VALUE)){
length = Integer.MAX_VALUE;
break;
}
length += nodePairLengthMatrix.get(start).get(end);
nodePairPathList.add(nodePairPathMatrix.get(start).get(end));
}
if (length < minLength) {
minLength = length;
nodePairPathListWithMinLength.clear();
nodePairPathListWithMinLength.add(nodePairPathList);
} else if (length.equals(minLength)) {
nodePairPathListWithMinLength.add(nodePairPathList);
}
}
Set<List<String>> tripleResults = new HashSet<>();
for (var equalTotalLengthNodePairPathListWithMinLength : nodePairPathListWithMinLength) {
for (var nodePairPathList : equalTotalLengthNodePairPathListWithMinLength) {
for (int i = 0; i < nodePairPathList.size(); i++) {
JSONArray nodePairPathWithEqualLength = nodePairPathList.getJSONArray(i);
for (int j = 0; j < nodePairPathWithEqualLength.size(); j++) {
JSONArray nodePairPath = nodePairPathWithEqualLength.getJSONArray(j);
List<String> triple = nodePairPath.toJavaList(String.class);
tripleResults.add(triple);
}
}
}
}
JSONObject result = new JSONObject();
result.put("triples", tripleResults);
result.put("length", minLength);
return result;
}
节点的排列由以下函数生成。
private ArrayList<ArrayList<Integer>> permutationByMaxNum (int maxNum) {
Stack<Integer> stack = new Stack<>();
ArrayList<ArrayList<Integer>> results = new ArrayList<>();
ArrayList<Integer> sourceList = new ArrayList<>();
for (int i = 0; i < maxNum; i++) {
sourceList.add(i);
}
permutateFunction(results, sourceList, maxNum, 0, stack);
return results;
}
private static void permutateFunction(ArrayList<ArrayList<Integer>> results, ArrayList<Integer> sourceList, int targetNumber, int curNumber, Stack<Integer> stack) {
if(curNumber == targetNumber) {
// System.out.println(stack);
results.add(new ArrayList<>(stack));
return;
}
for (int j : sourceList) {
if (!stack.contains(j)) {
stack.add(j);
permutateFunction(results, sourceList, targetNumber, curNumber + 1, stack);
stack.pop();
}
}
}