void _add( const BSONObj& obj, const string& root , const BSONElement& e , BSONObjSet& keys ) const { BSONObjBuilder buf; buf.append( "" , root ); if ( e.eoo() ) buf.appendNull( "" ); else buf.appendAs( e , "" ); BSONObj key = buf.obj(); GEOQUADDEBUG( obj << "\n\t" << root << "\n\t" << key ); keys.insert( key ); }
void searchCommand( NamespaceDetails* nsd , int idxNo , 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 = (int)ceil( maxDistance / _bucketSize ); GeoHaystackSearchHopper hopper(n,maxDistance,limit,_geo); long long btreeMatches = 0; for ( int a=-scale; a<=scale; a++ ) { for ( int b=-scale; b<=scale; b++ ) { BSONObjBuilder bb; bb.append( "" , makeString( x + a , y + b ) ); for ( unsigned i=0; i<_other.size(); i++ ) { BSONElement e = search.getFieldDotted( _other[i] ); if ( e.eoo() ) bb.appendNull( "" ); else bb.appendAs( e , "" ); } BSONObj key = bb.obj(); GEOQUADDEBUG( "KEY: " << key ); set<DiskLoc> thisPass; scoped_ptr<BtreeCursor> cursor( BtreeCursor::make( nsd , idxNo , *getDetails() , key , key , true , 1 ) ); while ( cursor->ok() ) { pair<set<DiskLoc>::iterator, bool> p = thisPass.insert( cursor->currLoc() ); if ( p.second ) { hopper.got( cursor->currLoc() ); GEOQUADDEBUG( "\t" << cursor->current() ); btreeMatches++; } cursor->advance(); } } } BSONArrayBuilder arr( result.subarrayStart( "results" ) ); int num = hopper.append( 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(); } }