/*
* Copyright 2007-2012 Amazon Technologies, Inc. or its affiliates.
* Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
* of Amazon Technologies, Inc. or its affiliates. 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.amazon.carbonado.cursor;
import com.amazon.carbonado.Storable;
import com.amazon.carbonado.info.ChainedProperty;
import com.amazon.carbonado.info.StorableProperty;
import com.amazon.carbonado.filter.AndFilter;
import com.amazon.carbonado.filter.ClosedFilter;
import com.amazon.carbonado.filter.ExistsFilter;
import com.amazon.carbonado.filter.Filter;
import com.amazon.carbonado.filter.OpenFilter;
import com.amazon.carbonado.filter.OrFilter;
import com.amazon.carbonado.filter.PropertyFilter;
import com.amazon.carbonado.filter.Visitor;
/**
* Optimizes a Filter such that simpler tests are performed before more complex
* tests which may need to follow joins. This takes advantage of the
* short-circuit logic used by FilteredCursorGenerator.
*
* @author Brian S O'Neill
*/
class ShortCircuitOptimizer {
public static <S extends Storable> Filter<S> optimize(Filter<S> filter) {
return filter.accept(new Walker<S>(), null).mNewFilter;
}
private static class FilterAndCost<S extends Storable> {
final Filter<S> mNewFilter;
final ChainedProperty<S> mExpensiveProperty;
FilterAndCost(Filter<S> filter, ChainedProperty<S> expensive) {
mNewFilter = filter;
mExpensiveProperty = expensive;
}
/**
* Returns -1 if this is cheaper, 0 if same, 1 if this is more expensive
*/
int compareCost(FilterAndCost<S> other) {
if (mExpensiveProperty == null) {
if (other.mExpensiveProperty == null) {
return 0;
}
return -1;
} else if (other.mExpensiveProperty == null) {
return 1;
}
if (mExpensiveProperty.equals(other.mExpensiveProperty)) {
return 0;
}
int result = joinCompare(mExpensiveProperty.getPrimeProperty(),
other.mExpensiveProperty.getPrimeProperty());
if (result != 0) {
return result;
}
if (mExpensiveProperty.getChainCount() == 0) {
if (other.mExpensiveProperty.getChainCount() == 0) {
return 0;
}
} else if (other.mExpensiveProperty.getChainCount() == 0) {
return 1;
}
int length = Math.min(mExpensiveProperty.getChainCount(),
other.mExpensiveProperty.getChainCount());
for (int i=0; i<length; i++) {
result = joinCompare(mExpensiveProperty.getChainedProperty(i),
other.mExpensiveProperty.getChainedProperty(i));
if (result != 0) {
return result;
}
}
if (mExpensiveProperty.getChainCount() < other.mExpensiveProperty.getChainCount()) {
return -1;
}
if (mExpensiveProperty.getChainCount() > other.mExpensiveProperty.getChainCount()) {
return 1;
}
return 0;
}
/**
* Returns -1 if "a" has cheaper join, 0 if same, 1 if "a" has more expensive join
*/
private int joinCompare(StorableProperty<?> a, StorableProperty<?> b) {
if (a.isQuery()) {
if (b.isQuery()) {
return 0;
}
return 1;
} else if (b.isQuery()) {
return -1;
}
if (a.isJoin()) {
if (b.isJoin()) {
return 0;
}
return 1;
} else if (b.isJoin()) {
return -1;
}
return 0;
}
}
private static class Walker<S extends Storable> extends Visitor<S, FilterAndCost<S>, Object> {
@Override
public FilterAndCost<S> visit(OrFilter<S> filter, Object param) {
FilterAndCost<S> leftCost = filter.getLeftFilter().accept(this, param);
FilterAndCost<S> rightCost = filter.getRightFilter().accept(this, param);
int compare = leftCost.compareCost(rightCost);
Filter<S> newFilter;
ChainedProperty<S> expensiveProperty;
if (compare <= 0) {
// Current arrangement is fine.
newFilter = leftCost.mNewFilter.or(rightCost.mNewFilter);
expensiveProperty = rightCost.mExpensiveProperty;
} else {
// Swap nodes such that the expensive one is checked later.
newFilter = rightCost.mNewFilter.or(leftCost.mNewFilter);
expensiveProperty = leftCost.mExpensiveProperty;
}
return new FilterAndCost<S>(newFilter, expensiveProperty);
}
@Override
public FilterAndCost<S> visit(AndFilter<S> filter, Object param) {
FilterAndCost<S> leftCost = filter.getLeftFilter().accept(this, param);
FilterAndCost<S> rightCost = filter.getRightFilter().accept(this, param);
int compare = leftCost.compareCost(rightCost);
Filter<S> newFilter;
ChainedProperty<S> expensiveProperty;
if (compare <= 0) {
// Current arrangement is fine.
newFilter = leftCost.mNewFilter.and(rightCost.mNewFilter);
expensiveProperty = rightCost.mExpensiveProperty;
} else {
// Swap nodes such that the expensive one is checked later.
newFilter = rightCost.mNewFilter.and(leftCost.mNewFilter);
expensiveProperty = leftCost.mExpensiveProperty;
}
return new FilterAndCost<S>(newFilter, expensiveProperty);
}
@Override
public FilterAndCost<S> visit(PropertyFilter<S> filter, Object param) {
return new FilterAndCost<S>(filter, filter.getChainedProperty());
}
@Override
public FilterAndCost<S> visit(ExistsFilter<S> filter, Object param) {
return new FilterAndCost<S>(filter, filter.getChainedProperty());
}
@Override
public FilterAndCost<S> visit(OpenFilter<S> filter, Object param) {
return new FilterAndCost<S>(filter, null);
}
@Override
public FilterAndCost<S> visit(ClosedFilter<S> filter, Object param) {
return new FilterAndCost<S>(filter, null);
}
}
}