blob: ac98872c4165c6095e95223ced07cc509dc35893 [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 static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import org.apache.hugegraph.backend.id.Id;
import org.apache.hugegraph.structure.HugeEdge;
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.EdgeRecord;
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
import org.apache.hugegraph.traversal.algorithm.strategy.TraverseStrategy;
import org.apache.tinkerpop.gremlin.structure.Edge;
public abstract class PathTraverser {
protected final HugeTraverser traverser;
protected final long capacity;
protected final long limit;
protected int stepCount;
protected int totalSteps; // TODO: delete or implement abstract method
protected Map<Id, List<HugeTraverser.Node>> sources;
protected Map<Id, List<HugeTraverser.Node>> sourcesAll;
protected Map<Id, List<HugeTraverser.Node>> targets;
protected Map<Id, List<HugeTraverser.Node>> targetsAll;
protected Map<Id, List<HugeTraverser.Node>> newVertices;
protected Set<HugeTraverser.Path> paths;
protected TraverseStrategy traverseStrategy;
protected EdgeRecord edgeResults;
public PathTraverser(HugeTraverser traverser, TraverseStrategy strategy,
Collection<Id> sources, Collection<Id> targets,
long capacity, long limit, boolean concurrent) {
this.traverser = traverser;
this.traverseStrategy = strategy;
this.capacity = capacity;
this.limit = limit;
this.stepCount = 0;
this.sources = this.newMultiValueMap();
this.sourcesAll = this.newMultiValueMap();
this.targets = this.newMultiValueMap();
this.targetsAll = this.newMultiValueMap();
for (Id id : sources) {
this.addNode(this.sources, id, new HugeTraverser.Node(id));
}
for (Id id : targets) {
this.addNode(this.targets, id, new HugeTraverser.Node(id));
}
this.sourcesAll.putAll(this.sources);
this.targetsAll.putAll(this.targets);
this.paths = this.newPathSet();
this.edgeResults = new EdgeRecord(concurrent);
}
public void forward() {
EdgeStep currentStep = this.nextStep(true);
if (currentStep == null) {
return;
}
this.beforeTraverse(true);
// Traversal vertices of previous level
this.traverseOneLayer(this.sources, currentStep, this::forward);
this.afterTraverse(currentStep, true);
}
public void backward() {
EdgeStep currentStep = this.nextStep(false);
if (currentStep == null) {
return;
}
this.beforeTraverse(false);
currentStep.swithDirection();
// Traversal vertices of previous level
this.traverseOneLayer(this.targets, currentStep, this::backward);
currentStep.swithDirection();
this.afterTraverse(currentStep, false);
}
public abstract EdgeStep nextStep(boolean forward);
public void beforeTraverse(boolean forward) {
this.clearNewVertices();
}
public void traverseOneLayer(Map<Id, List<HugeTraverser.Node>> vertices,
EdgeStep step,
BiConsumer<Id, EdgeStep> consumer) {
this.traverseStrategy.traverseOneLayer(vertices, step, consumer);
}
public void afterTraverse(EdgeStep step, boolean forward) {
this.reInitCurrentStepIfNeeded(step, forward);
this.stepCount++;
}
private void forward(Id v, EdgeStep step) {
this.traverseOne(v, step, true);
}
private void backward(Id v, EdgeStep step) {
this.traverseOne(v, step, false);
}
private void traverseOne(Id v, EdgeStep step, boolean forward) {
if (this.reachLimit()) {
return;
}
Iterator<Edge> edges = this.traverser.edgesOfVertex(v, step);
while (edges.hasNext()) {
HugeEdge edge = (HugeEdge) edges.next();
Id target = edge.id().otherVertexId();
this.traverser.edgeIterCounter.addAndGet(1L);
this.edgeResults.addEdge(v, target, edge);
this.processOne(v, target, forward);
}
this.traverser.vertexIterCounter.addAndGet(1L);
}
private void processOne(Id source, Id target, boolean forward) {
if (forward) {
this.processOneForForward(source, target);
} else {
this.processOneForBackward(source, target);
}
}
protected abstract void processOneForForward(Id source, Id target);
protected abstract void processOneForBackward(Id source, Id target);
protected abstract void reInitCurrentStepIfNeeded(EdgeStep step,
boolean forward);
public void clearNewVertices() {
this.newVertices = this.newMultiValueMap();
}
public void addNodeToNewVertices(Id id, HugeTraverser.Node node) {
this.addNode(this.newVertices, id, node);
}
public Map<Id, List<HugeTraverser.Node>> newMultiValueMap() {
return this.traverseStrategy.newMultiValueMap();
}
public Set<HugeTraverser.Path> newPathSet() {
return this.traverseStrategy.newPathSet();
}
public void addNode(Map<Id, List<HugeTraverser.Node>> vertices, Id id,
HugeTraverser.Node node) {
this.traverseStrategy.addNode(vertices, id, node);
}
public void addNewVerticesToAll(Map<Id, List<HugeTraverser.Node>> targets) {
this.traverseStrategy.addNewVerticesToAll(this.newVertices, targets);
}
public Set<HugeTraverser.Path> paths() {
return this.paths;
}
public int pathCount() {
return this.paths.size();
}
protected boolean finished() {
return this.stepCount >= this.totalSteps || this.reachLimit();
}
protected boolean reachLimit() {
HugeTraverser.checkCapacity(this.capacity, this.accessedNodes(),
"template paths");
return this.limit != NO_LIMIT && this.pathCount() >= this.limit;
}
protected int accessedNodes() {
int size = 0;
for (List<HugeTraverser.Node> value : this.sourcesAll.values()) {
size += value.size();
}
for (List<HugeTraverser.Node> value : this.targetsAll.values()) {
size += value.size();
}
return size;
}
}