@@ -1380,16 +1380,17 @@ QueryPlanner::runGreedyPlanningOnConnectedComponent(
1380
1380
std::vector<SubtreePlan> connectedComponent,
1381
1381
const vector<SparqlFilter>& filters, const TextLimitVec& textLimits,
1382
1382
const TripleGraph& tg) const {
1383
- auto & result = connectedComponent;
1384
- applyFiltersIfPossible<true >(result, filters);
1385
- applyTextLimitsIfPossible (result, textLimits, true );
1386
- const size_t numSeeds = findUniqueNodeIds (result);
1383
+ applyFiltersIfPossible<true >(connectedComponent, filters);
1384
+ applyTextLimitsIfPossible (connectedComponent, textLimits, true );
1385
+ const size_t numSeeds = findUniqueNodeIds (connectedComponent);
1386
+ if (numSeeds <= 1 ) {
1387
+ // Only 0 or 1 nodes in the input, nothing to plan.
1388
+ return connectedComponent;
1389
+ }
1387
1390
1388
1391
// Intermediate variables that will be filled by the `greedyStep` lambda
1389
1392
// below.
1390
1393
using Plans = std::vector<SubtreePlan>;
1391
- Plans precomputedCache;
1392
- Plans nextResult;
1393
1394
1394
1395
// Perform a single step of greedy query planning.
1395
1396
// `nextResult` contains the result of the last step of greedy query planning.
@@ -1401,48 +1402,47 @@ QueryPlanner::runGreedyPlanningOnConnectedComponent(
1401
1402
// above pre-/postconditions. Exception: if `isFirstStep` then `cache` and
1402
1403
// `nextResult` must be empty, and the first step of greedy planning is
1403
1404
// performed, which also establishes the pre-/postconditions.
1404
- auto greedyStep = [this , &tg, &filters, &textLimits](
1405
- Plans* input, Plans* cache, Plans* nextResult,
1406
- bool isFirstStep) {
1405
+ auto greedyStep = [this , &tg, &filters, &textLimits,
1406
+ input = std::move (connectedComponent), cache = Plans{}](
1407
+ Plans& nextBestPlan, bool isFirstStep) mutable {
1407
1408
checkCancellation ();
1408
- // We already have all combinations of two nodes in `input` in the cache, so
1409
- // we only have to add the combinations between `input` and `nextResult`. In
1410
- // the first step, we initially fill the cache.
1411
- auto newPlans = isFirstStep ? merge (*input, *input, tg)
1412
- : merge (*input, *nextResult, tg);
1409
+ // Normally, we already have all combinations of two nodes in `input` in the
1410
+ // cache, so we only have to add the combinations between `input` and
1411
+ // `nextResult`. In the first step, we need to initially compute all
1412
+ // possible combinations.
1413
+ auto newPlans =
1414
+ isFirstStep ? merge (input, input, tg) : merge (input, nextBestPlan, tg);
1413
1415
applyFiltersIfPossible<true >(newPlans, filters);
1414
1416
applyTextLimitsIfPossible (newPlans, textLimits, true );
1415
1417
AD_CORRECTNESS_CHECK (!newPlans.empty ());
1416
- ql::ranges::move (newPlans, std::back_inserter (*cache));
1417
- ql::ranges::move (*nextResult, std::back_inserter (*input));
1418
+ ql::ranges::move (newPlans, std::back_inserter (cache));
1419
+ ql::ranges::move (nextBestPlan, std::back_inserter (input));
1420
+
1418
1421
// All candidates for the next greedy step are in the `cache`, choose the
1419
1422
// cheapest one, remove it from the cache and make it the `nextResult`
1420
1423
{
1421
- auto smallestIdxNew = findSmallestExecutionTree (*cache);
1422
- auto & cheapestNewTree = cache->at (smallestIdxNew);
1423
- std::swap (cheapestNewTree, cache->back ());
1424
- nextResult->clear ();
1425
- nextResult->push_back (std::move (cache->back ()));
1426
- cache->pop_back ();
1427
- }
1428
- // All plans which are not disjoint with the newly chosen plan have to be
1424
+ auto smallestIdxNew = findSmallestExecutionTree (cache);
1425
+ auto & cheapestNewTree = cache.at (smallestIdxNew);
1426
+ std::swap (cheapestNewTree, cache.back ());
1427
+ nextBestPlan.clear ();
1428
+ nextBestPlan.push_back (std::move (cache.back ()));
1429
+ cache.pop_back ();
1430
+ }
1431
+
1432
+ // All plans which have a node in common with the chosen plan have to be
1429
1433
// deleted from the `input` and therefore also from the `cache`.
1430
- auto shouldBeErased = [&nextTree = nextResult-> front ()](const auto & plan) {
1434
+ auto shouldBeErased = [&nextTree = nextBestPlan. front ()](const auto & plan) {
1431
1435
return (nextTree._idsOfIncludedNodes & plan._idsOfIncludedNodes ) != 0 ;
1432
1436
};
1433
- std::erase_if (* input, shouldBeErased);
1434
- std::erase_if (* cache, shouldBeErased);
1437
+ std::erase_if (input, shouldBeErased);
1438
+ std::erase_if (cache, shouldBeErased);
1435
1439
};
1436
1440
1437
1441
bool first = true ;
1438
- if (numSeeds <= 1 ) {
1439
- // Only 0 or 1 nodes in the input, nothing to plan.
1440
- return std::move (result);
1441
- }
1442
-
1442
+ Plans nextResult;
1443
1443
for ([[maybe_unused]] size_t i : ad_utility::integerRange (numSeeds - 1 )) {
1444
- bool isFirstStep = std::exchange (first, false );
1445
- greedyStep (&result, &precomputedCache, &nextResult, isFirstStep) ;
1444
+ greedyStep (nextResult, first );
1445
+ first = false ;
1446
1446
}
1447
1447
// TODO<joka921> Assert that all seeds are covered by the result.
1448
1448
return nextResult;
0 commit comments