// 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 org.janusgraph.core.JanusGraphRelation;
import org.janusgraph.diskstorage.keycolumnvalue.SliceQuery;
import org.janusgraph.graphdb.internal.OrderList;
import org.janusgraph.graphdb.query.BackendQueryHolder;
import org.janusgraph.graphdb.query.BaseQuery;
import org.janusgraph.graphdb.query.QueryUtil;
import org.janusgraph.graphdb.query.condition.Condition;
import org.janusgraph.graphdb.query.condition.FixedCondition;
import org.janusgraph.graphdb.query.profile.ProfileObservable;
import org.janusgraph.graphdb.query.profile.QueryProfiler;
import org.apache.tinkerpop.gremlin.structure.Direction;
import java.util.ArrayList;
import java.util.List;
/**
* The base implementation for {@link VertexCentricQuery} which does not yet contain a reference to the
* base vertex of the query. This query is constructed by {@link BasicVertexCentricQueryBuilder#constructQuery(org.janusgraph.graphdb.internal.RelationCategory)}
* and then later extended by single or multi-vertex query which add the vertex to the query.
* </p>
* This class override many methods in {@link org.janusgraph.graphdb.query.ElementQuery} - check there
* for a description.
*
* @author Matthias Broecheler (me@matthiasb.com)
*/
public class BaseVertexCentricQuery extends BaseQuery implements ProfileObservable {
/**
* The condition of this query in QNF
*/
protected final Condition<JanusGraphRelation> condition;
/**
* The individual component {@link SliceQuery} of this query. This query is considered an OR
* of the individual components (possibly filtered by the condition if not fitted).
*/
protected final List<BackendQueryHolder<SliceQuery>> queries;
/**
* The result order of this query (if any)
*/
private final OrderList orders;
/**
* The direction condition of this query. This is duplicated from the condition for efficiency reasons.
*/
protected final Direction direction;
public BaseVertexCentricQuery(Condition<JanusGraphRelation> condition, Direction direction,
List<BackendQueryHolder<SliceQuery>> queries, OrderList orders,
int limit) {
super(limit);
Preconditions.checkArgument(condition != null && queries != null && direction != null);
Preconditions.checkArgument(QueryUtil.isQueryNormalForm(condition) && limit>=0);
this.condition = condition;
this.queries = queries;
this.orders = orders;
this.direction=direction;
}
protected BaseVertexCentricQuery(BaseVertexCentricQuery query) {
this(query.getCondition(), query.getDirection(), query.getQueries(), query.getOrders(), query.getLimit());
}
/**
* Construct an empty query
*/
protected BaseVertexCentricQuery() {
this(new FixedCondition<JanusGraphRelation>(false), Direction.BOTH, new ArrayList<BackendQueryHolder<SliceQuery>>(0),OrderList.NO_ORDER,0);
}
public static BaseVertexCentricQuery emptyQuery() {
return new BaseVertexCentricQuery();
}
public Condition<JanusGraphRelation> getCondition() {
return condition;
}
public OrderList getOrders() {
return orders;
}
public Direction getDirection() {
return direction;
}
protected List<BackendQueryHolder<SliceQuery>> getQueries() {
return queries;
}
public boolean isEmpty() {
return getLimit()<=0;
}
public int numSubQueries() {
return queries.size();
}
/**
* A query is considered 'simple' if it is comprised of just one sub-query and that query
* is fitted (i.e. does not require an in-memory filtering).
* @return
*/
public boolean isSimple() {
return queries.size()==1 && queries.get(0).isFitted() && queries.get(0).isSorted();
}
public BackendQueryHolder<SliceQuery> getSubQuery(int position) {
return queries.get(position);
}
public boolean matches(JanusGraphRelation relation) {
return condition.evaluate(relation);
}
@Override
public String toString() {
String s = "["+condition.toString()+"]";
if (hasLimit()) s+=":"+getLimit();
return s;
}
@Override
public void observeWith(QueryProfiler profiler) {
profiler.setAnnotation(QueryProfiler.CONDITION_ANNOTATION,condition);
profiler.setAnnotation(QueryProfiler.ORDERS_ANNOTATION,orders);
if (hasLimit()) profiler.setAnnotation(QueryProfiler.LIMIT_ANNOTATION,getLimit());
queries.forEach(bqh -> bqh.observeWith(profiler));
}
}