diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
| commit | 959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch) | |
| tree | acc8d60aedb12b70048e676e8a7349deb0010db8 /src/mongo/db/query/query_shape/find_cmd_shape_test.cpp | |
| parent | 76588293975fc059cf076779e4283e6ffaf8afff (diff) | |
New upstream version 6.0.20upstream
Diffstat (limited to 'src/mongo/db/query/query_shape/find_cmd_shape_test.cpp')
| -rw-r--r-- | src/mongo/db/query/query_shape/find_cmd_shape_test.cpp | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/mongo/db/query/query_shape/find_cmd_shape_test.cpp b/src/mongo/db/query/query_shape/find_cmd_shape_test.cpp new file mode 100644 index 00000000000..0d839a5d3d2 --- /dev/null +++ b/src/mongo/db/query/query_shape/find_cmd_shape_test.cpp @@ -0,0 +1,238 @@ +/** + * Copyright (C) 2023-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/pipeline/expression_context_for_test.h" +#include "mongo/db/query/query_shape/find_cmd_shape.h" +#include "mongo/db/service_context_test_fixture.h" +#include "mongo/unittest/unittest.h" + +namespace mongo::query_shape { + +namespace { +/** + * TODO this was stolen from another test. Time for a library? + * Simplistic redaction strategy for testing which appends the field name to the prefix "REDACT_". + */ +std::string applyHmacForTest(StringData sd) { + return "REDACT_" + sd.toString(); +} + +static const NamespaceStringOrUUID kDefaultTestNss = + NamespaceStringOrUUID{NamespaceString("testDB.testColl")}; + +struct RequestOptions { + OptionalBool singleBatch = {}; + OptionalBool allowDiskUse = {}; + OptionalBool returnKey = {}; + OptionalBool showRecordId = {}; + OptionalBool tailable = {}; + OptionalBool awaitData = {}; + OptionalBool limit = {}; + OptionalBool skip = {}; +}; +class FindCmdShapeTest : public ServiceContextTest { +public: + void setUp() final { + _expCtx = make_intrusive<ExpressionContextForTest>(); + } + + std::unique_ptr<FindCmdShape> makeShapeFromSort(StringData sortJson) { + auto fcr = std::make_unique<FindCommandRequest>(kDefaultTestNss); + fcr->setSort(fromjson(sortJson.rawData())); + auto&& parsedRequest = + uassertStatusOK(::mongo::parsed_find_command::parse(_expCtx, std::move(fcr))); + return std::make_unique<FindCmdShape>(*parsedRequest, _expCtx); + } + + BSONObj sortShape(StringData sortJson) { + auto shape = makeShapeFromSort(sortJson); + return shape->components.sort; + } + + /** + * Returns the shape of the input sort, or boost::none if the input shape was a natural sort + * which got converted into a hint. + */ + boost::optional<BSONObj> maybeRedactedSortShape(StringData sortJson) { + auto shape = makeShapeFromSort(sortJson); + SerializationOptions opts = SerializationOptions::kDebugQueryShapeSerializeOptions; + opts.transformIdentifiers = true; + opts.transformIdentifiersCallback = applyHmacForTest; + auto shapeBson = shape->toBson(_expCtx->opCtx, opts); + if (auto sortElem = shapeBson["sort"]; !sortElem.eoo()) { + return sortElem.Obj().getOwned(); + } + return boost::none; + } + + BSONObj redactedSortShape(StringData sortJson) { + return *maybeRedactedSortShape(sortJson); + } + + boost::intrusive_ptr<ExpressionContext> _expCtx; + + std::unique_ptr<FindCmdShapeComponents> makeShapeComponentsFromFilter( + BSONObj filter, const RequestOptions& requestOptions = {}) { + auto fcr = std::make_unique<FindCommandRequest>(kDefaultTestNss); + fcr->setFilter(filter.getOwned()); + fcr->setSingleBatch(requestOptions.singleBatch); + fcr->setAllowDiskUse(requestOptions.allowDiskUse); + fcr->setReturnKey(requestOptions.returnKey); + fcr->setAllowDiskUse(requestOptions.showRecordId); + fcr->setTailable(requestOptions.tailable); + fcr->setAwaitData(requestOptions.awaitData); + auto parsedFind = uassertStatusOK(parsed_find_command::parse(_expCtx, {std::move(fcr)})); + return std::make_unique<FindCmdShapeComponents>(*parsedFind, _expCtx); + } + + std::unique_ptr<FindCmdShape> makeShapeFromFilter(const BSONObj& filter) { + auto fcr = std::make_unique<FindCommandRequest>(kDefaultTestNss); + fcr->setFilter(filter.getOwned()); + auto parsedFind = uassertStatusOK(parsed_find_command::parse(_expCtx, {std::move(fcr)})); + return std::make_unique<FindCmdShape>(*parsedFind, _expCtx); + } +}; + +TEST_F(FindCmdShapeTest, NormalSortPattern) { + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"a.b.c":1,"foo":-1})", + sortShape(R"({"a.b.c": 1, "foo": -1})")); +} + +TEST_F(FindCmdShapeTest, NaturalSortPattern) { + // $natural sorts are interpreted as a hint. Hints are not part of the shape (but should show up + // in the query stats key). + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({})", + sortShape(R"({$natural: 1})")); + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({})", + sortShape(R"({$natural: -1})")); +} + +TEST_F(FindCmdShapeTest, NaturalSortPatternWithMeta) { + ASSERT_THROWS_CODE( + sortShape(R"({$natural: 1, x: {$meta: "textScore"}})"), DBException, ErrorCodes::BadValue); +} + +TEST_F(FindCmdShapeTest, MetaPatternWithoutNatural) { + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"normal":1,"$computed1":{"$meta":"textScore"}})", + sortShape(R"({normal: 1, x: {$meta: "textScore"}})")); +} + +// Here we have one test to ensure that the redaction policy is accepted and applied in the +// query_shape utility, but there are more extensive redaction tests in sort_pattern_test.cpp +TEST_F(FindCmdShapeTest, RespectsRedactionPolicy) { + ASSERT_BSONOBJ_EQ_AUTO( // NOLINT + R"({"REDACT_normal":1,"REDACT_y":1})", + redactedSortShape(R"({normal: 1, y: 1})")); + + // No need to redact $natural. Again, this will be interpreted as a hint, but this test is + // interesting to ensure the $-prefix of $natural doesn't confuse us. + ASSERT(!maybeRedactedSortShape(R"({$natural: 1})")); +} + +TEST_F(FindCmdShapeTest, SizeOfShapeComponents) { + auto query = BSON("query" << 1 << "xEquals" << 42); + auto findCmdComponent = makeShapeComponentsFromFilter(query.getOwned()); + const auto querySize = findCmdComponent->filter.objsize(); + + const auto minimumSize = sizeof(FindCmdShapeComponents) + querySize; + ASSERT_GT(findCmdComponent->size(), minimumSize); + ASSERT_LTE(findCmdComponent->size(), + minimumSize + static_cast<size_t>(4 * BSONObj().objsize())); +} + +TEST_F(FindCmdShapeTest, EquivalentShapeComponentsSizes) { + auto query = BSON("query" << 1 << "xEquals" << 42); + // Tailable can not be set together with 'singleBatch' option. + auto mostlyTrueComponent = makeShapeComponentsFromFilter(query.getOwned(), + {/* singleBatch = */ false, + /* allowDiskUse = */ true, + /* returnKey = */ true, + /* showRecordId = */ true, + /* tailable = */ true, + /* awaitData = */ true, + /* limit = */ true, + /* skip = */ true}); + + auto mostlyFalseComponent = makeShapeComponentsFromFilter(query.getOwned(), + {/* singleBatch = */ false, + /* allowDiskUse = */ false, + /* returnKey = */ false, + /* showRecordId = */ false, + /* tailable = */ true, + /* awaitData = */ false, + /* limit = */ false, + /* skip = */ false}); + + ASSERT_EQ(mostlyTrueComponent->size(), mostlyFalseComponent->size()); +} + +TEST_F(FindCmdShapeTest, DifferentShapeComponentsSizes) { + auto smallQuery = BSON("query" << BSONObj()); + auto smallFindCmdComponent = makeShapeComponentsFromFilter(smallQuery.getOwned()); + + auto largeQuery = BSON("query" << 1 << "xEquals" << 42); + auto largeFindCmdComponent = makeShapeComponentsFromFilter(largeQuery.getOwned()); + + ASSERT_LT(smallQuery.objsize(), largeQuery.objsize()); + ASSERT_LT(smallFindCmdComponent->size(), largeFindCmdComponent->size()); +} + +TEST_F(FindCmdShapeTest, SizeOfShapeWithAndWithoutLet) { + auto filter = BSON("query" << 1 << "xEquals" << 42); + auto shapeWithoutLet = makeShapeFromFilter(filter.getOwned()); + + auto fcr = std::make_unique<FindCommandRequest>(kDefaultTestNss); + fcr->setFilter(filter.getOwned()); + fcr->setLet(fromjson(R"({x: 4})")); + auto parsedFind = uassertStatusOK(parsed_find_command::parse(_expCtx, {std::move(fcr)})); + auto shapeWithLet = std::make_unique<FindCmdShape>(*parsedFind, _expCtx); + + ASSERT_LT(shapeWithoutLet->size(), shapeWithLet->size()); +} + +TEST_F(FindCmdShapeTest, SizeOfShapeWithAndWithoutCollation) { + auto filter = BSON("query" << 1 << "xEquals" << 42); + auto shapeWithoutCollation = makeShapeFromFilter(filter.getOwned()); + + auto fcr = std::make_unique<FindCommandRequest>(kDefaultTestNss); + fcr->setFilter(filter.getOwned()); + fcr->setCollation(fromjson(R"({locale: "en_US"})")); + auto parsedFind = uassertStatusOK(parsed_find_command::parse(_expCtx, {std::move(fcr)})); + auto shapeWithCollation = std::make_unique<FindCmdShape>(*parsedFind, _expCtx); + + ASSERT_LT(shapeWithoutCollation->size(), shapeWithCollation->size()); +} + +} // namespace + +} // namespace mongo::query_shape |
