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/platform/basic.h"
32 
33 #include "mongo/db/index/sort_key_generator.h"
34 
35 #include "mongo/db/query/collation/collator_interface_mock.h"
36 #include "mongo/stdx/memory.h"
37 #include "mongo/unittest/death_test.h"
38 #include "mongo/unittest/unittest.h"
39 
40 namespace mongo {
41 namespace {
42 
TEST(SortKeyGeneratorTest,ExtractNumberKeyForNonCompoundSortNonNested)43 TEST(SortKeyGeneratorTest, ExtractNumberKeyForNonCompoundSortNonNested) {
44     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), nullptr);
45     auto sortKey = sortKeyGen->getSortKey(fromjson("{_id: 0, a: 5}"), nullptr);
46     ASSERT_OK(sortKey.getStatus());
47     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 5));
48 }
49 
TEST(SortKeyGeneratorTest,ExtractNumberKeyFromDocWithSeveralFields)50 TEST(SortKeyGeneratorTest, ExtractNumberKeyFromDocWithSeveralFields) {
51     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), nullptr);
52     auto sortKey = sortKeyGen->getSortKey(fromjson("{_id: 0, z: 10, a: 6, b: 16}"), nullptr);
53     ASSERT_OK(sortKey.getStatus());
54     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 6));
55 }
56 
TEST(SortKeyGeneratorTest,ExtractStringKeyNonCompoundNonNested)57 TEST(SortKeyGeneratorTest, ExtractStringKeyNonCompoundNonNested) {
58     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), nullptr);
59     auto sortKey =
60         sortKeyGen->getSortKey(fromjson("{_id: 0, z: 'thing1', a: 'thing2', b: 16}"), nullptr);
61     ASSERT_OK(sortKey.getStatus());
62     ASSERT_BSONOBJ_EQ(sortKey.getValue(),
63                       BSON(""
64                            << "thing2"));
65 }
66 
TEST(SortKeyGeneratorTest,CompoundSortPattern)67 TEST(SortKeyGeneratorTest, CompoundSortPattern) {
68     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1 << "b" << 1), nullptr);
69     auto sortKey =
70         sortKeyGen->getSortKey(fromjson("{_id: 0, z: 'thing1', a: 99, c: {a: 4}, b: 16}"), nullptr);
71     ASSERT_OK(sortKey.getStatus());
72     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 99 << "" << 16));
73 }
74 
TEST(SortKeyGeneratorTest,CompoundSortPatternWithDottedPath)75 TEST(SortKeyGeneratorTest, CompoundSortPatternWithDottedPath) {
76     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("c.a" << 1 << "b" << 1), nullptr);
77     auto sortKey =
78         sortKeyGen->getSortKey(fromjson("{_id: 0, z: 'thing1', a: 99, c: {a: 4}, b: 16}"), nullptr);
79     ASSERT_OK(sortKey.getStatus());
80     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 4 << "" << 16));
81 }
82 
TEST(SortKeyGeneratorTest,CompoundPatternLeadingFieldIsArray)83 TEST(SortKeyGeneratorTest, CompoundPatternLeadingFieldIsArray) {
84     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("c" << 1 << "b" << 1), nullptr);
85     auto sortKey = sortKeyGen->getSortKey(
86         fromjson("{_id: 0, z: 'thing1', a: 99, c: [2, 4, 1], b: 16}"), nullptr);
87     ASSERT_OK(sortKey.getStatus());
88     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 1 << "" << 16));
89 }
90 
TEST(SortKeyGeneratorTest,ExtractStringSortKeyWithCollatorUsesComparisonKey)91 TEST(SortKeyGeneratorTest, ExtractStringSortKeyWithCollatorUsesComparisonKey) {
92     CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
93     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), &collator);
94     auto sortKey =
95         sortKeyGen->getSortKey(fromjson("{_id: 0, z: 'thing1', a: 'thing2', b: 16}"), nullptr);
96     ASSERT_OK(sortKey.getStatus());
97     ASSERT_BSONOBJ_EQ(sortKey.getValue(),
98                       BSON(""
99                            << "2gniht"));
100 }
101 
TEST(SortKeyGeneratorTest,CollatorHasNoEffectWhenExtractingNonStringSortKey)102 TEST(SortKeyGeneratorTest, CollatorHasNoEffectWhenExtractingNonStringSortKey) {
103     CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
104     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), &collator);
105     auto sortKey = sortKeyGen->getSortKey(fromjson("{_id: 0, z: 10, a: 6, b: 16}"), nullptr);
106     ASSERT_OK(sortKey.getStatus());
107     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 6));
108 }
109 
TEST(SortKeyGeneratorTest,SortKeyGenerationForArraysChoosesCorrectKey)110 TEST(SortKeyGeneratorTest, SortKeyGenerationForArraysChoosesCorrectKey) {
111     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << -1), nullptr);
112     auto sortKey = sortKeyGen->getSortKey(fromjson("{_id: 0, a: [1, 2, 3, 4]}"), nullptr);
113     ASSERT_OK(sortKey.getStatus());
114     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 4));
115 }
116 
TEST(SortKeyGeneratorTest,EnsureSortKeyGenerationForArraysRespectsCollation)117 TEST(SortKeyGeneratorTest, EnsureSortKeyGenerationForArraysRespectsCollation) {
118     CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
119     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1), &collator);
120     auto sortKey =
121         sortKeyGen->getSortKey(fromjson("{_id: 0, a: ['aaz', 'zza', 'yya', 'zzb']}"), nullptr);
122     ASSERT_OK(sortKey.getStatus());
123     ASSERT_BSONOBJ_EQ(sortKey.getValue(),
124                       BSON(""
125                            << "ayy"));
126 }
127 
TEST(SortKeyGeneratorTest,SortKeyGenerationForArraysRespectsCompoundOrdering)128 TEST(SortKeyGeneratorTest, SortKeyGenerationForArraysRespectsCompoundOrdering) {
129     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a.b" << 1 << "a.c" << -1), nullptr);
130     auto sortKey = sortKeyGen->getSortKey(
131         fromjson("{_id: 0, a: [{b: 1, c: 0}, {b: 0, c: 3}, {b: 0, c: 1}]}"), nullptr);
132     ASSERT_OK(sortKey.getStatus());
133     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 0 << "" << 3));
134 }
135 
TEST(SortKeyGeneratorTest,SortKeyGenerationForMissingField)136 TEST(SortKeyGeneratorTest, SortKeyGenerationForMissingField) {
137     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("b" << 1), nullptr);
138     auto sortKey = sortKeyGen->getSortKey(BSON("a" << 1), nullptr);
139     ASSERT_OK(sortKey.getStatus());
140     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << BSONNULL));
141 }
142 
TEST(SortKeyGeneratorTest,SortKeyGenerationForMissingFieldInCompoundSortPattern)143 TEST(SortKeyGeneratorTest, SortKeyGenerationForMissingFieldInCompoundSortPattern) {
144     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << 1 << "b" << 1), nullptr);
145     auto sortKey = sortKeyGen->getSortKey(BSON("b" << 1), nullptr);
146     ASSERT_OK(sortKey.getStatus());
147     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << BSONNULL << "" << 1));
148 }
149 
TEST(SortKeyGeneratorTest,SortKeyGenerationForMissingFieldInEmbeddedDocument)150 TEST(SortKeyGeneratorTest, SortKeyGenerationForMissingFieldInEmbeddedDocument) {
151     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a.b" << 1), nullptr);
152     auto sortKey = sortKeyGen->getSortKey(BSON("a" << BSON("c" << 1)), nullptr);
153     ASSERT_OK(sortKey.getStatus());
154     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << BSONNULL));
155 }
156 
TEST(SortKeyGeneratorTest,SortKeyGenerationForMissingFieldInArrayElement)157 TEST(SortKeyGeneratorTest, SortKeyGenerationForMissingFieldInArrayElement) {
158     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a.b" << 1), nullptr);
159     auto sortKey = sortKeyGen->getSortKey(BSON("a" << BSON_ARRAY(BSONObj{})), nullptr);
160     ASSERT_OK(sortKey.getStatus());
161     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << BSONNULL));
162 }
163 
TEST(SortKeyGeneratorTest,SortKeyGenerationForInvalidPath)164 TEST(SortKeyGeneratorTest, SortKeyGenerationForInvalidPath) {
165     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a.b" << 1), nullptr);
166     auto sortKey = sortKeyGen->getSortKey(BSON("a" << 1), nullptr);
167     ASSERT_OK(sortKey.getStatus());
168     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << BSONNULL));
169 }
170 
171 DEATH_TEST(SortKeyGeneratorTest,
172            SortPatternComponentWithStringIsFatal,
173            "Invariant failure elt.type() == BSONType::Object") {
174     stdx::make_unique<SortKeyGenerator>(BSON("a"
175                                              << "foo"),
176                                         nullptr);
177 }
178 
179 DEATH_TEST(SortKeyGeneratorTest,
180            SortPatternComponentWhoseObjectHasMultipleKeysIsFatal,
181            "Invariant failure elt.embeddedObject().nFields() == 1") {
182     stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
183                                                          << "textScore"
184                                                          << "extra"
185                                                          << 1)),
186                                         nullptr);
187 }
188 
189 DEATH_TEST(SortKeyGeneratorTest,
190            SortPatternComponentWithNonMetaObjectSortIsFatal,
191            "Invariant failure metaElem.fieldNameStringData() == \"$meta\"_sd") {
192     stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$unknown"
193                                                          << "textScore")),
194                                         nullptr);
195 }
196 
197 DEATH_TEST(SortKeyGeneratorTest,
198            SortPatternComponentWithUnknownMetaKeywordIsFatal,
199            "Invariant failure metaElem.valueStringData() == \"randVal\"_sd") {
200     stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
201                                                          << "unknown")),
202                                         nullptr);
203 }
204 
205 DEATH_TEST(SortKeyGeneratorTest,
206            NoMetadataWhenPatternHasMetaTextScoreIsFatal,
207            "Invariant failure metadata") {
208     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
209                                                                            << "textScore")),
210                                                           nullptr);
211     uassertStatusOK(sortKeyGen->getSortKey(BSONObj{}, nullptr).getStatus());
212 }
213 
214 DEATH_TEST(SortKeyGeneratorTest,
215            NoMetadataWhenPatternHasMetaRandValIsFatal,
216            "Invariant failure metadata") {
217     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
218                                                                            << "randVal")),
219                                                           nullptr);
220     uassertStatusOK(sortKeyGen->getSortKey(BSONObj{}, nullptr).getStatus());
221 }
222 
TEST(SortKeyGeneratorTest,CanGenerateKeysForTextScoreMetaSort)223 TEST(SortKeyGeneratorTest, CanGenerateKeysForTextScoreMetaSort) {
224     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
225                                                                            << "textScore")),
226                                                           nullptr);
227     SortKeyGenerator::Metadata metadata;
228     metadata.textScore = 1.5;
229     auto sortKey = sortKeyGen->getSortKey(BSONObj{}, &metadata);
230     ASSERT_OK(sortKey.getStatus());
231     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 1.5));
232 }
233 
TEST(SortKeyGeneratorTest,CanGenerateKeysForRandValMetaSort)234 TEST(SortKeyGeneratorTest, CanGenerateKeysForRandValMetaSort) {
235     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(BSON("a" << BSON("$meta"
236                                                                            << "randVal")),
237                                                           nullptr);
238     SortKeyGenerator::Metadata metadata;
239     metadata.randVal = 0.3;
240     auto sortKey = sortKeyGen->getSortKey(BSONObj{}, &metadata);
241     ASSERT_OK(sortKey.getStatus());
242     ASSERT_BSONOBJ_EQ(sortKey.getValue(), BSON("" << 0.3));
243 }
244 
TEST(SortKeyGeneratorTest,CanGenerateKeysForCompoundMetaSort)245 TEST(SortKeyGeneratorTest, CanGenerateKeysForCompoundMetaSort) {
246     BSONObj pattern = fromjson(
247         "{a: 1, b: {$meta: 'randVal'}, c: {$meta: 'textScore'}, d: -1, e: {$meta: 'textScore'}}");
248     auto sortKeyGen = stdx::make_unique<SortKeyGenerator>(pattern, nullptr);
249     SortKeyGenerator::Metadata metadata;
250     metadata.randVal = 0.3;
251     metadata.textScore = 1.5;
252     auto sortKey = sortKeyGen->getSortKey(BSON("a" << 4 << "d" << 5), &metadata);
253     ASSERT_OK(sortKey.getStatus());
254     ASSERT_BSONOBJ_EQ(sortKey.getValue(),
255                       BSON("" << 4 << "" << 0.3 << "" << 1.5 << "" << 5 << "" << 1.5));
256 }
257 
258 }  // namespace
259 }  // namespace mongo
260