package org.aksw.sparqlify.database; import java.util.AbstractCollection; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; 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.Set; class IndexCandidate { IndexMetaNode node; int columnDepth; int nodeDepth; int childIndex; IndexCandidate child; List<Constraint> constraints; //Set<String> noRecheckColumns = new HashSet<String>(); // Column names that do not need a recheck thanks to the index public IndexCandidate(IndexMetaNode node, int columnDepth, int nodeDepth, int childIndex, List<Constraint> constraints) { this.node = node; this.columnDepth = columnDepth; this.nodeDepth = nodeDepth; this.constraints = constraints; this.childIndex = childIndex; } @Override public String toString() { return "IndexCandidate [node=" + node + ", columnDepth=" + columnDepth + ", nodeDepth=" + nodeDepth + ", childIndex=" + childIndex + ", child=" + child + ", constraints=" + constraints + "]"; } } class CollectionComparator<T> implements Comparator<Collection<T>> { @Override public int compare(Collection<T> o1, Collection<T> o2) { // TODO Auto-generated method stub return 0; } } class SubList<T> extends AbstractList<T> { private List<T> original; private int beginIndex; private int endIndex; public SubList(List<T> original, int beginIndex) { this.original = original; this.beginIndex = beginIndex; this.endIndex = original.size(); } public SubList(List<T> original, int beginIndex, int endIndex) { this.original = original; this.beginIndex = beginIndex; this.endIndex = endIndex; } public List<T> getOriginal() { return original; } @Override public T get(int index) { return original.get(beginIndex + index); } @Override public int size() { return endIndex - beginIndex; } @Override public SubList<T> subList(int beginIndex, int endIndex) { return new SubList<T>(original, this.beginIndex + beginIndex, this.beginIndex + beginIndex + endIndex); } //@Override public SubList<T> subList(int beginIndex) { return new SubList<T>(original, this.beginIndex + beginIndex, this.beginIndex + beginIndex + this.endIndex); } //public static SubList<T> create(List<T> original, ) } class TrieMap<K, V> { class Node { SubList<K> transition; Map<K, Node> map; V value; boolean isLeaf; public Node() { isLeaf = true; } public Node(SubList<K> transition, V value) { this.transition = transition; this.value = value; this.isLeaf = true; } } private Node root = new Node(); public void add(List<K> key, V value) { Node current = root; } public void add(Node current, int beginIndex, List<K> key, V value) { if(current.transition != null) { for(int j = 0; j < current.transition.size(); ++j) { K t = current.transition.get(j); K k = key.get(beginIndex + j); if(!t.equals(k)) { // We need to split Node branchOld = new Node(); branchOld.map = current.map; branchOld.transition = current.transition.subList(j); Node branchNew = new Node(); branchNew.transition = new SubList<K>(key, j); branchNew.isLeaf = true; branchNew.value = value; current.transition = current.transition.subList(0, j); current.map = new HashMap<K, Node>(); current.map.put(t, branchOld); current.map.put(k, branchNew); } } } } /* int i; for(K k : key) { Node o = current.get(k); if(o == null) { Leaf leaf = new Leaf(new SubList<K>(key, i), value); current.put(k, leaf); } else if (o instanceof Map) { Map<K, Object> next = (Map<K, Object>)o; current = next; } else if (o instanceof TrieMap.Leaf) { Leaf leaf = (Leaf)o; } else { throw new RuntimeException("Should not happen"); } ++i; } } public V getNearestValue(List<K> key) { Node current = root; int i = 0; for(K k : key) { if(current.transition != null) { } ++i; } } */ } /** * Checks whether the columns of a row satisfy a set of constraints * * @author raven * */ class RowRechecker // implements Filter<List<Object>> { private Map<Integer, Constraint> constraints; public RowRechecker(Map<Integer, Constraint> constraints) { this.constraints = constraints; } public boolean isAccepted(List<Object> row) { for(Entry<Integer, Constraint> entry : constraints.entrySet()) { Object value = row.get(entry.getKey()); boolean isSatisfied = entry.getValue().isSatisfiedBy(value); if(!isSatisfied) { return false; } } return true; } } public class IndexCollection<T> extends AbstractCollection<Index<T>> { private List<Index<T>> indexes = new ArrayList<Index<T>>(); @Override public boolean add(Index<T> index) { return indexes.add(index); } public void getRows(Map<String, Constraint> constraints, IndexMetaNode node, List<Integer> path) { } /** * * * * * @param columnNames * @return */ public Collection<List<Object>> get(Map<String, Constraint> constraints, IndexMap<String, Column> columns) { //Set<String> columnNames = constraints.keySet(); Index<T> bestMatch = null; IndexCandidate bestCandidate = null; for(Index<T> index : indexes) { IndexCandidate candidate = get(Collections.singletonList(index.getRoot()), 0, 0, constraints); if(candidate == null) { continue; } if(bestCandidate == null || (candidate.columnDepth > bestCandidate.columnDepth) || (candidate.columnDepth == bestCandidate.columnDepth && candidate.nodeDepth < bestCandidate.nodeDepth)) { bestCandidate = candidate; bestMatch = index; } } //return bestMatch; // Determine which constraints are not covered by the index Map<String, Constraint> recheck = new HashMap<String, Constraint>(constraints); IndexCandidate node = bestCandidate; while(node != null) { for(String columnName : node.node.getColumnNames()) { recheck.remove(columnName); } node = node.child; } RowRechecker rechecker = null; if(!recheck.isEmpty()) { Map<Integer, Constraint> recheckIndex = new HashMap<Integer, Constraint>(); for(Entry<String, Constraint> entry : recheck.entrySet()) { int index = columns.getIndex(entry.getKey()); recheckIndex.put(index, entry.getValue()); } rechecker = new RowRechecker(recheckIndex); } if(bestMatch == null) { if(indexes.isEmpty()) { throw new RuntimeException("No store or index found for lookup."); } else { bestMatch = indexes.iterator().next(); } } Collection<List<Object>> result = execute(bestMatch, bestCandidate, rechecker); return result; } public Collection<List<Object>> execute(Index index, IndexCandidate pathNode, RowRechecker rechecker) { Set<List<Object>> result = new HashSet<List<Object>>(); if(pathNode == null) { collectRows(index.getStore(), Collections.singleton(index.getRoot()), result, rechecker); } else { _execute(index.getStore(), pathNode, result, rechecker); } return result; } @SuppressWarnings("unchecked") public void _execute(Object store, IndexCandidate pathNode, Collection<List<Object>> result, RowRechecker rechecker) { IndexMetaNode node = pathNode.node; MapStoreAccessor accessor = node.getFactory(); Collection<Object> values = accessor.lookup(store, pathNode.constraints.iterator().next()); if(pathNode.child == null) { // values could be e.g. List< List< TreeMap< ... > > > (list of lookup, list for nodes, store for each node) for(Object value : values) { collectRows(value, node.getChildren(), result, rechecker); } } else { switch(node.getChildren().size()) { case 0: // No children, the value is a set of rows for(Object value : values) { List<List<Object>> rows = (List<List<Object>>)value; if(rows != null) { result.addAll(rows); } } break; case 1: // 1 child, the value is a map for(Object value : values) { _execute(value, pathNode.child, result, rechecker); } break; default: // The value is a list of stores for(Object value : values) { List<Object> stores = (List<Object>)value; _execute(stores.get(pathNode.childIndex), pathNode.child, result, rechecker); } break; } } } private void collectRows(Object store, Collection<IndexMetaNode> nodes, Collection<List<Object>> result, RowRechecker rechecker) { //MapStoreAccessor accessor = node.getFactory().getAccessor(); //Collection<Object> values = accessor.list(store); switch(nodes.size()) { case 0: // No children, the value is a set of rows Collection<List<Object>> rows = (Collection<List<Object>>)store; if(rows != null) { if(rechecker == null) { result.addAll(rows); } else { for(List<Object> row : rows) { if(rechecker.isAccepted(row)) { result.add(row); } } } } break; case 1: // 1 child, the value is a map IndexMetaNode subNode = nodes.iterator().next(); Collection<Object> values = subNode.getFactory().list(store); for(Object value : values) { collectRows(value, subNode.getChildren(), result, rechecker); } break; default: // Each value is a list of stores, just take the first node List<Object> stores = (List<Object>)store; IndexMetaNode subNodeN = nodes.iterator().next(); Collection<Object> valuesN = subNodeN.getFactory().list(stores.get(0)); for(Object value : valuesN) { collectRows(value, subNodeN.getChildren(), result, rechecker); } break; } } /* class IndexPathNode { List<Constraint> constraints; int childIndex; IndexPathNode childNode; }*/ // Pick the node that // .) Covers most columns // .) Uses least index nodes /** * TODO Given {a = const , b = const} and an index (a, b) with support for equals. * * How can we pass on the constraints??? * * For now lets assume only single column indexes... :/ * * * @param nodes * @param baseNodeDepth * @param baseColumnDepth * @param constraints * @return */ public IndexCandidate get(List<IndexMetaNode> nodes, int baseNodeDepth, int baseColumnDepth, Map<String, Constraint> constraints) { Set<String> columnNames = constraints.keySet(); //IndexMetaNode bestMatch = null; IndexCandidate bestCandidate = null; int childIndex = 0; for(IndexMetaNode node : nodes) { int columnDepth = baseNodeDepth; int nodeDepth = baseColumnDepth; List<String> idxColNames = node.getColumnNames(); if(!columnNames.containsAll(idxColNames)) { continue; } Set<Class<?>> requiredConstraintClasses = new HashSet<Class<?>>(); for(String idxColName : idxColNames) { requiredConstraintClasses.add(constraints.get(idxColName).getClass()); } if(!node.getFactory().getSupportedConstraintClasses().containsAll(requiredConstraintClasses)) { continue; } // Pack the index with the constraint List<Constraint> cs = new ArrayList<Constraint>(idxColNames.size()); for(String idxColName : idxColNames) { cs.add(constraints.get(idxColName)); } columnDepth += idxColNames.size(); nodeDepth += 1; IndexCandidate thisCandidate = new IndexCandidate(node, columnDepth, nodeDepth, childIndex, cs); /* if(bestCandidate == null || (columnDepth > bestCandidate.columnDepth) || (columnDepth == bestCandidate.columnDepth && nodeDepth < bestCandidate.nodeDepth)) { bestCandidate = thisCandidate; }*/ if(!node.getChildren().isEmpty()) { IndexCandidate bestChild = get(node.getChildren(), nodeDepth, columnDepth, constraints); if(bestChild != null) { thisCandidate.columnDepth = bestChild.columnDepth; thisCandidate.nodeDepth = bestChild.nodeDepth; thisCandidate.child = bestChild; } } if(bestCandidate == null || (thisCandidate.columnDepth > bestCandidate.columnDepth) || (thisCandidate.columnDepth == bestCandidate.columnDepth && thisCandidate.nodeDepth < bestCandidate.nodeDepth)) { //bestChild = candidate; bestCandidate = thisCandidate; } ++childIndex; } return bestCandidate; } @Override public Iterator<Index<T>> iterator() { return indexes.iterator(); } @Override public int size() { return indexes.size(); } }