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