Package com.hp.hpl.jena.sparql.algebra.optimize

Source Code of com.hp.hpl.jena.sparql.algebra.optimize.TransformFilterEquality

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.hp.hpl.jena.sparql.algebra.optimize;

import static org.apache.jena.atlas.lib.CollectionUtils.disjoint ;

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

import org.apache.jena.atlas.lib.Pair ;

import com.hp.hpl.jena.query.ARQ ;
import com.hp.hpl.jena.sparql.algebra.Op ;
import com.hp.hpl.jena.sparql.algebra.OpVars ;
import com.hp.hpl.jena.sparql.algebra.TransformCopy ;
import com.hp.hpl.jena.sparql.algebra.op.* ;
import com.hp.hpl.jena.sparql.core.Substitute ;
import com.hp.hpl.jena.sparql.core.Var ;
import com.hp.hpl.jena.sparql.core.VarExprList ;
import com.hp.hpl.jena.sparql.expr.* ;

public class TransformFilterEquality extends TransformCopy
{
    // The approach taken for { OPTIONAL{} OPTIONAL{} } is more general ... and better?
    // Still need to be careful of double-nested OPTIONALS as intermedates of a different
    // value can block overall results so don't mask immediately.
    public TransformFilterEquality()
    { }
   
    @Override
    public Op transform(OpFilter opFilter, Op subOp)
    {
        Op op = apply(opFilter.getExprs(), subOp) ;
        if ( op == null )
            return super.transform(opFilter, subOp) ;
        return op ;
    }
   
    private static Op apply(ExprList exprs, Op subOp)
    {
        // ---- Find and extract any equality filters.
        Pair<List<Pair<Var, NodeValue>>, ExprList> p = preprocessFilterEquality(exprs) ;
        if ( p == null || p.getLeft().size() == 0 )
            return null ;
       
        List<Pair<Var, NodeValue>> equalities = p.getLeft() ;
        Collection<Var> varsMentioned = varsMentionedInEqualityFilters(equalities) ;
        ExprList remaining = p.getRight() ;
       
        // ---- Check if the subOp is the right shape to transform.
        Op op = subOp ;
       
        // Special case : deduce that the filter will always "eval unbound"
        // hence elimate all rows.  Return the empty table.
       
        if ( testSpecialCaseUnused(subOp, equalities, remaining))
            return OpTable.empty() ;
       
        // Special case: the deep left op of a OpConditional/OpLeftJoin is unit table.
        // This is
        // { OPTIONAL{P1} OPTIONAL{P2} ... FILTER(?x = :x) }
        if ( testSpecialCase1(subOp, equalities, remaining))
        {
            // Find backbone of ops
            List<Op> ops = extractOptionals(subOp) ;
            ops = processSpecialCase1(ops, equalities) ;
            // Put back together
            op = rebuild((Op2)subOp, ops) ;
            // Put all filters - either we optimized, or we left alone.
            // Either way, the complete set of filter expressions.
            op = OpFilter.filter(exprs, op) ;
            return op ;
        }
       
        // ---- Transform

        if ( ! safeToTransform(varsMentioned, op) )
            return null ;
        for ( Pair<Var, NodeValue> equalityTest : equalities )
            op = processFilterWorker(op, equalityTest.getLeft(), equalityTest.getRight()) ;

        // ---- Place any filter expressions around the processed sub op.
        if ( remaining.size() > 0 )
            op = OpFilter.filter(remaining, op) ;
        return op ;
    }

    // --- find and extract
    private static Pair<List<Pair<Var, NodeValue>>, ExprList> preprocessFilterEquality(ExprList exprs)
    {
        List<Pair<Var, NodeValue>> exprsFilterEquality = new ArrayList<Pair<Var, NodeValue>>() ;
        ExprList exprsOther = new ExprList() ;
        for ( Expr e : exprs.getList() )
        {
            Pair<Var, NodeValue> p = preprocess(e) ;
            if ( p != null )
                exprsFilterEquality.add(p) ;
            else
                exprsOther.add(e) ;
        }
        if ( exprsFilterEquality.size() == 0 )
            return null ;
        return Pair.create(exprsFilterEquality, exprsOther) ;
    }
   
