From 0a8e1f91c83bce14480e9cda00a1f630a5c64e93 Mon Sep 17 00:00:00 2001 From: Eliot Horowitz Date: Mon, 1 Mar 2010 22:06:30 -0500 Subject: [PATCH] handle multiple geo indexes with $near SERVER-686 --- db/index.h | 2 +- db/index_geo2d.cpp | 9 ++++++--- db/queryoptimizer.cpp | 5 +++-- jstests/geo2.js | 7 +------ jstests/geo9.js | 28 ++++++++++++++++++++++++++++ shell/utils.js | 25 +++++++++++++++++++++++++ 6 files changed, 64 insertions(+), 12 deletions(-) create mode 100644 jstests/geo9.js diff --git a/db/index.h b/db/index.h index afff6818abb..0f22784674b 100644 --- a/db/index.h +++ b/db/index.h @@ -30,7 +30,7 @@ namespace mongo { class IndexPlugin; class IndexDetails; - enum IndexSuitability { USELESS , HELPFUL , OPTIMAL }; + enum IndexSuitability { USELESS = 0 , HELPFUL = 1 , OPTIMAL = 2 }; /** * this represents an instance of a index plugin diff --git a/db/index_geo2d.cpp b/db/index_geo2d.cpp index 5b2916c53e3..e81557dd573 100644 --- a/db/index_geo2d.cpp +++ b/db/index_geo2d.cpp @@ -28,6 +28,9 @@ #include "matcher.h" namespace mongo { + +#define GEODEBUG(x) cout << x << endl; + //#define GEODEBUG(x) const string GEO2DNAME = "2d"; @@ -733,7 +736,7 @@ namespace mongo { _lookedAt++; double d = _g->distance( _near , node.key.firstElement() ); - //cout << "\t" << node.recordLoc.obj() << "\t" << d << endl; + GEODEBUG( "\t" << node.recordLoc.obj() << "\t" << d ); if ( _points.size() >= _max && d > farthest() ) return; @@ -874,7 +877,7 @@ namespace mongo { if ( ! _prefix.constrains() ) break; _prefix = _prefix.up(); - //cout << _prefix << "\t" << _found << "\t" << endl; + GEODEBUG( _prefix << "\t" << _found ); } } @@ -1017,7 +1020,7 @@ namespace mongo { e = e.embeddedObject().firstElement(); n = _tohash( e ); } - uassert( 13042 , "no geo field" , n.constrains() ); + uassert( 13042 , (string)"missing geo field (" + _geo + ") in : " + query.toString() , n.constrains() ); shared_ptr s( new GeoSearch( this , n , numWanted , query ) ); s->exec(); diff --git a/db/queryoptimizer.cpp b/db/queryoptimizer.cpp index 62e9d60a573..e81da385028 100644 --- a/db/queryoptimizer.cpp +++ b/db/queryoptimizer.cpp @@ -327,7 +327,8 @@ namespace mongo { while( i.more() ) { int j = i.pos(); IndexDetails& ii = i.next(); - if ( ii.getSpec().getTypeName() == special ){ + const IndexSpec& spec = ii.getSpec(); + if ( spec.getTypeName() == special && spec.suitability( query_ , order_ ) ){ usingPrerecordedPlan_ = true; mayRecordPlan_ = true; plans_.push_back( PlanPtr( new QueryPlan( d , j , fbs_ , order_ , @@ -335,7 +336,7 @@ namespace mongo { return; } } - uassert( 13038 , (string)"can't find special index: " + special , 0 ); + uassert( 13038 , (string)"can't find special index: " + special + " for: " + query_.toString() , 0 ); } if ( honorRecordedPlan_ ) { diff --git a/jstests/geo2.js b/jstests/geo2.js index f3b9d47377b..b9452c890e4 100644 --- a/jstests/geo2.js +++ b/jstests/geo2.js @@ -23,17 +23,12 @@ assert.lt( fast.stats.nscanned * 10 , slow.stats.nscanned , "A1" + v ); assert.lt( fast.stats.objectsLoaded , slow.stats.objectsLoaded , "A2" + v ); assert.eq( fast.stats.avgDistance , slow.stats.avgDistance , "A3" + v ); -function dis( a , b ){ - return Math.sqrt( Math.pow( b[0] - a[0] , 2 ) + - Math.pow( b[1] - a[1] , 2 ) ); -} - function a( cur ){ var total = 0; var outof = 0; while ( cur.hasNext() ){ var o = cur.next(); - total += dis( [ 50 , 50 ] , o.loc ); + total += Geo.distance( [ 50 , 50 ] , o.loc ); outof++; } return total/outof; diff --git a/jstests/geo9.js b/jstests/geo9.js new file mode 100644 index 00000000000..8b6510f03b5 --- /dev/null +++ b/jstests/geo9.js @@ -0,0 +1,28 @@ + +t = db.geo9 +t.drop(); + +t.save( { _id : 1 , a : [ 10 , 10 ] , b : [ 50 , 50 ] } ) +t.save( { _id : 2 , a : [ 11 , 11 ] , b : [ 51 , 52 ] } ) +t.save( { _id : 3 , a : [ 12 , 12 ] , b : [ 52 , 52 ] } ) + +t.save( { _id : 4 , a : [ 50 , 50 ] , b : [ 10 , 10 ] } ) +t.save( { _id : 5 , a : [ 51 , 51 ] , b : [ 11 , 11 ] } ) +t.save( { _id : 6 , a : [ 52 , 52 ] , b : [ 12 , 12 ] } ) + +t.ensureIndex( { a : "2d" } ) +t.ensureIndex( { b : "2d" } ) + +function check( field ){ + var q = {} + q[field] = { $near : [ 11 , 11 ] } + arr = t.find( q ).limit(3).map( + function(z){ + return Geo.distance( [ 11 , 11 ] , z[field] ); + } + ); + assert.eq( 2 * Math.sqrt( 2 ) , Array.sum( arr ) , "test " + field ); +} + +check( "a" ) +check( "b" ) diff --git a/shell/utils.js b/shell/utils.js index cfaa799f278..647aac7d160 100644 --- a/shell/utils.js +++ b/shell/utils.js @@ -924,3 +924,28 @@ killWithUris = function( uris ) { } } } + +Geo = {}; +Geo.distance = function( a , b ){ + var ax = null; + var ay = null; + var bx = null; + var by = null; + + for ( var key in a ){ + if ( ax == null ) + ax = a[key]; + else if ( ay == null ) + ay = a[key]; + } + + for ( var key in b ){ + if ( bx == null ) + bx = b[key]; + else if ( by == null ) + by = b[key]; + } + + return Math.sqrt( Math.pow( by - ay , 2 ) + + Math.pow( bx - ax , 2 ) ); +}