void DocumentSourceGraphLookUp::doBreadthFirstSearch() { long long depth = 0; bool shouldPerformAnotherQuery; do { shouldPerformAnotherQuery = false; // Check whether each key in the frontier exists in the cache or needs to be queried. BSONObjSet cached = SimpleBSONObjComparator::kInstance.makeBSONObjSet(); auto matchStage = makeMatchStageFromFrontier(&cached); ValueUnorderedSet queried = pExpCtx->getValueComparator().makeUnorderedValueSet(); _frontier->swap(queried); _frontierUsageBytes = 0; // Process cached values, populating '_frontier' for the next iteration of search. while (!cached.empty()) { auto it = cached.begin(); shouldPerformAnotherQuery = addToVisitedAndFrontier(*it, depth) || shouldPerformAnotherQuery; cached.erase(it); checkMemoryUsage(); } if (matchStage) { // Query for all keys that were in the frontier and not in the cache, populating // '_frontier' for the next iteration of search. // We've already allocated space for the trailing $match stage in '_fromPipeline'. _fromPipeline.back() = *matchStage; auto pipeline = uassertStatusOK(_mongod->makePipeline(_fromPipeline, _fromExpCtx)); while (auto next = pipeline->output()->getNext()) { uassert(40271, str::stream() << "Documents in the '" << _from.ns() << "' namespace must contain an _id for de-duplication in $graphLookup", !(*next)["_id"].missing()); BSONObj result = next->toBson(); shouldPerformAnotherQuery = addToVisitedAndFrontier(result.getOwned(), depth) || shouldPerformAnotherQuery; addToCache(result, queried); } checkMemoryUsage(); } ++depth; } while (shouldPerformAnotherQuery && depth < std::numeric_limits<long long>::max() && (!_maxDepth || depth <= *_maxDepth)); _frontier->clear(); _frontierUsageBytes = 0; }
void DocumentSourceGraphLookUp::doBreadthFirstSearch() { long long depth = 0; bool shouldPerformAnotherQuery; do { shouldPerformAnotherQuery = false; // Check whether each key in the frontier exists in the cache or needs to be queried. BSONObjSet cached; auto query = constructQuery(&cached); std::unordered_set<Value, Value::Hash> queried; _frontier.swap(queried); _frontierUsageBytes = 0; // Process cached values, populating '_frontier' for the next iteration of search. while (!cached.empty()) { auto it = cached.begin(); shouldPerformAnotherQuery = addToVisitedAndFrontier(*it, depth) || shouldPerformAnotherQuery; cached.erase(it); checkMemoryUsage(); } if (query) { // Query for all keys that were in the frontier and not in the cache, populating // '_frontier' for the next iteration of search. unique_ptr<DBClientCursor> cursor = _mongod->directClient()->query(_from.ns(), *query); // Iterate the cursor. while (cursor->more()) { BSONObj result = cursor->nextSafe(); shouldPerformAnotherQuery = addToVisitedAndFrontier(result.getOwned(), depth) || shouldPerformAnotherQuery; addToCache(result, queried); } checkMemoryUsage(); } ++depth; } while (shouldPerformAnotherQuery && depth < std::numeric_limits<long long>::max() && (!_maxDepth || depth <= *_maxDepth)); _frontier.clear(); _frontierUsageBytes = 0; }