diff options
Diffstat (limited to 'src/mongo/db/exec/projection_exec_test.cpp')
-rw-r--r-- | src/mongo/db/exec/projection_exec_test.cpp | 310 |
1 files changed, 0 insertions, 310 deletions
diff --git a/src/mongo/db/exec/projection_exec_test.cpp b/src/mongo/db/exec/projection_exec_test.cpp deleted file mode 100644 index f15094fd3aa..00000000000 --- a/src/mongo/db/exec/projection_exec_test.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/** - * Copyright (C) 2018-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. - */ - -/** - * This file contains tests for mongo/db/exec/projection_exec.cpp - */ - -#include "boost/optional.hpp" -#include "boost/optional/optional_io.hpp" -#include <memory> - -#include "mongo/db/exec/projection_exec.h" - -#include "mongo/db/json.h" -#include "mongo/db/matcher/expression_parser.h" -#include "mongo/db/pipeline/expression_context_for_test.h" -#include "mongo/db/query/collation/collator_interface_mock.h" -#include "mongo/stdx/variant.h" -#include "mongo/unittest/unittest.h" - -using namespace mongo; -using namespace std::string_literals; - -namespace { - -/** - * Utility function to create a MatchExpression. - */ -std::unique_ptr<MatchExpression> parseMatchExpression(const BSONObj& obj) { - boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - StatusWithMatchExpression status = MatchExpressionParser::parse(obj, std::move(expCtx)); - ASSERT_TRUE(status.isOK()); - return std::move(status.getValue()); -} - -/** - * Test encapsulation for single call to ProjectionExec::project() or - * ProjectionExec::projectCovered(). - */ -boost::optional<std::string> project( - const char* specStr, - const char* queryStr, - const stdx::variant<const char*, const IndexKeyDatum> objStrOrDatum, - const boost::optional<const CollatorInterface&> collator = boost::none, - const BSONObj& sortKey = BSONObj(), - const double textScore = 0.0) { - // Create projection exec object. - BSONObj spec = fromjson(specStr); - BSONObj query = fromjson(queryStr); - std::unique_ptr<MatchExpression> queryExpression = parseMatchExpression(query); - QueryTestServiceContext serviceCtx; - auto opCtx = serviceCtx.makeOperationContext(); - ProjectionExec exec(opCtx.get(), spec, queryExpression.get(), collator.get_ptr()); - - auto objStr = stdx::get_if<const char*>(&objStrOrDatum); - auto projected = objStr - ? exec.project(fromjson(*objStr), boost::none, Value{}, sortKey, textScore) - : exec.projectCovered({stdx::get<const IndexKeyDatum>(objStrOrDatum)}, - boost::none, - Value{}, - sortKey, - textScore); - - if (!projected.isOK()) - return boost::none; - else - return boost::make_optional(projected.getValue().toString()); -} - -// -// position $ -// - -TEST(ProjectionExecTest, TransformPositionalDollar) { - // Valid position $ projections. - ASSERT_EQ(boost::make_optional("{ a: [ 10 ] }"s), - project("{'a.$': 1}", "{a: 10}", "{a: [10, 20, 30]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 20 ] }"s), - project("{'a.$': 1}", "{a: 20}", "{a: [10, 20, 30]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 5 ] }"s), - project("{'a.$': 1}", "{a: {$gt: 4}}", "{a: [5]}")); - - // Invalid position $ projections. - ASSERT_EQ(boost::none, project("{'a.$': 1}", "{a: {$size: 1}}", "{a: [5]}")); -} - -// -// $elemMatch -// - -TEST(ProjectionExecTest, TransformElemMatch) { - const char* s = "{a: [{x: 1, y: 10}, {x: 1, y: 20}, {x: 2, y: 10}]}"; - - // Valid $elemMatch projections. - ASSERT_EQ(boost::make_optional("{ a: [ { x: 1, y: 10 } ] }"s), - project("{a: {$elemMatch: {x: 1}}}", "{}", s)); - ASSERT_EQ(boost::make_optional("{ a: [ { x: 1, y: 20 } ] }"s), - project("{a: {$elemMatch: {x: 1, y: 20}}}", "{}", s)); - ASSERT_EQ(boost::make_optional("{ a: [ { x: 2, y: 10 } ] }"s), - project("{a: {$elemMatch: {x: 2}}}", "{}", s)); - ASSERT_EQ(boost::make_optional("{}"s), project("{a: {$elemMatch: {x: 3}}}", "{}", s)); - - // $elemMatch on unknown field z - ASSERT_EQ(boost::make_optional("{}"s), project("{a: {$elemMatch: {z: 1}}}", "{}", s)); -} - -TEST(ProjectionExecTest, ElemMatchProjectionRespectsCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); - ASSERT_EQ(boost::make_optional("{ a: [ \"zdd\" ] }"s), - project("{a: {$elemMatch: {$gte: 'abc'}}}", - "{}", - "{a: ['zaa', 'zbb', 'zdd', 'zee']}", - collator)); -} - -// -// $slice -// - -TEST(ProjectionExecTest, TransformSliceCount) { - // Valid $slice projections using format {$slice: count}. - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: -10}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: -3}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 8 ] }"s), - project("{a: {$slice: -1}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [] }"s), - project("{a: {$slice: 0}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4 ] }"s), - project("{a: {$slice: 1}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: 3}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: 10}}", "{}", "{a: [4, 6, 8]}")); -} - -TEST(ProjectionExecTest, TransformSliceSkipLimit) { - // Valid $slice projections using format {$slice: [skip, limit]}. - // Non-positive limits are rejected at the query parser and therefore not handled by - // the projection execution stage. In fact, it will abort on an invalid limit. - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: [-10, 10]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6, 8 ] }"s), - project("{a: {$slice: [-3, 5]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 8 ] }"s), - project("{a: {$slice: [-1, 1]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4, 6 ] }"s), - project("{a: {$slice: [0, 2]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 4 ] }"s), - project("{a: {$slice: [0, 1]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [ 6 ] }"s), - project("{a: {$slice: [1, 1]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [] }"s), - project("{a: {$slice: [3, 5]}}", "{}", "{a: [4, 6, 8]}")); - ASSERT_EQ(boost::make_optional("{ a: [] }"s), - project("{a: {$slice: [10, 10]}}", "{}", "{a: [4, 6, 8]}")); -} - -// -// Dotted projections. -// - -TEST(ProjectionExecTest, TransformCoveredDottedProjection) { - BSONObj keyPattern = fromjson("{a: 1, 'b.c': 1, 'b.d': 1, 'b.f.g': 1, 'b.f.h': 1}"); - BSONObj keyData = fromjson("{'': 1, '': 2, '': 3, '': 4, '': 5}"); - ASSERT_EQ(boost::make_optional("{ b: { c: 2, d: 3, f: { g: 4, h: 5 } } }"s), - project("{'b.c': 1, 'b.d': 1, 'b.f.g': 1, 'b.f.h': 1}", - "{}", - IndexKeyDatum(keyPattern, keyData, 0, SnapshotId{}))); -} - -TEST(ProjectionExecTest, TransformNonCoveredDottedProjection) { - ASSERT_EQ(boost::make_optional("{ b: { c: 2, d: 3, f: { g: 4, h: 5 } } }"s), - project("{'b.c': 1, 'b.d': 1, 'b.f.g': 1, 'b.f.h': 1}", - "{}", - "{a: 1, b: {c: 2, d: 3, f: {g: 4, h: 5}}}")); -} - -// -// $meta -// $meta projections add computed values to the projected object. -// - -TEST(ProjectionExecTest, TransformMetaTextScore) { - // Query {} is ignored. - ASSERT_EQ(boost::make_optional("{ a: \"hello\", b: 100.0 }"s), - project("{b: {$meta: 'textScore'}}", - "{}", - "{a: 'hello'}", - boost::none, // collator - BSONObj(), // sortKey - 100.0)); // textScore - // Projected meta field should overwrite existing field. - ASSERT_EQ(boost::make_optional("{ a: \"hello\", b: 100.0 }"s), - project("{b: {$meta: 'textScore'}}", - "{}", - "{a: 'hello', b: -1}", - boost::none, // collator - BSONObj(), // sortKey - 100.0)); // textScore -} - -TEST(ProjectionExecTest, TransformMetaSortKey) { - // Query {} is ignored. - ASSERT_EQ(boost::make_optional("{ a: \"hello\", b: { : 99 } }"s), - project("{b: {$meta: 'sortKey'}}", - "{}", - "{a: 'hello'}", - boost::none, // collator - BSON("" << 99))); // sortKey - - // Projected meta field should overwrite existing field. - ASSERT_EQ(boost::make_optional("{ a: { : 99 } }"s), - project("{a: {$meta: 'sortKey'}}", - "{}", - "{a: 'hello'}", - boost::none, // collator - BSON("" << 99))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredNormal) { - ASSERT_EQ(boost::make_optional("{ a: 5, b: { : 5 } }"s), - project("{_id: 0, a: 1, b: {$meta: 'sortKey'}}", - "{}", - IndexKeyDatum(BSON("a" << 1), BSON("" << 5), 0, SnapshotId{}), - boost::none, // collator - BSON("" << 5))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredOverwrite) { - ASSERT_EQ(boost::make_optional("{ a: { : 5 } }"s), - project("{_id: 0, a: 1, a: {$meta: 'sortKey'}}", - "{}", - IndexKeyDatum(BSON("a" << 1), BSON("" << 5), 0, SnapshotId{}), - boost::none, // collator - BSON("" << 5))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredAdditionalData) { - ASSERT_EQ(boost::make_optional("{ a: 5, c: 6, b: { : 5 } }"s), - project("{_id: 0, a: 1, b: {$meta: 'sortKey'}, c: 1}", - "{}", - IndexKeyDatum( - BSON("a" << 1 << "c" << 1), BSON("" << 5 << "" << 6), 0, SnapshotId{}), - boost::none, // collator - BSON("" << 5))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredCompound) { - ASSERT_EQ(boost::make_optional("{ a: 5, b: { : 5, : 6 } }"s), - project("{_id: 0, a: 1, b: {$meta: 'sortKey'}}", - "{}", - IndexKeyDatum( - BSON("a" << 1 << "c" << 1), BSON("" << 5 << "" << 6), 0, SnapshotId{}), - boost::none, // collator - BSON("" << 5 << "" << 6))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredCompound2) { - ASSERT_EQ(boost::make_optional("{ a: 5, c: 4, b: { : 5, : 6 } }"s), - project("{_id: 0, a: 1, c: 1, b: {$meta: 'sortKey'}}", - "{}", - IndexKeyDatum(BSON("a" << 1 << "b" << 1 << "c" << 1), - BSON("" << 5 << "" << 6 << "" << 4), - 0, - SnapshotId{}), - boost::none, // collator - BSON("" << 5 << "" << 6))); // sortKey -} - -TEST(ProjectionExecTest, TransformMetaSortKeyCoveredCompound3) { - ASSERT_EQ(boost::make_optional("{ c: 4, d: 9000, b: { : 6, : 4 } }"s), - project("{_id: 0, c: 1, d: 1, b: {$meta: 'sortKey'}}", - "{}", - IndexKeyDatum(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1), - BSON("" << 5 << "" << 6 << "" << 4 << "" << 9000), - 0, - SnapshotId{}), - boost::none, // collator - BSON("" << 6 << "" << 4))); // sortKey -} - -} // namespace |