Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.query.impl.predicates;
import com.hazelcast.core.TypeConverter;
import com.hazelcast.query.Predicate;
import com.hazelcast.query.impl.FalsePredicate;
import com.hazelcast.query.impl.Index;
import com.hazelcast.query.impl.Indexes;
import com.hazelcast.util.collection.ArrayUtils;
import com.hazelcast.util.collection.InternalMultiMap;
import java.util.List;
import java.util.Map;
import static com.hazelcast.util.collection.ArrayUtils.createCopy;
/**
* Replaces expression from (age >= X and age <= Y) into (age between X Y)
* It detects some predicates which are trivally false.
*
* Imagine this: (age >= 5 and age <= 4) This predicate is always false.
*
* It also eliminates some conditions. Imagine this: (age >= 10 and age <= 20 and age < 40).
* In this case the condition (age < 40) can be eliminated safely and the predicate
* be be rewritten as (age between 10 20)
*
* When resulting AndPredicate contains only a single inner predicate then it returns
* only the inner predicate. In other words: (and p) is rewritten as (p). I used prefix
* notation here as it's easier to understand.
*
*/
public class BetweenVisitor extends AbstractVisitor {
@Override
public Predicate visit(AndPredicate andPredicate, Indexes indexes) {
final Predicate[] originalPredicates = andPredicate.predicates;
InternalMultiMap candidates =
findCandidatesAndGroupByAttribute(originalPredicates, indexes);
if (candidates == null) {
return andPredicate;
}
//how many predicates is eliminated
int toBeRemovedCounter = 0;
boolean modified = false;
Predicate[] target = originalPredicates;
for (Map.Entry> entry : candidates.entrySet()) {
List predicates = entry.getValue();
if (predicates.size() == 1) {
continue;
}
String attributeName = entry.getKey();
Boundaries boundaries = findBoundaryOrNull(attributeName, predicates, indexes);
if (boundaries == null) {
//no boundaries were found. it's not in the form `foo >= X and foo <= Y`
continue;
}
if (boundaries.isOverlapping()) {
// the predicate is never true. imagine this: `foo >= 5 and foo <= 4`
// it can never be true, we can't replace the whole AND Node with FalsePredicate
// We can return FalsePredicate even if there are additional predicate
// as `foo >= 5 and foo <= 5 and bar = 3` is still always false. the `bar = 3` can
// be eliminated too.
return FalsePredicate.INSTANCE;
}
if (!modified) {
modified = true;
target = createCopy(target);
}
toBeRemovedCounter = rewriteAttribute(boundaries, target, toBeRemovedCounter);
}
Predicate[] newPredicates = removeEliminatedPredicates(target, toBeRemovedCounter);
if (newPredicates == originalPredicates) {
return andPredicate;
}
if (newPredicates.length == 1) {
return newPredicates[0];
}
return new AndPredicate(newPredicates);
}
private Boundaries findBoundaryOrNull(String attributeName,
List predicates, Indexes indexes) {
GreaterLessPredicate mostRightGreaterOrEquals = null;
GreaterLessPredicate mostLeftLessThanOrEquals = null;
Index index = indexes.getIndex(attributeName);
TypeConverter converter = index.getConverter();
for (GreaterLessPredicate predicate : predicates) {
if (predicate.less) {
if (mostLeftLessThanOrEquals == null || isLessThan(mostLeftLessThanOrEquals, predicate, converter)) {
mostLeftLessThanOrEquals = predicate;
}
} else {
if (mostRightGreaterOrEquals == null || isGreaterThan(mostRightGreaterOrEquals, predicate, converter)) {
mostRightGreaterOrEquals = predicate;
}
}
}
if (mostRightGreaterOrEquals == null || mostLeftLessThanOrEquals == null) {
return null;
}
return new Boundaries(mostRightGreaterOrEquals, mostLeftLessThanOrEquals, converter);
}
private Predicate[] removeEliminatedPredicates(Predicate[] originalPredicates, int toBeRemoved) {
if (toBeRemoved == 0) {
return originalPredicates;
}
int newSize = originalPredicates.length - toBeRemoved;
Predicate[] newPredicates = new Predicate[newSize];
ArrayUtils.copyWithoutNulls(originalPredicates, newPredicates);
return newPredicates;
}
private int rewriteAttribute(Boundaries boundaries,
Predicate[] originalPredicates, int toBeRemovedCount) {
GreaterLessPredicate leftBoundary = boundaries.leftBoundary;
GreaterLessPredicate rightBoundary = boundaries.rightBoundary;
Predicate rewritten = boundaries.createEquivalentPredicate();
for (int i = 0; i < originalPredicates.length; i++) {
Predicate currentPredicate = originalPredicates[i];
if (currentPredicate == leftBoundary) {
originalPredicates[i] = rewritten;
} else if (currentPredicate == rightBoundary) {
originalPredicates[i] = null;
toBeRemovedCount++;
} else if (boundaries.canBeEliminated(currentPredicate)) {
originalPredicates[i] = null;
toBeRemovedCount++;
}
}
return toBeRemovedCount;
}
/**
* Find GreaterLessPredicates with equal flag set to true and group them by attribute name
*/
private InternalMultiMap findCandidatesAndGroupByAttribute(Predicate[] predicates,
Indexes indexService) {
InternalMultiMap candidates = null;
for (Predicate predicate : predicates) {
if (!(predicate instanceof GreaterLessPredicate)) {
continue;
}
GreaterLessPredicate greaterLessPredicate = (GreaterLessPredicate) predicate;
if (!(greaterLessPredicate.equal)) {
continue;
}
String attributeName = greaterLessPredicate.attributeName;
Index index = indexService.getIndex(attributeName);
if (index == null || index.getConverter() == null) {
continue;
}
candidates = addIntoCandidates(greaterLessPredicate, candidates);
}
return candidates;
}
private InternalMultiMap addIntoCandidates(
GreaterLessPredicate predicate, InternalMultiMap currentCandidates) {
if (currentCandidates == null) {
currentCandidates = new InternalMultiMap();
}
String attributeName = predicate.attributeName;
currentCandidates.put(attributeName, predicate);
return currentCandidates;
}
/**
* Represent a boundary of series of GreatLessPredicates connected by AND
*
* Example:
* (age >= 5 and age >= 6 and age <= 10)
* has a left boundary (age >= 6)
* and a right boundary (age <= 10)
*
*/
private class Boundaries {
private final GreaterLessPredicate leftBoundary;
private final GreaterLessPredicate rightBoundary;
private final TypeConverter typeConverter;
Boundaries(GreaterLessPredicate leftBoundary, GreaterLessPredicate rightBoundary,
TypeConverter converter) {
this.leftBoundary = leftBoundary;
this.rightBoundary = rightBoundary;
this.typeConverter = converter;
}
/**
* Overlapping boundaries has a form of (age >= 5 and age <= 4) - predicates with overlaps
* are always evaluated as false.
*
* @return
*/
boolean isOverlapping() {
return (isGreaterThan(rightBoundary, leftBoundary, typeConverter));
}
Predicate createEquivalentPredicate() {
String attributeName = leftBoundary.attributeName;
if (isSame()) {
return new EqualPredicate(attributeName, leftBoundary.value);
} else {
return new BetweenPredicate(attributeName, leftBoundary.value, rightBoundary.value);
}
}
boolean isSame() {
return leftBoundary.value == rightBoundary.value;
}
private boolean canBeEliminated(Predicate predicate) {
if (!(predicate instanceof GreaterLessPredicate)) {
return false;
}
GreaterLessPredicate greaterLessPredicate = (GreaterLessPredicate) predicate;
if (!greaterLessPredicate.attributeName.equals(leftBoundary.attributeName)) {
return false;
}
if (greaterLessPredicate.less) {
return isGreaterThan(rightBoundary, greaterLessPredicate, typeConverter);
} else {
return isLessThan(leftBoundary, greaterLessPredicate, typeConverter);
}
}
}
private boolean isGreaterThan(GreaterLessPredicate leftPredicate,
GreaterLessPredicate rightPredicate, TypeConverter converter) {
Comparable rightValue = converter.convert(rightPredicate.value);
Comparable leftValue = converter.convert(leftPredicate.value);
return rightValue.compareTo(leftValue) > 0;
}
private boolean isLessThan(GreaterLessPredicate leftPredicate, GreaterLessPredicate rightPredicate,
TypeConverter converter) {
Comparable rightValue = converter.convert(rightPredicate.value);
Comparable leftValue = converter.convert(leftPredicate.value);
return rightValue.compareTo(leftValue) < 0;
}
}