    private static Pair<Var, NodeValue> preprocess(Expr e)
    {
        if ( !(e instanceof E_Equals) && !(e instanceof E_SameTerm) )
            return null ;

        ExprFunction2 eq = (ExprFunction2)e ;
        Expr left = eq.getArg1() ;
        Expr right = eq.getArg2() ;

        Var var = null ;
        NodeValue constant = null ;

        if ( left.isVariable() && right.isConstant() )
        {
            var = left.asVar() ;
            constant = right.getConstant() ;
        }
        else if ( right.isVariable() && left.isConstant() )
        {
            var = right.asVar() ;
            constant = left.getConstant() ;
        }

        if ( var == null || constant == null )
            return null ;

        // Corner case: sameTerm is false for string/plain literal,
        // but true in the graph for graph matching.
        if (e instanceof E_SameTerm)
        {
            if ( ! ARQ.isStrictMode() && constant.isString() )
                return null ;
        }
       
        // Final check for "=" where a FILTER = can do value matching when the graph does not.
        if ( e instanceof E_Equals )
        {
            // Value based?
            if ( ! ARQ.isStrictMode() && constant.isLiteral() )
                return null ;
        }
       
        return Pair.create(var, constant) ;
    }

    private static Collection<Var> varsMentionedInEqualityFilters(List<Pair<Var, NodeValue>> equalities)
    {
        List<Var> vars = new ArrayList<Var>() ;
        for ( Pair<Var, NodeValue> p : equalities )
            vars.add(p.getLeft()) ;
        return vars ;
    }

    private static boolean safeToTransform(Collection<Var> varsEquality, Op op)
    {
        // Structure as a visitor?
        if ( op instanceof OpBGP || op instanceof OpQuadPattern )
            return true ;
       
        if ( op instanceof OpFilter )
        {
            OpFilter opf = (OpFilter)op ;
            // Expressions are always safe transform by substitution.
//            Collection<Var> fvars = opf.getExprs().getVarsMentioned() ;
//            if ( ! disjoint(fvars, varsEquality) )
//                return false ;
            return safeToTransform(varsEquality, opf.getSubOp()) ;
        }
       
        // This will be applied also in sub-calls of the Transform but queries
        // are very rarely so deep that it matters.
        if ( op instanceof OpSequence )
        {
            OpN opN = (OpN)op ;
            for ( Op subOp : opN.getElements() )
            {
                if ( ! safeToTransform(varsEquality, subOp) )
                    return false ;
            }
            return true ;
        }
       
        if ( op instanceof OpJoin || op instanceof OpUnion)
        {
            Op2 op2 = (Op2)op ;
            return safeToTransform(varsEquality, op2.getLeft()) && safeToTransform(varsEquality, op2.getRight()) ;
        }

        // Not safe unless filter variables are mentioned on the LHS.
        if ( op instanceof OpConditional || op instanceof OpLeftJoin )
        {
            Op2 opleftjoin = (Op2)op ;
           
            if ( ! safeToTransform(varsEquality, opleftjoin.getLeft()) ||
                 ! safeToTransform(varsEquality, opleftjoin.getRight()) )
                return false ;
           
            // Not only must the left and right be safe to transform,
            // but the equality variable must be known to be always set.

            // If the varsLeft are disjoint from assigned vars,
            // we may be able to push assign down right
            // (this generalises the unit table case specialcase1)
            // Needs more investigation.
           
            Op opLeft = opleftjoin.getLeft() ;
            Set<Var> varsLeft = OpVars.visibleVars(opLeft) ;
            if ( varsLeft.containsAll(varsEquality) )
                return true ;
            return false ;
        }       
       
        if ( op instanceof OpGraph )
        {
            OpGraph opg = (OpGraph)op ;
            return safeToTransform(varsEquality, opg.getSubOp()) ;
        }
       
        // Subquery - assume scope rewriting has already been applied. 
        if ( op instanceof OpModifier )
        {
            // ORDER BY?
            OpModifier opMod = (OpModifier)op ;
            if ( opMod instanceof OpProject )
            {
                OpProject opProject = (OpProject)op ;
                // Writing "SELECT ?var" for "?var" -> a value would need AS-ification.
                for ( Var v : opProject.getVars() )
                {
                    if ( varsEquality.contains(v) )
                        return false ;
                }
            }
            return safeToTransform(varsEquality, opMod.getSubOp()) ;
        }
               
        if ( op instanceof OpGroup )
        {
            OpGroup opGroup = (OpGroup)op ;
            VarExprList varExprList = opGroup.getGroupVars() ;
            return safeToTransform(varsEquality, varExprList) &&
                   safeToTransform(varsEquality, opGroup.getSubOp()) ;
        }
       
        if ( op instanceof OpTable )
        {
            OpTable opTable = (OpTable)op ;
            if ( opTable.isJoinIdentity() )
                return true ;
        }

        // Op1 - OpGroup
        // Op1 - OpOrder
        // Op1 - OpAssign, OpExtend
        // Op1 - OpFilter - done.
        // Op1 - OpLabel - easy
        // Op1 - OpService - no.
       
        return false ;
    }
   
