// 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 com.google.common.base.Preconditions; import org.janusgraph.core.JanusGraphEdge; import org.janusgraph.core.JanusGraphVertex; import org.janusgraph.core.JanusGraphVertexProperty; import org.janusgraph.graphdb.vertices.PreloadedVertex; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; import org.apache.tinkerpop.gremlin.process.computer.MessageScope; import org.apache.tinkerpop.gremlin.process.computer.Messenger; import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram; import org.apache.tinkerpop.gremlin.structure.Edge; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import java.util.*; import java.util.function.BiFunction; import java.util.stream.Stream; /** * @author Matthias Broecheler (me@matthiasb.com) */ class VertexMemoryHandler<M> implements PreloadedVertex.PropertyMixing, Messenger<M> { protected final FulgoraVertexMemory<M> vertexMemory; private final PreloadedVertex vertex; protected final long vertexId; private boolean inExecute; VertexMemoryHandler(FulgoraVertexMemory<M> vertexMemory, PreloadedVertex vertex) { assert vertex!=null && vertexMemory!=null; this.vertexMemory = vertexMemory; this.vertex = vertex; this.vertexId = vertexMemory.getCanonicalId(vertex.longId()); this.inExecute = false; } void removeKey(String key) { vertexMemory.setProperty(vertexId,key,null); } <V> JanusGraphVertexProperty<V> constructProperty(String key, V value) { assert key!=null && value!=null; return new FulgoraVertexProperty<V>(this,vertex,key,value); } @Override public <V> Iterator<VertexProperty<V>> properties(String... keys) { final Set<String> memoryKeys = vertexMemory.getMemoryKeys(); if (memoryKeys.isEmpty()) return Collections.emptyIterator(); if (keys==null || keys.length==0) { keys = memoryKeys.stream().filter(k -> !k.equals(TraversalVertexProgram.HALTED_TRAVERSERS)).toArray(String[]::new); } final List<VertexProperty<V>> result = new ArrayList<>(Math.min(keys.length,memoryKeys.size())); for (String key : keys) { if (!supports(key)) continue; V value = vertexMemory.getProperty(vertexId,key); if (value!=null) result.add(constructProperty(key,value)); } return result.iterator(); } @Override public boolean supports(String key) { return vertexMemory.getMemoryKeys().contains(key); } @Override public <V> JanusGraphVertexProperty<V> property(VertexProperty.Cardinality cardinality, String key, V value) { if (!supports(key)) throw GraphComputer.Exceptions.providedKeyIsNotAnElementComputeKey(key); Preconditions.checkArgument(value != null); Preconditions.checkArgument(cardinality== VertexProperty.Cardinality.single,"Only single cardinality is supported, provided: %s",cardinality); vertexMemory.setProperty(vertexId, key, value); return constructProperty(key,value); } public boolean isInExecute() { return inExecute; } public void setInExecute(boolean inExecute) { this.inExecute = inExecute; } public Stream<M> receiveMessages(MessageScope messageScope) { if (messageScope instanceof MessageScope.Global) { M message = vertexMemory.getMessage(vertexId,messageScope); if (message == null) return Stream.empty(); else return Stream.of(message); } else { final MessageScope.Local<M> localMessageScope = (MessageScope.Local) messageScope; final Traversal<Vertex, Edge> reverseIncident = FulgoraUtil.getReverseElementTraversal(localMessageScope,vertex,vertex.tx()); final BiFunction<M,Edge,M> edgeFct = localMessageScope.getEdgeFunction(); return IteratorUtils.stream(reverseIncident) .map(e -> { M msg = vertexMemory.getMessage(vertexMemory.getCanonicalId(((JanusGraphEdge) e).otherVertex(vertex).longId()), localMessageScope); return msg == null ? null : edgeFct.apply(msg, e); }) .filter(m -> m != null); } } @Override public Iterator<M> receiveMessages() { Stream<M> combinedStream = Stream.empty(); for (MessageScope scope : vertexMemory.getPreviousScopes()) { combinedStream = Stream.concat(receiveMessages(scope),combinedStream); } return combinedStream.iterator(); } @Override public void sendMessage(MessageScope messageScope, M m) { if (messageScope instanceof MessageScope.Local) { vertexMemory.sendMessage(vertexId, m, messageScope); } else { ((MessageScope.Global) messageScope).vertices().forEach(v -> { long vertexId; if (v instanceof JanusGraphVertex) vertexId=((JanusGraphVertex)v).longId(); else vertexId = (Long)v.id(); vertexMemory.sendMessage(vertexMemory.getCanonicalId(vertexId), m, messageScope); }); } } static class Partition<M> extends VertexMemoryHandler<M> { Partition(FulgoraVertexMemory<M> vertexMemory, PreloadedVertex vertex) { super(vertexMemory, vertex); } @Override public Stream<M> receiveMessages(MessageScope messageScope) { if (messageScope instanceof MessageScope.Global) { return super.receiveMessages(messageScope); } else { final MessageScope.Local<M> localMessageScope = (MessageScope.Local) messageScope; M aggregateMsg = vertexMemory.getAggregateMessage(vertexId,localMessageScope); if (aggregateMsg==null) return Stream.empty(); else return Stream.of(aggregateMsg); } } } }