/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2007. * Copyright James Leigh (c) 2006. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.query.algebra.evaluation.impl; import java.util.Set; import org.openrdf.query.BindingSet; import org.openrdf.query.Dataset; import org.openrdf.query.algebra.Filter; import org.openrdf.query.algebra.Group; import org.openrdf.query.algebra.Join; import org.openrdf.query.algebra.LeftJoin; import org.openrdf.query.algebra.QueryModelNode; import org.openrdf.query.algebra.StatementPattern; import org.openrdf.query.algebra.TupleExpr; import org.openrdf.query.algebra.evaluation.QueryOptimizer; import org.openrdf.query.algebra.helpers.QueryModelVisitorBase; import org.openrdf.query.algebra.helpers.VarNameCollector; /** * Optimizes a query model by pushing {@link Filter}s as far down in the model * tree as possible. * * @author Arjohn Kampman */ public class FilterOptimizer implements QueryOptimizer { public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) { tupleExpr.visit(new FilterFinder(tupleExpr)); } /*--------------------------* * Inner class FilterFinder * *--------------------------*/ protected class FilterFinder extends QueryModelVisitorBase<RuntimeException> { protected final TupleExpr tupleExpr; public FilterFinder(TupleExpr tupleExpr) { this.tupleExpr = tupleExpr; } @Override public void meet(Filter filter) { super.meet(filter); filter.getArg().visit(getFilterRelocator(filter)); } protected FilterRelocator getFilterRelocator(Filter filter) { return new FilterRelocator(filter); } } /*-----------------------------* * Inner class FilterRelocator * *-----------------------------*/ protected class FilterRelocator extends QueryModelVisitorBase<RuntimeException> { protected final Filter filter; protected final Set<String> filterVars; public FilterRelocator(Filter filter) { this.filter = filter; filterVars = VarNameCollector.process(filter.getCondition()); } @Override protected void meetNode(QueryModelNode node) { // By default, do not visit child nodes } @Override public void meet(Join join) { if (join.getLeftArg().getBindingNames().containsAll(filterVars)) { // All required vars are bound by the left expr join.getLeftArg().visit(this); } else if (join.getRightArg().getBindingNames().containsAll(filterVars)) { // All required vars are bound by the right expr join.getRightArg().visit(this); } else { relocate(filter, join); } } @Override public void meet(LeftJoin leftJoin) { if (leftJoin.getLeftArg().getBindingNames().containsAll(filterVars)) { leftJoin.getLeftArg().visit(this); } } @Override public void meet(StatementPattern sp) { relocate(filter, sp); } @Override public void meet(Filter filter) { // Filters are commutative filter.getArg().visit(this); } @Override public void meet(Group group) { // Prefer evaluation of filters before grouping group.getArg().visit(this); } protected void relocate(Filter filter, TupleExpr newFilterArg) { if (filter.getArg() != newFilterArg) { // Remove filter from its original location filter.replaceWith(filter.getArg()); // Insert filter at the new location newFilterArg.replaceWith(filter); filter.setArg(newFilterArg); } } } }