// 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.tinkerpop.optimize;
import com.google.common.collect.Iterables;
import org.janusgraph.core.BaseVertexQuery;
import org.janusgraph.core.JanusGraphElement;
import org.janusgraph.core.JanusGraphMultiVertexQuery;
import org.janusgraph.core.JanusGraphVertex;
import org.janusgraph.core.JanusGraphVertexQuery;
import org.janusgraph.graphdb.query.BaseQuery;
import org.janusgraph.graphdb.query.Query;
import org.janusgraph.graphdb.query.JanusGraphPredicate;
import org.janusgraph.graphdb.query.profile.QueryProfiler;
import org.janusgraph.graphdb.query.vertex.BasicVertexCentricQueryBuilder;
import org.janusgraph.graphdb.tinkerpop.profile.TP3ProfileWrapper;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Profiling;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class JanusGraphVertexStep<E extends Element> extends VertexStep<E> implements HasStepFolder<Vertex, E>, Profiling, MultiQueriable<Vertex,E> {
public JanusGraphVertexStep(VertexStep<E> originalStep) {
super(originalStep.getTraversal(), originalStep.getReturnClass(), originalStep.getDirection(), originalStep.getEdgeLabels());
originalStep.getLabels().forEach(this::addLabel);
this.hasContainers = new ArrayList<>();
this.limit = Query.NO_LIMIT;
}
private boolean initialized = false;
private boolean useMultiQuery = false;
private Map<JanusGraphVertex, Iterable<? extends JanusGraphElement>> multiQueryResults = null;
private QueryProfiler queryProfiler = QueryProfiler.NO_OP;
@Override
public void setUseMultiQuery(boolean useMultiQuery) {
this.useMultiQuery = useMultiQuery;
}
public <Q extends BaseVertexQuery> Q makeQuery(Q query) {
query.labels(getEdgeLabels());
query.direction(getDirection());
for (HasContainer condition : hasContainers) {
query.has(condition.getKey(), JanusGraphPredicate.Converter.convert(condition.getBiPredicate()), condition.getValue());
}
for (OrderEntry order : orders) query.orderBy(order.key, order.order);
if (limit != BaseQuery.NO_LIMIT) query.limit(limit);
((BasicVertexCentricQueryBuilder) query).profiler(queryProfiler);
return query;
}
@SuppressWarnings("deprecation")
private void initialize() {
assert !initialized;
initialized = true;
if (useMultiQuery) {
if (!starts.hasNext()) throw FastNoSuchElementException.instance();
JanusGraphMultiVertexQuery mquery = JanusGraphTraversalUtil.getTx(traversal).multiQuery();
List<Traverser.Admin<Vertex>> vertices = new ArrayList<>();
starts.forEachRemaining(v -> {
vertices.add(v);
mquery.addVertex(v.get());
});
starts.add(vertices.iterator());
assert vertices.size() > 0;
makeQuery(mquery);
multiQueryResults = (Vertex.class.isAssignableFrom(getReturnClass())) ? mquery.vertices() : mquery.edges();
}
}
@Override
protected Traverser.Admin<E> processNextStart() {
if (!initialized) initialize();
return super.processNextStart();
}
@Override
protected Iterator<E> flatMap(final Traverser.Admin<Vertex> traverser) {
if (useMultiQuery) {
assert multiQueryResults != null;
return (Iterator<E>) multiQueryResults.get(traverser.get()).iterator();
} else {
JanusGraphVertexQuery query = makeQuery((JanusGraphTraversalUtil.getJanusGraphVertex(traverser)).query());
return (Vertex.class.isAssignableFrom(getReturnClass())) ? query.vertices().iterator() : query.edges().iterator();
}
}
@Override
public void reset() {
super.reset();
this.initialized = false;
}
@Override
public JanusGraphVertexStep<E> clone() {
final JanusGraphVertexStep<E> clone = (JanusGraphVertexStep<E>) super.clone();
clone.initialized = false;
return clone;
}
/*
===== HOLDER =====
*/
private final List<HasContainer> hasContainers;
private int limit = BaseQuery.NO_LIMIT;
private List<OrderEntry> orders = new ArrayList<>();
@Override
public void addAll(Iterable<HasContainer> has) {
HasStepFolder.splitAndP(hasContainers, has);
}
@Override
public void orderBy(String key, Order order) {
orders.add(new OrderEntry(key, order));
}
@Override
public void setLimit(int limit) {
this.limit = limit;
}
@Override
public int getLimit() {
return this.limit;
}
@Override
public String toString() {
return this.hasContainers.isEmpty() ? super.toString() : StringFactory.stepString(this, this.hasContainers);
}
@Override
public void setMetrics(MutableMetrics metrics) {
queryProfiler = new TP3ProfileWrapper(metrics);
}
}