// 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.graph; import com.google.common.base.Preconditions; import org.janusgraph.core.JanusGraphElement; import org.janusgraph.graphdb.internal.ElementCategory; import org.janusgraph.graphdb.internal.OrderList; import org.janusgraph.graphdb.query.BackendQueryHolder; import org.janusgraph.graphdb.query.BaseQuery; import org.janusgraph.graphdb.query.ElementQuery; 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.commons.collections.comparators.ComparableComparator; import org.apache.commons.lang.builder.HashCodeBuilder; import java.util.Comparator; /** * An executable {@link ElementQuery} for {@link org.janusgraph.core.JanusGraphQuery}. This query contains * the condition, and only one sub-query {@link JointIndexQuery}. * It also maintains the ordering for the query result which is needed by the {@link org.janusgraph.graphdb.query.QueryProcessor} * to correctly order the result. * * @author Matthias Broecheler (me@matthiasb.com) */ public class GraphCentricQuery extends BaseQuery implements ElementQuery<JanusGraphElement, JointIndexQuery>, ProfileObservable { /** * The condition of this query, the result set is the set of all elements in the graph for which this * condition evaluates to true. */ private final Condition<JanusGraphElement> condition; /** * The {@link JointIndexQuery} to execute against the indexing backends and index store. */ private final BackendQueryHolder<JointIndexQuery> indexQuery; /** * The result order of this query (if any) */ private final OrderList orders; /** * The type of element this query is asking for: vertex, edge, or property. */ private final ElementCategory resultType; public GraphCentricQuery(ElementCategory resultType, Condition<JanusGraphElement> condition, OrderList orders, BackendQueryHolder<JointIndexQuery> indexQuery, int limit) { super(limit); Preconditions.checkNotNull(condition); Preconditions.checkArgument(orders != null && orders.isImmutable()); Preconditions.checkArgument(QueryUtil.isQueryNormalForm(condition)); Preconditions.checkNotNull(resultType); Preconditions.checkNotNull(indexQuery); this.condition = condition; this.orders = orders; this.resultType = resultType; this.indexQuery = indexQuery; } public static final GraphCentricQuery emptyQuery(ElementCategory resultType) { Condition<JanusGraphElement> cond = new FixedCondition<JanusGraphElement>(false); return new GraphCentricQuery(resultType, cond, OrderList.NO_ORDER, new BackendQueryHolder<JointIndexQuery>(new JointIndexQuery(), true, false), 0); } public Condition<JanusGraphElement> getCondition() { return condition; } public ElementCategory getResultType() { return resultType; } public OrderList getOrder() { return orders; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("[").append(condition.toString()).append("]"); if (!orders.isEmpty()) b.append(getLimit()); if (hasLimit()) b.append("(").append(getLimit()).append(")"); b.append(":").append(resultType.toString()); return b.toString(); } @Override public int hashCode() { return new HashCodeBuilder().append(condition).append(resultType).append(orders).append(getLimit()).toHashCode(); } @Override public boolean equals(Object other) { if (this == other) return true; else if (other == null) return false; else if (!getClass().isInstance(other)) return false; GraphCentricQuery oth = (GraphCentricQuery) other; return resultType == oth.resultType && condition.equals(oth.condition) && orders.equals(oth.getOrder()) && getLimit() == oth.getLimit(); } @Override public boolean isEmpty() { return getLimit() <= 0; } @Override public int numSubQueries() { return 1; } @Override public BackendQueryHolder<JointIndexQuery> getSubQuery(int position) { if (position == 0) return indexQuery; else throw new IndexOutOfBoundsException(); } @Override public boolean isSorted() { return !orders.isEmpty(); } @Override public Comparator<JanusGraphElement> getSortOrder() { if (orders.isEmpty()) return new ComparableComparator(); else return orders; } @Override public boolean hasDuplicateResults() { return false; } @Override public boolean matches(JanusGraphElement element) { return condition.evaluate(element); } @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()); indexQuery.observeWith(profiler); } }