You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by mi...@apache.org on 2016/02/07 19:26:16 UTC

[13/16] incubator-rya git commit: RYA-32 Improve how metadata and values are written to Accumulo PCJ tables

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/c12f58f4/extras/indexing/src/main/java/mvm/rya/indexing/external/PrecompJoinOptimizer.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/external/PrecompJoinOptimizer.java b/extras/indexing/src/main/java/mvm/rya/indexing/external/PrecompJoinOptimizer.java
index 65a775f..eb0f042 100644
--- a/extras/indexing/src/main/java/mvm/rya/indexing/external/PrecompJoinOptimizer.java
+++ b/extras/indexing/src/main/java/mvm/rya/indexing/external/PrecompJoinOptimizer.java
@@ -8,9 +8,9 @@ package mvm.rya.indexing.external;
  * 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
@@ -19,13 +19,11 @@ package mvm.rya.indexing.external;
  * under the License.
  */
 
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 
 import mvm.rya.api.RdfCloudTripleStoreConfiguration;
@@ -38,25 +36,22 @@ import mvm.rya.indexing.accumulo.ConfigUtils;
 import mvm.rya.indexing.external.QueryVariableNormalizer.VarCollector;
 import mvm.rya.indexing.external.tupleSet.AccumuloIndexSet;
 import mvm.rya.indexing.external.tupleSet.ExternalTupleSet;
+import mvm.rya.indexing.external.tupleSet.PcjTables;
+import mvm.rya.indexing.external.tupleSet.PcjTables.PcjException;
 import mvm.rya.rdftriplestore.inference.DoNotExpandSP;
 import mvm.rya.rdftriplestore.utils.FixedStatementPattern;
 
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
 import org.apache.accumulo.core.client.Connector;
-import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.client.TableNotFoundException;
-import org.apache.accumulo.core.data.Key;
-import org.apache.accumulo.core.data.Range;
-import org.apache.accumulo.core.data.Value;
-import org.apache.accumulo.core.security.Authorizations;
 import org.apache.hadoop.conf.Configurable;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.io.Text;
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.Dataset;
 import org.openrdf.query.MalformedQueryException;
 import org.openrdf.query.QueryEvaluationException;
+import org.openrdf.query.algebra.BinaryTupleOperator;
 import org.openrdf.query.algebra.BindingSetAssignment;
 import org.openrdf.query.algebra.Difference;
 import org.openrdf.query.algebra.Distinct;