    private static boolean safeToTransform(Collection<Var> varsEquality, VarExprList varsExprList)
    {
        // If the named variable is used, unsafe to rewrite.
        return disjoint(varsExprList.getVars(), varsEquality) ;
    }
   
    // -- A special case

    private static boolean testSpecialCaseUnused(Op op, List<Pair<Var, NodeValue>> equalities, ExprList remaining)
    {
        // If the op does not contain the var at all, for some equality
        // then the filter expression will be "eval unbound" i.e. false.
        // We can return empty table.
        Set<Var> patternVars = OpVars.visibleVars(op) ;
        for ( Pair<Var, NodeValue> p : equalities )
        {
            if ( ! patternVars.contains(p.getLeft()))
                return true ;
        }
        return false ;
    }
   
    // If a sequence of OPTIONALS, and nothing prior to the first, we end up with
    // a unit table on the left sid of a next of LeftJoin/conditionals.

    private static boolean testSpecialCase1(Op op, List<Pair<Var, NodeValue>> equalities , ExprList remaining )
    {
        while ( op instanceof OpConditional || op instanceof OpLeftJoin )
        {
            Op2 opleftjoin2 = (Op2)op ;
            op = opleftjoin2.getLeft() ;
        }
        return isUnitTable(op) ;
    }
   
    private static List<Op> extractOptionals(Op op)
    {
        List<Op> chain = new ArrayList<Op>() ;
        while ( op instanceof OpConditional || op instanceof OpLeftJoin )
        {
            Op2 opleftjoin2 = (Op2)op ;
            chain.add(opleftjoin2.getRight()) ;
            op = opleftjoin2.getLeft() ;
        }
        return chain ;
    }

    private static List<Op> processSpecialCase1(List<Op> ops, List<Pair<Var, NodeValue>> equalities)
    {
        List<Op> ops2 = new ArrayList<Op>() ;
        Collection<Var> vars = varsMentionedInEqualityFilters(equalities) ;
       
        for ( Op op : ops )
        {
            Op op2 = op ;
            if ( safeToTransform(vars, op) )
            {
                for ( Pair<Var, NodeValue> p : equalities )
                        op2 = processFilterWorker(op, p.getLeft(), p.getRight()) ;
            }
            ops2.add(op2) ;
        }
        return ops2 ;
    }

    private static Op rebuild(Op2 subOp, List<Op> ops)
    {
        Op chain = OpTable.unit() ;
        for ( Op op : ops )
        {
            chain = subOp.copy(chain, op) ;
        }
        return chain ;
    }
 
    private static boolean isUnitTable(Op op)
    {
        if (op instanceof OpTable )
        {
            if ( ((OpTable)op).isJoinIdentity() )
                return true
        }
        return false ;
    }
   
    // ---- Transformation
       
    private static Op processFilterWorker(Op op, Var var, NodeValue constant)
    {
        return subst(op, var, constant) ;
    }
   
    private static Op subst(Op subOp , Var var, NodeValue nv)
    {
        Op op = Substitute.substitute(subOp, var, nv.asNode()) ;
        return OpAssign.assign(op, var, nv) ;
    }
   
    // Helper for TransformFilterDisjunction.
   
    /** Apply the FilterEquality transform or return null if no change */

    static Op processFilter(Expr e, Op subOp)
    {
        return apply(new ExprList(e), subOp) ;
    }
}
TOP

Related Classes of com.hp.hpl.jena.sparql.algebra.optimize.TransformFilterEquality

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.