diff options
author | Anne Lim <anne.lim@mongodb.com> | 2017-07-24 14:54:14 -0400 |
---|---|---|
committer | Anne Lim <anne.lim@mongodb.com> | 2017-08-10 08:23:50 -0400 |
commit | 7741128c7fe6066e968d6de972af25531e38db18 (patch) | |
tree | 24e8295e26070fc94393c01d32b3b0770aa0688d /src/mongo | |
parent | 2173ffb9fd23c6894c6820fbf8c08aa876392b55 (diff) | |
download | mongo-7741128c7fe6066e968d6de972af25531e38db18.tar.gz |
SERVER-29586: Create a $_internalSchemaAllElemMatchFromIndex MatchExpression
Diffstat (limited to 'src/mongo')
12 files changed, 386 insertions, 0 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript index 4f7af21e9a1..da6e5fda909 100644 --- a/src/mongo/db/matcher/SConscript +++ b/src/mongo/db/matcher/SConscript @@ -47,6 +47,7 @@ env.Library( 'match_details.cpp', 'matchable.cpp', 'matcher.cpp', + 'schema/expression_internal_schema_all_elem_match_from_index.cpp', 'schema/expression_internal_schema_cond.cpp', 'schema/expression_internal_schema_fmod.cpp', 'schema/expression_internal_schema_match_array_index.cpp', @@ -84,6 +85,7 @@ env.CppUnitTest( 'expression_tree_test.cpp', 'expression_with_placeholder_test.cpp', 'path_accepting_keyword_test.cpp', + 'schema/expression_internal_schema_all_elem_match_from_index_test.cpp', 'schema/expression_internal_schema_cond_test.cpp', 'schema/expression_internal_schema_fmod_test.cpp', 'schema/expression_internal_schema_match_array_index_test.cpp', diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h index cac23a28f3d..03d9bac6ac2 100644 --- a/src/mongo/db/matcher/expression.h +++ b/src/mongo/db/matcher/expression.h @@ -99,6 +99,7 @@ public: INTERNAL_2D_POINT_IN_ANNULUS, // JSON Schema expressions. + INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX, INTERNAL_SCHEMA_COND, INTERNAL_SCHEMA_FMOD, INTERNAL_SCHEMA_MAX_ITEMS, diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index 1eff1a1138a..e1cd121ae25 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -27,6 +27,7 @@ * exception statement from all source files in the program, then also delete * it in the license file. */ +#include "mongo/platform/basic.h" #include "mongo/db/matcher/expression_parser.h" @@ -40,6 +41,7 @@ #include "mongo/db/matcher/expression_leaf.h" #include "mongo/db/matcher/expression_tree.h" #include "mongo/db/matcher/expression_with_placeholder.h" +#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h" #include "mongo/db/matcher/schema/expression_internal_schema_cond.h" #include "mongo/db/matcher/schema/expression_internal_schema_fmod.h" #include "mongo/db/matcher/schema/expression_internal_schema_match_array_index.h" @@ -373,6 +375,63 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField( case PathAcceptingKeyword::INTERNAL_SCHEMA_MATCH_ARRAY_INDEX: { return _parseInternalSchemaMatchArrayIndex(name, e, collator); } + + case PathAcceptingKeyword::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX: { + if (e.type() != BSONType::Array) { + return Status(ErrorCodes::FailedToParse, + str::stream() + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << " must be an array"); + } + auto elemMatchObj = e.embeddedObject(); + auto iter = elemMatchObj.begin(); + if (!iter.more()) { + return Status(ErrorCodes::FailedToParse, + str::stream() + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << " must be an array of size 2"); + } + auto first = iter.next(); + auto parsedIndex = parseIntegerElementToNonNegativeLong(first); + if (!parsedIndex.isOK()) { + return Status(ErrorCodes::TypeMismatch, + str::stream() + << "first element of " + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << " must be a non-negative integer"); + } + if (!iter.more()) { + return Status(ErrorCodes::FailedToParse, + str::stream() + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << " must be an array of size 2"); + } + auto second = iter.next(); + if (iter.more()) { + return Status(ErrorCodes::FailedToParse, + str::stream() + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << " has too many elements, must be an array of size 2"); + } + if (second.type() != BSONType::Object) { + return Status(ErrorCodes::TypeMismatch, + str::stream() + << "second element of " + << InternalSchemaAllElemMatchFromIndexMatchExpression::kName + << "must be an object"); + } + StatusWithMatchExpression query = + _parse(second.embeddedObject(), collator, expCtx, topLevel); + if (!query.isOK()) { + return query.getStatus(); + } + auto expr = stdx::make_unique<InternalSchemaAllElemMatchFromIndexMatchExpression>(); + auto status = expr->init(name, parsedIndex.getValue(), std::move(query.getValue())); + if (!status.isOK()) { + return status; + } + return {std::move(expr)}; + } } return {Status(ErrorCodes::BadValue, mongoutils::str::stream() << "not handled: " << e.fieldName())}; @@ -1456,6 +1515,8 @@ MONGO_INITIALIZER(MatchExpressionParser)(InitializerContext* context) { {"bitsAllClear", PathAcceptingKeyword::BITS_ALL_CLEAR}, {"bitsAnySet", PathAcceptingKeyword::BITS_ANY_SET}, {"bitsAnyClear", PathAcceptingKeyword::BITS_ANY_CLEAR}, + {"_internalSchemaAllElemMatchFromIndex", + PathAcceptingKeyword::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX}, {"_internalSchemaFmod", PathAcceptingKeyword::INTERNAL_SCHEMA_FMOD}, {"_internalSchemaMinItems", PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS}, {"_internalSchemaMaxItems", PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS}, diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h index 88ac25551a9..63cf24c39df 100644 --- a/src/mongo/db/matcher/expression_parser.h +++ b/src/mongo/db/matcher/expression_parser.h @@ -68,6 +68,7 @@ enum class PathAcceptingKeyword { BITS_ALL_CLEAR, BITS_ANY_SET, BITS_ANY_CLEAR, + INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX, INTERNAL_SCHEMA_FMOD, INTERNAL_SCHEMA_MIN_ITEMS, INTERNAL_SCHEMA_MAX_ITEMS, diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp index 3b0a1ef2cf9..379fd6635d1 100644 --- a/src/mongo/db/matcher/expression_serialization_test.cpp +++ b/src/mongo/db/matcher/expression_serialization_test.cpp @@ -970,6 +970,17 @@ TEST(SerializeBasic, ExpressionAlwaysFalseSerializesCorrectly) { ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); } +TEST(SerializeInternalSchema, ExpressionInternalSchemaAllElemMatchFromIndexSerializesCorrectly) { + Matcher original(fromjson("{x: {$_internalSchemaAllElemMatchFromIndex: [2, {y: 1}]}}"), + ExtensionsCallbackDisallowExtensions(), + kSimpleCollator); + Matcher reserialized(serialize(original.getMatchExpression()), + ExtensionsCallbackDisallowExtensions(), + kSimpleCollator); + ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), + fromjson("{x: {$_internalSchemaAllElemMatchFromIndex: [2, {y: {$eq: 1}}]}}")); +} + TEST(SerializeInternalSchema, ExpressionInternalSchemaMinItemsSerializesCorrectly) { Matcher original(fromjson("{x: {$_internalSchemaMinItems: 1}}"), ExtensionsCallbackDisallowExtensions(), diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp new file mode 100644 index 00000000000..dd918d8791b --- /dev/null +++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2017 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/platform/basic.h" + +#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h" + +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" + +namespace mongo { + +constexpr StringData InternalSchemaAllElemMatchFromIndexMatchExpression::kName; + +std::unique_ptr<MatchExpression> InternalSchemaAllElemMatchFromIndexMatchExpression::shallowClone() + const { + auto clone = stdx::make_unique<InternalSchemaAllElemMatchFromIndexMatchExpression>(); + invariantOK(clone->init(path(), _index, _query->shallowClone())); + if (getTag()) { + clone->setTag(getTag()->clone()); + } + return std::move(clone); +} + +bool InternalSchemaAllElemMatchFromIndexMatchExpression::equivalent( + const MatchExpression* other) const { + if (matchType() != other->matchType()) { + return false; + } + const InternalSchemaAllElemMatchFromIndexMatchExpression* realOther = + static_cast<const InternalSchemaAllElemMatchFromIndexMatchExpression*>(other); + return (_index == realOther->_index && _query->equivalent(realOther->_query.get())); +} + +void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuilder& debug, + int level) const { + _debugAddSpace(debug, level); + debug << kName << "\n"; + debug << " index: " << _index << ", query:\n"; + _query->debugString(debug, level + 1); +} + +void InternalSchemaAllElemMatchFromIndexMatchExpression::serialize(BSONObjBuilder* out) const { + BSONObjBuilder objMatchBob(out->subobjStart(path())); + BSONArrayBuilder subArray(objMatchBob.subarrayStart(kName)); + subArray.append(_index); + { + BSONObjBuilder eBuilder(subArray.subobjStart()); + _query->serialize(&eBuilder); + eBuilder.doneFast(); + } + subArray.doneFast(); + objMatchBob.doneFast(); +} +} // namespace mongo diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h new file mode 100644 index 00000000000..3f7e79fda8f --- /dev/null +++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2017 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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. + */ +#pragma once + +#include "mongo/base/string_data.h" +#include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_array.h" + +namespace mongo { +/** + * A match expression similar to $elemMatch, but only matches arrays for which every element + * matches the sub-expression. + */ +class InternalSchemaAllElemMatchFromIndexMatchExpression final + : public ArrayMatchingMatchExpression { +public: + static constexpr StringData kName = "$_internalSchemaAllElemMatchFromIndex"_sd; + + InternalSchemaAllElemMatchFromIndexMatchExpression() + : ArrayMatchingMatchExpression(MatchExpression::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX) { + } + + Status init(StringData path, long long index, std::unique_ptr<MatchExpression> query) { + _index = index; + _query = std::move(query); + return setPath(path); + } + + std::unique_ptr<MatchExpression> shallowClone() const final; + + bool matchesArray(const BSONObj& array, MatchDetails*) const final { + auto iter = array.begin(); + for (int i = 0; iter.more() && i < _index; i++) { + iter.next(); + } + while (iter.more()) { + if (!_query->matchesBSONElement(iter.next())) { + return false; + } + } + return true; + } + + void debugString(StringBuilder& debug, int level) const final; + + void serialize(BSONObjBuilder* out) const final; + + bool equivalent(const MatchExpression* other) const final; + +private: + long long _index; + std::unique_ptr<MatchExpression> _query; +}; +} // namespace mongo diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp new file mode 100644 index 00000000000..a0e0d6573ba --- /dev/null +++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp @@ -0,0 +1,103 @@ +/** + * Copyright (C) 2017 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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/platform/basic.h" + +#include "mongo/db/jsobj.h" +#include "mongo/db/json.h" +#include "mongo/db/matcher/expression_parser.h" +#include "mongo/db/matcher/extensions_callback_disallow_extensions.h" +#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +constexpr CollatorInterface* kSimpleCollator = nullptr; + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, MatchesEmptyQuery) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3 << 4)))); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, MatchesValidQueries) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3 << 4)))); + + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3 << 4)))); + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(10 << 2 << 3 << 4)))); + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(10 << 20 << 3 << 4)))); + ASSERT_FALSE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3 << 40)))); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, RejectsNonArrayElements) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_FALSE(expr.getValue()->matchesBSON(BSON("a" << BSON("a" << 1)))); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, MatchesArraysWithLessElementsThanIndex) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_TRUE(expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1)))); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, NestedArraysMatchSubexpression) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_TRUE( + expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << BSON_ARRAY(3 << 4) << 4)))); + ASSERT_TRUE( + expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << BSON_ARRAY(6 << 4) << 4)))); + ASSERT_FALSE( + expr.getValue()->matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << BSON_ARRAY(5 << 6) << 4)))); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, MatchedQueriesWithDottedPaths) { + auto query = fromjson("{'a.b': {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + ASSERT_TRUE( + expr.getValue()->matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY(1 << 2 << 3 << 4))))); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp index 6afe8dce846..c738d0f34f2 100644 --- a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp +++ b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp @@ -586,5 +586,45 @@ TEST(MatchExpressionParserSchemaTest, MatchArrayIndexParsesSuccessfully) { ASSERT_FALSE(matchArrayIndex.getValue()->matchesBSON(fromjson("{foo: [2, 'blah']}"))); ASSERT_FALSE(matchArrayIndex.getValue()->matchesBSON(fromjson("{foo: [{x: 'baz'}]}"))); } + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, FailsToParseWithNegativeIndex) { + BSONObj matchPredicate = + fromjson("{$_internalSchemaAllElemMatchFromIndex: [-2, {a: { $lt: 0 }}]}"); + auto expr = MatchExpressionParser::parse( + matchPredicate, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_NOT_OK(expr.getStatus()); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, FailsToParseWithNonObjectExpression) { + BSONObj matchPredicate = fromjson("{$_internalSchemaAllElemMatchFromIndex: [-2, 4]}"); + auto expr = MatchExpressionParser::parse( + matchPredicate, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_NOT_OK(expr.getStatus()); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, FailsToParseWithInvalidExpression) { + BSONObj matchPredicate = + fromjson("{$_internalSchemaAllElemMatchFromIndex: [-2, {$fakeExpression: 4}]}"); + auto expr = MatchExpressionParser::parse( + matchPredicate, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_NOT_OK(expr.getStatus()); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, FailsToParseWithEmptyArray) { + BSONObj matchPredicate = fromjson("{$_internalSchemaAllElemMatchFromIndex: []}"); + auto expr = MatchExpressionParser::parse( + matchPredicate, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_NOT_OK(expr.getStatus()); +} + +TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, ParsesCorreclyWithValidInput) { + auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: { $lt: 4 }}]}}"); + auto expr = MatchExpressionParser::parse( + query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator); + ASSERT_OK(expr.getStatus()); + + ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{a: [5, 3, 3, 3, 3, 3]}"))); + ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{a: [3, 3, 3, 5, 3, 3]}"))); +} } // namespace } // namespace mongo diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp index 250aa387c01..d9436221087 100644 --- a/src/mongo/db/pipeline/document_source_match.cpp +++ b/src/mongo/db/pipeline/document_source_match.cpp @@ -261,6 +261,7 @@ Document redactSafePortionDollarOps(BSONObj expr) { case PathAcceptingKeyword::EXISTS: case PathAcceptingKeyword::WITHIN: case PathAcceptingKeyword::GEO_INTERSECTS: + case PathAcceptingKeyword::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX: case PathAcceptingKeyword::INTERNAL_SCHEMA_FMOD: case PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS: case PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS: diff --git a/src/mongo/db/pipeline/document_source_match_test.cpp b/src/mongo/db/pipeline/document_source_match_test.cpp index 6eb793d76d6..86ff30f9fd3 100644 --- a/src/mongo/db/pipeline/document_source_match_test.cpp +++ b/src/mongo/db/pipeline/document_source_match_test.cpp @@ -123,6 +123,9 @@ TEST_F(DocumentSourceMatchTest, RedactSafePortion) { "{index: 0, namePlaceholder: 'i', expression: {i: {$gt: 0}}}}}", "{}"); + assertExpectedRedactSafePortion( + "{a: {$_internalSchemaAllElemMatchFromIndex: [3, {a: {$lt: 4}}]}}", "{}"); + // Combinations assertExpectedRedactSafePortion("{a:1, b: 'asdf'}", "{a:1, b: 'asdf'}"); diff --git a/src/mongo/db/query/plan_cache.cpp b/src/mongo/db/query/plan_cache.cpp index 013e1c8163c..0965298f849 100644 --- a/src/mongo/db/query/plan_cache.cpp +++ b/src/mongo/db/query/plan_cache.cpp @@ -173,6 +173,11 @@ const char* encodeMatchType(MatchExpression::MatchType mt) { case MatchExpression::BITS_ANY_CLEAR: return "yc"; + break; + + case MatchExpression::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX: + return "internalSchemaAllElemMatchFromIndex"; + break; case MatchExpression::INTERNAL_SCHEMA_COND: return "internalSchemaCond"; |