#include "processor/operator/scan/reachable_count.h"
#include <unordered_set>
#include "main/client_context.h"
#include "processor/execution_context.h"
#include "transaction/transaction.h"
using namespace lbug::common;
using namespace lbug::graph;
using namespace lbug::storage;
using namespace lbug::transaction;
namespace lbug {
namespace processor {
void ReachableCount::initLocalStateInternal(ResultSet* resultSet, ExecutionContext* context) {
countVector = resultSet->getValueVector(countOutputPos).get();
hasExecuted = false;
graph = std::make_unique<OnDiskGraph>(context->clientContext, graphEntry.copy());
relInfos = graph->getRelInfos(boundTableID);
for (auto& relInfo : relInfos) {
auto scanState = graph->prepareRelScan(*relInfo.relGroupEntry, relInfo.relTableID,
relInfo.dstTableID, std::vector<std::string>{});
scanStates.push_back(std::move(scanState));
}
}
offset_t ReachableCount::computeReachableCount() {
std::unordered_set<offset_t> seen;
std::vector<offset_t> frontier = startOffsets;
if (lowerBound == 0) {
for (auto offset : startOffsets) {
seen.insert(offset);
}
}
for (uint16_t d = 1; d <= upperBound; ++d) {
std::unordered_set<offset_t> nextFrontier;
for (auto offset : frontier) {
nodeID_t nodeID{offset, boundTableID};
for (auto& scanState : scanStates) {
for (auto chunk : graph->scanFwd(nodeID, *scanState)) {
chunk.forEach([&](auto neighbors, auto , auto i) {
auto nbr = neighbors[i];
if (nbr.tableID != nbrTableID) {
return;
}
nextFrontier.insert(nbr.offset);
if (d >= lowerBound) {
seen.insert(nbr.offset);
}
});
}
}
}
if (nextFrontier.empty()) {
break;
}
frontier.assign(nextFrontier.begin(), nextFrontier.end());
}
return static_cast<offset_t>(seen.size());
}
bool ReachableCount::getNextTuplesInternal(ExecutionContext*) {
if (hasExecuted) {
return false;
}
auto count = computeReachableCount();
countVector->state->getSelVectorUnsafe().setToUnfiltered(1);
countVector->setValue<int64_t>(0, static_cast<int64_t>(count));
hasExecuted = true;
return true;
}
} }