void HaystackAccessMethod::searchCommand(const BSONObj& nearObj, double maxDistance, const BSONObj& search, BSONObjBuilder* result, unsigned limit) { Timer t; LOG(1) << "SEARCH near:" << nearObj << " maxDistance:" << maxDistance << " search: " << search << endl; int x, y; { BSONObjIterator i(nearObj); x = HaystackKeyGenerator::hashHaystackElement(i.next(), _bucketSize); y = HaystackKeyGenerator::hashHaystackElement(i.next(), _bucketSize); } int scale = static_cast<int>(ceil(maxDistance / _bucketSize)); GeoHaystackSearchHopper hopper(nearObj, maxDistance, limit, _geoField); long long btreeMatches = 0; for (int a = -scale; a <= scale && !hopper.limitReached(); ++a) { for (int b = -scale; b <= scale && !hopper.limitReached(); ++b) { BSONObjBuilder bb; bb.append("", HaystackKeyGenerator::makeHaystackString(x + a, y + b)); for (unsigned i = 0; i < _otherFields.size(); i++) { // See if the non-geo field we're indexing on is in the provided search term. BSONElement e = search.getFieldDotted(_otherFields[i]); if (e.eoo()) bb.appendNull(""); else bb.appendAs(e, ""); } BSONObj key = bb.obj(); unordered_set<DiskLoc, DiskLoc::Hasher> thisPass; scoped_ptr<Runner> runner(InternalPlanner::indexScan(_btreeState->collection(), _descriptor, key, key, true)); Runner::RunnerState state; DiskLoc loc; while (Runner::RUNNER_ADVANCED == (state = runner->getNext(NULL, &loc))) { if (hopper.limitReached()) { break; } pair<unordered_set<DiskLoc, DiskLoc::Hasher>::iterator, bool> p = thisPass.insert(loc); // If a new element was inserted (haven't seen the DiskLoc before), p.second // is true. if (p.second) { hopper.consider(loc); btreeMatches++; } } } } BSONArrayBuilder arr(result->subarrayStart("results")); int num = hopper.appendResultsTo(&arr); arr.done(); { BSONObjBuilder b(result->subobjStart("stats")); b.append("time", t.millis()); b.appendNumber("btreeMatches", btreeMatches); b.append("n", num); b.done(); } }
void searchCommand(NamespaceDetails* nsd, const BSONObj& n /*near*/, double maxDistance, const BSONObj& search, BSONObjBuilder& result, unsigned limit) { Timer t; LOG(1) << "SEARCH near:" << n << " maxDistance:" << maxDistance << " search: " << search << endl; int x, y; { BSONObjIterator i(n); x = hash(i.next()); y = hash(i.next()); } int scale = static_cast<int>(ceil(maxDistance / _bucketSize)); GeoHaystackSearchHopper hopper(n, maxDistance, limit, _geoField); long long btreeMatches = 0; // TODO(hk): Consider starting with a (or b)=0, then going to a=+-1, then a=+-2, etc. // Would want a HaystackKeyIterator or similar for this, but it'd be a nice // encapsulation allowing us to S2-ify this trivially/abstract the key details. for (int a = -scale; a <= scale && !hopper.limitReached(); ++a) { for (int b = -scale; b <= scale && !hopper.limitReached(); ++b) { BSONObjBuilder bb; bb.append("", makeString(x + a, y + b)); for (unsigned i = 0; i < _otherFields.size(); i++) { // See if the non-geo field we're indexing on is in the provided search term. BSONElement e = search.getFieldDotted(_otherFields[i]); if (e.eoo()) bb.appendNull(""); else bb.appendAs(e, ""); } BSONObj key = bb.obj(); GEOQUADDEBUG("KEY: " << key); // TODO(hk): this keeps a set of all DiskLoc seen in this pass so that we don't // consider the element twice. Do we want to instead store a hash of the set? // Is this often big? set<DiskLoc> thisPass; // Lookup from key to key, inclusive. scoped_ptr<BtreeCursor> cursor(BtreeCursor::make(nsd, *getDetails(), key, key, true, 1)); while (cursor->ok() && !hopper.limitReached()) { pair<set<DiskLoc>::iterator, bool> p = thisPass.insert(cursor->currLoc()); // If a new element was inserted (haven't seen the DiskLoc before), p.second // is true. if (p.second) { hopper.consider(cursor->currLoc()); GEOQUADDEBUG("\t" << cursor->current()); btreeMatches++; } cursor->advance(); } } } BSONArrayBuilder arr(result.subarrayStart("results")); int num = hopper.appendResultsTo(&arr); arr.done(); { BSONObjBuilder b(result.subobjStart("stats")); b.append("time", t.millis()); b.appendNumber("btreeMatches", btreeMatches); b.append("n", num); b.done(); } }