diff options
Diffstat (limited to 'src/mongo/db/matcher/expression_geo_test.cpp')
| -rw-r--r-- | src/mongo/db/matcher/expression_geo_test.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/src/mongo/db/matcher/expression_geo_test.cpp b/src/mongo/db/matcher/expression_geo_test.cpp index 4115285de18..4c2d81cbfa8 100644 --- a/src/mongo/db/matcher/expression_geo_test.cpp +++ b/src/mongo/db/matcher/expression_geo_test.cpp @@ -33,11 +33,13 @@ #include <memory> +#include "mongo/db/exec/document_value/document_value_test_util.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/matcher/expression.h" #include "mongo/db/matcher/expression_geo.h" + namespace mongo { TEST(ExpressionGeoTest, Geo1) { @@ -153,6 +155,143 @@ TEST(ExpressionGeoTest, GeoNearEquivalent) { } } + +TEST(ExpressionGeoTest, SerializeGeoExpressions) { + SerializationOptions opts = {}; + opts.transformIdentifiers = true; + opts.literalPolicy = LiteralSerializationPolicy::kToDebugTypeString; + { + BSONObj query = fromjson("{$within: {$box: [{x: 4, y: 4}, [6, 6]]}}"); + std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query)); + + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$within":{"$box":"?array<>"}})", + ge->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson( + "{$geoWithin: {$geometry: {type: \"MultiPolygon\", coordinates: [[[[20.0, 70.0],[30.0, " + "70.0],[30.0, 50.0],[20.0, 50.0],[20.0, 70.0]]]]}}}"); + std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$geoWithin": { + "$geometry": { + "type": "MultiPolygon", + "coordinates": "?array<?array>" + } + } + })", + ge->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson( + R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiPolygon", + "coordinates": [[[ + [-20.0, -70.0], + [-30.0, -70.0], + [-30.0, -50.0], + [-20.0, -50.0], + [-20.0, -70.0] + ]]] + } + } + })"); + std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiPolygon", + "coordinates": "?array<?array>" + } + } + })", + ge->getSerializedRightHandSide(opts)); + } + { + BSONObj query1 = fromjson( + R"({$within: { + $geometry: { + type: 'Polygon', + coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]] + } + }})"); + std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query1)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$within":{"$geometry":{"type":"Polygon","coordinates":"?array<?array>"}}})", + ge->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson( + "{$near: {$maxDistance: 100, " + "$geometry: {type: 'Point', coordinates: [0, 0]}}}"); + std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$near": { + "$maxDistance": "?number", + "$geometry": { + "type": "Point", + "coordinates": "?array<?number>" + } + } + })", + gne->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson("{ $nearSphere: [0,0], $minDistance: 1, $maxDistance: 3 }"); + std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({ + "$nearSphere": "?array<?number>", + "$minDistance": "?number", + "$maxDistance": "?number" + })", + gne->getSerializedRightHandSide(opts)); + } + + { + BSONObj query = fromjson("{$near : [0, 0, 1] }"); + std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$near":"?array<?number>"})", + gne->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson("{$geoNear: [0, 0, 100]}"); + std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$geoNear":"?array<?number>"})", + gne->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson("{$geoNear: [0, 10], $maxDistance: 80 }"); + std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$geoNear":"?array<?number>","$maxDistance":"?number"})", + gne->getSerializedRightHandSide(opts)); + } + { + BSONObj query = fromjson("{$geoIntersects: {$geometry: [0, 0]}}"); + std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query)); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"$geoIntersects":{"$geometry":["?number","?number"]}})", + ge->getSerializedRightHandSide(opts)); + } + { + // Make sure we reject arrays with <2 or >2 elements. + BSONObj query = fromjson("{$geoIntersects: {$geometry: [0, 0, 1]}}"); + std::unique_ptr<GeoExpression> gq(new GeoExpression); + ASSERT_NOT_OK(gq->parseFrom(query)); + query = fromjson("{$geoIntersects: {$geometry: [0]}}"); + ASSERT_NOT_OK(gq->parseFrom(query)); + } +} + /** * A geo expression being not equivalent to another expression. */ @@ -182,4 +321,280 @@ TEST(ExpressionGeoTest, GeoNearNotEquivalent) { gne2(makeGeoNearMatchExpression(query2)); ASSERT(!gne1->equivalent(gne2.get())); } + +TEST(ExpressionGeoTest, SerializeWithCRSIFSpecifiedWithChangedOptions) { + BSONObj query1 = fromjson( + "{$within: {$geometry: {type: 'Polygon'," + "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]," + "crs: {" + "type: 'name'," + "properties: { name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' }" + "}}}}"); + std::unique_ptr<GeoMatchExpression> ge1(makeGeoMatchExpression(query1)); + auto opts = SerializationOptions{LiteralSerializationPolicy::kToRepresentativeParseableValue}; + auto serialized = ge1->getSerializedRightHandSide(opts); + ASSERT_BSONOBJ_EQ_AUTO( + R"({ + "$within": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 0, + 1 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ], + "crs": { + "type": "name", + "properties": { + "name": "urn:x-mongodb:crs:strictwinding:EPSG:4326" + } + } + } + } + })", + serialized); + serialized = ge1->getSerializedRightHandSide(opts); + ASSERT_BSONOBJ_EQ_AUTO( + R"({ + "$within": { + "$geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 0, + 0 + ], + [ + 0, + 1 + ], + [ + 1, + 1 + ], + [ + 0, + 0 + ] + ] + ], + "crs": { + "type": "name", + "properties": { + "name": "urn:x-mongodb:crs:strictwinding:EPSG:4326" + } + } + } + } + })", + serialized); +} + +template <typename CreateFn> +void assertRepresentativeShapeIsStable(BSONObj inputExpr, + BSONObj expectedRepresentativeExpr, + CreateFn createFn) { + auto opts = SerializationOptions{LiteralSerializationPolicy::kToRepresentativeParseableValue}; + auto ge(createFn(inputExpr)); + + auto serializedExpr = ge->getSerializedRightHandSide(opts); + ASSERT_BSONOBJ_EQ(serializedExpr, expectedRepresentativeExpr); + + auto roundTripped = createFn(serializedExpr); + ASSERT_BSONOBJ_EQ(roundTripped->getSerializedRightHandSide(opts), serializedExpr); +} + +void assertRepresentativeGeoShapeIsStable(BSONObj inputExpr, BSONObj expectedRepresentativeExpr) { + assertRepresentativeShapeIsStable( + inputExpr, expectedRepresentativeExpr, [](const BSONObj& input) { + return makeGeoMatchExpression(input); + }); +} + +void assertRepresentativeGeoNearShapeIsStable(BSONObj inputExpr, + BSONObj expectedRepresentativeExpr) { + assertRepresentativeShapeIsStable( + inputExpr, expectedRepresentativeExpr, [](const BSONObj& input) { + return makeGeoNearMatchExpression(input); + }); +} + +TEST(ExpressionGeoTest, RoundTripSerializeGeoExpressions) { + assertRepresentativeGeoShapeIsStable(fromjson("{$within: {$box: [{x: 4, y: 4}, [6, 6]]}}"), + fromjson("{$within: {$box: [[1, 1],[1, 1]]}}")); + + assertRepresentativeGeoShapeIsStable( + fromjson( + R"({$geoWithin: {$geometry: {type: "MultiPolygon", coordinates: [[[[20.0, 70.0],[30.0, 70.0],[30.0, 50.0],[20.0, 50.0],[20.0, 70.0]]]]}}})"), + fromjson( + R"({$geoWithin: {$geometry: {type: "MultiPolygon", coordinates: [[[[0, 0],[0, 1],[1, 1],[0, 0]]]]}}})")); + + assertRepresentativeGeoShapeIsStable(fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiPolygon", + "coordinates": [[[ + [-20.0, -70.0], + [-30.0, -70.0], + [-30.0, -50.0], + [-20.0, -50.0], + [-20.0, -70.0] + ]]] + } + } + })"), + fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiPolygon", + "coordinates": [[[[0, 0],[0, 1],[1, 1],[0, 0]]]] + } + } + })")); + + assertRepresentativeGeoShapeIsStable(fromjson(R"({$within: { + $geometry: { + type: 'Polygon', + coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]] + } + }})"), + fromjson(R"({$within: { + $geometry: { + type: 'Polygon', + coordinates: [[[0, 0],[0, 1],[1, 1],[0, 0]]] + } + }})")); + + assertRepresentativeGeoNearShapeIsStable( + fromjson("{$near: {$maxDistance: 100, $geometry: {type: 'Point', coordinates: [0, 0]}}}"), + fromjson("{$near: {$maxDistance: 1, $geometry: {type: 'Point', coordinates: [1, 1]}}}")); + + assertRepresentativeGeoNearShapeIsStable( + fromjson("{$nearSphere: [0,0], $minDistance: 2, $maxDistance: 4 }"), + fromjson("{$nearSphere: [1,1], $minDistance: 1, $maxDistance: 1 }")); + + assertRepresentativeGeoNearShapeIsStable( + fromjson("{$minDistance: 2, $maxDistance: 4, $nearSphere: [0,0]}"), + fromjson("{$minDistance: 1, $maxDistance: 1, $nearSphere: [1,1]}")); + + assertRepresentativeGeoNearShapeIsStable(fromjson("{$near: [0, 0, 1]}"), + fromjson("{$near: [1, 1]}")); + + assertRepresentativeGeoNearShapeIsStable(fromjson("{$geoNear: [0, 0, 100]}"), + fromjson("{$geoNear: [1, 1]}")); + + assertRepresentativeGeoNearShapeIsStable(fromjson("{$geoNear: [0, 10], $maxDistance: 80 }"), + fromjson("{$geoNear: [1, 1], $maxDistance: 1}")); + + assertRepresentativeGeoShapeIsStable(fromjson("{$geoIntersects: {$geometry: [0, 0]}}"), + fromjson("{$geoIntersects: {$geometry: [1, 1]}}")); + // Test scenario with new $geometry query not specifying the geometry type. + assertRepresentativeGeoNearShapeIsStable( + fromjson("{$geoNear: { $geometry: {coordinates: [0, 10]}}}"), + fromjson("{$geoNear: { $geometry: {coordinates: [1, 1]}}}")); + + // Test scenario with new $geometry query specifying invalid type. + assertRepresentativeGeoNearShapeIsStable( + fromjson("{$geoNear: { $geometry: { type: 'b.c', coordinates: [0, 10]}}}"), + fromjson("{$geoNear: { $geometry: {type: 'b.c', coordinates: [1, 1]}}}")); + + // Test scenario with $nearSphere without $geometry and no type specified + assertRepresentativeGeoNearShapeIsStable(fromjson(R"({"$nearSphere":{"coordinates":[0,0]}})"), + fromjson(R"({"$nearSphere":{"coordinates":[1,1]}})")); + + // Test case with first field of $geometry as numeric field, arbitrary coordinate naming. + assertRepresentativeGeoShapeIsStable( + fromjson(R"({"$geoIntersects":{"$geometry":{"shardOptions":40,"y":5}}})"), + fromjson(R"({"$geoIntersects":{"$geometry":{"shardOptions":1,"y":1}}})")); + + assertRepresentativeGeoShapeIsStable(fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiLineString", + "coordinates": [[ + [2, 0], + [2, 2] + ], [ + [0, 4], + [1, 4] + ]] + } + } + })"), + fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "MultiLineString", + "coordinates": [[[0, 0], [1, 1]],[[0, 0], [1, 1]]] + } + } + })")); + + assertRepresentativeGeoShapeIsStable(fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "LineString", + "coordinates": [ + [2, 0], + [2, 2] + ] + } + } + })"), + fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "LineString", + "coordinates": [[0, 0], [1, 1]] + } + } + })")); + + assertRepresentativeGeoShapeIsStable(fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "GeometryCollection", + "geometries": [{ + "type": "LineString", + "coordinates": [ + [2, 0], + [2, 2] + ] + }, { + type: 'Point', coordinates: [2, 2] + }] + } + } + })"), + fromjson(R"({ + "$geoIntersects": { + "$geometry": { + "type": "GeometryCollection", + "geometries": [{ + "type": "LineString", + "coordinates": [[0, 0], [1, 1]] + }, { + type: 'Point', coordinates: [1, 1] + }] + } + } + })")); +} + } // namespace mongo |
