package org.aksw.jena_sparql_api.views.index; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Stream; import org.aksw.combinatorics.solvers.ProblemContainerNeighbourhoodAware; import org.aksw.combinatorics.solvers.ProblemNeighborhoodAware; import org.aksw.commons.collections.trees.Tree; import org.aksw.jena_sparql_api.concept_cache.collection.FeatureMap; import org.aksw.jena_sparql_api.concept_cache.combinatorics.ProblemVarMappingExpr; import org.aksw.jena_sparql_api.concept_cache.combinatorics.ProblemVarMappingQuad; import org.aksw.jena_sparql_api.utils.MapUtils; import org.aksw.jena_sparql_api.view_matcher.OpVarMap; import org.aksw.jena_sparql_api.view_matcher.SparqlViewMatcherUtils; import org.apache.jena.query.Query; import org.apache.jena.sparql.algebra.Op; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.expr.Expr; import org.apache.jena.sparql.function.library.e; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; public class SparqlViewMatcherSystemImpl implements SparqlViewMatcherSystem { private static final Logger logger = LoggerFactory.getLogger(SparqlViewMatcherSystemImpl.class); protected IndexSystem<Entry<Op, OpIndex>, Op> indexSystem; protected Function<Op, OpIndex> queryIndexer; //protected Map<Op, D> opToCacheData; public SparqlViewMatcherSystemImpl() { this.indexSystem = IndexSystemImpl.create(); this.queryIndexer = new OpIndexerImpl(); } public void registerView(String name, Op cacheOp) { //, D cacheData) { OpIndex queryIndex = queryIndexer.apply(cacheOp); // This is the op level indexing of cache indexSystem.add(new SimpleEntry<>(cacheOp, queryIndex)); //opToCacheData.put(cacheOp, cacheData); } @Override public Op rewriteQuery(Op queryOp) { OpIndex queryIndex = queryIndexer.apply(queryOp); // Create the initial set of cache candidates based on the query's algebra Collection<Entry<Op, OpIndex>> candidates = indexSystem.lookup(queryOp); for(Entry<Op, OpIndex> e : candidates) { OpIndex cacheIndex = e.getValue(); Multimap<Op, Op> candidateLeafMapping = getCandidateLeafMapping(cacheIndex, queryIndex); System.out.println("Leaf Mapping: " + candidateLeafMapping); } return null; } public static Multimap<Op, Op> getCandidateLeafMapping(OpIndex cacheIndex, OpIndex queryIndex) { Multimap<Op, Op> result = HashMultimap.create(); //QueryIndex cacheIndex = e.getValue(); FeatureMap<Expr, QuadPatternIndex> cacheQpi = cacheIndex.getQuadPatternIndex(); for(Entry<Set<Expr>, Collection<QuadPatternIndex>> f : queryIndex.getQuadPatternIndex().entrySet()) { Set<Expr> queryFeatureSet = f.getKey(); Collection<QuadPatternIndex> queryQps = f.getValue(); //Collection<Entry<Set<Expr>, QuadPatternIndex>> cacheQpiCandidates = cacheQpi.getIfSupersetOf(queryFeatureSet); Collection<Entry<Set<Expr>, QuadPatternIndex>> cacheQpiCandidates = cacheQpi.getIfSubsetOf(queryFeatureSet); for(QuadPatternIndex queryQp : queryQps) { Op queryLeaf = queryQp.getOpRef().getNode(); for(Entry<Set<Expr>, QuadPatternIndex> g : cacheQpiCandidates) { QuadPatternIndex cacheQp = g.getValue(); Op cacheLeaf = cacheQp.getOpRef().getNode(); result.put(cacheLeaf, queryLeaf); // // System.out.println("CacheQP: " + cacheQp); // System.out.println("QueryQP: " + queryQp); // // generateVarMappings(cacheQp, queryQp) // .forEach(x -> { // System.out.println("solution: " + x); // //cacheQp.getGroupedConjunction() // // }); // System.out.println("-----"); } } } return result; } public static Stream<ProblemNeighborhoodAware<Map<Var, Var>, Var>> createProblems(Multimap<Expr, Expr> sigToCache, Multimap<Expr, Expr> sigToQuery) { Map<Expr, Entry<Set<Expr>, Set<Expr>>> group = MapUtils.groupByKey(sigToCache.asMap(), sigToQuery.asMap()); Stream<ProblemNeighborhoodAware<Map<Var, Var>, Var>> result = group.values().stream() .map(x -> { Set<Expr> cacheExprs = x.getKey(); Set<Expr> queryExprs = x.getValue(); ProblemNeighborhoodAware<Map<Var, Var>, Var> p = new ProblemVarMappingExpr(cacheExprs, queryExprs, Collections.emptyMap()); return p; }); return result; } public static Stream<Map<Var, Var>> generateVarMappings(QuadPatternIndex cache, QuadPatternIndex query) { Multimap<Expr, Expr> cacheMap = cache.getGroupedConjunction(); Multimap<Expr, Expr> queryMap = query.getGroupedConjunction(); Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> problems = new ArrayList<>(); createProblems(cacheMap, queryMap).forEach(problems::add); // group.values().stream() // .map(x -> { // Set<Expr> cacheExprs = x.getKey(); // Set<Expr> queryExprs = x.getValue(); // ProblemNeighborhoodAware<Map<Var, Var>, Var> p = new ProblemVarMappingExpr(cacheExprs, queryExprs, Collections.emptyMap()); // // //System.out.println("cacheExprs: " + cacheExprs); // //System.out.println("queryExprs: " + queryExprs); // // //Stream<Map<Var, Var>> r = p.generateSolutions(); // // return p; // }) // .forEach(problems::add); ProblemVarMappingQuad quadProblem = new ProblemVarMappingQuad(cache.getQfpc().getQuads(), query.getQfpc().getQuads(), Collections.emptyMap()); problems.add(quadProblem); // for(int i = 0; i < 1000; ++i) { // Stopwatch sw = Stopwatch.createStarted(); Stream<Map<Var, Var>> result = ProblemContainerNeighbourhoodAware.solve( problems, Collections.emptyMap(), Map::keySet, MapUtils::mergeIfCompatible, Objects::isNull); // } return result; } @Deprecated // TODO: This method is similar to SparqlQueryContainmentUtils.tryMatch public static Stream<OpVarMap> match(Query a, Query b) { Function<Op, OpIndex> opIndexer = new OpIndexerImpl(); OpIndex viewIndex = opIndexer.apply(SparqlViewMatcherOpImpl.queryToNormalizedOp(a)); OpIndex queryIndex = opIndexer.apply(SparqlViewMatcherOpImpl.queryToNormalizedOp(b)); Multimap<Op, Op> candOpMapping = SparqlViewMatcherSystemImpl.getCandidateLeafMapping(viewIndex, queryIndex); if(logger.isDebugEnabled()) { for(Entry<Op, Collection<Op>> e : candOpMapping.asMap().entrySet()) { logger.debug("Candidate leaf mapping: " + e.getKey()); for(Op f : e.getValue()) { logger.debug(" Target: " + f); } } } Tree<Op> cacheTree = viewIndex.getTree(); Tree<Op> queryTree = queryIndex.getTree(); // TODO: Require a complete match of the tree - i.e. cache and query trees must have same number of nodes / same depth / some other criteria that can be checked quickly // In fact, we could use these features as an additional index Stream<OpVarMap> result = SparqlViewMatcherUtils.generateTreeVarMapping(candOpMapping, cacheTree, queryTree); return result; } /** * * @param cacheParentToNodeMaps: Mapping from a cache parent, to the candidate op-mappings from cache op to query op. Example: { 5: { A: {1}, B: {1} } } * */ public static void matchOpTrees(Map<Op, Multimap<Op, Op>> cacheParentToNodeMaps, Tree<Op> cacheTree, Tree<Op> queryTree) { } } //public static <T> Tree<T> removeUnaryNodes(Tree<T> tree) { // // Predicate<T> isMultiary = (node) -> tree.getChildren(node).size() > 1; // // //Map<T, T> childToParent = new HashMap<>(); // ListMultimap<T, T> parentToChildren = ArrayListMultimap.create(); // // // for every leaf get the first non-unary parent // Collection<T> parents = TreeUtils.getLeafs(tree); // Collection<T> children = null; // while(!parents.isEmpty()) { // children = parents; // // parents = new LinkedHashSet<T>(); // for(T child : children) { // T parent = TreeUtils.findAncestor(tree, child, isMultiary); // if(parent != null) { // parents.add(parent); // parentToChildren.put(parent, child); // } // } // } // // // There can be at most 1 root // T root = children.iterator().next(); //parents.isEmpty() ? null : parents.iterator().next(); // // // Tree<T> result = root == null // ? null // : TreeImpl.create(root, (node) -> parentToChildren.get(node)); // // return result; //}