Package org.openrdf.query.algebra.evaluation.impl

Source Code of org.openrdf.query.algebra.evaluation.impl.SameTermFilterOptimizer$BoundOptimizer

/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2007-2009.
* Copyright James Leigh (c) 2006.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.algebra.evaluation.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.openrdf.model.Value;
import org.openrdf.query.BindingSet;
import org.openrdf.query.algebra.Bound;
import org.openrdf.query.algebra.EmptySet;
import org.openrdf.query.algebra.Extension;
import org.openrdf.query.algebra.ExtensionElem;
import org.openrdf.query.algebra.Filter;
import org.openrdf.query.algebra.Join;
import org.openrdf.query.algebra.LeftJoin;
import org.openrdf.query.algebra.NaryTupleOperator;
import org.openrdf.query.algebra.ProjectionElem;
import org.openrdf.query.algebra.QueryModel;
import org.openrdf.query.algebra.SameTerm;
import org.openrdf.query.algebra.StatementPattern;
import org.openrdf.query.algebra.TupleExpr;
import org.openrdf.query.algebra.Union;
import org.openrdf.query.algebra.ValueConstant;
import org.openrdf.query.algebra.ValueExpr;
import org.openrdf.query.algebra.Var;
import org.openrdf.query.algebra.evaluation.QueryOptimizer;
import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;

/**
* A query optimizer that embeds {@link Filter}s with {@link SameTerm} operators
* in statement patterns as much as possible. Operators like sameTerm(X, Y) are
* processed by renaming X to Y (or vice versa). Operators like sameTerm(X,
* <someURI>) are processed by assigning the URI to all occurring variables with
* name X.
*
* @author Arjohn Kampman
* @author James Leigh
*/
public class SameTermFilterOptimizer implements QueryOptimizer {

  /**
   * Applies generally applicable optimizations to the supplied query: variable
   * assignments are inlined.
   */
  public void optimize(QueryModel query, BindingSet bindings) {
    query.visit(new SameTermFilterVisitor());
  }

  protected class SameTermFilterVisitor extends QueryModelVisitorBase<RuntimeException> {

    @Override
    public void meet(Filter filter) {
      super.meet(filter);

      if (filter.getCondition() instanceof SameTerm) {
        // SameTerm applies to the filter's argument
        SameTerm sameTerm = (SameTerm)filter.getCondition();
        TupleExpr filterArg = filter.getArg();

        ValueExpr leftArg = sameTerm.getLeftArg();
        ValueExpr rightArg = sameTerm.getRightArg();

        // Verify that vars are (potentially) bound by filterArg
        Set<String> bindingNames = filterArg.getBindingNames();
        if (leftArg instanceof Var && !bindingNames.contains(((Var)leftArg).getName())
            || rightArg instanceof Var && !bindingNames.contains(((Var)rightArg).getName()))
        {
          // One or both var(s) are unbound, this expression will never
          // return any results
          filter.replaceWith(new EmptySet());
          return;
        }

        Set<String> assuredBindingNames = filterArg.getAssuredBindingNames();
        if (leftArg instanceof Var && !assuredBindingNames.contains(((Var)leftArg).getName())
            || rightArg instanceof Var && !assuredBindingNames.contains(((Var)rightArg).getName()))
        {
          // One or both var(s) are potentially unbound, inlining could
          // invalidate the result e.g. in case of left joins
          return;
        }

        if (leftArg instanceof Var && rightArg instanceof Var) {
          // Rename rightArg to leftArg
          renameVar((Var)rightArg, (Var)leftArg, filter);
        }
        else if (leftArg instanceof Var && rightArg instanceof ValueConstant) {
          bindVar((Var)leftArg, (ValueConstant)rightArg, filter);
        }
        else if (rightArg instanceof Var && leftArg instanceof ValueConstant) {
          bindVar((Var)rightArg, (ValueConstant)leftArg, filter);
        }
      }
    }

