// Copyright 2017 JanusGraph Authors
//
// Licensed 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.janusgraph.graphdb.olap.computer;
import org.janusgraph.core.JanusGraph;
import org.janusgraph.core.JanusGraphVertex;
import org.janusgraph.diskstorage.EntryList;
import org.janusgraph.diskstorage.configuration.Configuration;
import org.janusgraph.diskstorage.keycolumnvalue.SliceQuery;
import org.janusgraph.diskstorage.keycolumnvalue.scan.ScanMetrics;
import org.janusgraph.graphdb.database.StandardJanusGraph;
import org.janusgraph.graphdb.database.idhandling.IDHandler;
import org.janusgraph.graphdb.idmanagement.IDManager;
import org.janusgraph.graphdb.internal.RelationCategory;
import org.janusgraph.graphdb.olap.QueryContainer;
import org.janusgraph.graphdb.olap.VertexJobConverter;
import org.janusgraph.graphdb.olap.VertexScanJob;
import org.janusgraph.graphdb.tinkerpop.optimize.JanusGraphVertexStep;
import org.janusgraph.graphdb.vertices.PreloadedVertex;
import org.apache.tinkerpop.gremlin.process.computer.MessageCombiner;
import org.apache.tinkerpop.gremlin.process.computer.MessageScope;
import org.apache.tinkerpop.gremlin.process.computer.VertexProgram;
import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import java.io.Closeable;
import java.util.Iterator;
import java.util.List;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class VertexProgramScanJob<M> implements VertexScanJob {
private final IDManager idManager;
private final FulgoraMemory memory;
private final FulgoraVertexMemory<M> vertexMemory;
private final VertexProgram<M> vertexProgram;
private final MessageCombiner<M> combiner;
private VertexProgramScanJob(IDManager idManager, FulgoraMemory memory,
FulgoraVertexMemory vertexMemory, VertexProgram<M> vertexProgram) {
this.idManager = idManager;
this.memory = memory;
this.vertexMemory = vertexMemory;
this.vertexProgram = vertexProgram;
this.combiner = FulgoraUtil.getMessageCombiner(vertexProgram);
}
@Override
public VertexProgramScanJob<M> clone() {
return new VertexProgramScanJob<>(this.idManager, this.memory, this.vertexMemory, this.vertexProgram
.clone());
}
@Override
public void workerIterationStart(JanusGraph graph, Configuration config, ScanMetrics metrics) {
vertexProgram.workerIterationStart(memory.asImmutable());
}
@Override
public void workerIterationEnd(ScanMetrics metrics) {
vertexProgram.workerIterationEnd(memory.asImmutable());
}
@Override
public void process(JanusGraphVertex vertex, ScanMetrics metrics) {
PreloadedVertex v = (PreloadedVertex)vertex;
long vertexId = v.longId();
VertexMemoryHandler<M> vh = new VertexMemoryHandler(vertexMemory,v);
vh.setInExecute(true);
v.setAccessCheck(PreloadedVertex.OPENSTAR_CHECK);
if (idManager.isPartitionedVertex(vertexId)) {
if (idManager.isCanonicalVertexId(vertexId)) {
EntryList results = v.getFromCache(SYSTEM_PROPS_QUERY);
if (results == null) results = EntryList.EMPTY_LIST;
vertexMemory.setLoadedProperties(vertexId,results);
}
for (MessageScope scope : vertexMemory.getPreviousScopes()) {
if (scope instanceof MessageScope.Local) {
M combinedMsg = null;
for (Iterator<M> msgIter = vh.receiveMessages(scope).iterator(); msgIter.hasNext(); ) {
M msg = msgIter.next();
if (combinedMsg==null) combinedMsg=msg;
else combinedMsg = combiner.combine(combinedMsg,msg);
}
if (combinedMsg!=null) vertexMemory.aggregateMessage(vertexId,combinedMsg,scope);
}
}
} else {
v.setPropertyMixing(vh);
vertexProgram.execute(v, vh, memory);
}
vh.setInExecute(false);
}
@Override
public void getQueries(QueryContainer queries) {
if (vertexProgram instanceof TraversalVertexProgram) {
//TraversalVertexProgram currently makes the assumption that the entire star-graph around a vertex
//is available (in-memory). Hence, this special treatment here.
//TODO: After TraversalVertexProgram is adjusted, remove this
queries.addQuery().direction(Direction.BOTH).edges();
return;
}
for (MessageScope scope : vertexMemory.getPreviousScopes()) {
if (scope instanceof MessageScope.Global) {
queries.addQuery().direction(Direction.BOTH).edges();
} else {
assert scope instanceof MessageScope.Local;
JanusGraphVertexStep<Vertex> startStep = FulgoraUtil.getReverseJanusGraphVertexStep((MessageScope.Local) scope,queries.getTransaction());
QueryContainer.QueryBuilder qb = queries.addQuery();
startStep.makeQuery(qb);
qb.edges();
}
}
}
public static<M> Executor getVertexProgramScanJob(StandardJanusGraph graph, FulgoraMemory memory,
FulgoraVertexMemory vertexMemory, VertexProgram<M> vertexProgram) {
VertexProgramScanJob<M> job = new VertexProgramScanJob<M>(graph.getIDManager(),memory,vertexMemory,vertexProgram);
return new Executor(graph,job);
}
//Query for all system properties+edges and normal properties
static final SliceQuery SYSTEM_PROPS_QUERY = new SliceQuery(
IDHandler.getBounds(RelationCategory.PROPERTY, true)[0],
IDHandler.getBounds(RelationCategory.PROPERTY,false)[1]);
public static class Executor extends VertexJobConverter implements Closeable {
private Executor(JanusGraph graph, VertexProgramScanJob job) {
super(graph, job);
open(this.graph.get().getConfiguration().getConfiguration());
}
private Executor(final Executor copy) {
super(copy);
open(this.graph.get().getConfiguration().getConfiguration());
}
@Override
public List<SliceQuery> getQueries() {
List<SliceQuery> queries = super.getQueries();
queries.add(SYSTEM_PROPS_QUERY);
return queries;
}
@Override
public void workerIterationStart(Configuration jobConfig, Configuration graphConfig, ScanMetrics metrics) {
job.workerIterationStart(graph.get(), jobConfig, metrics);
}
@Override
public void workerIterationEnd(ScanMetrics metrics) {
job.workerIterationEnd(metrics);
}
@Override
public Executor clone() { return new Executor(this); }
@Override
public void close() {
super.close();
}
}
}