// 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.query.vertex; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import org.janusgraph.core.*; import org.janusgraph.diskstorage.keycolumnvalue.SliceQuery; import org.janusgraph.graphdb.internal.InternalVertex; import org.janusgraph.graphdb.internal.RelationCategory; import org.janusgraph.graphdb.query.BackendQueryHolder; import org.janusgraph.graphdb.query.profile.QueryProfiler; import org.janusgraph.graphdb.transaction.StandardJanusGraphTx; import org.apache.tinkerpop.gremlin.structure.Vertex; import java.util.*; /** * Implementation of {@link JanusGraphMultiVertexQuery} that extends {@link BasicVertexCentricQueryBuilder} * for all the query building and optimization and adds only the execution logic in * {@link #execute(org.janusgraph.graphdb.internal.RelationCategory, BasicVertexCentricQueryBuilder.ResultConstructor)}. * </p> * All other methods just prepare or transform that result set to fit the particular method semantics. * * @author Matthias Broecheler (me@matthiasb.com) */ public class MultiVertexCentricQueryBuilder extends BasicVertexCentricQueryBuilder<MultiVertexCentricQueryBuilder> implements JanusGraphMultiVertexQuery<MultiVertexCentricQueryBuilder> { /** * The base vertices of this query */ private final Set<InternalVertex> vertices; public MultiVertexCentricQueryBuilder(final StandardJanusGraphTx tx) { super(tx); vertices = Sets.newHashSet(); } @Override protected MultiVertexCentricQueryBuilder getThis() { return this; } /* --------------------------------------------------------------- * Query Construction * --------------------------------------------------------------- */ @Override public JanusGraphMultiVertexQuery addVertex(Vertex vertex) { assert vertex != null; assert vertex instanceof InternalVertex; vertices.add(((InternalVertex)vertex).it()); return this; } @Override public JanusGraphMultiVertexQuery addAllVertices(Collection<? extends Vertex> vertices) { for (Vertex v : vertices) addVertex(v); return this; } /* --------------------------------------------------------------- * Query Execution * --------------------------------------------------------------- */ /** * Constructs the BaseVertexCentricQuery through {@link BasicVertexCentricQueryBuilder#constructQuery(org.janusgraph.graphdb.internal.RelationCategory)}. * If the query asks for an implicit key, the resulting map is computed and returned directly. * If the query is empty, a map that maps each vertex to an empty list is returned. * Otherwise, the query is executed for all vertices through the transaction which will effectively * pre-load the return result sets into the associated {@link org.janusgraph.graphdb.vertices.CacheVertex} or * don't do anything at all if the vertex is new (and hence no edges in the storage backend). * After that, a map is constructed that maps each vertex to the corresponding VertexCentricQuery and wrapped * into a QueryProcessor. Hence, upon iteration the query will be executed like any other VertexCentricQuery * with the performance difference that the SliceQueries will have already been preloaded and not further * calls to the storage backend are needed. * * @param returnType * @return */ protected<Q> Map<JanusGraphVertex,Q> execute(RelationCategory returnType, ResultConstructor<Q> resultConstructor) { Preconditions.checkArgument(!vertices.isEmpty(), "Need to add at least one vertex to query"); Map<JanusGraphVertex, Q> result = new HashMap<JanusGraphVertex, Q>(vertices.size()); BaseVertexCentricQuery bq = super.constructQuery(returnType); profiler.setAnnotation(QueryProfiler.MULTIQUERY_ANNOTATION,true); profiler.setAnnotation(QueryProfiler.NUMVERTICES_ANNOTATION,vertices.size()); if (!bq.isEmpty()) { for (BackendQueryHolder<SliceQuery> sq : bq.getQueries()) { Set<InternalVertex> adjVertices = Sets.newHashSet(vertices); for (InternalVertex v : vertices) { if (isPartitionedVertex(v)) { profiler.setAnnotation(QueryProfiler.PARTITIONED_VERTEX_ANNOTATION,true); adjVertices.remove(v); adjVertices.addAll(allRequiredRepresentatives(v)); } } //Overwrite with more accurate size accounting for partitioned vertices profiler.setAnnotation(QueryProfiler.NUMVERTICES_ANNOTATION,adjVertices.size()); tx.executeMultiQuery(adjVertices, sq.getBackendQuery(), sq.getProfiler()); } for (InternalVertex v : vertices) { result.put(v, resultConstructor.getResult(v, bq)); } } else { for (JanusGraphVertex v : vertices) result.put(v, resultConstructor.emptyResult()); } return result; } public Map<JanusGraphVertex, Iterable<? extends JanusGraphRelation>> executeImplicitKeyQuery() { return new HashMap<JanusGraphVertex, Iterable<? extends JanusGraphRelation>>(vertices.size()){{ for (InternalVertex v : vertices ) put(v,executeImplicitKeyQuery(v)); }}; } @Override public Map<JanusGraphVertex, Iterable<JanusGraphEdge>> edges() { return (Map) execute(RelationCategory.EDGE, new RelationConstructor()); } @Override public Map<JanusGraphVertex, Iterable<JanusGraphVertexProperty>> properties() { return (Map)(isImplicitKeyQuery(RelationCategory.PROPERTY)? executeImplicitKeyQuery(): execute(RelationCategory.PROPERTY, new RelationConstructor())); } @Override public Map<JanusGraphVertex, Iterable<JanusGraphRelation>> relations() { return (Map)(isImplicitKeyQuery(RelationCategory.RELATION)? executeImplicitKeyQuery(): execute(RelationCategory.RELATION, new RelationConstructor())); } @Override public Map<JanusGraphVertex, Iterable<JanusGraphVertex>> vertices() { return execute(RelationCategory.EDGE, new VertexConstructor()); } @Override public Map<JanusGraphVertex, VertexList> vertexIds() { return execute(RelationCategory.EDGE, new VertexIdConstructor()); } }