@@ -86,688 +81,783 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-//optimizer which matches TupleExpressions associated with pre-computed queries
-//to sub-queries of a given query. Each matched sub-query is replaced by an indexing node
-//to delegate that portion of the query to the pre-computed query index
+/**
+ * {@link QueryOptimizer} which matches TupleExpressions associated with
+ * pre-computed queries to sub-queries of a given query. Each matched sub-query
+ * is replaced by an indexing node to delegate that portion of the query to the
+ * pre-computed query index.
+ * <p>
+ *
+ * A query can be broken up into "Join segments", which subsets of the query
+ * joined only by {@link Join} nodes. Any portions of the query that are
+ * attached by {@link BinaryTupleOperator} or {@link UnaryTupleOperator} nodes other
+ * than a Join node mark the beginning of a new Join segment. Pre-computed query
+ * indices, or {@link ExternalTupleset} objects, are compared against the query
+ * nodes in each of its Join segments and replace any nodes which match the
+ * nodes in the ExternalTupleSet's TupleExpr.
+ *
+ */
+
 public class PrecompJoinOptimizer implements QueryOptimizer, Configurable {
 
-    private List<ExternalTupleSet> indexSet;
-    private Configuration conf;
-    private boolean init = false;
-    
-    public PrecompJoinOptimizer() {
-    }
-    
-    public PrecompJoinOptimizer(Configuration conf) {
-        this.conf = conf;
-        try {
-            indexSet = getAccIndices(conf);
-            init = true;
-        } catch (MalformedQueryException e) {
-            e.printStackTrace();
-        } catch (SailException e) {
-            e.printStackTrace();
-        } catch (QueryEvaluationException e) {
-            e.printStackTrace();
-        } catch (TableNotFoundException e) {
-            e.printStackTrace();
-        } catch (AccumuloException e) {
-            e.printStackTrace();
-        } catch (AccumuloSecurityException e) {
-            e.printStackTrace();
-        }
-    }
-    
-    public PrecompJoinOptimizer(List<ExternalTupleSet> indices, boolean useOptimalPcj) {
-        this.indexSet = indices;
-        conf = new Configuration();
-        conf.setBoolean(ConfigUtils.USE_OPTIMAL_PCJ, useOptimalPcj);
-    }
-    
-    public void setConf(Configuration conf) {
-        this.conf = conf;
-        if (!init) {
-            try {
-                indexSet = getAccIndices(conf);
-                init = true;
-            } catch (MalformedQueryException e) {
-                e.printStackTrace();
-            } catch (SailException e) {
-                e.printStackTrace();
-            } catch (QueryEvaluationException e) {
-                e.printStackTrace();
-            } catch (TableNotFoundException e) {
-                e.printStackTrace();
-            } catch (AccumuloException e) {
-                e.printStackTrace();
-            } catch (AccumuloSecurityException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-    
-    @Override
-    public Configuration getConf() {
-        return conf;
-    }
-    
-
-    @Override
-    public void optimize(TupleExpr tupleExpr, Dataset dataset, BindingSet bindings) {
-
-        IndexedExecutionPlanGenerator iep = new IndexedExecutionPlanGenerator(tupleExpr, indexSet);
-        JoinVisitor jv = new JoinVisitor();
-        
-        if (ConfigUtils.getUseOptimalPCJ(conf) && indexSet.size() > 0) {
-            
-            //get potential relevant index combinations
-            ValidIndexCombinationGenerator vic = new ValidIndexCombinationGenerator(tupleExpr);
-            Iterator<List<ExternalTupleSet>> iter = vic.getValidIndexCombos(iep.getNormalizedIndices());
-            TupleExpr bestTup = null;
-            TupleExpr tempTup = null;
-            double tempCost = 0;
-            double minCost = Double.MAX_VALUE;
-            
-            while (iter.hasNext()) {
-                //apply join visitor to place external index nodes in query
-                TupleExpr clone = tupleExpr.clone();
-                jv.setExternalTupList(iter.next());
-                jv.setSegmentFilters(new ArrayList<Filter>());
-                clone.visit(jv);
-                
-                //get all valid execution plans for given external index combination by considering all 
-                //permutations of nodes in TupleExpr
-                IndexPlanValidator ipv = new IndexPlanValidator(false);
-                Iterator<TupleExpr> validTups = ipv.getValidTuples(TupleReArranger.getTupleReOrderings(clone).iterator());
-                
-                //set valid plan according to a specified cost threshold, where cost depends on specified weights
-                //for number of external index nodes, common variables among joins in execution plan, and number of
-                //external products in execution plan
-                ThreshholdPlanSelector tps = new ThreshholdPlanSelector(tupleExpr);
-                tempTup = tps.getThreshholdQueryPlan(validTups, .4, .5, .2, .3);
-                
-                //choose best threshhold TupleExpr among all index node combinations
-                tempCost = tps.getCost(tempTup, .5, .2, .3);
-                if(tempCost < minCost ) {
-                    minCost = tempCost;
-                    bestTup = tempTup;
-                }    
-            }
-            if (bestTup != null) {
-                ((UnaryTupleOperator) tupleExpr).setArg(((UnaryTupleOperator) bestTup).getArg());
-            }
-            return;
-        } else {
-            if (indexSet.size() > 0) {
-                jv.setExternalTupList(iep.getNormalizedIndices());
-                tupleExpr.visit(jv);
-            }
-            return;
-        }
-    }
-
-    protected class JoinVisitor extends QueryModelVisitorBase<RuntimeException> {
-
-        private List<ExternalTupleSet> tupList;
-        private List<Filter> segmentFilters = Lists.newArrayList();
-        
-        public void setExternalTupList(List<ExternalTupleSet> tupList) {
-            this.tupList = tupList;
-        }
-        
-        public void setSegmentFilters(List<Filter> segmentFilters) {
-            this.segmentFilters = segmentFilters;
-        }
-        
-        @Override
-        public void meet(Join node) {
-
-            //get all filters with bindings in this segment
-            updateFilters(segmentFilters, true);
-            
-            try {
-                if (node.getLeftArg() instanceof FixedStatementPattern && node.getRightArg() instanceof DoNotExpandSP) {
-                    return;
-                }
-
-                //get nodes in this join segment
-                TupleExpr newJoin = null;
-                List<QueryModelNode> args = getJoinArgs(node, new ArrayList<QueryModelNode>(), false);
-                List<TupleExpr> joinArgs = Lists.newArrayList();
-                
-                for (QueryModelNode qNode : args) {
-                    assert (qNode instanceof TupleExpr);
-                    joinArgs.add((TupleExpr) qNode);
-                }
-                
-                //insert all matching ExternalTupleSets in tupList into this segment
-                joinArgs = matchExternalTupleSets(joinArgs, tupList);
-
-                //push down any filters that have bindings in lower segments
-                //and update the filters in this segment
-                updateFilters(segmentFilters, false);
-                
-                //form join from matching ExternalTupleSets, remaining nodes, and filters
-                //that can't be pushed down any further
-                newJoin = getNewJoin(joinArgs, getFilterChain(segmentFilters));
-
-                // Replace old join hierarchy
-                node.replaceWith(newJoin);
-
-                //visit remaining nodes to match ExternalTupleSets with nodes further down
-                for (TupleExpr te : joinArgs) {
-                    if (!(te instanceof StatementPattern) && !(te instanceof ExternalTupleSet)) {
-                        segmentFilters = Lists.newArrayList();
-                        te.visit(this);
-                    }
-                }
-
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-        
-        
-        @Override
-        public void meet(Filter node) {
-            segmentFilters.add(node);
-            node.getArg().visit(this);
-        }
-
-        //chain filters together and return front and back of chain
-        private List<TupleExpr> getFilterChain(List<Filter> filters) {
-            List<TupleExpr> filterTopBottom = Lists.newArrayList();
-            Filter filterChainTop = null;
-            Filter filterChainBottom = null;
-            
-            for (Filter filter: filters) {
-                if (filterChainTop == null) {
-                    filterChainTop = filter;
-                } else if (filterChainBottom == null) {
-                    filterChainBottom = filter;
-                    filterChainTop.setArg(filterChainBottom);
-                } else {
-                    filterChainBottom.setArg(filter);
-                    filterChainBottom = filter;
-                }
-            }
-            if(filterChainTop != null) {
-                filterTopBottom.add(filterChainTop);
-            }
-            if(filterChainBottom != null) {
-                filterTopBottom.add(filterChainBottom);
-            }
-            return filterTopBottom;
-        }
-        
-        //build newJoin node given remaining joinArgs and chain of filters
-        private TupleExpr getNewJoin(List<TupleExpr> args, List<TupleExpr> filterChain) {
-            TupleExpr newJoin;
-            List<TupleExpr> joinArgs = Lists.newArrayList(args);
-
-            if (joinArgs.size() > 1) {
-                if (filterChain.size() > 0) {
-                    TupleExpr finalJoinArg = joinArgs.remove(0);
-                    TupleExpr tempJoin;
-                    TupleExpr temp = filterChain.get(0);
-
-                    if (joinArgs.size() > 1) {
-                        tempJoin = new Join(joinArgs.remove(0), joinArgs.remove(0));
-                        for (TupleExpr te : joinArgs) {
-                            tempJoin = new Join(tempJoin, te);
-                        }
-                    } else {
-                        tempJoin = joinArgs.remove(0);
-                    }
-
-                    if (filterChain.size() == 1) {
-                        ((Filter) temp).setArg(tempJoin);
-                    } else {
-                        ((Filter) filterChain.get(1)).setArg(tempJoin);
-                    }
-                    newJoin = new Join(temp, finalJoinArg);
-                } else {
-                    newJoin = new Join(joinArgs.get(0), joinArgs.get(1));
-                    joinArgs.remove(0);
-                    joinArgs.remove(0);
-
-                    for (TupleExpr te : joinArgs) {
-                        newJoin = new Join(newJoin, te);
-                    }
-                }
-            } else if (joinArgs.size() == 1) {
-                if (filterChain.size() > 0) {
-                    newJoin = filterChain.get(0);
-                    if (filterChain.size() == 1) {
-                        ((Filter) newJoin).setArg(joinArgs.get(0));
-                    } else {
-                        ((Filter) filterChain.get(1)).setArg(joinArgs.get(0));
-                    }
-                } else {
-                    newJoin = joinArgs.get(0);
-                }
-            } else {
-                throw new IllegalStateException("JoinArgs size cannot be zero.");
-            }
-            return newJoin;
-        }
-
-      
-       private List<TupleExpr> matchExternalTupleSets(List<TupleExpr> joinArgs, List<ExternalTupleSet> tupList) {
-           
-           Set<QueryModelNode> argSet = Sets.newHashSet();
-           argSet.addAll(joinArgs);
-           
-           if(argSet.size() < joinArgs.size()) {
-               throw new IllegalArgumentException("Query has duplicate nodes in segment!");
-           }
-           
-           Set<QueryModelNode> firstJoinFilterCond = Sets.newHashSet();
-           
-           for(Filter filter: segmentFilters) {
-               firstJoinFilterCond.add(filter.getCondition());
-           }
-           
-           argSet.addAll(firstJoinFilterCond);
-             
-           //see if ExternalTupleSet nodes are a subset of joinArgs, and if so, replacing matching nodes
-           //with ExternalTupleSet
-            for (ExternalTupleSet tup : tupList) {
-                TupleExpr tupleArg = tup.getTupleExpr();
-                if (isTupleValid(tupleArg)) {
-                    List<QueryModelNode> tupJoinArgs = getJoinArgs(tupleArg, 
-                            new ArrayList<QueryModelNode>(), true);
-                    Set<QueryModelNode> tupJoinArgSet = Sets.newHashSet(tupJoinArgs);
-                    if(tupJoinArgSet.size() < tupJoinArgs.size()) {
-                        throw new IllegalArgumentException("ExternalTuple contains duplicate nodes!");
-                    }
-                    if (argSet.containsAll(tupJoinArgSet)) {
-                        argSet = Sets.newHashSet(Sets.difference(argSet, tupJoinArgSet));
-                        argSet.add((ExternalTupleSet) tup.clone());
-                    }
-                }
-            }
-          
-            //update segment filters by removing those use in ExternalTupleSet
-            Iterator<Filter> iter = segmentFilters.iterator();
-            
-            while(iter.hasNext()) {
-                Filter filt = iter.next();
-                if(!argSet.contains(filt.getCondition())) {
-                    filt.replaceWith(filt.getArg());
-                    iter.remove();
-                }    
-            }
-            
-            //update joinArgs
-            joinArgs = Lists.newArrayList();
-            for(QueryModelNode node: argSet) {
-                if(!(node instanceof ValueExpr)) {
-                    joinArgs.add((TupleExpr)node);
-                }
-            }
-           
-           return joinArgs;
-       }
-
-       
-        private void updateFilters(List<Filter> filters, boolean firstJoin) {
-
-            Iterator<Filter> iter = segmentFilters.iterator();
-
-            while (iter.hasNext()) {
-                if (!FilterRelocator.relocate(iter.next(), firstJoin)) {
-                    iter.remove();
-                }
-            }
-        }
-       
-        protected List<QueryModelNode> getJoinArgs(TupleExpr tupleExpr, List<QueryModelNode> joinArgs, boolean getFilters) {
-            if (tupleExpr instanceof Join) {
-                if (!(((Join) tupleExpr).getLeftArg() instanceof FixedStatementPattern)
-                        && !(((Join) tupleExpr).getRightArg() instanceof DoNotExpandSP)) {
-                    Join join = (Join) tupleExpr;
-                    getJoinArgs(join.getLeftArg(), joinArgs, getFilters);
-                    getJoinArgs(join.getRightArg(), joinArgs, getFilters);
-                } 
-            } else if(tupleExpr instanceof Filter) {
-                if (getFilters) {
-                    joinArgs.add(((Filter) tupleExpr).getCondition());
-                }
-                getJoinArgs(((Filter)tupleExpr).getArg(), joinArgs, getFilters);
-            } else if(tupleExpr instanceof Projection) {
-                getJoinArgs(((Projection)tupleExpr).getArg(), joinArgs, getFilters);
-            } else {
-                joinArgs.add(tupleExpr);
-            }
-
-            return joinArgs;
-        }
-    }
-    
-    protected static class FilterRelocator extends QueryModelVisitorBase<RuntimeException> {
-
-   
-        protected final Filter filter;
-
-        protected final Set<String> filterVars;
-        private boolean stopAtFirstJoin = false;
-        private boolean isFirstJoinFilter = false;
-        private boolean inSegment = true;
-        
-        
-        public FilterRelocator(Filter filter) {
-            this.filter = filter;
-            filterVars = VarNameCollector.process(filter.getCondition());
-        }
-        
-        public FilterRelocator(Filter filter, boolean stopAtFirstJoin) {
-            this.filter = filter;
-            filterVars = VarNameCollector.process(filter.getCondition());
-            this.stopAtFirstJoin = stopAtFirstJoin;
-        }
-        
-        public static boolean relocate(Filter filter) {
-            FilterRelocator fr = new FilterRelocator(filter);
-            filter.visit(fr);
-            return fr.inSegment;
-        }
-        
-        public static boolean relocate(Filter filter, boolean stopAtFirstJoin) {
-            if (stopAtFirstJoin) {
-                FilterRelocator fr = new FilterRelocator(filter, stopAtFirstJoin);
-                filter.visit(fr);
-                return fr.isFirstJoinFilter;
-            } else {
-                FilterRelocator fr = new FilterRelocator(filter);
-                filter.visit(fr);
-                return fr.inSegment;
-            }
-        }
-
-     
-        @Override
-        protected void meetNode(QueryModelNode node) {
-            // By default, do not traverse
-            assert node instanceof TupleExpr;
-            
-            if(node instanceof UnaryTupleOperator) {
-                if (((UnaryTupleOperator)node).getArg().getBindingNames().containsAll(filterVars)) {
-                    if (stopAtFirstJoin) {
-                        ((UnaryTupleOperator) node).getArg().visit(this);
-                    } else {
-                        inSegment = false;
-                        relocate(filter, ((UnaryTupleOperator) node).getArg());
-                    }
-                }
-            }
-            
-            relocate(filter, (TupleExpr) node);
-        }
-       
-
-        @Override
-        public void meet(Join join) {
-
-            if (stopAtFirstJoin) {
-                isFirstJoinFilter = true;
-                relocate(filter, join);
-            } else {
-
-                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)) {
-                inSegment = false;
-                if (stopAtFirstJoin) {
-                    leftJoin.getLeftArg().visit(this);
-                } else {
-                    relocate(filter, leftJoin.getLeftArg());
-                }
-            }
-            else {
-                relocate(filter, leftJoin);
-            }
-        }
-
-        @Override
-        public void meet(Union union) {
-            Filter clone = new Filter();
-            clone.setCondition(filter.getCondition().clone());
-
-            relocate(filter, union.getLeftArg());
-            relocate(clone, union.getRightArg());
-            
-            inSegment = false;
-
-        }
-
-        @Override
-        public void meet(Difference node) {
-            Filter clone = new Filter();
-            clone.setCondition(filter.getCondition().clone());
-        
-            relocate(filter, node.getLeftArg());
-            relocate(clone, node.getRightArg());
-            
-            inSegment = false;
-        
-        }
-
-        @Override
-        public void meet(Intersection node) {
-            Filter clone = new Filter();
-            clone.setCondition(filter.getCondition().clone());
-        
-            relocate(filter, node.getLeftArg());
-            relocate(clone, node.getRightArg());
-            
-            inSegment = false;
-        
-        }
-
-        @Override
-        public void meet(Extension node) {
-            if (node.getArg().getBindingNames().containsAll(filterVars)) {
-                if (stopAtFirstJoin) {
-                    node.getArg().visit(this);
-                } else {
-                    relocate(filter, node.getArg());
-                    inSegment = false;
-                }
-            }
-            else {
-                relocate(filter, node);
-            }
-        }
-
-        @Override
-        public void meet(EmptySet node) {
-            if (filter.getParentNode() != null) {
-                // Remove filter from its original location
-                filter.replaceWith(filter.getArg());
-            }
-        }
-
-        @Override
-        public void meet(Filter filter) {
-            // Filters are commutative
-            filter.getArg().visit(this);
-        }
-
-        @Override
-        public void meet(Distinct node) {
-            node.getArg().visit(this);
-        }
-
-        @Override
-        public void meet(Order node) {
-            node.getArg().visit(this);
-        }
-
-        @Override
-        public void meet(QueryRoot node) {
-            node.getArg().visit(this);
-        }
-
-        @Override
-        public void meet(Reduced node) {
-            node.getArg().visit(this);
-        }
-
-        protected void relocate(Filter filter, TupleExpr newFilterArg) {
-            if (filter.getArg() != newFilterArg) {
-                if (filter.getParentNode() != null) {
-                    // Remove filter from its original location
-                    filter.replaceWith(filter.getArg());
-                }
-
-                // Insert filter at the new location
-                newFilterArg.replaceWith(filter);
-                filter.setArg(newFilterArg);
-            }
-        }
-    }
-    
-    
-    private static boolean isTupleValid(QueryModelNode node) {
-
-        ValidQueryVisitor vqv = new ValidQueryVisitor();
-        node.visit(vqv);
-
-        if (vqv.isValid() && vqv.getSPs().size() > 1) {   
-            if(vqv.getFilters().size() > 0) {
-                Set<String> spVars = getVarNames(vqv.getSPs());
-                Set<String> fVarNames = getVarNames(vqv.getFilters());
-                //check that all vars contained in filters also occur in SPs
-                return Sets.intersection(fVarNames,spVars).equals(fVarNames);
-            } else {
-                return true;
-            }
-        } else {
-            return false;
-        }
-    }
-    
-    
-    private static Set<String> getVarNames(Collection<QueryModelNode> nodes) {
-
-        List<String> tempVars;
-        Set<String> nodeVarNames = Sets.newHashSet();
-
-        for (QueryModelNode s : nodes) {
-            tempVars = VarCollector.process(s);
-            for (String t : tempVars)
-                nodeVarNames.add(t);
-        }
-        return nodeVarNames;
-    }
-    
-    
-    private static class ValidQueryVisitor extends QueryModelVisitorBase<RuntimeException> {
-
-        private boolean isValid = true;
-        private Set<QueryModelNode> filterSet = Sets.newHashSet();
-        private Set<QueryModelNode> spSet = Sets.newHashSet();
-        
-        public Set<QueryModelNode> getFilters() {
-            return filterSet;
-        }
-        
-        public Set<QueryModelNode> getSPs() {
-            return spSet;
-        }
-
-        public boolean isValid() {
-            return isValid;
-        }
-
-        public void meet(Projection node) {
-            node.getArg().visit(this);
-        }
-
-        @Override
-        public void meet(Filter node) {
-            filterSet.add(node.getCondition());
-            node.getArg().visit(this);
-        }
-        
-        @Override
-        public void meet(StatementPattern node) {
-            spSet.add(node);
-        }
-     
-        public void meetNode(QueryModelNode node) {
-
-            if (!((node instanceof Join) || (node instanceof StatementPattern) || (node instanceof BindingSetAssignment) || 
-                    (node instanceof Var) || (node instanceof Union) || (node instanceof LeftJoin))) {
-                isValid = false;
-                return;
-           
-            } else{
-                super.meetNode(node);
-            }
-        }
-
-    }
-    
-    
-    private static List<ExternalTupleSet> getAccIndices(Configuration conf) throws MalformedQueryException,
-            SailException, QueryEvaluationException, TableNotFoundException, AccumuloException,
-            AccumuloSecurityException {
-
-        List<String> tables = null;
-
-        if (conf instanceof RdfCloudTripleStoreConfiguration) {
-            tables = ((RdfCloudTripleStoreConfiguration) conf).getPcjTables();
-        }
-
-        String tablePrefix = conf.get(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX);
-        Connector c = ConfigUtils.getConnector(conf);
-        Map<String, String> indexTables = Maps.newLinkedHashMap();
-
-        if (tables != null && !tables.isEmpty()) {
-            for (String table : tables) {
-                Scanner s = c.createScanner(table, new Authorizations());
-                s.setRange(Range.exact(new Text("~SPARQL")));
-                for (Entry<Key, Value> e : s) {
-                    indexTables.put(table, e.getValue().toString());
-                }
-            }
-        } else {
-            for (String table : c.tableOperations().list()) {
-                if (table.startsWith(tablePrefix + "INDEX")) {
-                    Scanner s = c.createScanner(table, new Authorizations());
-                    s.setRange(Range.exact(new Text("~SPARQL")));
-                    for (Entry<Key, Value> e : s) {
-                        indexTables.put(table, e.getValue().toString());
-                    }
-                }
-            }
-
-        }
-        List<ExternalTupleSet> index = Lists.newArrayList();
-
-        if (indexTables.isEmpty()) {
-            System.out.println("No Index found");
-        } else {
-            for (String table : indexTables.keySet()) {
-                String indexSparqlString = indexTables.get(table);
-                index.add(new AccumuloIndexSet(indexSparqlString, c, table));
-            }
-        }
-        return index;
-    }
+	private List<ExternalTupleSet> indexSet;
+	private Configuration conf;
+	private boolean init = false;
+
+	public PrecompJoinOptimizer() {
+	}
+
+	public PrecompJoinOptimizer(Configuration conf) {
+		this.conf = conf;
+		try {
+			indexSet = getAccIndices(conf);
+		} catch (MalformedQueryException | SailException
+				| QueryEvaluationException | TableNotFoundException
+				| AccumuloException | AccumuloSecurityException | PcjException e) {
+			e.printStackTrace();
+		}
+		init = true;
+	}
+
+	public PrecompJoinOptimizer(List<ExternalTupleSet> indices,
+			boolean useOptimalPcj) {
+		this.indexSet = indices;
+		conf = new Configuration();
+		conf.setBoolean(ConfigUtils.USE_OPTIMAL_PCJ, useOptimalPcj);
+	}
+
+	@Override
+	public void setConf(Configuration conf) {
+		this.conf = conf;
+		if (!init) {
+			try {
+				indexSet = getAccIndices(conf);
+			} catch (MalformedQueryException | SailException
+					| QueryEvaluationException | TableNotFoundException
+					| AccumuloException | AccumuloSecurityException
+					| PcjException e) {
+				e.printStackTrace();
+			}
+			init = true;
+		}
+	}
+
+	@Override
+	public Configuration getConf() {
+		return conf;
+	}
+
+	/**
+	 * @param tupleExpr
+	 *            -- query whose query plan will be optimized -- specified
+	 *            ExternalTupleSet nodes contained in will be placed in query
+	 *            plan where an ExternalTupleSet TupleExpr matches the query's
+	 *            sub-query
+	 */
+	@Override
+	public void optimize(TupleExpr tupleExpr, Dataset dataset,
+			BindingSet bindings) {
+
+		final IndexedExecutionPlanGenerator iep = new IndexedExecutionPlanGenerator(
+				tupleExpr, indexSet);
+		final JoinVisitor jv = new JoinVisitor();
+
+		if (ConfigUtils.getUseOptimalPCJ(conf) && indexSet.size() > 0) {
+
+			// get potential relevant index combinations
+			final ValidIndexCombinationGenerator vic = new ValidIndexCombinationGenerator(
+					tupleExpr);
+			final Iterator<List<ExternalTupleSet>> iter = vic
+					.getValidIndexCombos(iep.getNormalizedIndices());
+			TupleExpr bestTup = null;
+			TupleExpr tempTup = null;
+			double tempCost = 0;
+			double minCost = Double.MAX_VALUE;
+
+			while (iter.hasNext()) {
+				// apply join visitor to place external index nodes in query
+				final TupleExpr clone = tupleExpr.clone();
+				jv.setExternalTupList(iter.next());
+				jv.setSegmentFilters(new ArrayList<Filter>());
+				clone.visit(jv);
+
+				// get all valid execution plans for given external index
+				// combination by considering all
+				// permutations of nodes in TupleExpr
+				final IndexPlanValidator ipv = new IndexPlanValidator(false);
+				final Iterator<TupleExpr> validTups = ipv
+						.getValidTuples(TupleReArranger.getTupleReOrderings(
+								clone).iterator());
+
+				// set valid plan according to a specified cost threshold, where
+				// cost depends on specified weights
+				// for number of external index nodes, common variables among
+				// joins in execution plan, and number of
+				// external products in execution plan
+				final ThreshholdPlanSelector tps = new ThreshholdPlanSelector(
+						tupleExpr);
+				tempTup = tps.getThreshholdQueryPlan(validTups, .4, .5, .2, .3);
+
+				// choose best threshhold TupleExpr among all index node
+				// combinations
+				tempCost = tps.getCost(tempTup, .5, .2, .3);
+				if (tempCost < minCost) {
+					minCost = tempCost;
+					bestTup = tempTup;
+				}
+			}
+			if (bestTup != null) {
+				((UnaryTupleOperator) tupleExpr)
+						.setArg(((UnaryTupleOperator) bestTup).getArg());
+			}
+			return;
+		} else {
+			if (indexSet.size() > 0) {
+				jv.setExternalTupList(iep.getNormalizedIndices());
+				tupleExpr.visit(jv);
+			}
+			return;
+		}
+	}
+
+	/**
+	 * Given a list of @ ExternalTuleSet} , this visitor navigates the query
+	 * {@link TupleExpr} specified in the
+	 * {@link PrecompJoinOptimizer#optimize(TupleExpr, Dataset, BindingSet) and
+	 * matches the TupleExpr in the ExternalTupleSet with sub-queries of the
+	 * query and replaces the sub-query with the ExternalTupleSet node.
+	 *
+	 */
+	protected class JoinVisitor extends QueryModelVisitorBase<RuntimeException> {
+
+		private List<ExternalTupleSet> tupList;
+		private List<Filter> segmentFilters = Lists.newArrayList();
+
+		public void setExternalTupList(List<ExternalTupleSet> tupList) {
+			this.tupList = tupList;
+		}
+
+		public void setSegmentFilters(List<Filter> segmentFilters) {
+			this.segmentFilters = segmentFilters;
+		}
+
+		@Override
+		public void meet(Join node) {
+
+			// get all filters with bindings in this segment
+			updateFilters(segmentFilters, true);
+
+			try {
+				if (node.getLeftArg() instanceof FixedStatementPattern
+						&& node.getRightArg() instanceof DoNotExpandSP) {
+					return;
+				}
+
+				// get nodes in this join segment
+				TupleExpr newJoin = null;
+				final List<QueryModelNode> args = getJoinArgs(node,
+						new ArrayList<QueryModelNode>(), false);
+				List<TupleExpr> joinArgs = Lists.newArrayList();
+
+				for (final QueryModelNode qNode : args) {
+					assert qNode instanceof TupleExpr;
+					joinArgs.add((TupleExpr) qNode);
+				}
+
+				// insert all matching ExternalTupleSets in tupList into this
+				// segment
+				joinArgs = matchExternalTupleSets(joinArgs, tupList);
+
+				// push down any filters that have bindings in lower segments
+				// and update the filters in this segment
+				updateFilters(segmentFilters, false);
+
+				// form join from matching ExternalTupleSets, remaining nodes,
+				// and filters
+				// that can't be pushed down any further
+				newJoin = getNewJoin(joinArgs, getFilterChain(segmentFilters));
+
+				// Replace old join hierarchy
+				node.replaceWith(newJoin);
+
+				// visit remaining nodes to match ExternalTupleSets with nodes
+				// further down
+				for (final TupleExpr te : joinArgs) {
+					if (!(te instanceof StatementPattern)
+							&& !(te instanceof ExternalTupleSet)) {
+						segmentFilters = Lists.newArrayList();
+						te.visit(this);
+					}
+				}
+
+			} catch (final Exception e) {
+				e.printStackTrace();
+			}
+		}
+
+		@Override
+		public void meet(Filter node) {
+			segmentFilters.add(node);
+			node.getArg().visit(this);
+		}
+
+		// chain filters together and return front and back of chain
+		private List<TupleExpr> getFilterChain(List<Filter> filters) {
+			final List<TupleExpr> filterTopBottom = Lists.newArrayList();
+			Filter filterChainTop = null;
+			Filter filterChainBottom = null;
+
+			for (final Filter filter : filters) {
+				filter.replaceWith(filter.getArg());
+				if (filterChainTop == null) {
+					filterChainTop = filter;
+				} else if (filterChainBottom == null) {
+					filterChainBottom = filter;
+					filterChainTop.setArg(filterChainBottom);
+				} else {
+					filterChainBottom.setArg(filter);
+					filterChainBottom = filter;
+				}
+			}
+			if (filterChainTop != null) {
+				filterTopBottom.add(filterChainTop);
+			}
+			if (filterChainBottom != null) {
+				filterTopBottom.add(filterChainBottom);
+			}
+			return filterTopBottom;
+		}
+
+		// build newJoin node given remaining joinArgs and chain of filters
+		private TupleExpr getNewJoin(List<TupleExpr> args,
+				List<TupleExpr> filterChain) {
+			TupleExpr newJoin;
+			final List<TupleExpr> joinArgs = Lists.newArrayList(args);
+
+			if (joinArgs.size() > 1) {
+				if (filterChain.size() > 0) {
+					final TupleExpr finalJoinArg = joinArgs.remove(0);
+					TupleExpr tempJoin;
+					final TupleExpr temp = filterChain.get(0);
+
+					if (joinArgs.size() > 1) {
+						tempJoin = new Join(joinArgs.remove(0),
+								joinArgs.remove(0));
+						for (final TupleExpr te : joinArgs) {
+							tempJoin = new Join(tempJoin, te);
+						}
+					} else {
+						tempJoin = joinArgs.remove(0);
+					}
+
+					if (filterChain.size() == 1) {
+						((Filter) temp).setArg(tempJoin);
+					} else {
+						((Filter) filterChain.get(1)).setArg(tempJoin);
+					}
+					newJoin = new Join(temp, finalJoinArg);
+				} else {
+					newJoin = new Join(joinArgs.get(0), joinArgs.get(1));
+					joinArgs.remove(0);
+					joinArgs.remove(0);
+
+					for (final TupleExpr te : joinArgs) {
+						newJoin = new Join(newJoin, te);
+					}
+				}
+			} else if (joinArgs.size() == 1) {
+				if (filterChain.size() > 0) {
+					newJoin = filterChain.get(0);
+					if (filterChain.size() == 1) {
+						((Filter) newJoin).setArg(joinArgs.get(0));
+					} else {
+						((Filter) filterChain.get(1)).setArg(joinArgs.get(0));
+					}
+				} else {
+					newJoin = joinArgs.get(0);
+				}
+			} else {
+				throw new IllegalStateException("JoinArgs size cannot be zero.");
+			}
+			return newJoin;
+		}
+
+		/**
+		 *
+		 * @param joinArgs
+		 *            -- list of non-join nodes contained in the join segment
+		 * @param tupList
+		 *            -- list of indices to match sub-queries in this join
+		 *            segment
+		 * @return updated list of non-join nodes, where any nodes matching an
+		 *         index are replaced by that index
+		 */
+		private List<TupleExpr> matchExternalTupleSets(
+				List<TupleExpr> joinArgs, List<ExternalTupleSet> tupList) {
+
+			List<TupleExpr> bsaList = new ArrayList<>();
+			Set<QueryModelNode> argSet = Sets.newHashSet();
+			for (TupleExpr te : joinArgs) {
+				if (te instanceof BindingSetAssignment) {
+					bsaList.add(te);
+				} else {
+					argSet.add(te);
+				}
+			}
+
+			if (argSet.size() + bsaList.size() < joinArgs.size()) {
+				throw new IllegalArgumentException(
+						"Query has duplicate nodes in segment!");
+			}
+
+			final Set<QueryModelNode> firstJoinFilterCond = Sets.newHashSet();
+
+			for (final Filter filter : segmentFilters) {
+				firstJoinFilterCond.add(filter.getCondition());
+			}
+
+			argSet.addAll(firstJoinFilterCond);
+
+			// see if ExternalTupleSet nodes are a subset of joinArgs, and if
+			// so, replacing matching nodes
+			// with ExternalTupleSet
+			for (final ExternalTupleSet tup : tupList) {
+				final TupleExpr tupleArg = tup.getTupleExpr();
+				if (isTupleValid(tupleArg)) {
+					final List<QueryModelNode> tupJoinArgs = getJoinArgs(
+							tupleArg, new ArrayList<QueryModelNode>(), true);
+					final Set<QueryModelNode> tupJoinArgSet = Sets
+							.newHashSet(tupJoinArgs);
+					if (tupJoinArgSet.size() < tupJoinArgs.size()) {
+						throw new IllegalArgumentException(
+								"ExternalTuple contains duplicate nodes!");
+					}
+					if (argSet.containsAll(tupJoinArgSet)) {
+						argSet = Sets.newHashSet(Sets.difference(argSet,
+								tupJoinArgSet));
+						argSet.add(tup.clone());
+					}
+				}
+			}
+
+			// update segment filters by removing those use in ExternalTupleSet
+			final Iterator<Filter> iter = segmentFilters.iterator();
+
+			while (iter.hasNext()) {
+				final Filter filt = iter.next();
+				if (!argSet.contains(filt.getCondition())) {
+					filt.replaceWith(filt.getArg());
+					iter.remove();
+				}
+			}
+
+			// update joinArgs
+			joinArgs = Lists.newArrayList();
+			for (final QueryModelNode node : argSet) {
+				if (!(node instanceof ValueExpr)) {
+					joinArgs.add((TupleExpr) node);
+				}
+			}
+			joinArgs.addAll(bsaList);
+
+			return joinArgs;
+		}
+
+		private void updateFilters(List<Filter> filters, boolean firstJoin) {
+
+			final Iterator<Filter> iter = segmentFilters.iterator();
+
+			while (iter.hasNext()) {
+				if (!FilterRelocator.relocate(iter.next(), firstJoin)) {
+					iter.remove();
+				}
+			}
+		}
+
+		/**
+		 *
+		 * @param tupleExpr
+		 *            -- the query
+		 * @param joinArgs
+		 *            -- the non-join nodes contained in the join segment
+		 * @param getFilters
+		 *            -- the filters contained in the query
+		 * @return -- the non-join nodes contained in the join segment
+		 */
+		protected List<QueryModelNode> getJoinArgs(TupleExpr tupleExpr,
+				List<QueryModelNode> joinArgs, boolean getFilters) {
+			if (tupleExpr instanceof Join) {
+				if (!(((Join) tupleExpr).getLeftArg() instanceof FixedStatementPattern)
+						&& !(((Join) tupleExpr).getRightArg() instanceof DoNotExpandSP)) {
+					final Join join = (Join) tupleExpr;
+					getJoinArgs(join.getLeftArg(), joinArgs, getFilters);
+					getJoinArgs(join.getRightArg(), joinArgs, getFilters);
+				}
+			} else if (tupleExpr instanceof Filter) {
+				if (getFilters) {
+					joinArgs.add(((Filter) tupleExpr).getCondition());
+				}
+				getJoinArgs(((Filter) tupleExpr).getArg(), joinArgs, getFilters);
+			} else if (tupleExpr instanceof Projection) {
+				getJoinArgs(((Projection) tupleExpr).getArg(), joinArgs,
+						getFilters);
+			} else {
+				joinArgs.add(tupleExpr);
+			}
+
+			return joinArgs;
+		}
+	}
+
+	/**
+	 * Relocates filters based on the binding variables contained in the
+	 * {@link Filter}. If you don't specify the FilterRelocator to stop at the
+	 * first {@link Join}, the relocator pushes the filter as far down the query
+	 * plan as possible, checking if the nodes below contain its binding
+	 * variables. If stopAtFirstJoin = true, the Filter is inserted at the first
+	 * Join node encountered. The relocator tracks whether the node stays in the
+	 * join segment or is inserted outside of the Join segment and returns true
+	 * if the Filter stays in the segment and false otherwise.
+	 *
+	 */
+
+	protected static class FilterRelocator extends
+			QueryModelVisitorBase<RuntimeException> {
+
+		protected final Filter filter;
+
+		protected final Set<String> filterVars;
+		private boolean stopAtFirstJoin = false;
+		private boolean isFirstJoinFilter = false;
+		private boolean inSegment = true;
+
+		public FilterRelocator(Filter filter) {
+			this.filter = filter;
+			filterVars = VarNameCollector.process(filter.getCondition());
+		}
+
+		public FilterRelocator(Filter filter, boolean stopAtFirstJoin) {
+			this.filter = filter;
+			filterVars = VarNameCollector.process(filter.getCondition());
+			this.stopAtFirstJoin = stopAtFirstJoin;
+		}
+
+		public static boolean relocate(Filter filter) {
+			final FilterRelocator fr = new FilterRelocator(filter);
+			filter.visit(fr);
+			return fr.inSegment;
+		}
+
+		public static boolean relocate(Filter filter, boolean stopAtFirstJoin) {
+			if (stopAtFirstJoin) {
+				final FilterRelocator fr = new FilterRelocator(filter,
+						stopAtFirstJoin);
+				filter.visit(fr);
+				return fr.isFirstJoinFilter;
+			} else {
+				final FilterRelocator fr = new FilterRelocator(filter);
+				filter.visit(fr);
+				return fr.inSegment;
+			}
+		}
+
+		@Override
+		protected void meetNode(QueryModelNode node) {
+			// By default, do not traverse
+			assert node instanceof TupleExpr;
+
+			if (node instanceof UnaryTupleOperator) {
+				if (((UnaryTupleOperator) node).getArg().getBindingNames()
+						.containsAll(filterVars)) {
+					if (stopAtFirstJoin) {
+						((UnaryTupleOperator) node).getArg().visit(this);
+					} else {
+						inSegment = false;
+						relocate(filter, ((UnaryTupleOperator) node).getArg());
+					}
+				}
+			}
+
+			relocate(filter, (TupleExpr) node);
+		}
+
+		@Override
+		public void meet(Join join) {
+
+			if (stopAtFirstJoin) {
+				isFirstJoinFilter = true;
+				relocate(filter, join);
+			} else {
+
+				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)) {
+				inSegment = false;
+				if (stopAtFirstJoin) {
+					leftJoin.getLeftArg().visit(this);
+				} else {
+					relocate(filter, leftJoin.getLeftArg());
+				}
+			} else {
+				relocate(filter, leftJoin);
+			}
+		}
+
+		@Override
+		public void meet(Union union) {
+			final Filter clone = new Filter();
+			clone.setCondition(filter.getCondition().clone());
+
+			relocate(filter, union.getLeftArg());
+			relocate(clone, union.getRightArg());
+
+			inSegment = false;
+
+		}
+
+		@Override
+		public void meet(Difference node) {
+			final Filter clone = new Filter();
+			clone.setCondition(filter.getCondition().clone());
+
+			relocate(filter, node.getLeftArg());
+			relocate(clone, node.getRightArg());
+
+			inSegment = false;
+
+		}
+
+		@Override
+		public void meet(Intersection node) {
+			final Filter clone = new Filter();
+			clone.setCondition(filter.getCondition().clone());
+
+			relocate(filter, node.getLeftArg());
+			relocate(clone, node.getRightArg());
+
+			inSegment = false;
+
+		}
+
+		@Override
+		public void meet(Extension node) {
+			if (node.getArg().getBindingNames().containsAll(filterVars)) {
+				if (stopAtFirstJoin) {
+					node.getArg().visit(this);
+				} else {
+					relocate(filter, node.getArg());
+					inSegment = false;
+				}
+			} else {
+				relocate(filter, node);
+			}
+		}
+
+		@Override
+		public void meet(EmptySet node) {
+			if (filter.getParentNode() != null) {
+				// Remove filter from its original location
+				filter.replaceWith(filter.getArg());
+			}
+		}
+
+		@Override
+		public void meet(Filter filter) {
+			// Filters are commutative
+			filter.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(Distinct node) {
+			node.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(Order node) {
+			node.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(QueryRoot node) {
+			node.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(Reduced node) {
+			node.getArg().visit(this);
+		}
+
+		protected void relocate(Filter filter, TupleExpr newFilterArg) {
+			if (filter.getArg() != newFilterArg) {
+				if (filter.getParentNode() != null) {
+					// Remove filter from its original location
+					filter.replaceWith(filter.getArg());
+				}
+
+				// Insert filter at the new location
+				newFilterArg.replaceWith(filter);
+				filter.setArg(newFilterArg);
+			}
+		}
+	}
+
+	/**
+	 * This method determines whether an index node is valid. Criteria for a
+	 * valid node are that is have two or more {@link StatementPattern} nodes or
+	 * at least one {@link Filter} and one StatementPattern node. Additionally,
+	 * the number of variables in the Filter cannot exceed the number of
+	 * variables among all non-Filter nodes in the TupleExpr. Also, this method
+	 * calls the {@link ValidQueryVisitor} to determine if the
+	 * ExternalTupleSet's TupleExpr contains an invalid node type.
+	 *
+	 * @param node
+	 *            -- typically an {@link ExternalTupleSet} index node
+	 * @return
+	 */
+	private static boolean isTupleValid(QueryModelNode node) {
+
+		final ValidQueryVisitor vqv = new ValidQueryVisitor();
+		node.visit(vqv);
+
+		if (vqv.isValid() && vqv.getSPs().size() + vqv.getFilters().size() > 1) {
+			if (vqv.getFilters().size() > 0) {
+				final Set<String> spVars = getVarNames(vqv.getSPs());
+				final Set<String> fVarNames = getVarNames(vqv.getFilters());
+				// check that all vars contained in filters also occur in SPs
+				return Sets.intersection(fVarNames, spVars).equals(fVarNames);
+			} else {
+				return true;
+			}
+		} else {
+			return false;
+		}
+	}
+
+	private static Set<String> getVarNames(Collection<QueryModelNode> nodes) {
+
+		List<String> tempVars;
+		final Set<String> nodeVarNames = Sets.newHashSet();
+
+		for (final QueryModelNode s : nodes) {
+			tempVars = VarCollector.process(s);
+			for (final String t : tempVars) {
+				nodeVarNames.add(t);
+			}
+		}
+		return nodeVarNames;
+	}
+
+	/**
+	 * A visitor which checks a TupleExpr associated with an ExternalTupleSet to
+	 * determine whether the TupleExpr contains an invalid node.
+	 *
+	 */
+	private static class ValidQueryVisitor extends
+			QueryModelVisitorBase<RuntimeException> {
+
+		private boolean isValid = true;
+		private final Set<QueryModelNode> filterSet = Sets.newHashSet();
+		private final Set<QueryModelNode> spSet = Sets.newHashSet();
+
+		public Set<QueryModelNode> getFilters() {
+			return filterSet;
+		}
+
+		public Set<QueryModelNode> getSPs() {
+			return spSet;
+		}
+
+		public boolean isValid() {
+			return isValid;
+		}
+
+		@Override
+		public void meet(Projection node) {
+			node.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(Filter node) {
+			filterSet.add(node.getCondition());
+			node.getArg().visit(this);
+		}
+
+		@Override
+		public void meet(StatementPattern node) {
+			spSet.add(node);
+		}
+
+		@Override
+		public void meetNode(QueryModelNode node) {
+
+			if (!(node instanceof Join || node instanceof StatementPattern
+					|| node instanceof BindingSetAssignment
+					|| node instanceof Var || node instanceof Union || node instanceof LeftJoin)) {
+				isValid = false;
+				return;
+
+			} else {
+				super.meetNode(node);
+			}
+		}
+
+	}
+
+	private static List<ExternalTupleSet> getAccIndices(Configuration conf)
+			throws MalformedQueryException, SailException,
+			QueryEvaluationException, TableNotFoundException,
+			AccumuloException, AccumuloSecurityException, PcjException {
+
+		List<String> tables = null;
+
+		if (conf instanceof RdfCloudTripleStoreConfiguration) {
+			tables = ((RdfCloudTripleStoreConfiguration) conf).getPcjTables();
+		}
+
+		final String tablePrefix = conf
+				.get(RdfCloudTripleStoreConfiguration.CONF_TBL_PREFIX);
+		final Connector c = ConfigUtils.getConnector(conf);
+		final Map<String, String> indexTables = Maps.newLinkedHashMap();
+		PcjTables pcj = new PcjTables();
+
+		if (tables != null && !tables.isEmpty()) {
+			for (final String table : tables) {
+				indexTables
+						.put(table, pcj.getPcjMetadata(c, table).getSparql());
+			}
+		} else {
+			for (final String table : c.tableOperations().list()) {
+				if (table.startsWith(tablePrefix + "INDEX")) {
+					indexTables.put(table, pcj.getPcjMetadata(c, table)
+							.getSparql());
+				}
+			}
+
+		}
+		final List<ExternalTupleSet> index = Lists.newArrayList();
+
+		if (indexTables.isEmpty()) {
+			System.out.println("No Index found");
+		} else {
+			for (final String table : indexTables.keySet()) {
+				final String indexSparqlString = indexTables.get(table);
+				index.add(new AccumuloIndexSet(indexSparqlString, c, table));
+			}
+		}
+		return index;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/c12f58f4/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/AccumuloIndexSet.java
----------------------------------------------------------------------
diff --git a/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/AccumuloIndexSet.java b/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/AccumuloIndexSet.java
index dda452d..456c465 100644
--- a/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/AccumuloIndexSet.java
+++ b/extras/indexing/src/main/java/mvm/rya/indexing/external/tupleSet/AccumuloIndexSet.java
@@ -8,9 +8,9 @@ package mvm.rya.indexing.external.tupleSet;
  * 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
@@ -19,608 +19,373 @@ package mvm.rya.indexing.external.tupleSet;
  * under the License.
  */
 
-
-
 import info.aduna.iteration.CloseableIteration;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import mvm.rya.accumulo.precompQuery.AccumuloPrecompQueryIndexer;
+import mvm.rya.accumulo.precompQuery.AccumuloPcjQuery;
+import mvm.rya.api.utils.IteratorWrapper;
+import mvm.rya.indexing.PcjQuery;
+import mvm.rya.indexing.external.tupleSet.PcjTables.PcjException;
+import mvm.rya.indexing.external.tupleSet.PcjTables.PcjMetadata;
+import mvm.rya.indexing.external.tupleSet.PcjTables.VariableOrder;
 import mvm.rya.rdftriplestore.evaluation.ExternalBatchingIterator;
 
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
-import org.apache.accumulo.core.client.BatchWriter;
 import org.apache.accumulo.core.client.Connector;
 import org.apache.accumulo.core.client.MutationsRejectedException;
-import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.client.TableNotFoundException;
-import org.apache.accumulo.core.data.Key;
-import org.apache.accumulo.core.data.Mutation;
-import org.apache.accumulo.core.data.Range;
-import org.apache.accumulo.core.data.Value;
-import org.apache.accumulo.core.security.Authorizations;
-import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.io.Text;
-import org.openrdf.model.Literal;
-import org.openrdf.model.URI;
-import org.openrdf.model.impl.LiteralImpl;
-import org.openrdf.model.impl.URIImpl;
 import org.openrdf.query.Binding;
 import org.openrdf.query.BindingSet;
 import org.openrdf.query.MalformedQueryException;
 import org.openrdf.query.QueryEvaluationException;
 import org.openrdf.query.algebra.Projection;
-import org.openrdf.query.algebra.QueryModelNode;
-import org.openrdf.query.algebra.StatementPattern;
-import org.openrdf.query.algebra.ValueExpr;
+import org.openrdf.query.algebra.TupleExpr;
 import org.openrdf.query.algebra.Var;
 import org.openrdf.query.algebra.helpers.QueryModelVisitorBase;
-import org.openrdf.query.impl.EmptyBindingSet;
 import org.openrdf.query.parser.ParsedTupleQuery;
 import org.openrdf.query.parser.sparql.SPARQLParser;
-import org.openrdf.repository.sail.SailRepositoryConnection;
 import org.openrdf.sail.SailException;
 
-import com.beust.jcommander.internal.Sets;
 import com.google.common.base.Joiner;
-import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-
+import com.google.common.collect.Sets;
+
+/**
+ * During query planning, this node is inserted into the parsed query to
+ * represent part of the original query (a sub-query). This sub-query is the
+ * value returned by {@link ExternalTupleSet#getTupleExpr()}. The results
+ * associated with this sub-query are stored in an external Accumulo table,
+ * where accCon and tablename are the associated {@link Connector} and table
+ * name. During evaluation, the portion of the query in
+ * {@link AccumuloIndexSet} is evaluated by scanning the external Accumulo
+ * table. This class is extremely useful for caching queries and reusing results
+ * from previous SPARQL queries.
+ * <p>
+ *
+ * The the {@link TupleExpr} returned by {@link ExternalTupleSet#getTupleExpr()}
+ * may have different variables than the query and variables stored in the
+ * external Accumulo table. The mapping of variables from the TupleExpr to the
+ * table variables are given by {@link ExternalTupleSet#getTableVarMap()}. In
+ * addition to allowing the variables to differ, it is possible for TupleExpr to
+ * have fewer variables than the table query--that is, some of the variables in
+ * the table query may appear as constants in the TupleExpr. Theses expression
+ * are extracted from TupleExpr by the methods
+ * {@link AccumuloIndexSet#getConstantConstraints()} and by the Visitor
+ * {@link ValueMapVisitor} to be used as constraints when scanning the Accumulo
+ * table. This allows for the pre-computed results to be used for a larger class
+ * of sub-queries.
+ *
+ */
 public class AccumuloIndexSet extends ExternalTupleSet implements ExternalBatchingIterator {
 
-    private static final int WRITER_MAX_WRITE_THREADS = 30;
-    private static final long WRITER_MAX_LATNECY = Long.MAX_VALUE;
-    private static final long WRITER_MAX_MEMORY = 500L * 1024L * 1024L;
-    private Map<String,AccValueFactory> bindings;
-    private List<String> bindingslist;
-    private final Connector accCon;
-    private final String tablename;
-    private long tableSize = 0;
-    private List<String> varOrder = null; 
-    
-    
-    public static interface AccValueFactory {
-        public org.openrdf.model.Value create(String str);
-
-        public String create(org.openrdf.model.Value val);
-    }
-
-    public static class AccUrlFactory implements AccValueFactory {
-        @Override
-        public org.openrdf.model.Value create(final String str) {
-            return new URIImpl(str);
-        }
+    private final Connector accCon;  //connector to Accumulo table where results are stored
+    private final String tablename;  //name of Accumulo table
+    private List<String> varOrder = null; // orders in which results are written to table
+    private PcjTables pcj = new PcjTables();
 
-        @Override
-        public String create(org.openrdf.model.Value val) {
-            return val.stringValue();
-        }
+    @Override
+    public Map<String, Set<String>> getSupportedVariableOrders() {
+        return this.getSupportedVariableOrderMap();
     }
-    
-    public static class AccValueFactoryImpl implements AccValueFactory {
-        @Override
-        public org.openrdf.model.Value create(final String str) {
-            String[] split = str.split("\u0001");
-            if (split.length > 1 && split[1].equals("1")) {
-                return new URIImpl(split[0]);
-            }
-            if (split[0].contains(":")) {
-                return new URIImpl(split[0]);
-            }
-            return new LiteralImpl(split[0]);
-        }
 
-        @Override
-        public String create(org.openrdf.model.Value val) {
-            if (val instanceof URI) {
-                return val.stringValue() + "\u0001" + 1;
-            }
-            if (val instanceof Literal) {
-                Literal v = (Literal) val;
-                return v.getLabel() + "\u0001" + 2;
-            }
-            return null;
-        }
+    @Override
+    public String getSignature() {
+        return "AccumuloIndexSet(" + tablename + ") : " + Joiner.on(", ").join(this.getTupleExpr().getAssuredBindingNames());
     }
 
-    
-    //TODO set supportedVarOrderMap 
-    public AccumuloIndexSet(String sparql, SailRepositoryConnection conn, Connector accCon, String tablename) throws MalformedQueryException, SailException,
-            QueryEvaluationException, MutationsRejectedException, TableNotFoundException {
-        super(null);
+    /**
+     *
+     * @param sparql - name of sparql query whose results will be stored in PCJ table
+     * @param accCon - connection to a valid Accumulo instance
+     * @param tablename - name of an existing PCJ table
+     * @throws MalformedQueryException
+     * @throws SailException
+     * @throws QueryEvaluationException
+     * @throws MutationsRejectedException
+     * @throws TableNotFoundException
+     */
+    public AccumuloIndexSet(String sparql, Connector accCon, String tablename) throws MalformedQueryException, SailException, QueryEvaluationException,
+            MutationsRejectedException, TableNotFoundException {
         this.tablename = tablename;
         this.accCon = accCon;
-        SPARQLParser sp = new SPARQLParser();
-        ParsedTupleQuery pq = (ParsedTupleQuery) sp.parseQuery(sparql, null);
-
+        final SPARQLParser sp = new SPARQLParser();
+        final ParsedTupleQuery pq = (ParsedTupleQuery) sp.parseQuery(sparql, null);
         setProjectionExpr((Projection) pq.getTupleExpr());
-        CloseableIteration<BindingSet,QueryEvaluationException> iter = (CloseableIteration<BindingSet,QueryEvaluationException>) conn.getSailConnection()
-                .evaluate(getTupleExpr(), null, new EmptyBindingSet(), false);
-
-        BatchWriter w = accCon.createBatchWriter(tablename, WRITER_MAX_MEMORY, WRITER_MAX_LATNECY, WRITER_MAX_WRITE_THREADS);
-        this.bindingslist = Lists.newArrayList(pq.getTupleExpr().getAssuredBindingNames());
-
-        this.bindings = Maps.newHashMap();
-
-        pq.getTupleExpr().visit(new QueryModelVisitorBase<RuntimeException>() {
-            @Override
-            public void meet(Var node) {
-                QueryModelNode parent = node.getParentNode();
-                if (parent instanceof StatementPattern) {
-                    StatementPattern statement = (StatementPattern) parent;
-                    if (node.equals(statement.getSubjectVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getPredicateVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getObjectVar())) {
-                        bindings.put(node.getName(), new AccValueFactoryImpl());
-                    }
-                    if (node.equals(statement.getContextVar())) {
-                        // TODO is this correct?
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                } else if(parent instanceof ValueExpr) {
-                    bindings.put(node.getName(), new AccValueFactoryImpl());
-                } 
-            };
-        });
-        
-        
-        
-        
-       
-        varOrder = new ArrayList<String>(bindingslist.size());
- 
-        while (iter.hasNext()) {
-            
-            BindingSet bs = iter.next();
-            List<String> shiftBindingList = null;
-            for (int j = 0; j < bindingslist.size(); j++) {
-                StringBuffer sb = new StringBuffer();
-                shiftBindingList = listShift(bindingslist, j);  //TODO calling this each time not efficient
-                String order = "";
-                for (String b : shiftBindingList) {
-                    String val = bindings.get(b).create(bs.getValue(b));
-                    sb.append(val).append("\u0000");
-                    if (order.length() == 0) {
-                        order = b;
-                    } else {
-                        order = order + "\u0000" + b;
-                    }
-                }
-                
-                if (varOrder.size() < bindingslist.size()) {
-                    varOrder.add(order);
-                }
-                
-                //System.out.println("String buffer is " + sb);
-                Mutation m = new Mutation(sb.deleteCharAt(sb.length() - 1).toString());
-                m.put(new Text(varOrder.get(j)), new Text(""), new org.apache.accumulo.core.data.Value(new byte[]{}));
-                w.addMutation(m);
-            }
-            tableSize += 1;
+        Set<VariableOrder> orders = null;
+        try {
+			orders = pcj.getPcjMetadata(accCon, tablename).getVarOrders();
+		} catch (final PcjException e) {
+			e.printStackTrace();
+		}
+
+        varOrder = Lists.newArrayList();
+        for(final VariableOrder var: orders) {
+            varOrder.add(var.toString());
         }
-        
         setLocalityGroups(tablename, accCon, varOrder);
-        this.setSupportedVariableOrderMap(createSupportedVarOrderMap(varOrder));
-         
-        
-        String orders = "";
-        
-        for(String s : varOrder) {
-            s = s.replace("\u0000", ";");
-            if(orders.length() == 0) {
-                orders = s;
-            } else {
-                orders = orders + "\u0000" + s;
-            }
-        }
-        
-        
-        Mutation m = new Mutation("~SPARQL");
-        Value v = new Value(sparql.getBytes());
-        m.put(new Text("" + tableSize), new Text(orders), v);
-        w.addMutation(m);
-     
-        w.close();
-        iter.close();
+        this.setSupportedVariableOrderMap(varOrder);
     }
 
-    
-    
-    
+    /**
+     *
+     * @param accCon - connection to a valid Accumulo instance
+     * @param tablename - name of an existing PCJ table
+     * @throws MalformedQueryException
+     * @throws SailException
+     * @throws QueryEvaluationException
+     * @throws MutationsRejectedException
+     * @throws TableNotFoundException
+     */
+	public AccumuloIndexSet(Connector accCon, String tablename)
+			throws MalformedQueryException, SailException,
+			QueryEvaluationException, MutationsRejectedException,
+			TableNotFoundException {
+		PcjMetadata meta = null;
+		try {
+			meta = pcj.getPcjMetadata(accCon, tablename);
+		} catch (final PcjException e) {
+			e.printStackTrace();
+		}
+
+		this.tablename = tablename;
+		this.accCon = accCon;
+		final SPARQLParser sp = new SPARQLParser();
+		final ParsedTupleQuery pq = (ParsedTupleQuery) sp.parseQuery(meta.getSparql(),
+				null);
+		setProjectionExpr((Projection) pq.getTupleExpr());
+		final Set<VariableOrder> orders = meta.getVarOrders();
+
+		varOrder = Lists.newArrayList();
+		for (final VariableOrder var : orders) {
+			varOrder.add(var.toString());
+		}
+		setLocalityGroups(tablename, accCon, varOrder);
+		this.setSupportedVariableOrderMap(varOrder);
+	}
+
+	/**
+	 * returns size of table for query planning
+	 */
     @Override
-    public Map<String, Set<String>> getSupportedVariableOrders() {
-   
-        return this.getSupportedVariableOrderMap();
-
+    public double cardinality() {
+    	double cardinality = 0;
+        try {
+			cardinality = pcj.getPcjMetadata(accCon, tablename).getCardinality();
+		} catch (PcjException e) {
+			e.printStackTrace();
+		}
+        return cardinality;
     }
-    
-    
-    @Override
-    public boolean supportsBindingSet(Set<String> bindingNames) {
-
-        Map<String, Set<String>> varOrderMap = this.getSupportedVariableOrders();
-        Collection<Set<String>> values = varOrderMap.values();
-        Set<String> bNames = Sets.newHashSet();
 
-        for (String s : this.getTupleExpr().getAssuredBindingNames()) {
-            if (bindingNames.contains(s)) {
-                bNames.add(s);
-            }
-        }
-
-        return values.contains(bNames);
-    }
-        
-    
-    private String getVarOrder(Set<String> variables) {
 
-        Map<String, Set<String>> varOrderMap = this.getSupportedVariableOrders();
+    /**
+     *
+     * @param tableName
+     * @param conn
+     * @param groups  - locality groups to be created
+     *
+     * Sets locality groups for more efficient scans - these are usually the variable
+     * orders in the table so that scans for specific orders are more efficient
+     */
+    private void setLocalityGroups(String tableName, Connector conn, List<String> groups) {
 
-        Set<Map.Entry<String, Set<String>>> entries = varOrderMap.entrySet();
+		final HashMap<String, Set<Text>> localityGroups = new HashMap<String, Set<Text>>();
+		for (int i = 0; i < groups.size(); i++) {
+			final HashSet<Text> tempColumn = new HashSet<Text>();
+			tempColumn.add(new Text(groups.get(i)));
+			final String groupName = groups.get(i).replace(VAR_ORDER_DELIM, "");
+			localityGroups.put(groupName, tempColumn);
+		}
 
-        for (Map.Entry<String, Set<String>> e : entries) {
+		try {
+			conn.tableOperations().setLocalityGroups(tableName, localityGroups);
+		} catch (AccumuloException | AccumuloSecurityException
+				| TableNotFoundException e) {
+			e.printStackTrace();
+		}
 
-            if (e.getValue().equals(variables)) {
-                return e.getKey();
-            }
+	}
 
-        }
 
-        return null;
 
+    @Override
+    public CloseableIteration<BindingSet,QueryEvaluationException> evaluate(BindingSet bindingset) throws QueryEvaluationException {
+        return this.evaluate(Collections.singleton(bindingset));
     }
 
-    private String prefixToOrder(String order) {
-
-        Map<String, String> invMap = HashBiMap.create(this.getTableVarMap()).inverse();
-        String[] temp = order.split("\u0000");
-
-        for (int i = 0; i < temp.length; i++) {
-            temp[i] = this.getTableVarMap().get(temp[i]);
+    /**
+     * Core evaluation method used during query evaluation - given a collection of binding set constraints, this
+     * method finds common binding labels between the constraints and table, uses those to build a prefix scan
+     * of the Accumulo table, and creates a solution binding set by iterating of the scan results.
+     */
+    @Override
+    public CloseableIteration<BindingSet,QueryEvaluationException> evaluate(final Collection<BindingSet> bindingset) throws QueryEvaluationException {
+        String localityGroup = "";
+        final Set<String> commonVars = Sets.newHashSet();
+        // if bindingset is empty, there are no results, so return empty iterator
+        if (bindingset.isEmpty()) {
+        	return new IteratorWrapper<BindingSet, QueryEvaluationException>(new HashSet<BindingSet>().iterator());
         }
-
-        order = Joiner.on("\u0000").join(temp);
-
-        for (String s : varOrder) {
-            if (s.startsWith(order)) {
-
-                temp = s.split("\u0000");
-
-                for (int i = 0; i < temp.length; i++) {
-                    temp[i] = invMap.get(temp[i]);
+      //to build range prefix, find common vars of bindingset and PCJ bindings
+        else {
+        	final BindingSet bs = bindingset.iterator().next();
+            for (final String b : this.getTupleExpr().getAssuredBindingNames()) {
+                final Binding v = bs.getBinding(b);
+                if (v != null) {
+                    commonVars.add(b);
                 }
-                return Joiner.on("\u0000").join(temp);
             }
         }
-        throw new NoSuchElementException("Order is not a prefix of any locality group value!");
+        //add any constant constraints to common vars to be used in range prefix
+        commonVars.addAll(getConstantConstraints());
+        PcjQuery apq = null;
+        List<String> fullVarOrder =  null;
+        String commonVarOrder = null;
+        try {
+            if (commonVars.size() > 0) {
+                commonVarOrder = getVarOrder(commonVars);
+                if(commonVarOrder == null) {
+                    throw new IllegalStateException("Index does not support binding set!");
+                }
+                fullVarOrder = Lists.newArrayList(prefixToOrder(commonVarOrder).split(VAR_ORDER_DELIM));
+                //use varOrder and tableVarMap to set correct scan column
+                localityGroup = orderToLocGroup(fullVarOrder);
+            } else {
+                localityGroup = varOrder.get(0);
+            }
+            apq = new AccumuloPcjQuery(accCon, tablename);
+            final ValueMapVisitor vmv = new ValueMapVisitor();
+            this.getTupleExpr().visit(vmv);
+
+            List<String> commonVarOrderList = null;
+            if(commonVarOrder != null) {
+            	commonVarOrderList = Lists.newArrayList(commonVarOrder.split(VAR_ORDER_DELIM));
+            } else {
+            	commonVarOrderList = new ArrayList<>();
+            }
+
+            return apq.queryPrecompJoin(commonVarOrderList, localityGroup, vmv.getValMap(),
+            		HashBiMap.create(this.getTableVarMap()).inverse(), bindingset);
+        } catch(final TableNotFoundException e) {
+            throw new QueryEvaluationException(e);
+        }
     }
 
+    /**
+     *
+     * @param order - variable order as indicated by query
+     * @return - locality group or column family used in scan - this
+     * is just the variable order expressed in terms of the variables stored
+     * in the table
+     */
     private String orderToLocGroup(List<String> order) {
         String localityGroup = "";
-        for (String s : order) {
+        for (final String s : order) {
             if (localityGroup.length() == 0) {
                 localityGroup = this.getTableVarMap().get(s);
             } else {
-                localityGroup = localityGroup + "\u0000" + this.getTableVarMap().get(s);
+                localityGroup = localityGroup + VAR_ORDER_DELIM + this.getTableVarMap().get(s);
             }
         }
         return localityGroup;
-
     }
-    
-    
-    private void setLocalityGroups(String tableName, Connector conn, List<String> groups) {
-        
-        HashMap<String, Set<Text>> localityGroups = new HashMap<String, Set<Text>>();
-
-        
-        
-        for (int i = 0; i < groups.size(); i++) {
-            HashSet<Text> tempColumn = new HashSet<Text>();
-            tempColumn.add(new Text(groups.get(i)));
-            String groupName = groups.get(i).replace("\u0000","");
-            localityGroups.put(groupName, tempColumn);
-        }
-        
 
-        try {
-            conn.tableOperations().setLocalityGroups(tableName, localityGroups);
-        } catch (AccumuloException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        } catch (AccumuloSecurityException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        } catch (TableNotFoundException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-
-        
-        
-    }
-    
-    
-    
-    
-    
-    
-    
-    private List<String> listShift(List<String> list, int j) {
-        
-        if(j >= list.size()) {
-            throw new IllegalArgumentException();
-        }
-        
-        List<String> shiftList = Lists.newArrayList();
-        for(int i=0; i<list.size(); i++) {
-            shiftList.add(list.get((i+j)%list.size()));
+    /**
+     *
+     * @param order - prefix of a full variable order
+     * @return - full variable order that includes all variables whose values
+     * are stored in the table - used to obtain the locality group
+     */
+    //given partial order of query vars, convert to PCJ vars and determine
+    //if converted partial order is a substring of a full var order of PCJ variables.
+    //if converted partial order is a prefix, convert corresponding full PCJ var order to query vars
+    private String prefixToOrder(String order) {
+        final Map<String, String> invMap = HashBiMap.create(this.getTableVarMap()).inverse();
+        String[] temp = order.split(VAR_ORDER_DELIM);
+        //get order in terms of PCJ variables
+        for (int i = 0; i < temp.length; i++) {
+            temp[i] = this.getTableVarMap().get(temp[i]);
         }
-
-        return shiftList;
-    }
-    
-    
-    
-    private Set<String> getConstantConstraints() {
-
-        Map<String, String> tableMap = this.getTableVarMap();
-        Set<String> keys = tableMap.keySet();
-        Set<String> constants = Sets.newHashSet();
-
-        for (String s : keys) {
-            if (s.startsWith("-const-")) {
-                constants.add(s);
+        order = Joiner.on(VAR_ORDER_DELIM).join(temp);
+        for (final String s : varOrder) {
+        	//verify that partial order is prefix of a PCJ varOrder
+            if (s.startsWith(order)) {
+                temp = s.split(VAR_ORDER_DELIM);
+                //convert full PCJ varOrder back to query varOrder
+                for (int i = 0; i < temp.length; i++) {
+                    temp[i] = invMap.get(temp[i]);
+                }
+                return Joiner.on(VAR_ORDER_DELIM).join(temp);
             }
-
         }
-
-        return constants;
-
-    }
-    
-    
-    
-    
-    public AccumuloIndexSet(String sparql, Connector accCon, String tablename) throws MalformedQueryException, SailException, QueryEvaluationException,
-            MutationsRejectedException, TableNotFoundException {
-        super(null);
-        this.tablename = tablename;
-        this.accCon = accCon;
-        SPARQLParser sp = new SPARQLParser();
-        ParsedTupleQuery pq = (ParsedTupleQuery) sp.parseQuery(sparql, null);
-
-        setProjectionExpr((Projection) pq.getTupleExpr());
-
-        this.bindingslist = Lists.newArrayList(pq.getTupleExpr().getAssuredBindingNames());
-
-        this.bindings = Maps.newHashMap();
-        pq.getTupleExpr().visit(new QueryModelVisitorBase<RuntimeException>() {
-            @Override
-            public void meet(Var node) {
-                QueryModelNode parent = node.getParentNode();
-                if (parent instanceof StatementPattern) {
-                    StatementPattern statement = (StatementPattern) parent;
-                    if (node.equals(statement.getSubjectVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getPredicateVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getObjectVar())) {
-                        bindings.put(node.getName(), new AccValueFactoryImpl());
-                    }
-                    if (node.equals(statement.getContextVar())) {
-                        // TODO is this correct?
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                } else if(parent instanceof ValueExpr) {
-                    bindings.put(node.getName(), new AccValueFactoryImpl());
-                } 
-            };
-        });
-        
-        
-        
-        
-        Scanner s = accCon.createScanner(tablename, new Authorizations()); 
-        s.setRange(Range.exact(new Text("~SPARQL")));       
-        Iterator<Entry<Key,Value>> i = s.iterator();
-        
-        String[] tempVarOrders = null;
-        
-        if (i.hasNext()) {
-            Entry<Key, Value> entry = i.next();
-            Text ts = entry.getKey().getColumnFamily();
-            tempVarOrders = entry.getKey().getColumnQualifier().toString().split("\u0000");
-            tableSize = Long.parseLong(ts.toString());
-
-        } else {
-            throw new IllegalStateException("Index table contains no metadata!");
-        }      
-
-        
-        varOrder = Lists.newArrayList();
-        
-        for(String t: tempVarOrders) {
-            t = t.replace(";","\u0000");
-            varOrder.add(t);
-        }
-        
-        setLocalityGroups(tablename, accCon, varOrder);
-        this.setSupportedVariableOrderMap(createSupportedVarOrderMap(varOrder));
-        
+        throw new NoSuchElementException("Order is not a prefix of any locality group value!");
     }
 
-    
-    
-    
-    private Map<String, Set<String>> createSupportedVarOrderMap(List<String> orders) {
-        
-        Map<String, Set<String>> supportedVars = Maps.newHashMap();
-        
-        for (String t : orders) {
-            
-            String[] tempOrder = t.split("\u0000");
-            Set<String> varSet = Sets.newHashSet();
-            String u = "";
-            
-            for (String s : tempOrder) {
-                if(u.length() == 0) {
-                    u = s;
-                } else{
-                    u = u+ "\u0000" + s;
-                }
-                varSet.add(s);
-                supportedVars.put(u, new HashSet<String>(varSet));
-
+    /**
+     *
+     * @param variables
+     * @return - string representation of the Set variables, in an order that is in the
+     * table
+     */
+    private String getVarOrder(Set<String> variables) {
+        final Map<String, Set<String>> varOrderMap = this.getSupportedVariableOrders();
+        final Set<Map.Entry<String, Set<String>>> entries = varOrderMap.entrySet();
+        for (final Map.Entry<String, Set<String>> e : entries) {
+            if (e.getValue().equals(variables)) {
+                return e.getKey();
             }
-
         }
-        
-        return supportedVars;
-    }
-    
-    
-    
-    @Override
-    public void setProjectionExpr(Projection tupleExpr) {
-        super.setProjectionExpr(tupleExpr);
-        this.bindingslist = Lists.newArrayList(tupleExpr.getAssuredBindingNames());
-
-        this.bindings = Maps.newHashMap();
-        tupleExpr.visit(new QueryModelVisitorBase<RuntimeException>() {
-            @Override
-            public void meet(Var node) {
-                QueryModelNode parent = node.getParentNode();
-                if (parent instanceof StatementPattern) {
-                    StatementPattern statement = (StatementPattern) parent;
-                    if (node.equals(statement.getSubjectVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getPredicateVar())) {
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                    if (node.equals(statement.getObjectVar())) {
-                        bindings.put(node.getName(), new AccValueFactoryImpl());
-                    }
-                    if (node.equals(statement.getContextVar())) {
-                        // TODO is this correct?
-                        bindings.put(node.getName(), new AccUrlFactory());
-                    }
-                } else if (parent instanceof ValueExpr) {  //Add bindings associated with Filters
-                    bindings.put(node.getName(), new AccValueFactoryImpl());
-                } 
-            };
-        });
-
-    }
-
-    @Override
-    public String getSignature() {
-        return "AccumuloIndexSet(" + tablename + ") : " + Joiner.on(", ").join(bindingslist);
-    }
-
-    @Override
-    public CloseableIteration<BindingSet,QueryEvaluationException> evaluate(BindingSet bindingset) throws QueryEvaluationException {
-        return this.evaluate(Collections.singleton(bindingset));
-    }
-    
-    @Override
-    public double cardinality() {
-        return tableSize;
+        return null;
     }
 
-    @Override
-    public CloseableIteration<BindingSet,QueryEvaluationException> evaluate(final Collection<BindingSet> bindingset) throws QueryEvaluationException {
-        
-        String localityGroup = "";
-        Set<String> commonVars = Sets.newHashSet();
-
-        if (!bindingset.isEmpty()) {
-
-            BindingSet bs = bindingset.iterator().next();
-            for (String b : bindingslist) {
-                Binding v = bs.getBinding(b);
-                if (v != null) {
-                    commonVars.add(b);
-                }
-
-            }
-        }
-        
-        commonVars.addAll(getConstantConstraints());
-        AccumuloPrecompQueryIndexer apq = null;
-        List<String> fullVarOrder =  null;
-        try {
-       
-            if (commonVars.size() > 0) {
-                String commonVarOrder = getVarOrder(commonVars);
-                if(commonVarOrder == null) {
-                    throw new IllegalStateException("Index does not support binding set!");
-                }
-                fullVarOrder = Lists.newArrayList(prefixToOrder(commonVarOrder).split("\u0000"));
-                localityGroup = orderToLocGroup(fullVarOrder);
-                fullVarOrder.add("" + commonVars.size());
-                
-            } else {
-                fullVarOrder = bindingslist;
-                localityGroup = orderToLocGroup(fullVarOrder);
-                fullVarOrder.add("" + 0);
+    /**
+     * @return - all constraints which correspond to variables
+     * in {@link AccumuloIndexSet#getTupleExpr()} which are set
+     * equal to a constant, but are non-constant in Accumulo table
+     */
+    private Set<String> getConstantConstraints() {
+        final Map<String, String> tableMap = this.getTableVarMap();
+        final Set<String> keys = tableMap.keySet();
+        final Set<String> constants = Sets.newHashSet();
+        for (final String s : keys) {
+            if (s.startsWith("-const-")) {
+                constants.add(s);
             }
-
-            
-            apq = new AccumuloPrecompQueryIndexer(accCon, tablename);
-            ValueMapVisitor vmv = new ValueMapVisitor();
-            this.getTupleExpr().visit(vmv);
-            
-            return apq.queryPrecompJoin(fullVarOrder, localityGroup, this.bindings, vmv.getValMap(), bindingset);
-            
-        } catch(TableNotFoundException e) {
-            throw new QueryEvaluationException(e);
-        } finally {
-            IOUtils.closeQuietly(apq);
         }
+        return constants;
     }
-    
-    
-    public class ValueMapVisitor extends QueryModelVisitorBase<RuntimeException> {
 
+    /**
+     *
+     * Extracts the values associated with constant labels in the query
+     * Used to create binding sets from range scan
+     */
+    public class ValueMapVisitor extends QueryModelVisitorBase<RuntimeException> {
         Map<String, org.openrdf.model.Value> valMap = Maps.newHashMap();
-
-        
         public Map<String, org.openrdf.model.Value> getValMap() {
             return valMap;
         }
-        
         @Override
         public void meet(Var node) {
             if (node.getName().startsWith("-const-")) {
                 valMap.put(node.getName(), node.getValue());
             }
-
         }
-
     }
-    
-    
+
 }
-    
+
+