use of com.thinkaurelius.titan.graphdb.internal.InternalRelationType in project atlas by apache.
the class GraphCentricQueryBuilder method constructQuery.
public GraphCentricQuery constructQuery(final ElementCategory resultType) {
Preconditions.checkNotNull(resultType);
if (limit == 0)
return GraphCentricQuery.emptyQuery(resultType);
// Prepare constraints
And<TitanElement> conditions = QueryUtil.constraints2QNF(tx, constraints);
if (conditions == null)
return GraphCentricQuery.emptyQuery(resultType);
// Prepare orders
orders.makeImmutable();
if (orders.isEmpty())
orders = OrderList.NO_ORDER;
// Compile all indexes that cover at least one of the query conditions
final Set<IndexType> indexCandidates = new HashSet<>();
ConditionUtil.traversal(conditions, new Predicate<Condition<TitanElement>>() {
@Override
public boolean apply(@Nullable Condition<TitanElement> condition) {
if (condition instanceof PredicateCondition) {
RelationType type = ((PredicateCondition<RelationType, TitanElement>) condition).getKey();
Preconditions.checkArgument(type != null && type.isPropertyKey());
Iterables.addAll(indexCandidates, Iterables.filter(((InternalRelationType) type).getKeyIndexes(), new Predicate<IndexType>() {
@Override
public boolean apply(@Nullable IndexType indexType) {
return indexType.getElement() == resultType;
}
}));
}
return true;
}
});
/*
Determine the best join index query to answer this query:
Iterate over all potential indexes (as compiled above) and compute a score based on how many clauses
this index covers. The index with the highest score (as long as it covers at least one additional clause)
is picked and added to the joint query for as long as such exist.
*/
JointIndexQuery jointQuery = new JointIndexQuery();
boolean isSorted = orders.isEmpty();
Set<Condition> coveredClauses = Sets.newHashSet();
while (true) {
IndexType bestCandidate = null;
double candidateScore = 0.0;
Set<Condition> candidateSubcover = null;
boolean candidateSupportsSort = false;
Object candidateSubcondition = null;
for (IndexType index : indexCandidates) {
Set<Condition> subcover = Sets.newHashSet();
Object subcondition;
boolean supportsSort = orders.isEmpty();
// Check that this index actually applies in case of a schema constraint
if (index.hasSchemaTypeConstraint()) {
TitanSchemaType type = index.getSchemaTypeConstraint();
Map.Entry<Condition, Collection<Object>> equalCon = getEqualityConditionValues(conditions, ImplicitKey.LABEL);
if (equalCon == null)
continue;
Collection<Object> labels = equalCon.getValue();
assert labels.size() >= 1;
if (labels.size() > 1) {
log.warn("The query optimizer currently does not support multiple label constraints in query: {}", this);
continue;
}
if (!type.getName().equals(Iterables.getOnlyElement(labels)))
continue;
subcover.add(equalCon.getKey());
}
if (index.isCompositeIndex()) {
CompositeIndexType compositeIndex = (CompositeIndexType) index;
subcondition = indexCover(compositeIndex, conditions, subcover);
// if this is unique index, use it!!
if (compositeIndex.getCardinality() == Cardinality.SINGLE && subcondition != null) {
// will cause the outer while() to bail out
bestCandidate = null;
candidateSubcover = subcover;
candidateSubcondition = subcondition;
candidateSupportsSort = supportsSort;
if (log.isDebugEnabled()) {
log.debug("selected unique index {}", compositeIndex.getName());
}
if (coveredClauses.isEmpty()) {
isSorted = candidateSupportsSort;
}
coveredClauses.clear();
;
coveredClauses.addAll(candidateSubcover);
jointQuery = new JointIndexQuery();
jointQuery.add(compositeIndex, serializer.getQuery(compositeIndex, (List<Object[]>) candidateSubcondition));
break;
}
} else {
subcondition = indexCover((MixedIndexType) index, conditions, serializer, subcover);
if (coveredClauses.isEmpty() && !supportsSort && indexCoversOrder((MixedIndexType) index, orders))
supportsSort = true;
}
if (subcondition == null)
continue;
assert !subcover.isEmpty();
double score = 0.0;
boolean coversAdditionalClause = false;
for (Condition c : subcover) {
double s = (c instanceof PredicateCondition && ((PredicateCondition) c).getPredicate() == Cmp.EQUAL) ? EQUAL_CONDITION_SCORE : OTHER_CONDITION_SCORE;
if (coveredClauses.contains(c))
s = s * ALREADY_MATCHED_ADJUSTOR;
else
coversAdditionalClause = true;
score += s;
if (index.isCompositeIndex())
score += ((CompositeIndexType) index).getCardinality() == Cardinality.SINGLE ? CARDINALITY_SINGE_SCORE : CARDINALITY_OTHER_SCORE;
}
if (supportsSort)
score += ORDER_MATCH;
if (coversAdditionalClause && score > candidateScore) {
candidateScore = score;
bestCandidate = index;
candidateSubcover = subcover;
candidateSubcondition = subcondition;
candidateSupportsSort = supportsSort;
}
}
if (bestCandidate != null) {
if (coveredClauses.isEmpty())
isSorted = candidateSupportsSort;
coveredClauses.addAll(candidateSubcover);
log.debug("Index chosen for query {} {} ", bestCandidate.isCompositeIndex() ? "COMPOSITE" : "MIXED", coveredClauses);
if (bestCandidate.isCompositeIndex()) {
jointQuery.add((CompositeIndexType) bestCandidate, serializer.getQuery((CompositeIndexType) bestCandidate, (List<Object[]>) candidateSubcondition));
} else {
jointQuery.add((MixedIndexType) bestCandidate, serializer.getQuery((MixedIndexType) bestCandidate, (Condition) candidateSubcondition, orders));
}
} else {
break;
}
/* TODO: smarter optimization:
- use in-memory histograms to estimate selectivity of PredicateConditions and filter out low-selectivity ones
if they would result in an individual index call (better to filter afterwards in memory)
- move OR's up and extend GraphCentricQuery to allow multiple JointIndexQuery for proper or'ing of queries
*/
}
BackendQueryHolder<JointIndexQuery> query;
if (!coveredClauses.isEmpty()) {
int indexLimit = limit == Query.NO_LIMIT ? HARD_MAX_LIMIT : limit;
if (tx.getGraph().getConfiguration().adjustQueryLimit()) {
indexLimit = limit == Query.NO_LIMIT ? DEFAULT_NO_LIMIT : Math.min(MAX_BASE_LIMIT, limit);
}
indexLimit = Math.min(HARD_MAX_LIMIT, QueryUtil.adjustLimitForTxModifications(tx, coveredClauses.size(), indexLimit));
jointQuery.setLimit(indexLimit);
query = new BackendQueryHolder<>(jointQuery, coveredClauses.size() == conditions.numChildren(), isSorted, null);
} else {
query = new BackendQueryHolder<>(new JointIndexQuery(), false, isSorted, null);
}
return new GraphCentricQuery(resultType, conditions, orders, query, limit);
}
use of com.thinkaurelius.titan.graphdb.internal.InternalRelationType in project incubator-atlas by apache.
the class GraphCentricQueryBuilder method constructQuery.
public GraphCentricQuery constructQuery(final ElementCategory resultType) {
Preconditions.checkNotNull(resultType);
if (limit == 0)
return GraphCentricQuery.emptyQuery(resultType);
// Prepare constraints
And<TitanElement> conditions = QueryUtil.constraints2QNF(tx, constraints);
if (conditions == null)
return GraphCentricQuery.emptyQuery(resultType);
// Prepare orders
orders.makeImmutable();
if (orders.isEmpty())
orders = OrderList.NO_ORDER;
// Compile all indexes that cover at least one of the query conditions
final Set<IndexType> indexCandidates = new HashSet<>();
ConditionUtil.traversal(conditions, new Predicate<Condition<TitanElement>>() {
@Override
public boolean apply(@Nullable Condition<TitanElement> condition) {
if (condition instanceof PredicateCondition) {
RelationType type = ((PredicateCondition<RelationType, TitanElement>) condition).getKey();
Preconditions.checkArgument(type != null && type.isPropertyKey());
Iterables.addAll(indexCandidates, Iterables.filter(((InternalRelationType) type).getKeyIndexes(), new Predicate<IndexType>() {
@Override
public boolean apply(@Nullable IndexType indexType) {
return indexType.getElement() == resultType;
}
}));
}
return true;
}
});
/*
Determine the best join index query to answer this query:
Iterate over all potential indexes (as compiled above) and compute a score based on how many clauses
this index covers. The index with the highest score (as long as it covers at least one additional clause)
is picked and added to the joint query for as long as such exist.
*/
JointIndexQuery jointQuery = new JointIndexQuery();
boolean isSorted = orders.isEmpty();
Set<Condition> coveredClauses = Sets.newHashSet();
while (true) {
IndexType bestCandidate = null;
double candidateScore = 0.0;
Set<Condition> candidateSubcover = null;
boolean candidateSupportsSort = false;
Object candidateSubcondition = null;
for (IndexType index : indexCandidates) {
Set<Condition> subcover = Sets.newHashSet();
Object subcondition;
boolean supportsSort = orders.isEmpty();
// Check that this index actually applies in case of a schema constraint
if (index.hasSchemaTypeConstraint()) {
TitanSchemaType type = index.getSchemaTypeConstraint();
Map.Entry<Condition, Collection<Object>> equalCon = getEqualityConditionValues(conditions, ImplicitKey.LABEL);
if (equalCon == null)
continue;
Collection<Object> labels = equalCon.getValue();
assert labels.size() >= 1;
if (labels.size() > 1) {
log.warn("The query optimizer currently does not support multiple label constraints in query: {}", this);
continue;
}
if (!type.getName().equals(Iterables.getOnlyElement(labels)))
continue;
subcover.add(equalCon.getKey());
}
if (index.isCompositeIndex()) {
CompositeIndexType compositeIndex = (CompositeIndexType) index;
subcondition = indexCover(compositeIndex, conditions, subcover);
// if this is unique index, use it!!
if (compositeIndex.getCardinality() == Cardinality.SINGLE && subcondition != null) {
// will cause the outer while() to bail out
bestCandidate = null;
candidateSubcover = subcover;
candidateSubcondition = subcondition;
candidateSupportsSort = supportsSort;
if (log.isDebugEnabled()) {
log.debug("selected unique index {}", compositeIndex.getName());
}
if (coveredClauses.isEmpty()) {
isSorted = candidateSupportsSort;
}
coveredClauses.clear();
;
coveredClauses.addAll(candidateSubcover);
jointQuery = new JointIndexQuery();
jointQuery.add(compositeIndex, serializer.getQuery(compositeIndex, (List<Object[]>) candidateSubcondition));
break;
}
} else {
subcondition = indexCover((MixedIndexType) index, conditions, serializer, subcover);
if (coveredClauses.isEmpty() && !supportsSort && indexCoversOrder((MixedIndexType) index, orders))
supportsSort = true;
}
if (subcondition == null)
continue;
assert !subcover.isEmpty();
double score = 0.0;
boolean coversAdditionalClause = false;
for (Condition c : subcover) {
double s = (c instanceof PredicateCondition && ((PredicateCondition) c).getPredicate() == Cmp.EQUAL) ? EQUAL_CONDITION_SCORE : OTHER_CONDITION_SCORE;
if (coveredClauses.contains(c))
s = s * ALREADY_MATCHED_ADJUSTOR;
else
coversAdditionalClause = true;
score += s;
if (index.isCompositeIndex())
score += ((CompositeIndexType) index).getCardinality() == Cardinality.SINGLE ? CARDINALITY_SINGE_SCORE : CARDINALITY_OTHER_SCORE;
}
if (supportsSort)
score += ORDER_MATCH;
if (coversAdditionalClause && score > candidateScore) {
candidateScore = score;
bestCandidate = index;
candidateSubcover = subcover;
candidateSubcondition = subcondition;
candidateSupportsSort = supportsSort;
}
}
if (bestCandidate != null) {
if (coveredClauses.isEmpty())
isSorted = candidateSupportsSort;
coveredClauses.addAll(candidateSubcover);
log.debug("Index chosen for query {} {} ", bestCandidate.isCompositeIndex() ? "COMPOSITE" : "MIXED", coveredClauses);
if (bestCandidate.isCompositeIndex()) {
jointQuery.add((CompositeIndexType) bestCandidate, serializer.getQuery((CompositeIndexType) bestCandidate, (List<Object[]>) candidateSubcondition));
} else {
jointQuery.add((MixedIndexType) bestCandidate, serializer.getQuery((MixedIndexType) bestCandidate, (Condition) candidateSubcondition, orders));
}
} else {
break;
}
/* TODO: smarter optimization:
- use in-memory histograms to estimate selectivity of PredicateConditions and filter out low-selectivity ones
if they would result in an individual index call (better to filter afterwards in memory)
- move OR's up and extend GraphCentricQuery to allow multiple JointIndexQuery for proper or'ing of queries
*/
}
BackendQueryHolder<JointIndexQuery> query;
if (!coveredClauses.isEmpty()) {
int indexLimit = limit == Query.NO_LIMIT ? HARD_MAX_LIMIT : limit;
if (tx.getGraph().getConfiguration().adjustQueryLimit()) {
indexLimit = limit == Query.NO_LIMIT ? DEFAULT_NO_LIMIT : Math.min(MAX_BASE_LIMIT, limit);
}
indexLimit = Math.min(HARD_MAX_LIMIT, QueryUtil.adjustLimitForTxModifications(tx, coveredClauses.size(), indexLimit));
jointQuery.setLimit(indexLimit);
query = new BackendQueryHolder<>(jointQuery, coveredClauses.size() == conditions.numChildren(), isSorted, null);
} else {
query = new BackendQueryHolder<>(new JointIndexQuery(), false, isSorted, null);
}
return new GraphCentricQuery(resultType, conditions, orders, query, limit);
}
Aggregations