1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #include "mongo/db/update/push_sorter.h"
32 
33 #include "mongo/bson/mutable/algorithm.h"
34 #include "mongo/bson/mutable/document.h"
35 #include "mongo/bson/mutable/element.h"
36 #include "mongo/db/jsobj.h"
37 #include "mongo/db/json.h"
38 #include "mongo/db/query/collation/collator_interface.h"
39 #include "mongo/db/query/collation/collator_interface_mock.h"
40 #include "mongo/unittest/unittest.h"
41 
42 namespace mongo {
43 namespace {
44 
45 using mongo::mutablebson::Element;
46 using mongo::mutablebson::sortChildren;
47 
48 class ObjectArray : public mongo::unittest::Test {
49 public:
ObjectArray()50     ObjectArray() : _doc(), _size(0) {}
51 
setUp()52     virtual void setUp() {
53         Element arr = _doc.makeElementArray("x");
54         ASSERT_TRUE(arr.ok());
55         ASSERT_OK(_doc.root().pushBack(arr));
56     }
57 
addObj(BSONObj obj)58     void addObj(BSONObj obj) {
59         ASSERT_LESS_THAN_OR_EQUALS(_size, 3u);
60         _objs[_size] = obj;
61         _size++;
62 
63         ASSERT_OK(_doc.root()["x"].appendObject(mongo::StringData(), obj));
64     }
65 
getOrigObj(size_t i)66     BSONObj getOrigObj(size_t i) {
67         return _objs[i];
68     }
69 
getSortedObj(size_t i)70     BSONObj getSortedObj(size_t i) {
71         return getArray()[i].getValueObject();
72     }
73 
getArray()74     Element getArray() {
75         return _doc.root()["x"];
76     }
77 
78 private:
79     mutablebson::Document _doc;
80     BSONObj _objs[3];
81     size_t _size;
82 };
83 
TEST_F(ObjectArray,NormalOrder)84 TEST_F(ObjectArray, NormalOrder) {
85     const CollatorInterface* collator = nullptr;
86     addObj(fromjson("{b:1, a:1}"));
87     addObj(fromjson("{a:3, b:2}"));
88     addObj(fromjson("{b:3, a:2}"));
89 
90     sortChildren(getArray(), PatternElementCmp(fromjson("{'a':1,'b':1}"), collator));
91 
92     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(0));
93     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(2));
94     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(1));
95 }
96 
TEST_F(ObjectArray,MixedOrder)97 TEST_F(ObjectArray, MixedOrder) {
98     const CollatorInterface* collator = nullptr;
99     addObj(fromjson("{b:1, a:1}"));
100     addObj(fromjson("{a:3, b:2}"));
101     addObj(fromjson("{b:3, a:2}"));
102 
103     sortChildren(getArray(), PatternElementCmp(fromjson("{b:1,a:-1}"), collator));
104 
105     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(0));
106     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(1));
107     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(2));
108 }
109 
TEST_F(ObjectArray,ExtraFields)110 TEST_F(ObjectArray, ExtraFields) {
111     const CollatorInterface* collator = nullptr;
112     addObj(fromjson("{b:1, c:2, a:1}"));
113     addObj(fromjson("{c:1, a:3, b:2}"));
114     addObj(fromjson("{b:3, a:2}"));
115 
116     sortChildren(getArray(), PatternElementCmp(fromjson("{a:1,b:1}"), collator));
117 
118     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(0));
119     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(2));
120     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(1));
121 }
122 
TEST_F(ObjectArray,MissingFields)123 TEST_F(ObjectArray, MissingFields) {
124     const CollatorInterface* collator = nullptr;
125     addObj(fromjson("{a:2, b:2}"));
126     addObj(fromjson("{a:1}"));
127     addObj(fromjson("{a:3, b:3, c:3}"));
128 
129     sortChildren(getArray(), PatternElementCmp(fromjson("{b:1,c:1}"), collator));
130 
131     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(1));
132     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(0));
133     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(2));
134 }
135 
TEST_F(ObjectArray,NestedFields)136 TEST_F(ObjectArray, NestedFields) {
137     const CollatorInterface* collator = nullptr;
138     addObj(fromjson("{a:{b:{c:2, d:0}}}"));
139     addObj(fromjson("{a:{b:{c:1, d:2}}}"));
140     addObj(fromjson("{a:{b:{c:3, d:1}}}"));
141 
142     sortChildren(getArray(), PatternElementCmp(fromjson("{'a.b':1}"), collator));
143 
144     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(1));
145     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(0));
146     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(2));
147 }
148 
TEST_F(ObjectArray,SimpleNestedFields)149 TEST_F(ObjectArray, SimpleNestedFields) {
150     const CollatorInterface* collator = nullptr;
151     addObj(fromjson("{a:{b: -1}}"));
152     addObj(fromjson("{a:{b: -100}}"));
153     addObj(fromjson("{a:{b: 34}}"));
154 
155     sortChildren(getArray(), PatternElementCmp(fromjson("{'a.b':1}"), collator));
156 
157     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(1));
158     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(0));
159     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(2));
160 }
161 
TEST_F(ObjectArray,NestedInnerObjectDescending)162 TEST_F(ObjectArray, NestedInnerObjectDescending) {
163     const CollatorInterface* collator = nullptr;
164     addObj(fromjson("{a:{b:{c:2, d:0}}}"));
165     addObj(fromjson("{a:{b:{c:1, d:2}}}"));
166     addObj(fromjson("{a:{b:{c:3, d:1}}}"));
167 
168     sortChildren(getArray(), PatternElementCmp(fromjson("{'a.b.d':-1}"), collator));
169 
170     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(2));
171     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(0));
172     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(1));
173 }
174 
TEST_F(ObjectArray,NestedInnerObjectAscending)175 TEST_F(ObjectArray, NestedInnerObjectAscending) {
176     const CollatorInterface* collator = nullptr;
177     addObj(fromjson("{a:{b:{c:2, d:0}}}"));
178     addObj(fromjson("{a:{b:{c:1, d:2}}}"));
179     addObj(fromjson("{a:{b:{c:3, d:1}}}"));
180 
181     sortChildren(getArray(), PatternElementCmp(fromjson("{'a.b.d':1}"), collator));
182 
183     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(0));
184     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(1));
185     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(2));
186 }
187 
TEST_F(ObjectArray,SortRespectsCollation)188 TEST_F(ObjectArray, SortRespectsCollation) {
189     CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
190     addObj(fromjson("{a: 'abg'}"));
191     addObj(fromjson("{a: 'aca'}"));
192     addObj(fromjson("{a: 'adc'}"));
193 
194     sortChildren(getArray(), PatternElementCmp(fromjson("{a: 1}"), &collator));
195 
196     ASSERT_BSONOBJ_EQ(getOrigObj(0), getSortedObj(2));
197     ASSERT_BSONOBJ_EQ(getOrigObj(1), getSortedObj(0));
198     ASSERT_BSONOBJ_EQ(getOrigObj(2), getSortedObj(1));
199 }
200 
201 }  // namespace
202 }  // namespace mongo
203