diff options
Diffstat (limited to 'src/mongo/db/matcher/expression_geo.cpp')
-rw-r--r-- | src/mongo/db/matcher/expression_geo.cpp | 666 |
1 files changed, 334 insertions, 332 deletions
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp index d61dba70343..1cca795b743 100644 --- a/src/mongo/db/matcher/expression_geo.cpp +++ b/src/mongo/db/matcher/expression_geo.cpp @@ -39,404 +39,406 @@ namespace mongo { - using mongoutils::str::equals; +using mongoutils::str::equals; + +// +// GeoExpression +// + +// Put simple constructors here for unique_ptr. +GeoExpression::GeoExpression() : field(""), predicate(INVALID) {} +GeoExpression::GeoExpression(const std::string& f) : field(f), predicate(INVALID) {} + +Status GeoExpression::parseQuery(const BSONObj& obj) { + BSONObjIterator outerIt(obj); + // "within" / "geoWithin" / "geoIntersects" + BSONElement queryElt = outerIt.next(); + if (outerIt.more()) { + return Status(ErrorCodes::BadValue, + str::stream() << "can't parse extra field: " << outerIt.next()); + } - // - // GeoExpression - // + BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(queryElt.getGtLtOp()); + if (BSONObj::opGEO_INTERSECTS == matchType) { + predicate = GeoExpression::INTERSECT; + } else if (BSONObj::opWITHIN == matchType) { + predicate = GeoExpression::WITHIN; + } else { + // eoo() or unknown query predicate. + return Status(ErrorCodes::BadValue, + str::stream() << "invalid geo query predicate: " << obj); + } - // Put simple constructors here for unique_ptr. - GeoExpression::GeoExpression() : field(""), predicate(INVALID) {} - GeoExpression::GeoExpression(const std::string& f) : field(f), predicate(INVALID) {} + // Parse geometry after predicates. + if (Object != queryElt.type()) + return Status(ErrorCodes::BadValue, "geometry must be an object"); + BSONObj geoObj = queryElt.Obj(); - Status GeoExpression::parseQuery(const BSONObj &obj) { - BSONObjIterator outerIt(obj); - // "within" / "geoWithin" / "geoIntersects" - BSONElement queryElt = outerIt.next(); - if (outerIt.more()) { - return Status(ErrorCodes::BadValue, - str::stream() << "can't parse extra field: " << outerIt.next()); - } + BSONObjIterator geoIt(geoObj); - BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(queryElt.getGtLtOp()); - if (BSONObj::opGEO_INTERSECTS == matchType) { - predicate = GeoExpression::INTERSECT; - } else if (BSONObj::opWITHIN == matchType) { - predicate = GeoExpression::WITHIN; + while (geoIt.more()) { + BSONElement elt = geoIt.next(); + if (str::equals(elt.fieldName(), "$uniqueDocs")) { + // Deprecated "$uniqueDocs" field + warning() << "deprecated $uniqueDocs option: " << obj.toString() << endl; } else { - // eoo() or unknown query predicate. - return Status(ErrorCodes::BadValue, - str::stream() << "invalid geo query predicate: " << obj); + // The element must be a geo specifier. "$box", "$center", "$geometry", etc. + geoContainer.reset(new GeometryContainer()); + Status status = geoContainer->parseFromQuery(elt); + if (!status.isOK()) + return status; } + } - // Parse geometry after predicates. - if (Object != queryElt.type()) return Status(ErrorCodes::BadValue, "geometry must be an object"); - BSONObj geoObj = queryElt.Obj(); - - BSONObjIterator geoIt(geoObj); - - while (geoIt.more()) { - BSONElement elt = geoIt.next(); - if (str::equals(elt.fieldName(), "$uniqueDocs")) { - // Deprecated "$uniqueDocs" field - warning() << "deprecated $uniqueDocs option: " << obj.toString() << endl; - } else { - // The element must be a geo specifier. "$box", "$center", "$geometry", etc. - geoContainer.reset(new GeometryContainer()); - Status status = geoContainer->parseFromQuery(elt); - if (!status.isOK()) return status; - } - } + if (geoContainer == NULL) { + return Status(ErrorCodes::BadValue, "geo query doesn't have any geometry"); + } - if (geoContainer == NULL) { - return Status(ErrorCodes::BadValue, "geo query doesn't have any geometry"); - } + return Status::OK(); +} - return Status::OK(); - } +Status GeoExpression::parseFrom(const BSONObj& obj) { + // Initialize geoContainer and parse BSON object + Status status = parseQuery(obj); + if (!status.isOK()) + return status; - Status GeoExpression::parseFrom(const BSONObj &obj) { - // Initialize geoContainer and parse BSON object - Status status = parseQuery(obj); - if (!status.isOK()) return status; - - // Why do we only deal with $within {polygon}? - // 1. Finding things within a point is silly and only valid - // for points and degenerate lines/polys. - // - // 2. Finding points within a line is easy but that's called intersect. - // Finding lines within a line is kind of tricky given what S2 gives us. - // Doing line-within-line is a valid yet unsupported feature, - // though I wonder if we want to preserve orientation for lines or - // allow (a,b),(c,d) to be within (c,d),(a,b). Anyway, punt on - // this for now. - if (GeoExpression::WITHIN == predicate && !geoContainer->supportsContains()) { - return Status(ErrorCodes::BadValue, - str::stream() << "$within not supported with provided geometry: " << obj); - } + // Why do we only deal with $within {polygon}? + // 1. Finding things within a point is silly and only valid + // for points and degenerate lines/polys. + // + // 2. Finding points within a line is easy but that's called intersect. + // Finding lines within a line is kind of tricky given what S2 gives us. + // Doing line-within-line is a valid yet unsupported feature, + // though I wonder if we want to preserve orientation for lines or + // allow (a,b),(c,d) to be within (c,d),(a,b). Anyway, punt on + // this for now. + if (GeoExpression::WITHIN == predicate && !geoContainer->supportsContains()) { + return Status(ErrorCodes::BadValue, + str::stream() << "$within not supported with provided geometry: " << obj); + } - // Big polygon with strict winding order is represented as an S2Loop in SPHERE CRS. - // So converting the query to SPHERE CRS makes things easier than projecting all the data - // into STRICT_SPHERE CRS. - if (STRICT_SPHERE == geoContainer->getNativeCRS()) { - if (!geoContainer->supportsProject(SPHERE)) { - return Status(ErrorCodes::BadValue, - "only polygon supported with strict winding order"); - } - geoContainer->projectInto(SPHERE); + // Big polygon with strict winding order is represented as an S2Loop in SPHERE CRS. + // So converting the query to SPHERE CRS makes things easier than projecting all the data + // into STRICT_SPHERE CRS. + if (STRICT_SPHERE == geoContainer->getNativeCRS()) { + if (!geoContainer->supportsProject(SPHERE)) { + return Status(ErrorCodes::BadValue, "only polygon supported with strict winding order"); } + geoContainer->projectInto(SPHERE); + } - // $geoIntersect queries are hardcoded to *always* be in SPHERE CRS - // TODO: This is probably bad semantics, should not do this - if (GeoExpression::INTERSECT == predicate) { - if (!geoContainer->supportsProject(SPHERE)) { - return Status(ErrorCodes::BadValue, - str::stream() - << "$geoIntersect not supported with provided geometry: " - << obj); - } - geoContainer->projectInto(SPHERE); + // $geoIntersect queries are hardcoded to *always* be in SPHERE CRS + // TODO: This is probably bad semantics, should not do this + if (GeoExpression::INTERSECT == predicate) { + if (!geoContainer->supportsProject(SPHERE)) { + return Status(ErrorCodes::BadValue, + str::stream() + << "$geoIntersect not supported with provided geometry: " << obj); } - - return Status::OK(); + geoContainer->projectInto(SPHERE); } - // - // GeoNearExpression - // + return Status::OK(); +} - GeoNearExpression::GeoNearExpression() - : minDistance(0), - maxDistance(std::numeric_limits<double>::max()), - isNearSphere(false), - unitsAreRadians(false), - isWrappingQuery(false) { } - - GeoNearExpression::GeoNearExpression(const std::string& f) - : field(f), - minDistance(0), - maxDistance(std::numeric_limits<double>::max()), - isNearSphere(false), - unitsAreRadians(false), - isWrappingQuery(false) { } - - bool GeoNearExpression::parseLegacyQuery(const BSONObj &obj) { - - bool hasGeometry = false; - - // 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 : [0, 0, 1] } }); - // t.find({ loc : { $near: { someGeoJSONPoint}}) - // t.find({ loc : { $geoNear: { someGeoJSONPoint}}) - BSONObjIterator it(obj); - while (it.more()) { - BSONElement e = it.next(); - if (equals(e.fieldName(), "$near") || equals(e.fieldName(), "$geoNear") - || equals(e.fieldName(), "$nearSphere")) { - if (!e.isABSONObj()) { return false; } - BSONObj embeddedObj = e.embeddedObject(); +// +// GeoNearExpression +// + +GeoNearExpression::GeoNearExpression() + : minDistance(0), + maxDistance(std::numeric_limits<double>::max()), + isNearSphere(false), + unitsAreRadians(false), + isWrappingQuery(false) {} + +GeoNearExpression::GeoNearExpression(const std::string& f) + : field(f), + minDistance(0), + maxDistance(std::numeric_limits<double>::max()), + isNearSphere(false), + unitsAreRadians(false), + isWrappingQuery(false) {} + +bool GeoNearExpression::parseLegacyQuery(const BSONObj& obj) { + bool hasGeometry = false; + + // 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 : [0, 0, 1] } }); + // t.find({ loc : { $near: { someGeoJSONPoint}}) + // t.find({ loc : { $geoNear: { someGeoJSONPoint}}) + BSONObjIterator it(obj); + while (it.more()) { + BSONElement e = it.next(); + if (equals(e.fieldName(), "$near") || equals(e.fieldName(), "$geoNear") || + equals(e.fieldName(), "$nearSphere")) { + if (!e.isABSONObj()) { + return false; + } + BSONObj embeddedObj = e.embeddedObject(); - if (GeoParser::parseQueryPoint(e, centroid.get()).isOK() - || GeoParser::parsePointWithMaxDistance(embeddedObj, centroid.get(), &maxDistance)) { - uassert(18522, "max distance must be non-negative", maxDistance >= 0.0); - hasGeometry = true; - isNearSphere = equals(e.fieldName(), "$nearSphere"); - } - } else if (equals(e.fieldName(), "$minDistance")) { - uassert(16893, "$minDistance must be a number", e.isNumber()); - minDistance = e.Number(); - uassert(16894, "$minDistance must be non-negative", minDistance >= 0.0); - } else if (equals(e.fieldName(), "$maxDistance")) { - uassert(16895, "$maxDistance must be a number", e.isNumber()); - maxDistance = e.Number(); - uassert(16896, "$maxDistance must be non-negative", maxDistance >= 0.0); - } else if (equals(e.fieldName(), "$uniqueDocs")) { - warning() << "ignoring deprecated option $uniqueDocs"; + if (GeoParser::parseQueryPoint(e, centroid.get()).isOK() || + GeoParser::parsePointWithMaxDistance(embeddedObj, centroid.get(), &maxDistance)) { + uassert(18522, "max distance must be non-negative", maxDistance >= 0.0); + hasGeometry = true; + isNearSphere = equals(e.fieldName(), "$nearSphere"); } + } else if (equals(e.fieldName(), "$minDistance")) { + uassert(16893, "$minDistance must be a number", e.isNumber()); + minDistance = e.Number(); + uassert(16894, "$minDistance must be non-negative", minDistance >= 0.0); + } else if (equals(e.fieldName(), "$maxDistance")) { + uassert(16895, "$maxDistance must be a number", e.isNumber()); + maxDistance = e.Number(); + uassert(16896, "$maxDistance must be non-negative", maxDistance >= 0.0); + } else if (equals(e.fieldName(), "$uniqueDocs")) { + warning() << "ignoring deprecated option $uniqueDocs"; } - - return hasGeometry; } - Status GeoNearExpression::parseNewQuery(const BSONObj &obj) { - bool hasGeometry = false; + return hasGeometry; +} - BSONObjIterator objIt(obj); - if (!objIt.more()) { - return Status(ErrorCodes::BadValue, "empty geo near query object"); - } - BSONElement e = objIt.next(); - // Just one arg. to $geoNear. - if (objIt.more()) { - return Status(ErrorCodes::BadValue, mongoutils::str::stream() << - "geo near accepts just one argument when querying for a GeoJSON " << - "point. Extra field found: " << objIt.next()); - } +Status GeoNearExpression::parseNewQuery(const BSONObj& obj) { + bool hasGeometry = false; - // Parse "new" near: - // t.find({"geo" : {"$near" : {"$geometry": pointA, $minDistance: 1, $maxDistance: 3}}}) - // t.find({"geo" : {"$geoNear" : {"$geometry": pointA, $minDistance: 1, $maxDistance: 3}}}) - if (!e.isABSONObj()) { - return Status(ErrorCodes::BadValue, "geo near query argument is not an object"); - } - BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(e.getGtLtOp()); - if (BSONObj::opNEAR != matchType) { - return Status(ErrorCodes::BadValue, mongoutils::str::stream() << - "invalid geo near query operator: " << e.fieldName()); - } + BSONObjIterator objIt(obj); + if (!objIt.more()) { + return Status(ErrorCodes::BadValue, "empty geo near query object"); + } + BSONElement e = objIt.next(); + // Just one arg. to $geoNear. + if (objIt.more()) { + return Status(ErrorCodes::BadValue, + mongoutils::str::stream() + << "geo near accepts just one argument when querying for a GeoJSON " + << "point. Extra field found: " << objIt.next()); + } - // Iterate over the argument. - BSONObjIterator it(e.embeddedObject()); - while (it.more()) { - BSONElement e = it.next(); - if (equals(e.fieldName(), "$geometry")) { - if (e.isABSONObj()) { - BSONObj embeddedObj = e.embeddedObject(); - Status status = GeoParser::parseQueryPoint(e, centroid.get()); - if (!status.isOK()) { - return Status(ErrorCodes::BadValue, - str::stream() - << "invalid point in geo near query $geometry argument: " - << embeddedObj << " " << status.reason()); - } - uassert(16681, "$near requires geojson point, given " + embeddedObj.toString(), - (SPHERE == centroid->crs)); - hasGeometry = true; + // Parse "new" near: + // t.find({"geo" : {"$near" : {"$geometry": pointA, $minDistance: 1, $maxDistance: 3}}}) + // t.find({"geo" : {"$geoNear" : {"$geometry": pointA, $minDistance: 1, $maxDistance: 3}}}) + if (!e.isABSONObj()) { + return Status(ErrorCodes::BadValue, "geo near query argument is not an object"); + } + BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(e.getGtLtOp()); + if (BSONObj::opNEAR != matchType) { + return Status(ErrorCodes::BadValue, + mongoutils::str::stream() + << "invalid geo near query operator: " << e.fieldName()); + } + + // Iterate over the argument. + BSONObjIterator it(e.embeddedObject()); + while (it.more()) { + BSONElement e = it.next(); + if (equals(e.fieldName(), "$geometry")) { + if (e.isABSONObj()) { + BSONObj embeddedObj = e.embeddedObject(); + Status status = GeoParser::parseQueryPoint(e, centroid.get()); + if (!status.isOK()) { + return Status(ErrorCodes::BadValue, + str::stream() + << "invalid point in geo near query $geometry argument: " + << embeddedObj << " " << status.reason()); } - } else if (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 (equals(e.fieldName(), "$maxDistance")) { - uassert(16899, "$maxDistance must be a number", e.isNumber()); - maxDistance = e.Number(); - uassert(16900, "$maxDistance must be non-negative", maxDistance >= 0.0); + uassert(16681, + "$near requires geojson point, given " + embeddedObj.toString(), + (SPHERE == centroid->crs)); + hasGeometry = true; } + } else if (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 (equals(e.fieldName(), "$maxDistance")) { + uassert(16899, "$maxDistance must be a number", e.isNumber()); + maxDistance = e.Number(); + uassert(16900, "$maxDistance must be non-negative", maxDistance >= 0.0); } - - if (!hasGeometry) { - return Status(ErrorCodes::BadValue, "$geometry is required for geo near query"); - } - - return Status::OK(); } + if (!hasGeometry) { + return Status(ErrorCodes::BadValue, "$geometry is required for geo near query"); + } - Status GeoNearExpression::parseFrom(const BSONObj &obj) { - - Status status = Status::OK(); - centroid.reset(new PointWithCRS()); - - if (!parseLegacyQuery(obj)) { - // Clear out any half-baked data. - minDistance = 0; - isNearSphere = false; - maxDistance = std::numeric_limits<double>::max(); - // ...and try parsing new format. - status = parseNewQuery(obj); - } - - if (!status.isOK()) - return status; - - // Fixup the near query for anonoyances caused by $nearSphere - if (isNearSphere) { + return Status::OK(); +} - // The user-provided point can be flat for a spherical query - needs to be projectable - uassert(17444, - "Legacy point is out of bounds for spherical query", - ShapeProjection::supportsProject(*centroid, SPHERE)); - unitsAreRadians = SPHERE != centroid->crs; - // GeoJSON points imply wrapping queries - isWrappingQuery = SPHERE == centroid->crs; +Status GeoNearExpression::parseFrom(const BSONObj& obj) { + Status status = Status::OK(); + centroid.reset(new PointWithCRS()); - // Project the point to a spherical CRS now that we've got the settings we need - // We need to manually project here since we aren't using GeometryContainer - ShapeProjection::projectInto(centroid.get(), SPHERE); - } - else { - unitsAreRadians = false; - isWrappingQuery = SPHERE == centroid->crs; - } + if (!parseLegacyQuery(obj)) { + // Clear out any half-baked data. + minDistance = 0; + isNearSphere = false; + maxDistance = std::numeric_limits<double>::max(); + // ...and try parsing new format. + status = parseNewQuery(obj); + } + if (!status.isOK()) return status; + + // Fixup the near query for anonoyances caused by $nearSphere + if (isNearSphere) { + // The user-provided point can be flat for a spherical query - needs to be projectable + uassert(17444, + "Legacy point is out of bounds for spherical query", + ShapeProjection::supportsProject(*centroid, SPHERE)); + + unitsAreRadians = SPHERE != centroid->crs; + // GeoJSON points imply wrapping queries + isWrappingQuery = SPHERE == centroid->crs; + + // Project the point to a spherical CRS now that we've got the settings we need + // We need to manually project here since we aren't using GeometryContainer + ShapeProjection::projectInto(centroid.get(), SPHERE); + } else { + unitsAreRadians = false; + isWrappingQuery = SPHERE == centroid->crs; } - // - // GeoMatchExpression and GeoNearMatchExpression - // + return status; +} - // - // Geo queries we don't need an index to answer: geoWithin and geoIntersects - // +// +// GeoMatchExpression and GeoNearMatchExpression +// - Status GeoMatchExpression::init( StringData path, const GeoExpression* query, - const BSONObj& rawObj ) { - _query.reset(query); - _rawObj = rawObj; - return initPath( path ); - } +// +// Geo queries we don't need an index to answer: geoWithin and geoIntersects +// - bool GeoMatchExpression::matchesSingleElement( const BSONElement& e ) const { - if ( !e.isABSONObj()) - return false; +Status GeoMatchExpression::init(StringData path, + const GeoExpression* query, + const BSONObj& rawObj) { + _query.reset(query); + _rawObj = rawObj; + return initPath(path); +} - GeometryContainer geometry; - if ( !geometry.parseFromStorage( e ).isOK() ) - return false; +bool GeoMatchExpression::matchesSingleElement(const BSONElement& e) const { + if (!e.isABSONObj()) + return false; - // Never match big polygon - if (geometry.getNativeCRS() == STRICT_SPHERE) - return false; + GeometryContainer geometry; + if (!geometry.parseFromStorage(e).isOK()) + return false; - // Project this geometry into the CRS of the query - if (!geometry.supportsProject(_query->getGeometry().getNativeCRS())) - return false; + // Never match big polygon + if (geometry.getNativeCRS() == STRICT_SPHERE) + return false; - geometry.projectInto(_query->getGeometry().getNativeCRS()); + // Project this geometry into the CRS of the query + if (!geometry.supportsProject(_query->getGeometry().getNativeCRS())) + return false; - if (GeoExpression::WITHIN == _query->getPred()) { - return _query->getGeometry().contains(geometry); - } - else { - verify(GeoExpression::INTERSECT == _query->getPred()); - return _query->getGeometry().intersects(geometry); - } - } + geometry.projectInto(_query->getGeometry().getNativeCRS()); - void GeoMatchExpression::debugString( StringBuilder& debug, int level ) const { - _debugAddSpace( debug, level ); - debug << "GEO raw = " << _rawObj.toString(); - MatchExpression::TagData* td = getTag(); - if (NULL != td) { - debug << " "; - td->debugString(&debug); - } - debug << "\n"; + if (GeoExpression::WITHIN == _query->getPred()) { + return _query->getGeometry().contains(geometry); + } else { + verify(GeoExpression::INTERSECT == _query->getPred()); + return _query->getGeometry().intersects(geometry); } +} - void GeoMatchExpression::toBSON(BSONObjBuilder* out) const { - out->appendElements(_rawObj); +void GeoMatchExpression::debugString(StringBuilder& debug, int level) const { + _debugAddSpace(debug, level); + debug << "GEO raw = " << _rawObj.toString(); + MatchExpression::TagData* td = getTag(); + if (NULL != td) { + debug << " "; + td->debugString(&debug); } + debug << "\n"; +} - bool GeoMatchExpression::equivalent( const MatchExpression* other ) const { - if ( matchType() != other->matchType() ) - return false; +void GeoMatchExpression::toBSON(BSONObjBuilder* out) const { + out->appendElements(_rawObj); +} - const GeoMatchExpression* realOther = static_cast<const GeoMatchExpression*>( other ); +bool GeoMatchExpression::equivalent(const MatchExpression* other) const { + if (matchType() != other->matchType()) + return false; - if ( path() != realOther->path() ) - return false; + const GeoMatchExpression* realOther = static_cast<const GeoMatchExpression*>(other); - return _rawObj == realOther->_rawObj; - } + if (path() != realOther->path()) + return false; - LeafMatchExpression* GeoMatchExpression::shallowClone() const { - GeoMatchExpression* next = new GeoMatchExpression(); - next->init( path(), NULL, _rawObj); - next->_query = _query; - if (getTag()) { - next->setTag(getTag()->clone()); - } - return next; + return _rawObj == realOther->_rawObj; +} + +LeafMatchExpression* GeoMatchExpression::shallowClone() const { + GeoMatchExpression* next = new GeoMatchExpression(); + next->init(path(), NULL, _rawObj); + next->_query = _query; + if (getTag()) { + next->setTag(getTag()->clone()); } + return next; +} - // - // Parse-only geo expressions: geoNear (formerly known as near). - // +// +// Parse-only geo expressions: geoNear (formerly known as near). +// - Status GeoNearMatchExpression::init( StringData path, const GeoNearExpression* query, - const BSONObj& rawObj ) { - _query.reset(query); - _rawObj = rawObj; - return initPath( path ); - } +Status GeoNearMatchExpression::init(StringData path, + const GeoNearExpression* query, + const BSONObj& rawObj) { + _query.reset(query); + _rawObj = rawObj; + return initPath(path); +} - bool GeoNearMatchExpression::matchesSingleElement( const BSONElement& e ) const { - // See ops/update.cpp. - // This node is removed by the query planner. It's only ever called if we're getting an - // elemMatchKey. - return true; - } +bool GeoNearMatchExpression::matchesSingleElement(const BSONElement& e) const { + // See ops/update.cpp. + // This node is removed by the query planner. It's only ever called if we're getting an + // elemMatchKey. + return true; +} - void GeoNearMatchExpression::debugString( StringBuilder& debug, int level ) const { - _debugAddSpace( debug, level ); - debug << "GEONEAR " << _query->toString(); - MatchExpression::TagData* td = getTag(); - if (NULL != td) { - debug << " "; - td->debugString(&debug); - } - debug << "\n"; +void GeoNearMatchExpression::debugString(StringBuilder& debug, int level) const { + _debugAddSpace(debug, level); + debug << "GEONEAR " << _query->toString(); + MatchExpression::TagData* td = getTag(); + if (NULL != td) { + debug << " "; + td->debugString(&debug); } + debug << "\n"; +} - void GeoNearMatchExpression::toBSON(BSONObjBuilder* out) const { - out->appendElements(_rawObj); - } +void GeoNearMatchExpression::toBSON(BSONObjBuilder* out) const { + out->appendElements(_rawObj); +} - bool GeoNearMatchExpression::equivalent( const MatchExpression* other ) const { - if ( matchType() != other->matchType() ) - return false; +bool GeoNearMatchExpression::equivalent(const MatchExpression* other) const { + if (matchType() != other->matchType()) + return false; - const GeoNearMatchExpression* realOther = static_cast<const GeoNearMatchExpression*>(other); + const GeoNearMatchExpression* realOther = static_cast<const GeoNearMatchExpression*>(other); - if ( path() != realOther->path() ) - return false; + if (path() != realOther->path()) + return false; - return _rawObj == realOther->_rawObj; - } + return _rawObj == realOther->_rawObj; +} - LeafMatchExpression* GeoNearMatchExpression::shallowClone() const { - GeoNearMatchExpression* next = new GeoNearMatchExpression(); - next->init( path(), NULL, _rawObj ); - next->_query = _query; - if (getTag()) { - next->setTag(getTag()->clone()); - } - return next; +LeafMatchExpression* GeoNearMatchExpression::shallowClone() const { + GeoNearMatchExpression* next = new GeoNearMatchExpression(); + next->init(path(), NULL, _rawObj); + next->_query = _query; + if (getTag()) { + next->setTag(getTag()->clone()); } - + return next; +} } |