diff options
author | A. Jesse Jiryu Davis <jesse@10gen.com> | 2013-07-01 16:51:30 -0400 |
---|---|---|
committer | Matt Kangas <matt.kangas@10gen.com> | 2013-07-10 14:37:48 -0500 |
commit | 53dab689a519b4daf6a5c4772123b74616c0283b (patch) | |
tree | 46c1d0cf2aa8d2db26d0ae1b707c024644dec64d | |
parent | 1556be30b24b7a00038182e233a52e6c20385c26 (diff) | |
download | mongo-53dab689a519b4daf6a5c4772123b74616c0283b.tar.gz |
SERVER-9395 Implement $minDistance for geoNear command
Squashed:
- Spelling in comment.
- $minDistance option for $near queries with 2dsphere index. SERVER-9395
- Implement $minDistance for geoNear command. SERVER-9395
- uassert that geoNear's min/maxDistance param is non-negative
- More $nearSphere tests. SERVER-9395
- uassert that minDistance isn't used with 2d index. 2dsphere index is required.
- More informative error if assert.throws is misused.
Signed-off-by: Matt Kangas <matt.kangas@10gen.com>
-rw-r--r-- | jstests/geo_mindistance.js | 338 | ||||
-rw-r--r-- | jstests/geo_mindistance_boundaries.js | 119 | ||||
-rw-r--r-- | src/mongo/db/geo/geoquery.cpp | 45 | ||||
-rw-r--r-- | src/mongo/db/geo/geoquery.h | 9 | ||||
-rw-r--r-- | src/mongo/db/index/2d_index_cursor.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/index/s2_near_cursor.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/index/s2_near_cursor.h | 2 | ||||
-rw-r--r-- | src/mongo/shell/assert.js | 2 |
8 files changed, 514 insertions, 15 deletions
diff --git a/jstests/geo_mindistance.js b/jstests/geo_mindistance.js new file mode 100644 index 00000000000..d9909a5a4e9 --- /dev/null +++ b/jstests/geo_mindistance.js @@ -0,0 +1,338 @@ +/* Test $minDistance option for $near and $nearSphere queries, and geoNear + * command. SERVER-9395. +*/ +var t = db.geo_mindistance; +t.drop(); + +// +// Useful constants and functions. +// + +var km = 1000, + earthRadiusMeters = 6378.1 * km; + +function metersToRadians(m) { return m / earthRadiusMeters; } + +/* Count documents within some radius of (0, 0), in kilometers. + * With this function we can use the existing $maxDistance option to test + * the newer $minDistance option's behavior. + */ +function n_docs_within(radius_km) { + // geoNear's distances are in meters for geoJSON points. + var cmdResult = db.runCommand({ + geoNear: t.getName(), + near: {type: 'Point', coordinates: [0, 0]}, + spherical: true, + maxDistance: radius_km * km, + num: 1000 + }); + + return cmdResult.results.length; +} + +// +// Setup. +// + +/* Make 121 points from long, lat = (0, 0) (in Gulf of Guinea) to (10, 10) + * (inland Nigeria). + */ +for (var x = 0; x <= 10; x += 1) { + for (var y = 0; y <= 10; y += 1) { + t.insert({loc: [x, y]}); + } +} + +/* $minDistance is supported for 2dsphere index only, not 2d or geoHaystack. */ +t.ensureIndex({loc: "2dsphere"}); + +var n_docs = t.count(), + geoJSONPoint = {$geometry: {type: 'Point', coordinates: [0, 0]}}, + legacyPoint = [0, 0]; + +// +// Test $near with GeoJSON point (required for $near with 2dsphere index). +// min/maxDistance are in meters. +// + +var n_min1400_count = t.find({loc: { + $near: geoJSONPoint, $minDistance: 1400 * km +}}).count(); + +assert.eq( + n_docs - n_docs_within(1400), + n_min1400_count, + "Expected " + (n_docs - n_docs_within(1400)) + + " points $near (0, 0) with $minDistance 1400 km, got " + + n_min1400_count +); + +var n_bw500_and_1000_count = t.find({loc: { + $near: geoJSONPoint, + $minDistance: 500 * km, + $maxDistance: 1000 * km +}}).count(); + +assert.eq( + n_docs_within(1000) - n_docs_within(500), + n_bw500_and_1000_count, + "Expected " + (n_docs_within(1000) - n_docs_within(500)) + + " points $near (0, 0) with $minDistance 500 km and $maxDistance 1000 km, got " + + n_bw500_and_1000_count +); + +// +// $nearSphere with 2dsphere index can take a legacy or GeoJSON point. +// First test $nearSphere with legacy point. +// min/maxDistance are in radians. +// + +n_min1400_count = t.find({loc: { + $nearSphere: legacyPoint, $minDistance: metersToRadians(1400 * km) +}}).count(); + +assert.eq( + n_docs - n_docs_within(1400), + n_min1400_count, + "Expected " + (n_docs - n_docs_within(1400)) + + " points $nearSphere (0, 0) with $minDistance 1400 km, got " + + n_min1400_count +); + +n_bw500_and_1000_count = t.find({loc: { + $nearSphere: legacyPoint, + $minDistance: metersToRadians(500 * km), + $maxDistance: metersToRadians(1000 * km) +}}).count(); + +assert.eq( + n_docs_within(1000) - n_docs_within(500), + n_bw500_and_1000_count, + "Expected " + (n_docs_within(1000) - n_docs_within(500)) + + " points $nearSphere (0, 0) with $minDistance 500 km and $maxDistance 1000 km, got " + + n_bw500_and_1000_count +); + +// +// Test $nearSphere with GeoJSON point. +// min/maxDistance are in meters. +// + +n_min1400_count = t.find({loc: { + $nearSphere: geoJSONPoint, $minDistance: 1400 * km +}}).count(); + +assert.eq( + n_docs - n_docs_within(1400), + n_min1400_count, + "Expected " + (n_docs - n_docs_within(1400)) + + " points $nearSphere (0, 0) with $minDistance 1400 km, got " + + n_min1400_count +); + +n_bw500_and_1000_count = t.find({loc: { + $nearSphere: geoJSONPoint, + $minDistance: 500 * km, + $maxDistance: 1000 * km +}}).count(); + +assert.eq( + n_docs_within(1000) - n_docs_within(500), + n_bw500_and_1000_count, + "Expected " + (n_docs_within(1000) - n_docs_within(500)) + + " points $nearSphere (0, 0) with $minDistance 500 km and $maxDistance 1000 km, got " + + n_bw500_and_1000_count +); + + +// +// Test geoNear command with GeoJSON point. +// Distances are in meters. +// + +var cmdResult = db.runCommand({ + geoNear: t.getName(), + near: {type: 'Point', coordinates: [0, 0]}, + minDistance: 1400 * km, + spherical: true // spherical required for 2dsphere index +}); +assert.eq( + n_docs - n_docs_within(1400), + cmdResult.results.length, + "Expected " + (n_docs - n_docs_within(1400)) + + " points geoNear (0, 0) with $minDistance 1400 km, got " + + cmdResult.results.length +); + +cmdResult = db.runCommand({ + geoNear: t.getName(), + near: {type: 'Point', coordinates: [0, 0]}, + minDistance: 500 * km, + maxDistance: 1000 * km, + spherical: true +}); +assert.eq( + n_docs_within(1000) - n_docs_within(500), + cmdResult.results.length, + "Expected " + (n_docs_within(1000) - n_docs_within(500)) + + " points geoNear (0, 0) with $minDistance 500 km and $maxDistance 1000 km, got " + + cmdResult.results.length +); + +// +// Test geoNear command with legacy point. +// Distances are in radians. +// + +cmdResult = db.runCommand({ + geoNear: t.getName(), + near: legacyPoint, + minDistance: metersToRadians(1400 * km), + spherical: true // spherical required for 2dsphere index +}); +assert.eq( + n_docs - n_docs_within(1400), + cmdResult.results.length, + "Expected " + (n_docs - n_docs_within(1400)) + + " points geoNear (0, 0) with $minDistance 1400 km, got " + + cmdResult.results.length +); + +cmdResult = db.runCommand({ + geoNear: t.getName(), + near: legacyPoint, + minDistance: metersToRadians(500 * km), + maxDistance: metersToRadians(1000 * km), + spherical: true +}); +assert.eq( + n_docs_within(1000) - n_docs_within(500), + cmdResult.results.length, + "Expected " + (n_docs_within(1000) - n_docs_within(500)) + + " points geoNear (0, 0) with $minDistance 500 km and $maxDistance 1000 km, got " + + cmdResult.results.length +); + +// +// Test $minDistance input validation for $near and $nearSphere queries, +// and for geoNear command. +// + +/** Some bad inputs for $near and $nearSphere. */ +var badMinDistance, badMinDistances = [-1, undefined, 'foo']; +for (var i = 0; i < badMinDistances.length; i++) { + badMinDistance = badMinDistances[i]; + + assert.throws( + function(minDistance) { + t.find({loc: {$nearSphere: geoJSONPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$nearSphere with GeoJSON point should've failed with $minDistance = " + badMinDistance); + + assert.throws( + function(minDistance) { + t.find({loc: {$nearSphere: legacyPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$nearSphere with legacy coordinates should've failed with $minDistance = " + badMinDistance); + + assert.throws( + function(minDistance) { + t.find({loc: {$near: geoJSONPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$near with GeoJSON point should've failed with $minDistance = " + badMinDistance); + + assert.commandFailed( + db.runCommand({ + geoNear: t.getName(), + near: legacyPoint, + minDistance: badMinDistance, + spherical: true + }), + "geoNear with legacy coordinates should've failed with $minDistance = " + badMinDistance); + + assert.commandFailed( + db.runCommand({ + geoNear: t.getName(), + near: {type: 'Point', coordinates: [0, 0]}, + minDistance: badMinDistance, + spherical: true + }), + "geoNear with GeoJSON point should've failed with $minDistance = " + badMinDistance); +} + +/* Can't be more than half earth radius in meters. */ +badMinDistance = Math.PI * earthRadiusMeters + 10; +assert.throws( + function(minDistance) { + t.find({loc: {$near: geoJSONPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$near should've failed with $minDistance = " + badMinDistance); + +assert.throws( + function(minDistance) { + t.find({loc: {$nearSphere: geoJSONPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$nearSphere should've failed with $minDistance = " + badMinDistance); + +/* Can't be more than pi. */ +badMinDistance = Math.PI + 0.1; +assert.throws( + function(minDistance) { + t.find({loc: {$nearSphere: legacyPoint, $minDistance: minDistance}}).next(); + }, + [badMinDistance], + "$near should've failed with $minDistance = " + badMinDistance); + +// +// Verify that we throw errors using 2d index with $minDistance. +// ($minDistance requires a 2dsphere index, not supported with 2d.) +// + +t.dropIndexes(); +t.ensureIndex({loc: "2d"}); + +assert.throws( + function() { + t.find({loc: {$near: legacyPoint, $minDistance: 1}}).next(); + }, [], + "$near with legacy coordinates and $minDistance should've failed with 2d index" +); + +assert.throws( + function() { + t.find({loc: {$near: geoJSONPoint, $minDistance: 1}}).next(); + }, [], + "$near with GeoJSON point and $minDistance should've failed with 2d index" +); + +assert.throws( + function() { + t.find({loc: {$nearSphere: legacyPoint, $minDistance: 1}}).next(); + }, [], + "$nearSphere with legacy coordinates and $minDistance should've failed with 2d index" +); + +assert.throws( + function() { + t.find({loc: {$nearSphere: geoJSONPoint, $minDistance: 1}}).next(); + }, [], + "$nearSphere with GeoJSON point and $minDistance should've failed with 2d index" +); + +/* geoNear command with 2d index requires legacy point, not GeoJSON. + * It fails here, even with a legacy point, because of minDistance. +*/ +assert.commandFailed( + db.runCommand({ + geoNear: t.getName(), + near: legacyPoint, + minDistance: 1, + spherical: true + }), + "geoNear with legacy coordinates and $minDistance should've failed with 2d index" +); diff --git a/jstests/geo_mindistance_boundaries.js b/jstests/geo_mindistance_boundaries.js new file mode 100644 index 00000000000..2b6c26afb3f --- /dev/null +++ b/jstests/geo_mindistance_boundaries.js @@ -0,0 +1,119 @@ +/* Test boundary conditions for $minDistance option for $near and $nearSphere + * queries. SERVER-9395. +*/ +var t = db.geo_mindistance_boundaries; +t.drop(); +t.insert({loc: [1, 0]}); // 1 degree of longitude from origin. + +/* $minDistance is supported for 2dsphere index only, not 2d or geoHaystack. */ +t.ensureIndex({loc: "2dsphere"}); + +// +// Useful constants. +// + +var km = 1000, + earthRadiusMeters = 6378.1 * km, + geoJSONPoint = {$geometry: {type: 'Point', coordinates: [0, 0]}}, + // One degree of longitude at the equator, about 111 km. + degreeInMeters = 2 * Math.PI * earthRadiusMeters / 360, + metersEpsilon = Number.MIN_VALUE; + +/* Grow epsilon's exponent until epsilon exceeds the margin of error for the + * representation of degreeInMeters. The server uses 64-bit math, too, so we'll + * find the smallest epsilon the server can detect. +*/ +while (degreeInMeters + metersEpsilon == degreeInMeters) { metersEpsilon *= 2; } + +// +// Test boundary conditions for $near and GeoJSON, in meters. +// + +assert.eq( + 1, t.find({loc: { + $near: geoJSONPoint, + $minDistance: degreeInMeters + }}).count(), + "Expected to find (0, 1) within $minDistance 1 degree from origin" +); + +assert.eq( + 1, t.find({loc: { + $near: geoJSONPoint, + $minDistance: degreeInMeters - metersEpsilon + }}).count(), + "Expected to find (0, 1) within $minDistance (1 degree - epsilon) from origin" +); + +assert.eq( + 0, t.find({loc: { + $near: geoJSONPoint, + $minDistance: degreeInMeters + metersEpsilon + }}).count(), + "Expected *not* to find (0, 1) within $minDistance (1 degree + epsilon) from origin" +); + +// +// Test boundary conditions for $nearSphere and GeoJSON, in meters. +// + +assert.eq( + 1, t.find({loc: { + $nearSphere: geoJSONPoint, + $minDistance: degreeInMeters + }}).count(), + "Expected to find (0, 1) within $minDistance 1 degree from origin" +); + +assert.eq( + 1, t.find({loc: { + $nearSphere: geoJSONPoint, + $minDistance: degreeInMeters - metersEpsilon + }}).count(), + "Expected to find (0, 1) within $minDistance (1 degree - epsilon) from origin" +); + +assert.eq( + 0, t.find({loc: { + $nearSphere: geoJSONPoint, + $minDistance: degreeInMeters + metersEpsilon + }}).count(), + "Expected *not* to find (0, 1) within $minDistance (1 degree + epsilon) from origin" +); + +// +// Test boundary conditions for $nearSphere and a legacy point, in radians. +// +// $minDistance with legacy point requires $nearSphere; $near not +// supported. +// + +var legacyPoint = [0, 0], + degreeInRadians = 2 * Math.PI / 360, + radiansEpsilon = Number.MIN_VALUE; + +while (1 + radiansEpsilon == 1) { radiansEpsilon *= 2; } + +assert.eq( + 1, t.find({loc: { + $nearSphere: legacyPoint, + $minDistance: degreeInRadians + }}).count(), + "Expected to find (0, 1) within $minDistance 1 degree from origin" +); + +assert.eq( + 1, t.find({loc: { + $nearSphere: legacyPoint, + $minDistance: degreeInRadians - radiansEpsilon + }}).count(), + "Expected to find (0, 1) within $minDistance (1 degree - epsilon) from origin" +); + +assert.eq( + 0, t.find({loc: { + $nearSphere: legacyPoint, + $minDistance: degreeInRadians + radiansEpsilon + }}).count(), + "Expected *not* to find (0, 1) within $minDistance (1 degree + epsilon) from origin" +); diff --git a/src/mongo/db/geo/geoquery.cpp b/src/mongo/db/geo/geoquery.cpp index 46ef5da8642..6e561ddd287 100644 --- a/src/mongo/db/geo/geoquery.cpp +++ b/src/mongo/db/geo/geoquery.cpp @@ -28,9 +28,24 @@ namespace mongo { // The CRS for the legacy points dictates that distances are in radians. fromRadians = (FLAT == centroid.crs); + if (!obj["minDistance"].eoo()) { + if (obj["minDistance"].isNumber()) { + double distArg = obj["minDistance"].number(); + uassert(16901, "minDistance must be non-negative", distArg >= 0.0); + if (fromRadians) { + minDistance = distArg * radius; + } else { + minDistance = distArg; + } + } else { + return false; + } + } + if (!obj["maxDistance"].eoo()) { if (obj["maxDistance"].isNumber()) { double distArg = obj["maxDistance"].number(); + uassert(16902, "maxDistance must be non-negative", distArg >= 0.0); if (fromRadians) { maxDistance = distArg * radius; } else { @@ -46,14 +61,15 @@ namespace mongo { bool NearQuery::parseFrom(const BSONObj &obj, double radius) { bool hasGeometry = false; - // First, try legacy near. - // Legacy near parsing: t.find({ loc : { $nearSphere: [0,0], $maxDistance: 3 }}) - // Legacy near parsing: t.find({ loc : { $nearSphere: [0,0] }}) - // Legacy near parsing: t.find({ loc : { $near: { someGeoJSONPoint}}) + // First, try legacy near, e.g.: + // t.find({ loc : { $nearSphere: [0,0], $minDistance: 1, $maxDistance: 3 }}) + // t.find({ loc : { $nearSphere: [0,0] }}) + // t.find({ loc : { $near: { someGeoJSONPoint}}) BSONObjIterator it(obj); while (it.more()) { BSONElement e = it.next(); bool isNearSphere = mongoutils::str::equals(e.fieldName(), "$nearSphere"); + bool isMinDistance = mongoutils::str::equals(e.fieldName(), "$minDistance"); bool isMaxDistance = mongoutils::str::equals(e.fieldName(), "$maxDistance"); bool isNear = mongoutils::str::equals(e.fieldName(), "$near") || mongoutils::str::equals(e.fieldName(), "$geoNear"); @@ -71,19 +87,26 @@ namespace mongo { // We don't accept $near : [oldstylepoint]. hasGeometry = true; } + } else if (isMinDistance) { + uassert(16893, "$minDistance must be a number", e.isNumber()); + minDistance = e.Number(); + uassert(16894, "$minDistance must be non-negative", minDistance >= 0.0); } else if (isMaxDistance) { + uassert(16895, "$maxDistance must be a number", e.isNumber()); maxDistance = e.Number(); + uassert(16896, "$maxDistance must be non-negative", maxDistance >= 0.0); } } if (fromRadians) { + minDistance *= radius; maxDistance *= radius; } if (hasGeometry) { return true; } - // Next, try "new" near - // New near: t.find({ "geo" : { "$near" : { "$geometry" : pointA, $maxDistance : 20 }}}) + // Next, try "new" near: + // t.find({"geo" : {"$near" : {"$geometry": pointA, $minDistance: 1, $maxDistance: 3}}}) BSONElement e = obj.firstElement(); if (!e.isABSONObj()) { return false; } BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(e.getGtLtOp()); @@ -103,10 +126,14 @@ namespace mongo { (SPHERE == centroid.crs)); hasGeometry = true; } + } else if (mongoutils::str::equals(e.fieldName(), "$minDistance")) { + uassert(16897, "$minDistance must be a number", e.isNumber()); + minDistance = e.Number(); + uassert(16898, "$minDistance must be non-negative", minDistance >= 0.0); } else if (mongoutils::str::equals(e.fieldName(), "$maxDistance")) { - if (e.isNumber()) { - maxDistance = e.Number(); - } + uassert(16899, "$maxDistance must be a number", e.isNumber()); + maxDistance = e.Number(); + uassert(16900, "$maxDistance must be non-negative", maxDistance >= 0.0); } } return hasGeometry; diff --git a/src/mongo/db/geo/geoquery.h b/src/mongo/db/geo/geoquery.h index 31ecb5df311..7a18007b905 100644 --- a/src/mongo/db/geo/geoquery.h +++ b/src/mongo/db/geo/geoquery.h @@ -86,14 +86,17 @@ namespace mongo { class NearQuery { public: - NearQuery() : maxDistance(std::numeric_limits<double>::max()), fromRadians(false) {} - NearQuery(const string& f) : field(f), maxDistance(std::numeric_limits<double>::max()), + NearQuery() : minDistance(0), maxDistance(std::numeric_limits<double>::max()), + fromRadians(false) {} + NearQuery(const string& f) : field(f), minDistance(0), + maxDistance(std::numeric_limits<double>::max()), fromRadians(false) {} bool parseFrom(const BSONObj &obj, double radius); bool parseFromGeoNear(const BSONObj &obj, double radius); string field; PointWithCRS centroid; - // Distance IN METERS that we're willing to search. + // Min and max distance IN METERS from centroid that we're willing to search. + double minDistance; double maxDistance; // Did we convert to this distance from radians? (If so, we output distances in radians.) bool fromRadians; diff --git a/src/mongo/db/index/2d_index_cursor.cpp b/src/mongo/db/index/2d_index_cursor.cpp index ccad127e17a..9cd88fbc333 100644 --- a/src/mongo/db/index/2d_index_cursor.cpp +++ b/src/mongo/db/index/2d_index_cursor.cpp @@ -52,7 +52,7 @@ namespace mongo { }; inline double computeXScanDistance(double y, double maxDistDegrees) { - // TODO: this overestimates for large madDistDegrees far from the equator + // TODO: this overestimates for large maxDistDegrees far from the equator return maxDistDegrees / min(cos(deg2rad(min(+89.0, y + maxDistDegrees))), cos(deg2rad(max(-89.0, y - maxDistDegrees)))); } @@ -1778,6 +1778,9 @@ namespace mongo { const Point n(cmdObj["near"]); result.append("near", params.geoHashConverter->hash(cmdObj["near"]).toString()); + uassert(16903, "'minDistance' param not supported for 2d index, requires 2dsphere index", + cmdObj["minDistance"].eoo()); + double maxDistance = numeric_limits<double>::max(); if (cmdObj["maxDistance"].isNumber()) maxDistance = cmdObj["maxDistance"].number(); @@ -1883,6 +1886,10 @@ namespace mongo { type = twod_internal::GEO_PLANE; // prevents uninitialized warning } + uassert(16904, + "'$minDistance' param not supported for 2d index, requires 2dsphere index", + n["$minDistance"].eoo()); + double maxDistance = numeric_limits<double>::max(); if (e.isABSONObj() && e.embeddedObject().nFields() > 2) { BSONObjIterator i(e.embeddedObject()); diff --git a/src/mongo/db/index/s2_near_cursor.cpp b/src/mongo/db/index/s2_near_cursor.cpp index 2a5cd197b16..242b5c50227 100644 --- a/src/mongo/db/index/s2_near_cursor.cpp +++ b/src/mongo/db/index/s2_near_cursor.cpp @@ -67,13 +67,16 @@ namespace mongo { ++_nearFieldIndex; } + _minDistance = max(0.0, _nearQuery.minDistance); + // _outerRadius can't be greater than (pi * r) or we wrap around the opposite // side of the world. _maxDistance = min(M_PI * _params.radius, _nearQuery.maxDistance); + uassert(16892, "$minDistance too large", _minDistance < _maxDistance); // Start with a conservative _radiusIncrement. _radiusIncrement = 5 * S2::kAvgEdge.GetValue(_params.finestIndexedLevel) * _params.radius; - _innerRadius = _outerRadius = 0; + _innerRadius = _outerRadius = _minDistance; // We might want to adjust the sizes of our coverings if our search // isn't local to the start point. // Set up _outerRadius with proper checks (maybe maxDistance is really small?) diff --git a/src/mongo/db/index/s2_near_cursor.h b/src/mongo/db/index/s2_near_cursor.h index 480db1210ac..070d67f07dd 100644 --- a/src/mongo/db/index/s2_near_cursor.h +++ b/src/mongo/db/index/s2_near_cursor.h @@ -116,6 +116,8 @@ namespace mongo { BSONObj _specForFRV; // Geo-related variables. + // At what min distance (arc length) do we start looking for results? + double _minDistance; // What's the max distance (arc length) we're willing to look for results? double _maxDistance; diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js index dea85b762b1..5cd70582d8d 100644 --- a/src/mongo/shell/assert.js +++ b/src/mongo/shell/assert.js @@ -197,7 +197,7 @@ assert.time = function(f, msg, timeout /*ms*/) { assert.throws = function(func, params, msg){ if (assert._debug && msg) print("in assert for: " + msg); if (params && typeof(params) == "string") - throw "2nd argument to assert.throws has to be an array" + throw ("2nd argument to assert.throws has to be an array, not " + params); try { func.apply(null, params); } |