package org.aksw.jena_sparql_api.sparql.algebra.mapping; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import org.aksw.combinatorics.solvers.GenericProblem; import org.aksw.combinatorics.solvers.ProblemContainerNeighbourhoodAware; import org.aksw.combinatorics.solvers.ProblemNeighborhoodAware; import org.aksw.combinatorics.solvers.ProblemStaticSolutions; import org.aksw.commons.collections.multimaps.IBiSetMultimap; import org.aksw.jena_sparql_api.concept_cache.collection.FeatureMap; import org.aksw.jena_sparql_api.concept_cache.collection.FeatureMapImpl; 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.concept_cache.core.SparqlCacheUtils; import org.aksw.jena_sparql_api.concept_cache.domain.QuadFilterPatternCanonical; import org.aksw.jena_sparql_api.utils.MapUtils; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.core.Var; import org.apache.jena.sparql.expr.Expr; import org.apache.jena.sparql.expr.ExprVar; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Multimap; public class VarMapper { private static final Logger logger = LoggerFactory.getLogger(VarMapper.class); public static GenericProblem<Map<Var, Var>, ?> deriveProblem(List<Var> cacheVars, List<Var> userVars) { List<Expr> aExprs = cacheVars.stream().map(v -> new ExprVar(v)).collect(Collectors.toList()); List<Expr> bExprs = userVars.stream().map(v -> new ExprVar(v)).collect(Collectors.toList()); GenericProblem<Map<Var, Var>, ?> result = new ProblemVarMappingExpr(aExprs, bExprs, Collections.emptyMap()); return result; } public static Stream<ProblemNeighborhoodAware<Map<Var, Var>, Var>> createProblems(FeatureMap<Expr, Multimap<Expr, Expr>> cacheIndex, FeatureMap<Expr, Multimap<Expr, Expr>> queryIndex) { List<ProblemNeighborhoodAware<Map<Var, Var>, Var>> problems = new ArrayList<>(); for(Entry<Set<Expr>, Collection<Multimap<Expr, Expr>>> entry : queryIndex.entrySet()) { Set<Expr> querySig = entry.getKey(); Collection<Multimap<Expr, Expr>> queryMaps = entry.getValue(); if(logger.isTraceEnabled()) { logger.trace("CAND LOOKUP with " + querySig); } // Base on the signatures: For the current query clause, find cache clauses that are less restrictive Collection<Entry<Set<Expr>, Multimap<Expr, Expr>>> cands = cacheIndex.getIfSubsetOf(querySig); // If there is none, the view is not suitable // If the cache index is empty, such as in the case of view := { ?s ?p ?o } then the view is // satisfiable after all if(cands.isEmpty()) { problems = Collections.singletonList(new ProblemStaticSolutions<>(Collections.singleton(null))); break; } for(Entry<Set<Expr>, Multimap<Expr, Expr>> e : cands) { Multimap<Expr, Expr> cacheMap = e.getValue(); //System.out.println(" CACHE MAP: " + cacheMap); if(logger.isTraceEnabled()) { logger.trace(" CACHE MAP: " + cacheMap); } for(Multimap<Expr, Expr> queryMap : queryMaps) { Map<Expr, Entry<Set<Expr>, Set<Expr>>> group = MapUtils.groupByKey(cacheMap.asMap(), queryMap.asMap()); Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> localProblems = 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()); if(logger.isTraceEnabled()) { logger.trace("Registered problem instance " + p + " with an estimated cost of " + p.getEstimatedCost()); } if(logger.isTraceEnabled()) { logger.trace(" Enumerating its solutions yields " + p.generateSolutions().count() + " items: " + p.generateSolutions().collect(Collectors.toList())); } // System.out.println("Registered problem instance " + p + " with an estimated cost of " + p.getEstimatedCost()); // System.out.println(" Enumerating its solutions yields " + p.generateSolutions().count() + " items: " + p.generateSolutions().collect(Collectors.toList())); return p; }) .collect(Collectors.toList()); problems.addAll(localProblems); } } //cands.forEach(x -> System.out.println("CAND: " + x.getValue())); } return problems.stream(); } /** * TODO Return only the collection of problems at this stage * * @param cachePattern * @param queryPattern * @return */ public static Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> createProblems(QuadFilterPatternCanonical cachePattern, QuadFilterPatternCanonical queryPattern) { FeatureMap<Expr, Multimap<Expr, Expr>> cacheIndex = SparqlCacheUtils.indexDnf(cachePattern.getFilterDnf()); FeatureMap<Expr, Multimap<Expr, Expr>> queryIndex = SparqlCacheUtils.indexDnf(queryPattern.getFilterDnf()); Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> result = new ArrayList<>(); createProblems(cacheIndex, queryIndex).forEach(result::add); // TODO The code below does not correctly match quads: // Given a view (v1, {}) (i.e. no filters and a user query (u1, {u1.s = foo}), // we need to match u1 with any quad having a _subset_ of the constraints // Index the quads by the cnf (dnf)? IBiSetMultimap<Quad, Set<Set<Expr>>> cacheQuadIndex = SparqlCacheUtils.createMapQuadsToFilters(cachePattern); IBiSetMultimap<Quad, Set<Set<Expr>>> queryQuadIndex = SparqlCacheUtils.createMapQuadsToFilters(queryPattern); FeatureMap<Expr, Quad> queryQuadI = new FeatureMapImpl<>(); queryQuadIndex.entries().forEach(e -> e.getValue().forEach(x -> queryQuadI.put(x, e.getKey()) )); // // FeatureMap<Expr, Quad> queryQuadI = new FeatureMapImpl<>(); // queryQuadIndex.entries().forEach(e -> { // Set<Set<Expr>> cnf = e.getValue(); // if(cnf.isEmpty()) { // cnf = Collections.singleton(Collections.emptySet()); // } else { // cnf.forEach(x -> queryQuadI.put(x, e.getKey())); // } // }); //cacheQuadIndex.getInverse().asMap().entrySet().forEach(e -> { for(Entry<Set<Set<Expr>>, Collection<Quad>> e : cacheQuadIndex.getInverse().asMap().entrySet()) { Collection<Quad> viewQuads = e.getValue(); Set<Set<Expr>> dnf = e.getKey(); // Small hack to look up query quads having more than 'none' constraints // if(exprs.isEmpty()) { // exprs = Collections.singleton(Collections.emptySet()); // } Set<Quad> queryQuads = dnf.stream() .flatMap(clause -> queryQuadI.getIfSupersetOf(clause).stream().map(x -> x.getValue())) .collect(Collectors.toSet()); ProblemVarMappingQuad quadProblem = new ProblemVarMappingQuad(viewQuads, queryQuads, Collections.emptyMap()); if(logger.isTraceEnabled()) { logger.trace("Registered quad problem instance " + quadProblem + " with an estimated cost of " + quadProblem.getEstimatedCost()); } //System.out.println("Registered quad problem instance " + quadProblem + " with an estimated cost of " + quadProblem.getEstimatedCost()); //System.out.println("Registered quad problem instance " + quadProblem + " with an estimated cost of " + quadProblem.getEstimatedCost()); //System.out.println(" Enumerating its solutions yields " + quadProblem.generateSolutions().count()); //System.out.println("Registered quad problem instance " + quadProblem + " with " + quadProblem.generateSolutions().count() + " solutions "); result.add(quadProblem); } if(false) { Map<Set<Set<Expr>>, Entry<Set<Quad>, Set<Quad>>> quadGroups = MapUtils.groupByKey(cacheQuadIndex.getInverse(), queryQuadIndex.getInverse()); for(Entry<Set<Quad>, Set<Quad>> quadGroup : quadGroups.values()) { ProblemVarMappingQuad quadProblem = new ProblemVarMappingQuad(quadGroup.getKey(), quadGroup.getValue(), Collections.emptyMap()); if(logger.isTraceEnabled()) { logger.trace("Registered quad problem instance " + quadProblem + " with an estimated cost of " + quadProblem.getEstimatedCost()); } //System.out.println("Registered quad problem instance " + quadProblem + " with an estimated cost of " + quadProblem.getEstimatedCost()); //System.out.println(" Enumerating its solutions yields " + quadProblem.generateSolutions().count()); //System.out.println("Registered quad problem instance " + quadProblem + " with " + quadProblem.generateSolutions().count() + " solutions "); result.add(quadProblem); } } return result; } public static Stream<Map<Var, Var>> solve(Collection<? extends ProblemNeighborhoodAware<Map<Var, Var>, Var>> problems) { Stream<Map<Var, Var>> result = ProblemContainerNeighbourhoodAware.solve( problems, Collections.emptyMap(), Map::keySet, MapUtils::mergeIfCompatible, Objects::isNull); return result; } public static Stream<Map<Var, Var>> createVarMapCandidates(QuadFilterPatternCanonical cachePattern, QuadFilterPatternCanonical queryPattern) { Collection<ProblemNeighborhoodAware<Map<Var, Var>, Var>> problems = createProblems(cachePattern, queryPattern); Stream<Map<Var, Var>> result = solve(problems); return result; } }