1 // expression_geo_test.cpp
2 
3 
4 /**
5  *    Copyright (C) 2018-present MongoDB, Inc.
6  *
7  *    This program is free software: you can redistribute it and/or modify
8  *    it under the terms of the Server Side Public License, version 1,
9  *    as published by MongoDB, Inc.
10  *
11  *    This program is distributed in the hope that it will be useful,
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *    Server Side Public License for more details.
15  *
16  *    You should have received a copy of the Server Side Public License
17  *    along with this program. If not, see
18  *    <http://www.mongodb.com/licensing/server-side-public-license>.
19  *
20  *    As a special exception, the copyright holders give permission to link the
21  *    code of portions of this program with the OpenSSL library under certain
22  *    conditions as described in each individual source file and distribute
23  *    linked combinations including the program with the OpenSSL library. You
24  *    must comply with the Server Side Public License in all respects for
25  *    all of the code used other than as permitted herein. If you modify file(s)
26  *    with this exception, you may extend this exception to your version of the
27  *    file(s), but you are not obligated to do so. If you do not wish to do so,
28  *    delete this exception statement from your version. If you delete this
29  *    exception statement from all source files in the program, then also delete
30  *    it in the license file.
31  */
32 
33 /** Unit tests for MatchExpression operator implementations in match_operators.{h,cpp}. */
34 
35 #include "mongo/unittest/unittest.h"
36 
37 #include "mongo/db/jsobj.h"
38 #include "mongo/db/json.h"
39 #include "mongo/db/matcher/expression.h"
40 #include "mongo/db/matcher/expression_geo.h"
41 #include "mongo/stdx/memory.h"
42 
43 namespace mongo {
44 
TEST(ExpressionGeoTest,Geo1)45 TEST(ExpressionGeoTest, Geo1) {
46     BSONObj query = fromjson("{loc:{$within:{$box:[{x: 4, y:4},[6,6]]}}}");
47 
48     std::unique_ptr<GeoExpression> gq(new GeoExpression);
49     ASSERT_OK(gq->parseFrom(query["loc"].Obj()));
50 
51     GeoMatchExpression ge;
52     ASSERT(ge.init("a", gq.release(), query).isOK());
53 
54     ASSERT(!ge.matchesBSON(fromjson("{a: [3,4]}")));
55     ASSERT(ge.matchesBSON(fromjson("{a: [4,4]}")));
56     ASSERT(ge.matchesBSON(fromjson("{a: [5,5]}")));
57     ASSERT(ge.matchesBSON(fromjson("{a: [5,5.1]}")));
58     ASSERT(ge.matchesBSON(fromjson("{a: {x: 5, y:5.1}}")));
59 }
60 
TEST(ExpressionGeoTest,GeoNear1)61 TEST(ExpressionGeoTest, GeoNear1) {
62     BSONObj query = fromjson(
63         "{loc:{$near:{$maxDistance:100, "
64         "$geometry:{type:\"Point\", coordinates:[0,0]}}}}");
65     std::unique_ptr<GeoNearExpression> nq(new GeoNearExpression);
66     ASSERT_OK(nq->parseFrom(query["loc"].Obj()));
67 
68     GeoNearMatchExpression gne;
69     ASSERT(gne.init("a", nq.release(), query).isOK());
70 
71     // We can't match the data but we can make sure it was parsed OK.
72     ASSERT_EQUALS(gne.getData().centroid->crs, SPHERE);
73     ASSERT_EQUALS(gne.getData().minDistance, 0);
74     ASSERT_EQUALS(gne.getData().maxDistance, 100);
75 }
76 
makeGeoMatchExpression(const BSONObj & locQuery)77 std::unique_ptr<GeoMatchExpression> makeGeoMatchExpression(const BSONObj& locQuery) {
78     std::unique_ptr<GeoExpression> gq(new GeoExpression);
79     ASSERT_OK(gq->parseFrom(locQuery));
80 
81     std::unique_ptr<GeoMatchExpression> ge = stdx::make_unique<GeoMatchExpression>();
82     ASSERT_OK(ge->init("a", gq.release(), locQuery));
83 
84     return ge;
85 }
86 
makeGeoNearMatchExpression(const BSONObj & locQuery)87 std::unique_ptr<GeoNearMatchExpression> makeGeoNearMatchExpression(const BSONObj& locQuery) {
88     std::unique_ptr<GeoNearExpression> nq(new GeoNearExpression);
89     ASSERT_OK(nq->parseFrom(locQuery));
90 
91     std::unique_ptr<GeoNearMatchExpression> gne = stdx::make_unique<GeoNearMatchExpression>();
92     ASSERT_OK(gne->init("a", nq.release(), locQuery));
93 
94     return gne;
95 }
96 
97 
98 /**
99  * A bunch of cases in which a geo expression is equivalent() to both itself or to another
100  * expression.
101  */
TEST(ExpressionGeoTest,GeoEquivalent)102 TEST(ExpressionGeoTest, GeoEquivalent) {
103     {
104         BSONObj query = fromjson("{$within: {$box: [{x: 4, y: 4}, [6, 6]]}}");
105         std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
106         ASSERT(ge->equivalent(ge.get()));
107     }
108     {
109         BSONObj query = fromjson(
110             "{$within: {$geometry: {type: 'Polygon',"
111             "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]}}}");
112         std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
113         ASSERT(ge->equivalent(ge.get()));
114     }
115     {
116         BSONObj query1 = fromjson(
117                     "{$within: {$geometry: {type: 'Polygon',"
118                     "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]}}}"),
119                 query2 = fromjson(
120                     "{$within: {$geometry: {type: 'Polygon',"
121                     "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]}}}");
122         std::unique_ptr<GeoMatchExpression> ge1(makeGeoMatchExpression(query1)),
123             ge2(makeGeoMatchExpression(query2));
124         ASSERT(ge1->equivalent(ge2.get()));
125     }
126 }
127 
128 /**
129  * A bunch of cases in which a *geoNear* expression is equivalent both to itself or to
130  * another expression.
131  */
TEST(ExpressionGeoTest,GeoNearEquivalent)132 TEST(ExpressionGeoTest, GeoNearEquivalent) {
133     {
134         BSONObj query = fromjson(
135             "{$near: {$maxDistance: 100, "
136             "$geometry: {type: 'Point', coordinates: [0, 0]}}}");
137         std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
138         ASSERT(gne->equivalent(gne.get()));
139     }
140     {
141         BSONObj query = fromjson(
142             "{$near: {$minDistance: 10, $maxDistance: 100,"
143             "$geometry: {type: 'Point', coordinates: [0, 0]}}}");
144         std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
145         ASSERT(gne->equivalent(gne.get()));
146     }
147     {
148         BSONObj query1 = fromjson(
149                     "{$near: {$maxDistance: 100, "
150                     "$geometry: {type: 'Point', coordinates: [1, 0]}}}"),
151                 query2 = fromjson(
152                     "{$near: {$maxDistance: 100, "
153                     "$geometry: {type: 'Point', coordinates: [1, 0]}}}");
154         std::unique_ptr<GeoNearMatchExpression> gne1(makeGeoNearMatchExpression(query1)),
155             gne2(makeGeoNearMatchExpression(query2));
156         ASSERT(gne1->equivalent(gne2.get()));
157     }
158 }
159 
160 /**
161  * A geo expression being not equivalent to another expression.
162  */
TEST(ExpressionGeoTest,GeoNotEquivalent)163 TEST(ExpressionGeoTest, GeoNotEquivalent) {
164     BSONObj query1 = fromjson(
165                 "{$within: {$geometry: {type: 'Polygon',"
166                 "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]}}}"),
167             query2 = fromjson(
168                 "{$within: {$geometry: {type: 'Polygon',"
169                 "coordinates: [[[0, 0], [3, 6], [6, 2], [0, 0]]]}}}");
170     std::unique_ptr<GeoMatchExpression> ge1(makeGeoMatchExpression(query1)),
171         ge2(makeGeoMatchExpression(query2));
172     ASSERT(!ge1->equivalent(ge2.get()));
173 }
174 
175 /**
176  * A *geoNear* expression being not equivalent to another expression.
177  */
TEST(ExpressionGeoTest,GeoNearNotEquivalent)178 TEST(ExpressionGeoTest, GeoNearNotEquivalent) {
179     BSONObj query1 = fromjson(
180                 "{$near: {$maxDistance: 100, "
181                 "$geometry: {type: 'Point', coordinates: [0, 0]}}}"),
182             query2 = fromjson(
183                 "{$near: {$maxDistance: 100, "
184                 "$geometry: {type: 'Point', coordinates: [1, 0]}}}");
185     std::unique_ptr<GeoNearMatchExpression> gne1(makeGeoNearMatchExpression(query1)),
186         gne2(makeGeoNearMatchExpression(query2));
187     ASSERT(!gne1->equivalent(gne2.get()));
188 }
189 }
190