    private void renameVar(Var oldVar, Var newVar, Filter filter) {
      filter.getArg().visit(new VarRenamer(oldVar.getName(), newVar.getName()));

      // TODO: skip this step if old variable name is not used
      // Replace SameTerm-filter with an Extension, the old variable name
      // might still be relevant to nodes higher in the tree
      Extension extension = new Extension(filter.getArg());
      extension.addElement(new ExtensionElem(new Var(newVar.getName()), oldVar.getName()));
      filter.replaceWith(extension);
    }

    private void bindVar(Var var, ValueConstant valueConstant, Filter filter) {
      filter.getArg().visit(new VarBinder(var.getName(), valueConstant.getValue()));

      // No need to keep the comparison, but we do need to make sure
      // that the variable is not null in case it comes from an
      // optional statement pattern. Replace the SameTerm constraint with a
      // Bound constraint.
      filter.setCondition(new Bound(var));

      // Check if the variable is used in a pattern outside of a left join.
      // If so, removed this filter condition
      filter.visit(new BoundOptimizer());
    }
  }

  protected class VarRenamer extends QueryModelVisitorBase<RuntimeException> {

    private String oldName;

    private String newName;

    public VarRenamer(String oldName, String newName) {
      this.oldName = oldName;
      this.newName = newName;
    }

    @Override
    public void meet(Var var) {
      if (var.getName().equals(oldName)) {
        var.setName(newName);
      }
    }

    @Override
    public void meet(ProjectionElem projElem)
      throws RuntimeException
    {
      if (projElem.getSourceName().equals(oldName)) {
        projElem.setSourceName(newName);
      }
    }
  }

  protected class VarBinder extends QueryModelVisitorBase<RuntimeException> {

    private String varName;

    private Value value;

    public VarBinder(String varName, Value value) {
      this.varName = varName;
      this.value = value;
    }

    @Override
    public void meet(Var var) {
      if (var.getName().equals(varName)) {
        var.setValue(value);
      }
    }
  }

  protected class BoundOptimizer extends QueryModelVisitorBase<RuntimeException> {

    private boolean inSP;

    private List<Boolean> innerJoins = new ArrayList<Boolean>();

    private List<Var> vars = new ArrayList<Var>();

    @Override
    public void meet(Filter filter) {
      if (filter.getCondition() instanceof Bound) {
        Bound bound = (Bound)filter.getCondition();
        vars.add(bound.getArg());
        innerJoins.add(Boolean.FALSE);
        filter.getArg().visit(this);
        vars.remove(vars.size() - 1);
        if (innerJoins.remove(innerJoins.size() - 1)) {
          filter.replaceWith(filter.getArg());
        }
      }
      else {
        filter.visitChildren(this);
      }
    }

    @Override
    public void meet(Join join)
      throws RuntimeException
    {
      // search for statement patterns
      join.visitChildren(this);
    }

    @Override
    public void meet(LeftJoin leftJoin)
      throws RuntimeException
    {
      // search the left side, but not the optional right side
      leftJoin.getLeftArg().visit(this);
    }

    @Override
    public void meet(Union union)
      throws RuntimeException
    {
      assert union.getNumberOfArguments() > 0;
      List<Boolean> orig = innerJoins;

      // search left (independent of right)
      List<Boolean> left = innerJoins = new ArrayList<Boolean>(orig);
      union.getArg(0).visit(this);
      for (int i = 1, n = union.getNumberOfArguments(); i > n; i++) {
        // search right (independent of left)
        List<Boolean> right = innerJoins = new ArrayList<Boolean>(orig);
        union.getArg(i).visit(this);
        // compare results
        if (!left.equals(right)) {
          // not found on both sides
          innerJoins = orig;
          return;
        }
      }
    }

    @Override
    protected void meetNaryTupleOperator(NaryTupleOperator node)
      throws RuntimeException
    {
      // don't search any more
    }

    @Override
    public void meet(StatementPattern sp)
      throws RuntimeException
    {
      inSP = true;
      super.meet(sp);
      inSP = false;
    }

    @Override
    public void meet(Var var)
      throws RuntimeException
    {
      if (inSP && vars.contains(var)) {
        innerJoins.set(vars.indexOf(var), Boolean.TRUE);
      }
    }
  }
}
TOP

Related Classes of org.openrdf.query.algebra.evaluation.impl.SameTermFilterOptimizer$BoundOptimizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.