blob: b2ed8d73fb14bd056324aec20af3ebdc7ab75b49 [file] [log] [blame]
package org.apache.rya.indexing.pcj.matching;
/*
* 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.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.rya.api.domain.VarNameUtils;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.query.algebra.Filter;
import org.eclipse.rdf4j.query.algebra.NAryValueOperator;
import org.eclipse.rdf4j.query.algebra.ProjectionElem;
import org.eclipse.rdf4j.query.algebra.ProjectionElemList;
import org.eclipse.rdf4j.query.algebra.QueryModelNode;
import org.eclipse.rdf4j.query.algebra.StatementPattern;
import org.eclipse.rdf4j.query.algebra.TupleExpr;
import org.eclipse.rdf4j.query.algebra.ValueConstant;
import org.eclipse.rdf4j.query.algebra.ValueExpr;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class QueryVariableNormalizer {
/**
* @param tuple1
* tuple expression from a parsed query
* @param tuple2
* tuple expression from a parsed query (the proposed index whose
* variables are to be relabeled)
* @return list of all possible tuples obtained by substituting the
* variables of proposed index with the variables from query
* @throws Exception
* @throws IllegalArgumentException
*/
public static List<TupleExpr> getNormalizedIndex(TupleExpr tuple1, TupleExpr tuple2) throws Exception {
List<QueryModelNode> nodes1, nodes2;
TreeMap<String, List<QueryModelNode>> queryMap1, indexMap1;
List<HashMap<String, String>> varChanges = new ArrayList<HashMap<String, String>>();
List<TupleExpr> tupleList = new ArrayList<TupleExpr>();
// if tuples are equal, no need to do anything
if (tuple1.equals(tuple2)) {
tupleList.add(tuple1.clone());
return tupleList;
}
NormalizeQueryVisitor tupNVis = new NormalizeQueryVisitor(false);
NormalizeQueryVisitor indexNVis = new NormalizeQueryVisitor(true);
tuple1.visit(tupNVis);
tuple2.visit(indexNVis);
TupleExpr tuple;
queryMap1 = tupNVis.getMap();
indexMap1 = indexNVis.getMap();
// TreeMaps that used for comparators
TreeMap<String, Integer>[] trees = (TreeMap<String, Integer>[]) new TreeMap[4];
for (int i = 0; i < 4; i++) {
trees[i] = new TreeMap<String, Integer>();
}
trees[0] = tupNVis.getKeyMap(); // query tuple variable count
trees[2] = indexNVis.getKeyMap(); // index tuple variable count
// if query does not contain as many constant Vars as index,
// normalization not possible.
// if (!(trees[0].keySet().size() >= trees[2].keySet().size())) {
// System.out.println("In here:1");
// return tupleList;
// }
// sort keys according to size of associated StatementPattern list
// this optimization ensures that initial list of HashMaps (possible
// variable substitutions)
// is as small as possible
// Maybe add additional criteria to comparator taking into account size
// of query bin lists
Set<String> keys = indexMap1.keySet();
List<String> keyList = new ArrayList<String>(keys);
Collections.sort(keyList, new ConstantKeyComp(indexMap1, queryMap1));
// iterate through constant values associated with smaller tuple,
// check that larger tuple constants these constants, and use lists
// of associated statement patterns to begin to construct variable
// substitutions
// that are consistent
for (String s : keyList) {
if (queryMap1.containsKey(s)) {
nodes1 = queryMap1.get(s);
nodes2 = indexMap1.get(s);
if (!(nodes1.size() >= nodes2.size())) {
// System.out.println("In here: 2");
// System.out.println("Node lists are " + nodes1 + " and " +
// nodes2);
return tupleList;
}
trees[1] = getListVarCnt(nodes1, tupNVis.getVariableMap()); // query
// list
// variable
// count
trees[3] = getListVarCnt(nodes2, indexNVis.getVariableMap()); // index
// list
// variable
// count
Collections.sort(nodes1, new CountComp(trees[1], trees[0]));
Collections.sort(nodes2, new CountComp(trees[3], trees[2]));
varChanges = statementCompare(nodes1, nodes2, varChanges, trees);
if (varChanges.size() == 0) {
return tupleList;
}
}
else {
return tupleList;
}
}
List<QueryModelNode> filters2 = indexNVis.getFilters();
// determine if index contains filters whose variables need to be relabeled
if (filters2.size() != 0) {
List<QueryModelNode> filters1 = tupNVis.getFilters();
// only attempt to normalize variables if query contains more filters than index
if (filters1.size() >= filters2.size()) {
Collections.sort(filters1, new FilterComp());
Collections.sort(filters2, new FilterComp());
varChanges = statementCompare(filters1, filters2, varChanges, trees);
}
}
List<HashMap<String, String>> varChangeSet = new ArrayList<HashMap<String, String>>();
for (HashMap<String, String> s : varChanges) {
if (!varChangeSet.contains(s)) {
varChangeSet.add(s);
}
}
ValueMapVisitor valMapVis = new ValueMapVisitor();
tuple1.visit(valMapVis);
Map<String, Value> valMap = valMapVis.getValueMap();
for (HashMap<String, String> s : varChangeSet) {
//System.out.println(s);
tuple = tuple2.clone();
replaceTupleVariables(s, tuple, valMap);
tupleList.add(tuple);
}
return tupleList;
}
/**
* Produces a list of all possible substitutions stored in HashMaps that are
* consistent with the two lists of statement patterns
*
* @param qArray
* list of Statement nodes from query tuple
* @param iArray
* list of Statement nodes from index tuple
* @param hMaps
* HashMap containing variable substitutions
* @param trees
* TreeMaps used for statement pattern node ordering
* @return
*/
private static List<HashMap<String, String>> statementCompare(List<QueryModelNode> qArray,
List<QueryModelNode> iArray, List<HashMap<String, String>> hMaps, TreeMap<String, Integer>[] trees) {
if (hMaps.size() == 0) {
HashMap<HashMap<String, String>, Boolean> mapConsistent = new HashMap<HashMap<String, String>, Boolean>();
HashMap<String, String> hMap = new HashMap<String, String>();
mapConsistent.put(hMap, false);
evaluateMap(qArray, iArray, hMap, hMaps, mapConsistent, trees);
return hMaps;
}
else {
ArrayList<HashMap<String, String>> tempMaps = Lists.newArrayList(hMaps);
HashMap<HashMap<String, String>, Boolean> mapConsistent = new HashMap<HashMap<String, String>, Boolean>();
for (HashMap<String, String> s : hMaps) {
mapConsistent.put(s, false);
}
for (HashMap<String, String> s : hMaps) {
evaluateMap(qArray, iArray, s, tempMaps, mapConsistent, trees);
}
return tempMaps;
}
}
/**
* Adds or removes HashMap substitution schemes to the list of substitutions
* schemes depending on whether or not they are consistent with the two
* lists of statement patterns
*
* @param qArray
* List of StatementPatterns associated with query array
* @param iArray
* List of StatementPatterns associated with index array
* @param hMap
* HashMap of substitutions to be analyzed for consistent and
* added or removed
* @param hMaps
* List of HashMaps containing substitution schemes
* @param trees
* Array of TreeMaps used for comparison of StatementPattern
* nodes
*/
private static void evaluateMap(List<QueryModelNode> qArray, List<QueryModelNode> iArray,
HashMap<String, String> hMap, List<HashMap<String, String>> hMaps,
HashMap<HashMap<String, String>, Boolean> mapConsistent, TreeMap<String, Integer>[] trees) throws IllegalArgumentException {
// if all nodes in indexArray have been exhausted, add map of substitutions to
// list of possible substitution schemes.
if (iArray.size() == 0) {
if (!hMaps.contains(hMap)) {
hMaps.add(hMap);
}
mapConsistent.put(hMap, true);
return;
}
// for a given constant key, iterate through possible combinations of statement pattern nodes contained in associated query list and
// index list to generate all possible substitution schemes.
for (int i = 0; i < iArray.size(); i++) {
for (int j = 0; j < qArray.size(); j++) {
//System.out.println("Query list is " + qArray+ " and index list is " + iArray);
QueryModelNode node1 = qArray.get(j);
QueryModelNode node2 = iArray.get(i);
// if lists contain statement patterns, check to see if two given statement patterns have same structure
// independent of variables names (same constants in same place, non constant Vars in same place)
if ((node1 instanceof StatementPattern) && (node2 instanceof StatementPattern)) {
if (genConstantCompare((StatementPattern) node1, (StatementPattern) node2)) {
List<Var> variables1 = ((StatementPattern)node1).getVarList();
List<Var> variables2 = ((StatementPattern)node2).getVarList();
List<List<String>> vars = genGetCommonVars(variables1, variables2);
List<String> vars1 = vars.get(0);
List<String> vars2 = vars.get(1);
if (listConsistent(vars1, vars2, hMap)) {
HashMap<String, String> hashMap = Maps.newHashMap(hMap);
putVars(vars1, vars2, hashMap);
List<QueryModelNode> queryArray = Lists.newArrayList(qArray);
List<QueryModelNode> indexArray = Lists.newArrayList(iArray);
indexArray.remove(i);
queryArray.remove(j);
evaluateMap(queryArray, indexArray, hashMap, hMaps, mapConsistent, trees);
}
}
} // if lists contain filters, see if filters have same structure independent of variables names
//(check that conditions are same independent of variable names).
else if ((node1 instanceof Filter) && (node2 instanceof Filter)) {
try {
if (filterCompare((Filter) node1, (Filter) node2)) {
List<QueryModelNode> variables1 = FilterVarValueCollector.process(((Filter) node1).getCondition());
List<QueryModelNode> variables2 = FilterVarValueCollector.process(((Filter) node2).getCondition());
List<List<String>> vars = filterCommonVars(variables1, variables2);
List<String> vars1 = vars.get(0);
List<String> vars2 = vars.get(1);
if (listConsistent(vars1, vars2, hMap)) {
HashMap<String, String> hashMap = Maps.newHashMap(hMap);
putVars(vars1, vars2, hashMap);
List<QueryModelNode> queryArray = Lists.newArrayList(qArray);
List<QueryModelNode> indexArray = Lists.newArrayList(iArray);
indexArray.remove(i);
queryArray.remove(j);
evaluateMap(queryArray, indexArray, hashMap, hMaps, mapConsistent, trees);
}
}
} catch (Exception e) {
System.out.println("Invalid Filter! " + e);
}
} else {
throw new IllegalArgumentException("Invalid query tree.");
}
}
}
if (mapConsistent.containsKey(hMap))
if (mapConsistent.get(hMap) == false) {
hMaps.remove(hMap);
}
return;
}
private static List<List<String>> genGetCommonVars(List<Var> vars1, List<Var> vars2) {
List<List<String>> varList = Lists.newArrayList();
List<String> varList1 = Lists.newArrayList();
List<String> varList2 = Lists.newArrayList();
for (int i = 0; i < vars1.size(); i++) {
if (!vars1.get(i).isConstant() && !vars2.get(i).isConstant()) {
varList1.add(vars1.get(i).getName());
varList2.add(vars2.get(i).getName());
} else if(vars1.get(i).isConstant() && !vars2.get(i).isConstant()) {
varList1.add(vars1.get(i).getName());
varList2.add(vars2.get(i).getName());
}
}
varList.add(varList1);
varList.add(varList2);
return varList;
}
private static List<List<String>> filterCommonVars(List<QueryModelNode> vars1, List<QueryModelNode> vars2) {
List<List<String>> varList = Lists.newArrayList();
List<String> varList1 = Lists.newArrayList();
List<String> varList2 = Lists.newArrayList();
for (int i = 0; i < vars1.size(); i++) {
if ((vars1.get(i) instanceof ValueConstant) && (vars2.get(i) instanceof Var)) {
ValueConstant vc = (ValueConstant) vars1.get(i);
final String s = VarNameUtils.createUniqueConstVarName(vc.getValue());
varList1.add(s);
varList2.add(((Var)vars2.get(i)).getName());
} else if(!(vars1.get(i) instanceof ValueConstant)){
if (!((Var) vars1.get(i)).isConstant() && (vars2.get(i) instanceof Var)
&& !((Var) vars2.get(i)).isConstant()) {
varList1.add(((Var) vars1.get(i)).getName());
varList2.add(((Var) vars2.get(i)).getName());
} else if (((Var) vars1.get(i)).isConstant() && (vars2.get(i) instanceof Var)
&& !((Var) vars2.get(i)).isConstant()) {
varList1.add(((Var) vars1.get(i)).getName());
varList2.add(((Var) vars2.get(i)).getName());
}
}
}
varList.add(varList1);
varList.add(varList2);
return varList;
}
private static boolean genConstantCompare(StatementPattern queryNode, StatementPattern indexNode) {
ArrayList<Var> vars1 = (ArrayList<Var>) queryNode.getVarList();
ArrayList<Var> vars2 = (ArrayList<Var>) indexNode.getVarList();
for (int i = 0; i < vars1.size(); i++) {
if (vars1.get(i).isConstant() && vars2.get(i).isConstant()) {
if (!vars1.get(i).equals(vars2.get(i))) {
return false;
}
} else if(!vars1.get(i).isConstant() && vars2.get(i).isConstant() ) {
return false;
}
}
return true;
}
/**
* Method checks that substituting val for key is consistent with
* substitutions in hMap
*
* @param val
* substituting variable
* @param key
* variable to be substituted for
* @param hMap
* HashMap containing the substitutions to be made
* @return true if the proposed substitution is consistent with hMap, and
* false otherwise
*/
private static boolean checkVariables(String val, String key, HashMap<String, String> hMap) {
if (!hMap.containsKey(key) && !hMap.containsValue(val)) {
return true;
} else if (!hMap.containsKey(key) && hMap.containsValue(val) || hMap.containsKey(key)
&& !hMap.containsValue(val)) {
return false;
} else {
return hMap.get(key).equals(val);
}
}
// given two lists of variables and a HashMap, checks to see if substituting variable names in varList1
// for variable names in varList2 is consistent with map.
private static boolean listConsistent(List<String> varList1, List<String> varList2, HashMap<String, String> hMap) {
for (int k = 0; k < varList1.size(); k++) {
String s1 = varList1.get(k);
String s2 = varList2.get(k);
if (!checkVariables(s1, s2, hMap)) {
return false;
}
}
return true;
}
// given two lists of variables and a HashMap, substitutes variable names in varList1
// for variable names in varList2 by updating map.
private static void putVars(List<String> varList1, List<String> varList2, HashMap<String, String> hashMap) {
for (int k = 0; k < varList1.size(); k++) {
String s1 = varList1.get(k);
String s2 = varList2.get(k);
if (!hashMap.containsKey(s2)) {
hashMap.put(s2, s1);
}
}
}
/**
* @param filter1
* @param filter2
* @return true if filter2 is equal to filter1 once variables in filter2 are replaced with variables and constants
* occurring in same position in filter1 (allows filter1 to contain constants where filter2 contains variables)
* @throws Exception
*/
private static boolean filterCompare(Filter filter1, Filter filter2) throws Exception {
NodeCollector nc1 = new NodeCollector();
NodeCollector nc2 = new NodeCollector();
filter1.getCondition().visit(nc1);
filter2.getCondition().visit(nc2);
List<QueryModelNode> nodeList1 = nc1.getNodes();
List<QueryModelNode> nodeList2 = nc2.getNodes();
if (nodeList1.size() != nodeList2.size()) {
return false;
}
for (int i = 0; i < nodeList1.size(); i++) {
if ((nodeList1.get(i) instanceof ValueConstant) && (nodeList2.get(i) instanceof Var)) {
continue;
} else {
if (nodeList1.get(i).getClass() != nodeList2.get(i).getClass()) {
return false;
}
}
}
return true;
}
/**
* Given a HashMap containing variable substitutions and a tuple, this
* method uses a visitor to iterate through the tuple and make the necessary
* substitutions
*
* @param varChanges
* @param tuple
* @throws Exception
*/
private static void replaceTupleVariables(HashMap<String, String> varChanges, TupleExpr tuple, Map<String,Value> valMap) throws Exception {
TupleVarRenamer visitor = new TupleVarRenamer(varChanges, valMap);
tuple.visit(visitor);
}
/**
* Given a list of StatementPattern nodes and a TreeMap containing the
* variables in the tuple, this method counts the number of occurrences of
* each variable in the given list
*
* @param list
* List of StatementPattern nodes
* @param cnt
* TreeMap whose keys are tuple variables and whose value is 0
* @return TreeMap whose keys are tuple variables and whose value is the
* number of times variable appears in list
*/
private static TreeMap<String, Integer> getListVarCnt(List<QueryModelNode> list, TreeMap<String, Integer> cnt) {
int count = 0;
for (QueryModelNode qNode : list) {
List<String> vars = VarCollector.process(qNode);
for (String s : vars) {
count = cnt.get(s);
count++;
cnt.put(s, count);
}
}
return cnt;
}
/**
* Given a StatementPattern and two TreeMaps containing the variable counts
* associated with an associated list and tuple, this method assigns a
* number to the StatementPattern node which is determined by the number of
* times its variables (non-constant Vars) appear in the list and throughout
* the tuple
*
* @param sp
* StatementPattern node
* @param listCount
* TreeMap with variable count info associated with list
* @param tupCount
* TreeMap with variable count info associated with tuple
* @return count info associated with StatementPattern node
*/
private static int getSpCount(QueryModelNode sp, TreeMap<String, Integer> listCount,
TreeMap<String, Integer> tupCount) {
int spCount = 0;
List<String> vars = VarCollector.process(sp);
for (String var : vars) {
spCount = spCount + listCount.get(var) + tupCount.get(var);
}
return spCount;
}
/**
* @return NormalizedQueryVisitor
*/
public static NormalizeQueryVisitor getVisitor(boolean isIndex) {
return new NormalizeQueryVisitor(isIndex);
}
// ********************Definition of Comparators****************
// *************************************************************
public static class CountComp implements Comparator<QueryModelNode> {
private TreeMap<String, Integer> lCount, tupleCount;
public CountComp(TreeMap<String, Integer> lCount, TreeMap<String, Integer> tupleCount) {
this.lCount = lCount;
this.tupleCount = tupleCount;
}
// compares StatementPattern nodes based on frequency at which their
// variables appear in other StatementPattern nodes in associated
// tuple and list
public int compare(QueryModelNode sp1, QueryModelNode sp2) {
return -(getSpCount(sp1, lCount, tupleCount) - getSpCount(sp2, lCount, tupleCount));
}
}
// comparator to sort constant key list according to size of associated
// StatementPattern array
public static class ConstantKeyComp implements Comparator<String> {
private TreeMap<String, List<QueryModelNode>> indexMap, queryMap;
public ConstantKeyComp(TreeMap<String, List<QueryModelNode>> indexMap,
TreeMap<String, List<QueryModelNode>> queryMap) {
this.indexMap = indexMap;
this.queryMap = queryMap;
}
// Compare method to sort keys of HashMap<String,
// ArrayList<StatementPattern>
// for index based on whether key also appears in query Map--if key does
// not appear
// in query map, key is given value 0 so it is moved to front when key
// list is sorted.
// If key appears in query map, key is assigned value that is the sum of
// the size of the associated
// lists in index map and query map.
public int compare(String key1, String key2) {
int len1 = 0;
int len2 = 0;
if (queryMap.containsKey(key1) && indexMap.containsKey(key1))
len1 = indexMap.get(key1).size() + queryMap.get(key1).size();
if (queryMap.containsKey(key2) && indexMap.containsKey(key2))
len2 = indexMap.get(key2).size() + queryMap.get(key2).size();
return (len1 - len2);
}
}
public static class FilterComp implements Comparator<QueryModelNode> {
public int compare(QueryModelNode q1, QueryModelNode q2) {
int size1 = VarCollector.process(q1).size();
int size2 = VarCollector.process(q2).size();
return size1 - size2;
}
}
// ******************** Definition of Visitors*****************
// ************************************************************
public static class ValueMapVisitor extends AbstractQueryModelVisitor<Exception> {
private Map<String, Value> valMap = Maps.newHashMap();
public Map<String, Value> getValueMap() {
return valMap;
}
public void meet(Var var) {
if (var.isConstant()) {
valMap.put(var.getName(),var.getValue());
}
}
public void meet(ValueConstant val) {
final String s = VarNameUtils.createUniqueConstVarName(val.getValue());
valMap.put(s, val.getValue());
}
}
public static class NodeCollector extends AbstractQueryModelVisitor<Exception> {
private List<QueryModelNode> nodes = Lists.newArrayList();
public List<QueryModelNode> getNodes() {
return nodes;
}
@Override
public void meetNode(QueryModelNode node) throws Exception {
nodes.add(node);
super.meetNode(node);
}
}
public static class SpVarReNamer extends AbstractQueryModelVisitor<RuntimeException> {
private final HashMap<String, String> hMap;
private Map<String, Value> valMap;
public SpVarReNamer(HashMap<String, String> hMap, Map<String, Value> valMap) {
this.valMap = valMap;
this.hMap = hMap;
}
public void meet(Var var) {
if (!var.isConstant() && hMap.containsKey(var.getName())) {
String val = hMap.get(var.getName());
if (VarNameUtils.isConstant(val)) {
var.setName(val);
var.setValue(valMap.get(val));
var.setAnonymous(true); //TODO this might be a hack -- when are Vars not anonymous?
} else {
var.setName(val);
}
}
}
}
public static class FilterVarReNamer extends AbstractQueryModelVisitor<RuntimeException> {
private final HashMap<String, String> hMap;
private Map<String, Value> valMap;
public FilterVarReNamer(HashMap<String, String> hMap, Map<String, Value> valMap) {
this.valMap = valMap;
this.hMap = hMap;
}
@Override
public void meet(Var var) {
if (!(var.getParentNode() instanceof NAryValueOperator)) {
if (!var.isConstant() && hMap.containsKey(var.getName())) {
String val = hMap.get(var.getName());
if (VarNameUtils.isConstant(val)) {
var.replaceWith(new ValueConstant(valMap.get(val)));
} else {
var.setName(val);
}
}
}
}
@Override
public void meetNAryValueOperator(NAryValueOperator node) {
List<ValueExpr> oldValues = node.getArguments();
List<ValueExpr> newValues = Lists.newArrayList();
for (ValueExpr v : oldValues) {
if (v instanceof Var) {
Var var = (Var) v;
if (!(var.isConstant() && hMap.containsKey(var.getName()))) {
String val = hMap.get(var.getName());
if (VarNameUtils.isConstant(val)) {
newValues.add(new ValueConstant(valMap.get(val)));
} else {
var.setName(val);
newValues.add(var);
}
}
} else {
newValues.add(v);
}
}
node.setArguments(newValues);
}
}
public static class TupleVarRenamer extends AbstractQueryModelVisitor<RuntimeException> {
private final HashMap<String, String> varChanges;
private Map<String, Value> valMap;
public TupleVarRenamer(HashMap<String, String> varChanges, Map<String, Value> valMap) {
this.varChanges = varChanges;
this.valMap = valMap;
}
@Override
public void meet(ProjectionElemList node) {
List<ProjectionElem> proj = node.getElements();
for (ProjectionElem s : proj) {
if (varChanges.containsKey(s.getSourceName())) {
String name = s.getSourceName();
s.setSourceName(varChanges.get(name));
s.setTargetName(varChanges.get(name));
}
}
}
@Override
public void meet(StatementPattern node) {
SpVarReNamer spv = new SpVarReNamer(varChanges, valMap);
node.visit(spv);
}
@Override
public void meet(Filter node) {
FilterVarReNamer fvr = new FilterVarReNamer(varChanges, valMap);
node.getCondition().visit(fvr);
node.getArg().visit(this);
}
}
public static class VarCollector extends AbstractQueryModelVisitor<RuntimeException> {
public static List<String> process(QueryModelNode node) {
VarCollector collector = new VarCollector();
node.visit(collector);
return collector.getVarNames();
}
public static List<Var> processVar(QueryModelNode node) {
VarCollector collector = new VarCollector();
node.visit(collector);
return collector.getVars();
}
private List<String> varNames = new ArrayList<String>();
private List<Var> vars = Lists.newArrayList();
public List<String> getVarNames() {
return varNames;
}
public List<Var> getVars() {
return vars;
}
@Override
public void meet(Var var) {
if (!var.hasValue()) {
varNames.add(var.getName());
}
vars.add(var);
}
}
public static class FilterVarValueCollector extends AbstractQueryModelVisitor<RuntimeException> {
public static List<QueryModelNode> process(QueryModelNode node) {
FilterVarValueCollector collector = new FilterVarValueCollector();
node.visit(collector);
return collector.getVars();
}
private List<QueryModelNode> vars = Lists.newArrayList();
public List<QueryModelNode> getVars() {
return vars;
}
@Override
public void meet(Var node) {
vars.add(node);
}
@Override
public void meet(ValueConstant node) {
vars.add(node);
}
}
public static class NormalizeQueryVisitor extends AbstractQueryModelVisitor<Exception> {
private TreeMap<String, List<QueryModelNode>> map = new TreeMap<String, List<QueryModelNode>>();
private TreeMap<String, Integer> varMap = new TreeMap<String, Integer>();
private TreeMap<String, Integer> emptyVarMap = new TreeMap<String, Integer>();
private List<StatementPattern> statementList = new ArrayList<StatementPattern>();
private List<QueryModelNode> filters = new ArrayList<QueryModelNode>();
private boolean isIndex;
public NormalizeQueryVisitor(boolean isIndex) {
this.isIndex = isIndex;
}
private TreeMap<String, List<QueryModelNode>> getMap() {
return map;
}
private TreeMap<String, Integer> getKeyMap() {
return varMap;
}
private TreeMap<String, Integer> getVariableMap() {
return emptyVarMap;
}
public List<StatementPattern> getStatementPatterns() {
return statementList;
}
private List<QueryModelNode> getFilters() {
return filters;
}
@Override
public void meet(StatementPattern node) throws Exception {
statementList.add(node);
String s = "";
String t = "";
Var node1 = node.getSubjectVar();
Var node2 = node.getObjectVar();
Var node3 = node.getPredicateVar();
Var node4 = node.getContextVar();
String s1 = "";
String s2 = "";
String s3 = "";
String s4 = "";
if (node1.isConstant())
s1 = node1.getName().substring(7);
if (node2.isConstant())
s2 = node2.getName().substring(7);
if (node3.isConstant())
s3 = node3.getName().substring(7);
if (node4 != null) {
if (node4.isConstant())
s4 = node4.getName().substring(7);
}
if ((s1+s2+s3).length() == 0) {
s = "Nonconstant nodes have no variables.";
}
List<QueryModelNode> nodes;
if (s.length() > 0) {
if (map.containsKey(s)) {
nodes = map.get(s);
nodes.add(node);
} else {
nodes = new ArrayList<QueryModelNode>();
nodes.add(node);
}
map.put(s, nodes);
} else {
if (isIndex) {
t = s1 + s2 + s3 + s4;
if (map.containsKey(t)) {
nodes = map.get(t);
nodes.add(node);
} else {
nodes = new ArrayList<QueryModelNode>();
nodes.add(node);
}
map.put(t, nodes);
} else {
String[] comps = new String[4];
comps[0] = s1;
comps[1] = s2;
comps[2] = s3;
comps[3] = s4;
for (int i = 0; i < 3; i++) {
if (comps[i].length() != 0) {
if (map.containsKey(comps[i] + comps[3])) {
nodes = map.get(comps[i] + comps[3]);
nodes.add(node);
} else {
nodes = new ArrayList<QueryModelNode>();
nodes.add(node);
}
map.put(comps[i] + comps[3], nodes);
for (int j = i + 1; j < 3; j++) {
if (comps[j].length() != 0) {
if (map.containsKey(comps[i] + comps[j] + comps[3])) {
nodes = map.get(comps[i] + comps[j] + comps[3]);
nodes.add(node);
} else {
nodes = new ArrayList<QueryModelNode>();
nodes.add(node);
}
map.put(comps[i] + comps[j] + comps[3], nodes);
}
}
}
}
if (s1.length() != 0 && s2.length() != 0 && s3.length() != 0) {
if (map.containsKey(s1 + s2 + s3 + s4)) {
nodes = map.get(s1 + s2 + s3 + s4);
nodes.add(node);
} else {
nodes = new ArrayList<QueryModelNode>();
nodes.add(node);
}
map.put(s1 + s2 + s3 + s4, nodes);
}
}
}
super.meet(node);
}
@Override
public void meet(Var node) throws Exception {
int count = 1;
if (!node.isConstant()) {
if (varMap.containsKey(node.getName())) {
count = varMap.get(node.getName());
count++;
varMap.put(node.getName(), count);
} else
varMap.put(node.getName(), 1);
if (!emptyVarMap.containsKey(node.getName()))
emptyVarMap.put(node.getName(), 0);
}
super.meet(node);
}
public void meet(Filter filter) throws Exception {
filters.add(filter);
super.meet(filter);
}
}
}