ProjectionRemovalOptimizer.java
/*******************************************************************************
* Copyright (c) 2022 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.query.algebra.evaluation.optimizer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.Dataset;
import org.eclipse.rdf4j.query.algebra.Projection;
import org.eclipse.rdf4j.query.algebra.TupleExpr;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryOptimizer;
import org.eclipse.rdf4j.query.algebra.helpers.AbstractSimpleQueryModelVisitor;
/**
* If a projection node in the algebra does not contribute or change the results it can be removed from the tree.
* <p>
* For example <code>
* SELECT ?s ?p ?o
* WHERE {?s ?p ?o }
* </code> Does not need a projection as the inner statement pattern returns the same result.
* <p>
* While <code>
* * SELECT ?s ?p
* WHERE {?s ?p ?o }
* </code> Does as the statement pattern has one more variable in use than the projection.
* <p>
* Note: this optimiser should run after optimisations ran that depend on Projections. e.g.
*
* @author Jerven Bolleman
* @see UnionScopeChangeOptimizer
*/
public class ProjectionRemovalOptimizer implements QueryOptimizer {
@Override
public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
tupleExpr.visit(new ProjectionFinder());
}
private static class VariableFinder extends AbstractSimpleQueryModelVisitor<RuntimeException> {
private Set<String> vars;
private VariableFinder() {
super(true);
}
@Override
public void meet(Var node) throws RuntimeException {
if (node.getName() != null) {
if (vars == null) {
vars = new HashSet<>();
}
vars.add(node.getName());
}
}
public Set<String> getVars() {
return vars != null ? vars : Collections.emptySet();
}
}
private static class ProjectionFinder extends AbstractSimpleQueryModelVisitor<RuntimeException> {
private ProjectionFinder() {
super(true);
}
@Override
public void meet(Projection node) throws RuntimeException {
super.meet(node);
VariableFinder findVariables = new VariableFinder();
node.visit(findVariables);
Set<String> foundChildVariableNames = findVariables.getVars();
if (!foundChildVariableNames.isEmpty() && foundChildVariableNames.equals(node.getBindingNames())) {
TupleExpr child = node.getArg();
node.getParentNode().replaceChildNode(node, child);
}
}
}
}