blob: 949014e1927e706608cad1d28c20bdcb0c9638ff [file] [log] [blame]
/*
* 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.traversal.algorithm;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.hugegraph.HugeGraph;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.structure.HugeVertex;
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
import org.apache.hugegraph.traversal.algorithm.steps.RepeatEdgeStep;
import org.apache.hugegraph.traversal.algorithm.strategy.TraverseStrategy;
import org.apache.hugegraph.util.E;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;
public class TemplatePathsTraverser extends HugeTraverser {
public TemplatePathsTraverser(HugeGraph graph) {
super(graph);
}
public WrappedPathSet templatePaths(Iterator<Vertex> sources,
Iterator<Vertex> targets,
List<RepeatEdgeStep> steps,
boolean withRing, long capacity,
long limit) {
checkCapacity(capacity);
checkLimit(limit);
List<Id> sourceList = newList();
while (sources.hasNext()) {
sourceList.add(((HugeVertex) sources.next()).id());
}
int sourceSize = sourceList.size();
E.checkState(sourceSize >= 1 && sourceSize <= MAX_VERTICES,
"The number of source vertices must in [1, %s], " +
"but got: %s", MAX_VERTICES, sourceList.size());
List<Id> targetList = newList();
while (targets.hasNext()) {
targetList.add(((HugeVertex) targets.next()).id());
}
int targetSize = targetList.size();
E.checkState(targetSize >= 1 && targetSize <= MAX_VERTICES,
"The number of target vertices must in [1, %s], " +
"but got: %s", MAX_VERTICES, sourceList.size());
int totalSteps = 0;
for (RepeatEdgeStep step : steps) {
totalSteps += step.maxTimes();
}
boolean concurrent = totalSteps >= this.concurrentDepth();
TraverseStrategy strategy = TraverseStrategy.create(
concurrent, this.graph());
Traverser traverser = new Traverser(this, strategy,
sourceList, targetList, steps,
withRing, capacity, limit, concurrent);
do {
// Forward
traverser.forward();
if (traverser.finished()) {
Set<Path> paths = traverser.paths();
return new WrappedPathSet(paths, traverser.edgeResults.getEdges(paths));
}
// Backward
traverser.backward();
if (traverser.finished()) {
Set<Path> paths = traverser.paths();
return new WrappedPathSet(paths, traverser.edgeResults.getEdges(paths));
}
} while (true);
}
private static class Traverser extends PathTraverser {
protected final List<RepeatEdgeStep> steps;
protected boolean withRing;
protected int sourceIndex;
protected int targetIndex;
protected boolean sourceFinishOneStep;
protected boolean targetFinishOneStep;
public Traverser(HugeTraverser traverser, TraverseStrategy strategy,
Collection<Id> sources, Collection<Id> targets,
List<RepeatEdgeStep> steps, boolean withRing,
long capacity, long limit, boolean concurrent) {
super(traverser, strategy, sources, targets, capacity, limit, concurrent);
this.steps = steps;
this.withRing = withRing;
for (RepeatEdgeStep step : steps) {
this.totalSteps += step.maxTimes();
}
this.sourceIndex = 0;
this.targetIndex = this.steps.size() - 1;
this.sourceFinishOneStep = false;
this.targetFinishOneStep = false;
}
@Override
public RepeatEdgeStep nextStep(boolean forward) {
return forward ? this.forwardStep() : this.backwardStep();
}
@Override
public void beforeTraverse(boolean forward) {
this.clearNewVertices();
this.reInitAllIfNeeded(forward);
}
@Override
public void afterTraverse(EdgeStep step, boolean forward) {
Map<Id, List<Node>> all = forward ? this.sourcesAll :
this.targetsAll;
this.addNewVerticesToAll(all);
this.reInitCurrentStepIfNeeded(step, forward);
this.stepCount++;
}
@Override
protected void processOneForForward(Id sourceV, Id targetV) {
for (Node source : this.sources.get(sourceV)) {
// If have loop, skip target
if (!this.withRing && source.contains(targetV)) {
continue;
}
// If cross point exists, path found, concat them
if (this.lastSuperStep() &&
this.targetsAll.containsKey(targetV)) {
for (Node target : this.targetsAll.get(targetV)) {
List<Id> path = joinPath(source, target, this.withRing);
if (!path.isEmpty()) {
this.paths.add(new Path(targetV, path));
if (this.reachLimit()) {
return;
}
}
}
}
// Add node to next start-nodes
this.addNodeToNewVertices(targetV, new Node(targetV, source));
}
}
@Override
protected void processOneForBackward(Id sourceV, Id targetV) {
for (Node source : this.targets.get(sourceV)) {
// If have loop, skip target
if (!this.withRing && source.contains(targetV)) {
continue;
}
// If cross point exists, path found, concat them
if (this.lastSuperStep() &&
this.sourcesAll.containsKey(targetV)) {
for (Node target : this.sourcesAll.get(targetV)) {
List<Id> path = joinPath(source, target, this.withRing);
if (!path.isEmpty()) {
Path newPath = new Path(targetV, path);
newPath.reverse();
this.paths.add(newPath);
if (this.reachLimit()) {
return;
}
}
}
}
// Add node to next start-nodes
this.addNodeToNewVertices(targetV, new Node(targetV, source));
}
}
private void reInitAllIfNeeded(boolean forward) {
if (forward) {
/*
* Re-init source all if last forward finished one super step
* and current step is not last super step
*/
if (this.sourceFinishOneStep && !this.lastSuperStep()) {
this.sourcesAll = this.newMultiValueMap();
this.sourceFinishOneStep = false;
}
} else {
/*
* Re-init target all if last forward finished one super step
* and current step is not last super step
*/
if (this.targetFinishOneStep && !this.lastSuperStep()) {
this.targetsAll = this.newMultiValueMap();
this.targetFinishOneStep = false;
}
}
}
@Override
protected void reInitCurrentStepIfNeeded(EdgeStep step,
boolean forward) {
RepeatEdgeStep currentStep = (RepeatEdgeStep) step;
currentStep.decreaseTimes();
if (forward) {
// Re-init sources
if (currentStep.remainTimes() > 0) {
this.sources = this.newVertices;
} else {
this.sources = this.sourcesAll;
this.sourceFinishOneStep = true;
}
} else {
// Re-init targets
if (currentStep.remainTimes() > 0) {
this.targets = this.newVertices;
} else {
this.targets = this.targetsAll;
this.targetFinishOneStep = true;
}
}
}
public RepeatEdgeStep forwardStep() {
RepeatEdgeStep currentStep = null;
// Find next step to backward
for (int i = 0; i < this.steps.size(); i++) {
RepeatEdgeStep step = this.steps.get(i);
if (step.remainTimes() > 0) {
currentStep = step;
this.sourceIndex = i;
break;
}
}
return currentStep;
}
public RepeatEdgeStep backwardStep() {
RepeatEdgeStep currentStep = null;
// Find next step to backward
for (int i = this.steps.size() - 1; i >= 0; i--) {
RepeatEdgeStep step = this.steps.get(i);
if (step.remainTimes() > 0) {
currentStep = step;
this.targetIndex = i;
break;
}
}
return currentStep;
}
public boolean lastSuperStep() {
return this.targetIndex == this.sourceIndex ||
this.targetIndex == this.sourceIndex + 1;
}
}
public static class WrappedPathSet {
private final Set<Path> paths;
private final Set<Edge> edges;
public WrappedPathSet(Set<Path> paths, Set<Edge> edges) {
this.paths = paths;
this.edges = edges;
}
public Set<Path> paths() {
return paths;
}
public Set<Edge> edges() {
return edges;
}
}
}