TEST(QueryPlannerTest, testBFSLeaveOut) { try { { ParsedQuery pq = SparqlParser::parse( "SELECT ?x WHERE {?x ?p <X>. ?x ?p2 <Y>. <X> ?p <Y>}"); pq.expandPrefixes(); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq._rootGraphPattern); ASSERT_EQ(3u, tg._adjLists.size()); ad_utility::HashSet<size_t> lo; auto out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(3u, out.size()); lo.insert(1); out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(2u, out.size()); lo.insert(2); out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(1u, out.size()); lo.clear(); lo.insert(0); out = tg.bfsLeaveOut(1, lo); ASSERT_EQ(1u, out.size()); } { ParsedQuery pq = SparqlParser::parse( "SELECT ?x WHERE {<A> <B> ?x. ?x <C> ?y. ?y <X> <Y>}"); pq.expandPrefixes(); QueryPlanner qp(nullptr); auto tg = qp.createTripleGraph(&pq._rootGraphPattern); ad_utility::HashSet<size_t> lo; auto out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(3u, out.size()); lo.insert(1); out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(1u, out.size()); lo.insert(2); out = tg.bfsLeaveOut(0, lo); ASSERT_EQ(1u, out.size()); lo.clear(); lo.insert(0); out = tg.bfsLeaveOut(1, lo); ASSERT_EQ(2u, out.size()); } } catch (const ad_semsearch::Exception& e) { std::cout << "Caught: " << e.getFullErrorMessage() << std::endl; FAIL() << e.getFullErrorMessage(); } catch (const std::exception& e) { std::cout << "Caught: " << e.what() << std::endl; FAIL() << e.what(); } }
// _____________________________________________________________________________ vector<pair<QueryPlanner::TripleGraph, vector<SparqlFilter>>> QueryPlanner::TripleGraph::splitAtContextVars( const vector<SparqlFilter>& origFilters, unordered_map<string, vector<size_t>>& contextVarToTextNodes) const { vector<pair<QueryPlanner::TripleGraph, vector<SparqlFilter>>> retVal; // Recursively split the graph a context nodes. // Base-case: No no context nodes, return the graph itself. if (contextVarToTextNodes.size() == 0) { retVal.emplace_back(make_pair(*this, origFilters)); } else { // Just take the first contextVar at split at it. auto& cVar = contextVarToTextNodes.begin()->first; unordered_set<size_t> textNodeIds; textNodeIds.insert(contextVarToTextNodes.begin()->second.begin(), contextVarToTextNodes.begin()->second.end()); // For the next iteration / recursive call(s): // Leave out the first one because it has been worked on in this call. unordered_map<string, vector<size_t>> cTMapNextIteration; cTMapNextIteration.insert(++contextVarToTextNodes.begin(), contextVarToTextNodes.end()); // Find a node to starte the split. size_t startNode = 0; while (startNode < _adjLists.size() && textNodeIds.count(startNode) > 0) { ++startNode; } // If no start node was found, this means only text triples left. // --> don't enter code block below and return empty vector. if (startNode != _adjLists.size()) { // If we have a start node, do a BFS to obtain a set of reachable nodes auto reachableNodes = bfsLeaveOut(startNode, textNodeIds); if (reachableNodes.size() == _adjLists.size() - textNodeIds.size()) { // Case: cyclic or text operation was on the "outside" // -> only one split to work with further TripleGraph withoutText(*this, reachableNodes); vector<SparqlFilter> filters = pickFilters(origFilters, reachableNodes); auto recursiveResult = splitAtContextVars(filters, cTMapNextIteration); retVal.insert(retVal.begin(), recursiveResult.begin(), recursiveResult.end()); } else { // Case: The split created two or more non-empty parts. } } } return retVal; }