diff options
Diffstat (limited to 'db/geo/2d.cpp')
-rw-r--r-- | db/geo/2d.cpp | 710 |
1 files changed, 362 insertions, 348 deletions
diff --git a/db/geo/2d.cpp b/db/geo/2d.cpp index 6e9aac7c6ba..3102b8a3469 100644 --- a/db/geo/2d.cpp +++ b/db/geo/2d.cpp @@ -34,7 +34,7 @@ namespace mongo { #if 0 # define GEODEBUG(x) cout << x << endl; # define GEODEBUGPRINT(x) PRINT(x) - inline void PREFIXDEBUG(GeoHash prefix, const GeoConvert* g){ + inline void PREFIXDEBUG(GeoHash prefix, const GeoConvert* g) { if (!prefix.constrains()) { cout << "\t empty prefix" << endl; return ; @@ -47,14 +47,14 @@ namespace mongo { Point center ( (ll._x+tr._x)/2, (ll._y+tr._y)/2 ); double radius = fabs(ll._x - tr._x) / 2; - cout << "\t ll: " << ll.toString() << " tr: " << tr.toString() + cout << "\t ll: " << ll.toString() << " tr: " << tr.toString() << " center: " << center.toString() << " radius: " << radius << endl; } #else -# define GEODEBUG(x) -# define GEODEBUGPRINT(x) -# define PREFIXDEBUG(x, y) +# define GEODEBUG(x) +# define GEODEBUGPRINT(x) +# define PREFIXDEBUG(x, y) #endif const double EARTH_RADIUS_KM = 6371; @@ -65,9 +65,9 @@ namespace mongo { GEO_SPHERE }; - inline double computeXScanDistance(double y, double maxDistDegrees){ + inline double computeXScanDistance(double y, double maxDistDegrees) { // TODO: this overestimates for large madDistDegrees far from the equator - return maxDistDegrees / min(cos(deg2rad(min(+89.0, y + maxDistDegrees))), + return maxDistDegrees / min(cos(deg2rad(min(+89.0, y + maxDistDegrees))), cos(deg2rad(max(-89.0, y - maxDistDegrees)))); } @@ -78,14 +78,14 @@ namespace mongo { class Geo2dType : public IndexType , public GeoConvert { public: Geo2dType( const IndexPlugin * plugin , const IndexSpec* spec ) - : IndexType( plugin , spec ){ - + : IndexType( plugin , spec ) { + BSONObjBuilder orderBuilder; BSONObjIterator i( spec->keyPattern ); - while ( i.more() ){ + while ( i.more() ) { BSONElement e = i.next(); - if ( e.type() == String && GEO2DNAME == e.valuestr() ){ + if ( e.type() == String && GEO2DNAME == e.valuestr() ) { uassert( 13022 , "can't have 2 geo field" , _geo.size() == 0 ); uassert( 13023 , "2d has to be first in index" , _other.size() == 0 ); _geo = e.fieldName(); @@ -95,16 +95,16 @@ namespace mongo { } orderBuilder.append( "" , 1 ); } - + uassert( 13024 , "no geo field specified" , _geo.size() ); - + _bits = _configval( spec , "bits" , 26 ); // for lat/long, ~ 1ft uassert( 13028 , "can't have more than 32 bits in geo index" , _bits <= 32 ); _max = _configval( spec , "max" , 180 ); _min = _configval( spec , "min" , -180 ); - + _scaling = (1024*1024*1024*4.0)/(_max-_min); _order = orderBuilder.obj(); @@ -115,30 +115,30 @@ namespace mongo { _error = distance(a, b); } - int _configval( const IndexSpec* spec , const string& name , int def ){ + int _configval( const IndexSpec* spec , const string& name , int def ) { BSONElement e = spec->info[name]; if ( e.isNumber() ) return e.numberInt(); return def; } - ~Geo2dType(){ - + ~Geo2dType() { + } - virtual BSONObj fixKey( const BSONObj& in ) { + virtual BSONObj fixKey( const BSONObj& in ) { if ( in.firstElement().type() == BinData ) return in; BSONObjBuilder b(in.objsize()+16); - + if ( in.firstElement().isABSONObj() ) _hash( in.firstElement().embeddedObject() ).append( b , "" ); else if ( in.firstElement().type() == String ) GeoHash( in.firstElement().valuestr() ).append( b , "" ); else if ( in.firstElement().type() == RegEx ) GeoHash( in.firstElement().regex() ).append( b , "" ); - else + else return in; BSONObjIterator i(in); @@ -164,7 +164,7 @@ namespace mongo { _hash( embed ).append( b , "" ); - for ( size_t i=0; i<_other.size(); i++ ){ + for ( size_t i=0; i<_other.size(); i++ ) { BSONElement e = obj.getFieldDotted(_other[i]); if ( e.eoo() ) e = _spec->missingField(); @@ -172,11 +172,11 @@ namespace mongo { } keys.insert( b.obj() ); } - + GeoHash _tohash( const BSONElement& e ) const { if ( e.isABSONObj() ) return _hash( e.embeddedObject() ); - + return GeoHash( e , _bits ); } @@ -186,7 +186,7 @@ namespace mongo { BSONElement x = i.next(); uassert( 13068 , "geo field only has 1 element" , i.more() ); BSONElement y = i.next(); - + uassert( 13026 , "geo values have to be numbers: " + o.toString() , x.isNumber() && y.isNumber() ); return hash( x.number() , y.number() ); @@ -204,33 +204,33 @@ namespace mongo { b.append( "y" , _unconvert( y ) ); return b.obj(); } - + unsigned _convert( double in ) const { uassert( 13027 , "point not in range" , in <= (_max + _error) && in >= (_min - _error) ); in -= _min; assert( in > 0 ); return (unsigned)(in * _scaling); } - + double _unconvert( unsigned in ) const { double x = in; x /= _scaling; x += _min; return x; } - + void unhash( const GeoHash& h , double& x , double& y ) const { unsigned a,b; h.unhash(a,b); x = _unconvert( a ); y = _unconvert( b ); } - + double distance( const GeoHash& a , const GeoHash& b ) const { double ax,ay,bx,by; unhash( a , ax , ay ); unhash( b , bx , by ); - + double dx = bx - ax; double dy = by - ay; @@ -265,10 +265,10 @@ namespace mongo { virtual IndexSuitability suitability( const BSONObj& query , const BSONObj& order ) const { BSONElement e = query.getFieldDotted(_geo.c_str()); - switch ( e.type() ){ + switch ( e.type() ) { case Object: { BSONObj sub = e.embeddedObject(); - switch ( sub.firstElement().getGtLtOp() ){ + switch ( sub.firstElement().getGtLtOp() ) { case BSONObj::opNEAR: case BSONObj::opWITHIN: return OPTIMAL; @@ -284,7 +284,7 @@ namespace mongo { string _geo; vector<string> _other; - + unsigned _bits; int _max; int _min; @@ -296,38 +296,38 @@ namespace mongo { class Box { public: - + Box( const Geo2dType * g , const GeoHash& hash ) - : _min( g , hash ) , - _max( _min._x + g->sizeEdge( hash ) , _min._y + g->sizeEdge( hash ) ){ + : _min( g , hash ) , + _max( _min._x + g->sizeEdge( hash ) , _min._y + g->sizeEdge( hash ) ) { } - + Box( double x , double y , double size ) - : _min( x , y ) , - _max( x + size , y + size ){ + : _min( x , y ) , + _max( x + size , y + size ) { } Box( Point min , Point max ) - : _min( min ) , _max( max ){ + : _min( min ) , _max( max ) { } - Box(){} + Box() {} string toString() const { StringBuilder buf(64); buf << _min.toString() << " -->> " << _max.toString(); return buf.str(); } - + bool between( double min , double max , double val , double fudge=0) const { return val + fudge >= min && val <= max + fudge; } - + bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const { assert( amin <= amax ); assert( bmin <= bmax ); - if ( amin < bmin ){ + if ( amin < bmin ) { if ( amax < bmin ) return false; res = min ? bmin : amax; @@ -340,16 +340,16 @@ namespace mongo { } double intersects( const Box& other ) const { - + Point boundMin(0,0); Point boundMax(0,0); - + if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false || - mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false || - mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false || - mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false ) + mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false || + mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false || + mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false ) return 0; - + Box intersection( boundMin , boundMax ); return intersection.area() / ( ( area() + other.area() ) / 2 ); @@ -364,49 +364,49 @@ namespace mongo { ( _min._y + _max._y ) / 2 ); } - bool inside( Point p , double fudge = 0 ){ + bool inside( Point p , double fudge = 0 ) { bool res = inside( p._x , p._y , fudge ); //cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl; return res; } - - bool inside( double x , double y , double fudge = 0 ){ - return + + bool inside( double x , double y , double fudge = 0 ) { + return between( _min._x , _max._x , x , fudge ) && between( _min._y , _max._y , y , fudge ); } - bool contains(const Box& other, double fudge=0){ + bool contains(const Box& other, double fudge=0) { return inside(other._min, fudge) && inside(other._max, fudge); } - + Point _min; Point _max; }; - + class Geo2dPlugin : public IndexPlugin { public: - Geo2dPlugin() : IndexPlugin( GEO2DNAME ){ + Geo2dPlugin() : IndexPlugin( GEO2DNAME ) { } - + virtual IndexType* generate( const IndexSpec* spec ) const { return new Geo2dType( this , spec ); } } geo2dplugin; - + struct GeoUnitTest : public UnitTest { - - int round( double d ){ + + int round( double d ) { return (int)(.5+(d*1000)); } - + #define GEOHEQ(a,b) if ( a.toString() != b ){ cout << "[" << a.toString() << "] != [" << b << "]" << endl; assert( a == GeoHash(b) ); } - void run(){ + void run() { assert( ! GeoHash::isBitSet( 0 , 0 ) ); assert( ! GeoHash::isBitSet( 0 , 31 ) ); assert( GeoHash::isBitSet( 1 , 31 ) ); - + IndexSpec i( BSON( "loc" << "2d" ) ); Geo2dType g( &geo2dplugin , &i ); { @@ -432,7 +432,7 @@ namespace mongo { assert( round( in["x"].number() ) == round( out["x"].number() ) ); assert( round( in["y"].number() ) == round( out["y"].number() ) ); } - + { GeoHash h( "0000" ); h.move( 0 , 1 ); @@ -445,13 +445,13 @@ namespace mongo { GEOHEQ( h , "0100" ); h.move( 0 , -1 ); GEOHEQ( h , "0001" ); - + h.init( "0000" ); h.move( 1 , 0 ); GEOHEQ( h , "0010" ); } - + { Box b( 5 , 5 , 2 ); assert( "(5,5) -->> (7,7)" == b.toString() ); @@ -465,7 +465,7 @@ namespace mongo { b = g.hash( 42 , 44 ); assert( round(10) == round(g.distance( a , b )) ); } - + { GeoHash x("0000"); assert( 0 == x.getHash() ); @@ -475,7 +475,7 @@ namespace mongo { assert( GeoHash( "1100").hasPrefix( GeoHash( "11" ) ) ); assert( ! GeoHash( "1000").hasPrefix( GeoHash( "11" ) ) ); } - + { GeoHash x("1010"); GEOHEQ( x , "1010" ); @@ -483,8 +483,8 @@ namespace mongo { GEOHEQ( y , "101001" ); } - { - + { + GeoHash a = g.hash( 5 , 5 ); GeoHash b = g.hash( 5 , 7 ); GeoHash c = g.hash( 100 , 100 ); @@ -530,13 +530,13 @@ namespace mongo { assert( entry.hasPrefix( GeoHash( "1100" ) ) ); assert( entry.hasPrefix( prefix ) ); } - + { GeoHash a = g.hash( 50 , 50 ); GeoHash b = g.hash( 48 , 54 ); assert( round( 4.47214 ) == round( g.distance( a , b ) ) ); } - + { Box b( Point( 29.762283 , -95.364271 ) , Point( 29.764283000000002 , -95.36227099999999 ) ); @@ -555,7 +555,7 @@ namespace mongo { int N = 10000; { Timer t; - for ( int i=0; i<N; i++ ){ + for ( int i=0; i<N; i++ ) { unsigned x = (unsigned)rand(); unsigned y = (unsigned)rand(); GeoHash h( x , y ); @@ -569,7 +569,7 @@ namespace mongo { { Timer t; - for ( int i=0; i<N; i++ ){ + for ( int i=0; i<N; i++ ) { unsigned x = (unsigned)rand(); unsigned y = (unsigned)rand(); GeoHash h( x , y ); @@ -600,7 +600,7 @@ namespace mongo { { Point BNA (-1.5127, 0.6304); Point LAX (-2.0665, 0.5924); - + double dist1 = spheredist_rad(BNA, LAX); double dist2 = spheredist_rad(LAX, BNA); @@ -611,7 +611,7 @@ namespace mongo { { Point JFK (-73.77694444, 40.63861111 ); Point LAX (-118.40, 33.94); - + double dist = spheredist_deg(JFK, LAX) * EARTH_RADIUS_MILES; assert( dist > 2469 && dist < 2470 ); } @@ -635,18 +635,18 @@ namespace mongo { } } } geoUnitTest; - + class GeoPoint { public: - GeoPoint(){ + GeoPoint() { } GeoPoint( const KeyNode& node , double distance ) - : _key( node.key ) , _loc( node.recordLoc ) , _o( node.recordLoc.obj() ) , _distance( distance ){ + : _key( node.key ) , _loc( node.recordLoc ) , _o( node.recordLoc.obj() ) , _distance( distance ) { } GeoPoint( const BSONObj& key , DiskLoc loc , double distance ) - : _key(key) , _loc(loc) , _o( loc.obj() ) , _distance( distance ){ + : _key(key) , _loc(loc) , _o( loc.obj() ) , _distance( distance ) { } bool operator<( const GeoPoint& other ) const { @@ -667,44 +667,44 @@ namespace mongo { public: GeoAccumulator( const Geo2dType * g , const BSONObj& filter ) : _g(g) , _lookedAt(0) , _objectsLoaded(0) , _found(0) { - if ( ! filter.isEmpty() ){ + if ( ! filter.isEmpty() ) { _matcher.reset( new CoveredIndexMatcher( filter , g->keyPattern() ) ); } } - virtual ~GeoAccumulator(){ + virtual ~GeoAccumulator() { } - virtual void add( const KeyNode& node ){ + virtual void add( const KeyNode& node ) { // when looking at other boxes, don't want to look at some object twice pair<set<DiskLoc>::iterator,bool> seenBefore = _seen.insert( node.recordLoc ); - if ( ! seenBefore.second ){ + if ( ! seenBefore.second ) { GEODEBUG( "\t\t\t\t already seen : " << node.recordLoc.obj()["_id"] ); return; } _lookedAt++; - + // distance check double d = 0; - if ( ! checkDistance( GeoHash( node.key.firstElement() ) , d ) ){ + if ( ! checkDistance( GeoHash( node.key.firstElement() ) , d ) ) { GEODEBUG( "\t\t\t\t bad distance : " << node.recordLoc.obj() << "\t" << d ); return; - } + } GEODEBUG( "\t\t\t\t good distance : " << node.recordLoc.obj() << "\t" << d ); - + // matcher MatchDetails details; - if ( _matcher.get() ){ + if ( _matcher.get() ) { bool good = _matcher->matches( node.key , node.recordLoc , &details ); if ( details.loadedObject ) _objectsLoaded++; - - if ( ! good ){ + + if ( ! good ) { GEODEBUG( "\t\t\t\t didn't match : " << node.recordLoc.obj()["_id"] ); return; } } - + if ( ! details.loadedObject ) // dont double count _objectsLoaded++; @@ -718,7 +718,7 @@ namespace mongo { long long found() const { return _found; } - + const Geo2dType * _g; set<DiskLoc> _seen; auto_ptr<CoveredIndexMatcher> _matcher; @@ -727,7 +727,7 @@ namespace mongo { long long _objectsLoaded; long long _found; }; - + class GeoHopper : public GeoAccumulator { public: typedef multiset<GeoPoint> Holder; @@ -736,33 +736,34 @@ namespace mongo { : GeoAccumulator( g , filter ) , _max( max ) , _near( n ), _maxDistance( maxDistance ), _type( type ), _farthest(-1) {} - virtual bool checkDistance( const GeoHash& h , double& d ){ - switch (_type){ - case GEO_PLAIN: - d = _near.distance( Point(_g, h) ); - break; - case GEO_SPHERE: - d = spheredist_deg(_near, Point(_g, h)); - break; - default: - assert(0); + virtual bool checkDistance( const GeoHash& h , double& d ) { + switch (_type) { + case GEO_PLAIN: + d = _near.distance( Point(_g, h) ); + break; + case GEO_SPHERE: + d = spheredist_deg(_near, Point(_g, h)); + break; + default: + assert(0); } bool good = d < _maxDistance && ( _points.size() < _max || d < farthest() ); - GEODEBUG( "\t\t\t\t\t\t\t checkDistance " << _near.toString() << "\t" << h << "\t" << d + GEODEBUG( "\t\t\t\t\t\t\t checkDistance " << _near.toString() << "\t" << h << "\t" << d << " ok: " << good << " farthest: " << farthest() ); return good; } - - virtual void addSpecific( const KeyNode& node , double d ){ + + virtual void addSpecific( const KeyNode& node , double d ) { GEODEBUG( "\t\t" << GeoHash( node.key.firstElement() ) << "\t" << node.recordLoc.obj() << "\t" << d ); _points.insert( GeoPoint( node.key , node.recordLoc , d ) ); - if ( _points.size() > _max ){ + if ( _points.size() > _max ) { _points.erase( --_points.end() ); - + Holder::iterator i = _points.end(); i--; _farthest = i->_distance; - } else { + } + else { if (d > _farthest) _farthest = d; } @@ -780,42 +781,42 @@ namespace mongo { GeoDistType _type; double _farthest; }; - + struct BtreeLocation { int pos; bool found; DiskLoc bucket; - - BSONObj key(){ + + BSONObj key() { if ( bucket.isNull() ) return BSONObj(); return bucket.btree()->keyNode( pos ).key; } - - bool hasPrefix( const GeoHash& hash ){ + + bool hasPrefix( const GeoHash& hash ) { BSONElement e = key().firstElement(); if ( e.eoo() ) return false; return GeoHash( e ).hasPrefix( hash ); } - - bool advance( int direction , int& totalFound , GeoAccumulator* all ){ + + bool advance( int direction , int& totalFound , GeoAccumulator* all ) { if ( bucket.isNull() ) return false; bucket = bucket.btree()->advance( bucket , pos , direction , "btreelocation" ); - + if ( all ) return checkCur( totalFound , all ); - + return ! bucket.isNull(); } - bool checkCur( int& totalFound , GeoAccumulator* all ){ + bool checkCur( int& totalFound , GeoAccumulator* all ) { if ( bucket.isNull() ) return false; - if ( bucket.btree()->isUsed(pos) ){ + if ( bucket.btree()->isUsed(pos) ) { totalFound++; all->add( bucket.btree()->keyNode( pos ) ); } @@ -826,31 +827,30 @@ namespace mongo { return true; } - string toString(){ + string toString() { stringstream ss; ss << "bucket: " << bucket.toString() << " pos: " << pos << " found: " << found; return ss.str(); } - static bool initial( const IndexDetails& id , const Geo2dType * spec , - BtreeLocation& min , BtreeLocation& max , + static bool initial( const IndexDetails& id , const Geo2dType * spec , + BtreeLocation& min , BtreeLocation& max , GeoHash start , - int & found , GeoAccumulator * hopper ) - { - + int & found , GeoAccumulator * hopper ) { + Ordering ordering = Ordering::make(spec->_order); - min.bucket = id.head.btree()->locate( id , id.head , start.wrap() , + min.bucket = id.head.btree()->locate( id , id.head , start.wrap() , ordering , min.pos , min.found , minDiskLoc ); if (hopper) min.checkCur( found , hopper ); max = min; - - if ( min.bucket.isNull() || ( hopper && !(hopper->found()) ) ){ - min.bucket = id.head.btree()->locate( id , id.head , start.wrap() , + + if ( min.bucket.isNull() || ( hopper && !(hopper->found()) ) ) { + min.bucket = id.head.btree()->locate( id , id.head , start.wrap() , ordering , min.pos , min.found , minDiskLoc , -1 ); if (hopper) min.checkCur( found , hopper ); } - + return ! min.bucket.isNull() || ! max.bucket.isNull(); } }; @@ -860,29 +860,31 @@ namespace mongo { GeoSearch( const Geo2dType * g , const GeoHash& n , int numWanted=100 , BSONObj filter=BSONObj() , double maxDistance = numeric_limits<double>::max() , GeoDistType type=GEO_PLAIN) : _spec( g ) ,_startPt(g,n), _start( n ) , _numWanted( numWanted ) , _filter( filter ) , _maxDistance( maxDistance ) , - _hopper( new GeoHopper( g , numWanted , _startPt , filter , maxDistance, type ) ), _type(type) - { + _hopper( new GeoHopper( g , numWanted , _startPt , filter , maxDistance, type ) ), _type(type) { assert( g->getDetails() ); _nscanned = 0; _found = 0; - - if (type == GEO_PLAIN){ + + if (type == GEO_PLAIN) { _scanDistance = maxDistance; - } else if (type == GEO_SPHERE) { - if (maxDistance == numeric_limits<double>::max()){ + } + else if (type == GEO_SPHERE) { + if (maxDistance == numeric_limits<double>::max()) { _scanDistance = maxDistance; - } else { + } + else { //TODO: consider splitting into x and y scan distances _scanDistance = computeXScanDistance(_startPt._y, rad2deg(maxDistance)); } - } else { + } + else { assert(0); } } - - void exec(){ + + void exec() { const IndexDetails& id = *_spec->getDetails(); - + const BtreeBucket * head = id.head.btree(); assert( head ); /* @@ -892,20 +894,20 @@ namespace mongo { * 3) find optimal set of boxes that complete circle * 4) use regular btree cursors to scan those boxes */ - + GeoHopper * hopper = _hopper.get(); _prefix = _start; BtreeLocation min,max; - { // 1 regular geo hash algorithm - + { + // 1 regular geo hash algorithm + if ( ! BtreeLocation::initial( id , _spec , min , max , _start , _found , NULL ) ) return; - + while ( !_prefix.constrains() || // if next pass would cover universe, just keep going - ( _hopper->found() < _numWanted && _spec->sizeEdge( _prefix ) <= _scanDistance)) - { + ( _hopper->found() < _numWanted && _spec->sizeEdge( _prefix ) <= _scanDistance)) { GEODEBUG( _prefix << "\t" << _found << "\t DESC" ); while ( min.hasPrefix(_prefix) && min.checkCur(_found, hopper) && min.advance(-1, _found, NULL) ) _nscanned++; @@ -913,7 +915,7 @@ namespace mongo { while ( max.hasPrefix(_prefix) && max.checkCur(_found, hopper) && max.advance(+1, _found, NULL) ) _nscanned++; - if ( ! _prefix.constrains() ){ + if ( ! _prefix.constrains() ) { GEODEBUG( "done search w/o part 2" ) return; } @@ -927,10 +929,11 @@ namespace mongo { // 2 double farthest = hopper->farthest(); GEODEBUGPRINT(hopper->farthest()); - if (farthest == -1){ + if (farthest == -1) { // Nothing found in Phase 1 farthest = _scanDistance; - } else if (_type == GEO_SPHERE) { + } + else if (_type == GEO_SPHERE) { farthest = std::min(_scanDistance, computeXScanDistance(_startPt._y, rad2deg(farthest))); } GEODEBUGPRINT(farthest); @@ -939,13 +942,13 @@ namespace mongo { GEODEBUGPRINT(want.toString()); _prefix = _start; - while (_prefix.constrains() && _spec->sizeEdge( _prefix ) < farthest ){ + while (_prefix.constrains() && _spec->sizeEdge( _prefix ) < farthest ) { _prefix = _prefix.up(); } PREFIXDEBUG(_prefix, _spec); - if (_prefix.getBits() <= 1){ + if (_prefix.getBits() <= 1) { // TODO consider walking in $natural order while ( min.checkCur(_found, hopper) && min.advance(-1, _found, NULL) ) @@ -954,16 +957,16 @@ namespace mongo { _nscanned++; GEODEBUG( "done search after scanning whole collection" ) - return; + return; } - if ( logLevel > 0 ){ - log(1) << "want: " << want << " found:" << _found << " nscanned: " << _nscanned << " hash size:" << _spec->sizeEdge( _prefix ) + if ( logLevel > 0 ) { + log(1) << "want: " << want << " found:" << _found << " nscanned: " << _nscanned << " hash size:" << _spec->sizeEdge( _prefix ) << " farthest: " << farthest << " using box: " << Box( _spec , _prefix ).toString() << endl; } - for ( int x=-1; x<=1; x++ ){ - for ( int y=-1; y<=1; y++ ){ + for ( int x=-1; x<=1; x++ ) { + for ( int y=-1; y<=1; y++ ) { GeoHash toscan = _prefix; toscan.move( x , y ); @@ -973,51 +976,52 @@ namespace mongo { } } GEODEBUG( "done search" ) - + } - void doBox( const IndexDetails& id , const Box& want , const GeoHash& toscan , int depth = 0 ){ + void doBox( const IndexDetails& id , const Box& want , const GeoHash& toscan , int depth = 0 ) { Box testBox( _spec , toscan ); - if ( logLevel > 2 ){ + if ( logLevel > 2 ) { cout << "\t"; for ( int i=0; i<depth; i++ ) cout << "\t"; cout << " doBox: " << testBox.toString() << "\t" << toscan.toString() << " scanned so far: " << _nscanned << endl; - } else { + } + else { GEODEBUGPRINT(testBox.toString()); } - if (_alreadyScanned.contains(testBox, _spec->_error)){ + if (_alreadyScanned.contains(testBox, _spec->_error)) { GEODEBUG("skipping box: already scanned"); return; // been here, done this } double intPer = testBox.intersects( want ); - - if ( intPer <= 0 ){ + + if ( intPer <= 0 ) { GEODEBUG("skipping box: not in want"); return; } - + bool goDeeper = intPer < .5 && depth < 2; long long myscanned = 0; - + BtreeLocation loc; - loc.bucket = id.head.btree()->locate( id , id.head , toscan.wrap() , Ordering::make(_spec->_order) , - loc.pos , loc.found , minDiskLoc ); + loc.bucket = id.head.btree()->locate( id , id.head , toscan.wrap() , Ordering::make(_spec->_order) , + loc.pos , loc.found , minDiskLoc ); loc.checkCur( _found , _hopper.get() ); - while ( loc.hasPrefix( toscan ) && loc.advance( 1 , _found , _hopper.get() ) ){ + while ( loc.hasPrefix( toscan ) && loc.advance( 1 , _found , _hopper.get() ) ) { _nscanned++; - if ( ++myscanned > 100 && goDeeper ){ + if ( ++myscanned > 100 && goDeeper ) { doBox( id , want , toscan + "00" , depth + 1); doBox( id , want , toscan + "01" , depth + 1); doBox( id , want , toscan + "10" , depth + 1); doBox( id , want , toscan + "11" , depth + 1); - return; + return; } } - + } @@ -1042,17 +1046,17 @@ namespace mongo { class GeoCursorBase : public Cursor { public: GeoCursorBase( const Geo2dType * spec ) - : _spec( spec ), _id( _spec->getDetails() ){ + : _spec( spec ), _id( _spec->getDetails() ) { } - virtual DiskLoc refLoc(){ return DiskLoc(); } + virtual DiskLoc refLoc() { return DiskLoc(); } virtual BSONObj indexKeyPattern() { return _spec->keyPattern(); } - virtual void noteLocation() { + virtual void noteLocation() { // no-op since these are meant to be safe } @@ -1063,12 +1067,12 @@ namespace mongo { virtual bool supportGetMore() { return false; } virtual bool supportYields() { return false; } - + virtual bool getsetdup(DiskLoc loc) { return false; } virtual bool modifiedKeys() const { return true; } virtual bool isMultiKey() const { return false; } - + const Geo2dType * _spec; const IndexDetails * _id; @@ -1077,23 +1081,23 @@ namespace mongo { class GeoSearchCursor : public GeoCursorBase { public: GeoSearchCursor( shared_ptr<GeoSearch> s ) - : GeoCursorBase( s->_spec ) , + : GeoCursorBase( s->_spec ) , _s( s ) , _cur( s->_hopper->_points.begin() ) , _end( s->_hopper->_points.end() ), _nscanned() { - if ( _cur != _end ) { - ++_nscanned; - } + if ( _cur != _end ) { + ++_nscanned; + } } - + virtual ~GeoSearchCursor() {} - - virtual bool ok(){ + + virtual bool ok() { return _cur != _end; } - - virtual Record* _current(){ assert(ok()); return _cur->_loc.rec(); } - virtual BSONObj current(){ assert(ok()); return _cur->_o; } - virtual DiskLoc currLoc(){ assert(ok()); return _cur->_loc; } - virtual bool advance(){ _cur++; incNscanned(); return ok(); } + + virtual Record* _current() { assert(ok()); return _cur->_loc.rec(); } + virtual BSONObj current() { assert(ok()); return _cur->_o; } + virtual DiskLoc currLoc() { assert(ok()); return _cur->_loc; } + virtual bool advance() { _cur++; incNscanned(); return ok(); } virtual BSONObj currKey() const { return _cur->_key; } virtual string toString() { @@ -1101,21 +1105,21 @@ namespace mongo { } - virtual BSONObj prettyStartKey() const { - return BSON( _s->_spec->_geo << _s->_prefix.toString() ); + virtual BSONObj prettyStartKey() const { + return BSON( _s->_spec->_geo << _s->_prefix.toString() ); } - virtual BSONObj prettyEndKey() const { + virtual BSONObj prettyEndKey() const { GeoHash temp = _s->_prefix; temp.move( 1 , 1 ); - return BSON( _s->_spec->_geo << temp.toString() ); + return BSON( _s->_spec->_geo << temp.toString() ); } - + virtual long long nscanned() { return _nscanned; } shared_ptr<GeoSearch> _s; GeoHopper::Holder::iterator _cur; GeoHopper::Holder::iterator _end; - + void incNscanned() { if ( ok() ) { ++_nscanned; } } long long _nscanned; }; @@ -1126,14 +1130,14 @@ namespace mongo { : GeoCursorBase( g ) ,GeoAccumulator( g , filter ) , _type( type ) , _filter( filter ) , _firstCall(true), _nscanned() { } - + virtual string toString() { return (string)"GeoBrowse-" + _type; } - virtual bool ok(){ + virtual bool ok() { bool first = _firstCall; - if ( _firstCall ){ + if ( _firstCall ) { fillStack(); _firstCall = false; } @@ -1144,7 +1148,7 @@ namespace mongo { return true; } - while ( moreToDo() ){ + while ( moreToDo() ) { fillStack(); if ( ! _cur.isEmpty() ) { if ( first ) { @@ -1153,38 +1157,38 @@ namespace mongo { return true; } } - + return false; } - - virtual bool advance(){ + + virtual bool advance() { _cur._o = BSONObj(); - - if ( _stack.size() ){ + + if ( _stack.size() ) { _cur = _stack.front(); _stack.pop_front(); ++_nscanned; return true; } - + if ( ! moreToDo() ) return false; - + while ( _cur.isEmpty() && moreToDo() ) fillStack(); return ! _cur.isEmpty() && ++_nscanned; } - - virtual Record* _current(){ assert(ok()); return _cur._loc.rec(); } - virtual BSONObj current(){ assert(ok()); return _cur._o; } - virtual DiskLoc currLoc(){ assert(ok()); return _cur._loc; } + + virtual Record* _current() { assert(ok()); return _cur._loc.rec(); } + virtual BSONObj current() { assert(ok()); return _cur._o; } + virtual DiskLoc currLoc() { assert(ok()); return _cur._loc; } virtual BSONObj currKey() const { return _cur._key; } virtual bool moreToDo() = 0; virtual void fillStack() = 0; - virtual void addSpecific( const KeyNode& node , double d ){ + virtual void addSpecific( const KeyNode& node , double d ) { if ( _cur.isEmpty() ) _cur = GeoPoint( node , d ); else @@ -1196,31 +1200,31 @@ namespace mongo { ok(); } return _nscanned; - } - + } + string _type; BSONObj _filter; list<GeoPoint> _stack; GeoPoint _cur; bool _firstCall; - + long long _nscanned; }; class GeoCircleBrowse : public GeoBrowse { public: - + enum State { - START , + START , DOING_EXPAND , DOING_AROUND , DONE } _state; GeoCircleBrowse( const Geo2dType * g , const BSONObj& circle , BSONObj filter = BSONObj() , const string& type="$center") - : GeoBrowse( g , "circle" , filter ){ + : GeoBrowse( g , "circle" , filter ) { uassert( 13060 , "$center needs 2 fields (middle,max distance)" , circle.nFields() == 2 ); BSONObjIterator i(circle); @@ -1235,40 +1239,42 @@ namespace mongo { _state = START; _found = 0; - if (type == "$center"){ + if (type == "$center") { _type = GEO_PLAIN; _xScanDistance = _maxDistance; _yScanDistance = _maxDistance; - } else if (type == "$centerSphere") { + } + else if (type == "$centerSphere") { uassert(13461, "Spherical MaxDistance > PI. Are you sure you are using radians?", _maxDistance < M_PI); _type = GEO_SPHERE; _yScanDistance = rad2deg(_maxDistance); _xScanDistance = computeXScanDistance(_startPt._y, _yScanDistance); - uassert(13462, "Spherical distance would require wrapping, which isn't implemented yet", - (_startPt._x + _xScanDistance < 180) && (_startPt._x - _xScanDistance > -180) && - (_startPt._y + _yScanDistance < 90) && (_startPt._y - _yScanDistance > -90)); + uassert(13462, "Spherical distance would require wrapping, which isn't implemented yet", + (_startPt._x + _xScanDistance < 180) && (_startPt._x - _xScanDistance > -180) && + (_startPt._y + _yScanDistance < 90) && (_startPt._y - _yScanDistance > -90)); GEODEBUGPRINT(_maxDistance); GEODEBUGPRINT(_xScanDistance); GEODEBUGPRINT(_yScanDistance); - } else { + } + else { uassert(13460, "invalid $center query type: " + type, false); } ok(); } - virtual bool moreToDo(){ + virtual bool moreToDo() { return _state != DONE; } - - virtual void fillStack(){ - if ( _state == START ){ - if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , - _prefix , _found , this ) ){ + virtual void fillStack() { + + if ( _state == START ) { + if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , + _prefix , _found , this ) ) { _state = DONE; return; } @@ -1276,10 +1282,10 @@ namespace mongo { } - if ( _state == DOING_AROUND ){ + if ( _state == DOING_AROUND ) { // TODO could rework and return rather than looping - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ + for (int i=-1; i<=1; i++) { + for (int j=-1; j<=1; j++) { if (i == 0 && j == 0) continue; // main box @@ -1287,10 +1293,11 @@ namespace mongo { newBox.move(i, j); PREFIXDEBUG(newBox, _g); - if (needToCheckBox(newBox)){ + if (needToCheckBox(newBox)) { // TODO consider splitting into quadrants getPointsForPrefix(newBox); - } else { + } + else { GEODEBUG("skipping box"); } } @@ -1299,54 +1306,56 @@ namespace mongo { _state = DONE; return; } - - if (_state == DOING_EXPAND){ + + if (_state == DOING_EXPAND) { GEODEBUG( "circle prefix [" << _prefix << "]" ); PREFIXDEBUG(_prefix, _g); while ( _min.hasPrefix( _prefix ) && _min.advance( -1 , _found , this ) ); while ( _max.hasPrefix( _prefix ) && _max.advance( 1 , _found , this ) ); - if ( ! _prefix.constrains() ){ + if ( ! _prefix.constrains() ) { GEODEBUG( "\t exhausted the btree" ); _state = DONE; return; } - + Point ll (_g, _prefix); GeoHash trHash = _prefix; trHash.move( 1 , 1 ); Point tr (_g, trHash); double sideLen = fabs(tr._x - ll._x); - if (sideLen > std::max(_xScanDistance, _yScanDistance)){ // circle must be contained by surrounding squares - if ( (ll._x + _xScanDistance < _startPt._x && ll._y + _yScanDistance < _startPt._y) && - (tr._x - _xScanDistance > _startPt._x && tr._y - _yScanDistance > _startPt._y) ) - { + if (sideLen > std::max(_xScanDistance, _yScanDistance)) { // circle must be contained by surrounding squares + if ( (ll._x + _xScanDistance < _startPt._x && ll._y + _yScanDistance < _startPt._y) && + (tr._x - _xScanDistance > _startPt._x && tr._y - _yScanDistance > _startPt._y) ) { GEODEBUG("square fully contains circle"); _state = DONE; - } else if (_prefix.getBits() > 1){ + } + else if (_prefix.getBits() > 1) { GEODEBUG("checking surrounding squares"); _state = DOING_AROUND; - } else { + } + else { GEODEBUG("using simple search"); _prefix = _prefix.up(); } - } else { + } + else { _prefix = _prefix.up(); } return; } - + /* Clients are expected to use moreToDo before calling * fillStack, so DONE is checked for there. If any more * State values are defined, you should handle them - * here. */ + * here. */ assert(0); } - bool needToCheckBox(const GeoHash& prefix){ + bool needToCheckBox(const GeoHash& prefix) { Point ll (_g, prefix); if (fabs(ll._x - _startPt._x) <= _xScanDistance) return true; if (fabs(ll._y - _startPt._y) <= _yScanDistance) return true; @@ -1361,8 +1370,8 @@ namespace mongo { return false; } - void getPointsForPrefix(const GeoHash& prefix){ - if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , prefix , _found , this ) ){ + void getPointsForPrefix(const GeoHash& prefix) { + if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , prefix , _found , this ) ) { return; } @@ -1370,17 +1379,17 @@ namespace mongo { while ( _max.hasPrefix( prefix ) && _max.advance( 1 , _found , this ) ); } - - virtual bool checkDistance( const GeoHash& h , double& d ){ - switch (_type){ - case GEO_PLAIN: - d = _g->distance( _start , h ); - break; - case GEO_SPHERE: - d = spheredist_deg(_startPt, Point(_g, h)); - break; - default: - assert(0); + + virtual bool checkDistance( const GeoHash& h , double& d ) { + switch (_type) { + case GEO_PLAIN: + d = _g->distance( _start , h ); + break; + case GEO_SPHERE: + d = spheredist_deg(_startPt, Point(_g, h)); + break; + default: + assert(0); } GEODEBUG( "\t " << h << "\t" << d ); @@ -1393,27 +1402,27 @@ namespace mongo { double _maxDistance; // user input double _xScanDistance; // effected by GeoDistType double _yScanDistance; // effected by GeoDistType - + int _found; - - GeoHash _prefix; + + GeoHash _prefix; BtreeLocation _min; BtreeLocation _max; - }; + }; class GeoBoxBrowse : public GeoBrowse { public: - + enum State { - START , + START , DOING_EXPAND , DONE } _state; - GeoBoxBrowse( const Geo2dType * g , const BSONObj& box , BSONObj filter = BSONObj() ) - : GeoBrowse( g , "box" , filter ){ - + GeoBoxBrowse( const Geo2dType * g , const BSONObj& box , BSONObj filter = BSONObj() ) + : GeoBrowse( g , "box" , filter ) { + uassert( 13063 , "$box needs 2 fields (bottomLeft,topRight)" , box.nFields() == 2 ); BSONObjIterator i(box); _bl = g->_tohash( i.next() ); @@ -1429,7 +1438,7 @@ namespace mongo { Point center = _want.center(); _prefix = _g->hash( center._x , center._y ); - + GEODEBUG( "center : " << center.toString() << "\t" << _prefix ); { @@ -1444,42 +1453,43 @@ namespace mongo { ok(); } - virtual bool moreToDo(){ + virtual bool moreToDo() { return _state != DONE; } - - virtual void fillStack(){ - if ( _state == START ){ - if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , - _prefix , _found , this ) ){ + virtual void fillStack() { + if ( _state == START ) { + + if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , + _prefix , _found , this ) ) { _state = DONE; return; } _state = DOING_EXPAND; } - - if ( _state == DOING_EXPAND ){ + + if ( _state == DOING_EXPAND ) { int started = _found; - while ( started == _found || _state == DONE ){ + while ( started == _found || _state == DONE ) { GEODEBUG( "box prefix [" << _prefix << "]" ); while ( _min.hasPrefix( _prefix ) && _min.advance( -1 , _found , this ) ); while ( _max.hasPrefix( _prefix ) && _max.advance( 1 , _found , this ) ); - + if ( _state == DONE ) return; - if ( ! _prefix.constrains() ){ + if ( ! _prefix.constrains() ) { GEODEBUG( "box exhausted" ); _state = DONE; return; } - if (_g->sizeEdge(_prefix) < _wantLen){ + if (_g->sizeEdge(_prefix) < _wantLen) { _prefix = _prefix.up(); - } else { - for (int i=-1; i<=1; i++){ - for (int j=-1; j<=1; j++){ + } + else { + for (int i=-1; i<=1; i++) { + for (int j=-1; j<=1; j++) { if (i == 0 && j == 0) continue; // main box @@ -1490,36 +1500,37 @@ namespace mongo { PREFIXDEBUG(newBox, _g); Box cur( _g , newBox ); - if (_want.intersects(cur)){ + if (_want.intersects(cur)) { // TODO consider splitting into quadrants getPointsForPrefix(newBox); - } else { + } + else { GEODEBUG("skipping box"); } } } _state = DONE; } - + } return; } } - void getPointsForPrefix(const GeoHash& prefix){ - if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , prefix , _found , this ) ){ + void getPointsForPrefix(const GeoHash& prefix) { + if ( ! BtreeLocation::initial( *_id , _spec , _min , _max , prefix , _found , this ) ) { return; } while ( _min.hasPrefix( prefix ) && _min.advance( -1 , _found , this ) ); while ( _max.hasPrefix( prefix ) && _max.advance( 1 , _found , this ) ); } - - virtual bool checkDistance( const GeoHash& h , double& d ){ + + virtual bool checkDistance( const GeoHash& h , double& d ) { bool res = _want.inside( Point( _g , h ) , _fudge ); - GEODEBUG( "\t want : " << _want.toString() - << " point: " << Point( _g , h ).toString() + GEODEBUG( "\t want : " << _want.toString() + << " point: " << Point( _g , h ).toString() << " in : " << res ); return res; } @@ -1530,23 +1541,23 @@ namespace mongo { double _wantLen; int _found; - - GeoHash _prefix; + + GeoHash _prefix; BtreeLocation _min; BtreeLocation _max; double _fudge; - }; + }; shared_ptr<Cursor> Geo2dType::newCursor( const BSONObj& query , const BSONObj& order , int numWanted ) const { if ( numWanted < 0 ) numWanted = numWanted * -1; else if ( numWanted == 0 ) - numWanted = 100; - + numWanted = 100; + BSONObjIterator i(query); - while ( i.more() ){ + while ( i.more() ) { BSONElement e = i.next(); if ( _geo != e.fieldName() ) @@ -1554,8 +1565,8 @@ namespace mongo { if ( e.type() != Object ) continue; - - switch ( e.embeddedObject().firstElement().getGtLtOp() ){ + + switch ( e.embeddedObject().firstElement().getGtLtOp() ) { case BSONObj::opNEAR: { BSONObj n = e.embeddedObject(); e = n.firstElement(); @@ -1564,15 +1575,17 @@ namespace mongo { GeoDistType type; if (suffix[0] == '\0') { type = GEO_PLAIN; - } else if (strcmp(suffix, "Sphere") == 0) { + } + else if (strcmp(suffix, "Sphere") == 0) { type = GEO_SPHERE; - } else { + } + else { uassert(13464, string("invalid $near search type: ") + e.fieldName(), false); type = GEO_PLAIN; // prevents uninitialized warning } double maxDistance = numeric_limits<double>::max(); - if ( e.isABSONObj() && e.embeddedObject().nFields() > 2 ){ + if ( e.isABSONObj() && e.embeddedObject().nFields() > 2 ) { BSONObjIterator i(e.embeddedObject()); i.next(); i.next(); @@ -1589,25 +1602,26 @@ namespace mongo { s->exec(); shared_ptr<Cursor> c; c.reset( new GeoSearchCursor( s ) ); - return c; + return c; } case BSONObj::opWITHIN: { e = e.embeddedObject().firstElement(); uassert( 13057 , "$within has to take an object or array" , e.isABSONObj() ); e = e.embeddedObject().firstElement(); string type = e.fieldName(); - if ( startsWith(type, "$center") ){ + if ( startsWith(type, "$center") ) { uassert( 13059 , "$center has to take an object or array" , e.isABSONObj() ); shared_ptr<Cursor> c( new GeoCircleBrowse( this , e.embeddedObjectUserCheck() , query , type) ); - return c; - } else if ( type == "$box" ){ + return c; + } + else if ( type == "$box" ) { uassert( 13065 , "$box has to take an object or array" , e.isABSONObj() ); shared_ptr<Cursor> c( new GeoBoxBrowse( this , e.embeddedObjectUserCheck() , query ) ); - return c; + return c; } throw UserException( 13058 , (string)"unknown $with type: " + type ); } - default: + default: break; } } @@ -1621,41 +1635,41 @@ namespace mongo { class Geo2dFindNearCmd : public Command { public: - Geo2dFindNearCmd() : Command( "geoNear" ){} - virtual LockType locktype() const { return READ; } + Geo2dFindNearCmd() : Command( "geoNear" ) {} + virtual LockType locktype() const { return READ; } bool slaveOk() const { return true; } void help(stringstream& h) const { h << "http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-geoNearCommand"; } bool slaveOverrideOk() { return true; } - bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl){ + bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) { string ns = dbname + "." + cmdObj.firstElement().valuestr(); NamespaceDetails * d = nsdetails( ns.c_str() ); - if ( ! d ){ + if ( ! d ) { errmsg = "can't find ns"; return false; } vector<int> idxs; d->findIndexByType( GEO2DNAME , idxs ); - - if ( idxs.size() > 1 ){ + + if ( idxs.size() > 1 ) { errmsg = "more than 1 geo indexes :("; return false; } - - if ( idxs.size() == 0 ){ + + if ( idxs.size() == 0 ) { errmsg = "no geo index :("; return false; } int geoIdx = idxs[0]; - + result.append( "ns" , ns ); IndexDetails& id = d->idx( geoIdx ); Geo2dType * g = (Geo2dType*)id.getSpec().getType(); assert( &id == g->getDetails() ); - + int numWanted = 100; if ( cmdObj["num"].isNumber() ) numWanted = cmdObj["num"].numberInt(); @@ -1678,35 +1692,35 @@ namespace mongo { GeoSearch gs( g , n , numWanted , filter , maxDistance , type); - if ( cmdObj["start"].type() == String){ + if ( cmdObj["start"].type() == String) { GeoHash start ((string) cmdObj["start"].valuestr()); gs._start = start; } - + gs.exec(); double distanceMultiplier = 1; if ( cmdObj["distanceMultiplier"].isNumber() ) distanceMultiplier = cmdObj["distanceMultiplier"].number(); - + double totalDistance = 0; BSONObjBuilder arr( result.subarrayStart( "results" ) ); int x = 0; - for ( GeoHopper::Holder::iterator i=gs._hopper->_points.begin(); i!=gs._hopper->_points.end(); i++ ){ + for ( GeoHopper::Holder::iterator i=gs._hopper->_points.begin(); i!=gs._hopper->_points.end(); i++ ) { const GeoPoint& p = *i; - + double dis = distanceMultiplier * p._distance; totalDistance += dis; - + BSONObjBuilder bb( arr.subobjStart( BSONObjBuilder::numStr( x++ ) ) ); bb.append( "dis" , dis ); bb.append( "obj" , p._o ); bb.done(); } arr.done(); - + BSONObjBuilder stats( result.subobjStart( "stats" ) ); stats.append( "time" , cc().curop()->elapsedMillis() ); stats.appendNumber( "btreelocs" , gs._nscanned ); @@ -1715,23 +1729,23 @@ namespace mongo { stats.append( "avgDistance" , totalDistance / x ); stats.append( "maxDistance" , gs._hopper->farthest() ); stats.done(); - + return true; } - + } geo2dFindNearCmd; class GeoWalkCmd : public Command { public: - GeoWalkCmd() : Command( "geoWalk" ){} - virtual LockType locktype() const { return READ; } + GeoWalkCmd() : Command( "geoWalk" ) {} + virtual LockType locktype() const { return READ; } bool slaveOk() const { return true; } bool slaveOverrideOk() { return true; } - bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl){ + bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) { string ns = dbname + "." + cmdObj.firstElement().valuestr(); NamespaceDetails * d = nsdetails( ns.c_str() ); - if ( ! d ){ + if ( ! d ) { errmsg = "can't find ns"; return false; } @@ -1739,10 +1753,10 @@ namespace mongo { int geoIdx = -1; { NamespaceDetails::IndexIterator ii = d->ii(); - while ( ii.more() ){ + while ( ii.more() ) { IndexDetails& id = ii.next(); - if ( id.getSpec().getTypeName() == GEO2DNAME ){ - if ( geoIdx >= 0 ){ + if ( id.getSpec().getTypeName() == GEO2DNAME ) { + if ( geoIdx >= 0 ) { errmsg = "2 geo indexes :("; return false; } @@ -1750,12 +1764,12 @@ namespace mongo { } } } - - if ( geoIdx < 0 ){ + + if ( geoIdx < 0 ) { errmsg = "no geo index :("; return false; } - + IndexDetails& id = d->idx( geoIdx ); Geo2dType * g = (Geo2dType*)id.getSpec().getType(); @@ -1764,12 +1778,12 @@ namespace mongo { int max = 100000; BtreeCursor c( d , geoIdx , id , BSONObj() , BSONObj() , true , 1 ); - while ( c.ok() && max-- ){ + while ( c.ok() && max-- ) { GeoHash h( c.currKey().firstElement() ); int len; cout << "\t" << h.toString() - << "\t" << c.current()[g->_geo] - << "\t" << hex << h.getHash() + << "\t" << c.current()[g->_geo] + << "\t" << hex << h.getHash() << "\t" << hex << ((long long*)c.currKey().firstElement().binData(len))[0] << "\t" << c.current()["_id"] << endl; @@ -1778,7 +1792,7 @@ namespace mongo { return true; } - + } geoWalkCmd; } |