From fd1bc4895b51dc65185ac5fa633bb0435860b936 Mon Sep 17 00:00:00 2001 From: Alberto Lerner Date: Wed, 16 Jan 2013 10:36:40 -0500 Subject: SERVER-5710 Changed KeyPattern.hasField() sematics to consider dotted field prefixes. --- src/mongo/SConscript | 8 +- src/mongo/base/string_data-inl.h | 7 +- src/mongo/base/string_data.h | 2 +- src/mongo/db/SConscript | 15 ++++ src/mongo/db/field_ref.cpp | 123 ++++++++++++++++++++++++++ src/mongo/db/field_ref.h | 117 +++++++++++++++++++++++++ src/mongo/db/field_ref_test.cpp | 131 ++++++++++++++++++++++++++++ src/mongo/db/keypattern.cpp | 17 +++- src/mongo/db/keypattern.h | 36 ++++++-- src/mongo/db/ops/SConscript | 9 -- src/mongo/db/ops/field_ref.cpp | 123 -------------------------- src/mongo/db/ops/field_ref.h | 117 ------------------------- src/mongo/db/ops/field_ref_test.cpp | 131 ---------------------------- src/mongo/db/ops/update_internal.cpp | 4 +- src/mongo/dbtests/keypatterntests.cpp | 159 +++++++++++++++++++++++++++++++++- 15 files changed, 603 insertions(+), 396 deletions(-) create mode 100644 src/mongo/db/SConscript create mode 100644 src/mongo/db/field_ref.cpp create mode 100644 src/mongo/db/field_ref.h create mode 100644 src/mongo/db/field_ref_test.cpp delete mode 100644 src/mongo/db/ops/SConscript delete mode 100644 src/mongo/db/ops/field_ref.cpp delete mode 100644 src/mongo/db/ops/field_ref.h delete mode 100644 src/mongo/db/ops/field_ref_test.cpp diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 5dbfb7c1e8f..41f57bbe999 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -17,7 +17,7 @@ Import("darwin windows solaris linux nix") env.SConscript(['base/SConscript', 'db/auth/SConscript', 'db/fts/SConscript', - 'db/ops/SConscript', + 'db/SConscript', 'platform/SConscript', 's/SConscript', 'unittest/SConscript']) @@ -267,9 +267,11 @@ env.StaticLibrary("coredb", [ "s/shardconnection.cpp", ], LIBDEPS=['db/auth/serverauth', + 'db/common', 'server_parameters', 'geoparser', - 'geoquery']) + 'geoquery', + '$BUILD_DIR/mongo/foundation']) coreServerFiles = [ "db/client_basic.cpp", "db/common.cpp", @@ -547,7 +549,7 @@ env.StaticLibrary("serveronly", serverOnlyFiles, LIBDEPS=["coreshard", "db/auth/authmongod", "db/fts/ftsmongod", - "db/ops/update", + "db/common", "dbcmdline", "defaultversion", "geoparser", diff --git a/src/mongo/base/string_data-inl.h b/src/mongo/base/string_data-inl.h index 00b403a569c..9d6d024df9c 100644 --- a/src/mongo/base/string_data-inl.h +++ b/src/mongo/base/string_data-inl.h @@ -61,8 +61,11 @@ namespace mongo { dest[size()] = 0; } - inline size_t StringData::find( char c ) const { - const void* x = memchr( _data, c, size() ); + inline size_t StringData::find( char c, size_t fromPos ) const { + if ( fromPos >= size() ) + return string::npos; + + const void* x = memchr( _data + fromPos, c, _size - fromPos ); if ( x == 0 ) return string::npos; return static_cast( static_cast(x) - _data ); diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h index a54de185905..dca41fa1e9a 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -96,7 +96,7 @@ namespace mongo { // finders // - size_t find( char c ) const; + size_t find( char c , size_t fromPos = 0 ) const; size_t find( const StringData& needle ) const; /** diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript new file mode 100644 index 00000000000..2d3a4f65734 --- /dev/null +++ b/src/mongo/db/SConscript @@ -0,0 +1,15 @@ +# -*- mode: python -*- + +Import("env") + +# +# We'd like 'common' to have the abstractions that are shared by several components of the +# server. Ideally, many of the object in 'coredb' should be moved here when their dependencies +# get resolved. +# + +env.StaticLibrary('common', ['field_ref.cpp'], + LIBDEPS=['$BUILD_DIR/mongo/bson', + '$BUILD_DIR/mongo/foundation']) + +env.CppUnitTest('field_ref_test', ['field_ref_test.cpp'], LIBDEPS=['common']) diff --git a/src/mongo/db/field_ref.cpp b/src/mongo/db/field_ref.cpp new file mode 100644 index 00000000000..7b9f5631140 --- /dev/null +++ b/src/mongo/db/field_ref.cpp @@ -0,0 +1,123 @@ +/** + * Copyright (C) 2012 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 . + */ + +#include "mongo/db/field_ref.h" +#include "mongo/util/assert_util.h" + +namespace mongo { + + void FieldRef::parse(const StringData& dottedField) { + if (dottedField.size() == 0) { + return; + } + + // We guarantee that accesses through getPart() will be valid while 'this' is. So we + // take a copy. We're going to be "chopping" up the copy into c-strings. + _fieldBase.reset(new char[dottedField.size()+1]); + dottedField.copyTo( _fieldBase.get(), true ); + + // Separate the field parts using '.' as a delimiter. + char* beg = _fieldBase.get(); + char* cur = beg; + char* end = beg + dottedField.size(); + while (true) { + if (cur != end && *cur != '.') { + cur++; + continue; + } + + appendPart(StringData(beg, cur - beg)); + + if (cur != end) { + *cur = '\0'; + beg = ++cur; + continue; + } + + break; + } + } + + void FieldRef::setPart(size_t i, const StringData& part) { + dassert(i < _size); + + if (_replacements.size() != _size) { + _replacements.resize(_size); + } + + _replacements[i] = part.toString(); + if (i < kReserveAhead) { + _fixed[i] = _replacements[i]; + } + else { + _variable[getIndex(i)] = _replacements[i]; + } + } + + size_t FieldRef::appendPart(const StringData& part) { + if (_size < kReserveAhead) { + _fixed[_size] = part; + } + else { + _variable.push_back(part); + } + return ++_size; + } + + StringData FieldRef::getPart(size_t i) const { + dassert(i < _size); + + if (i < kReserveAhead) { + return _fixed[i]; + } + else { + return _variable[getIndex(i)]; + } + } + + void FieldRef::clear() { + _size = 0; + _variable.clear(); + _fieldBase.reset(); + _replacements.clear(); + } + + std::string FieldRef::dottedField() const { + std::string res; + if (_size == 0) { + return res; + } + + res.append(_fixed[0].rawData(), _fixed[0].size()); + for (size_t i=1; i<_size; i++) { + res.append(1, '.'); + StringData part = getPart(i); + res.append(part.rawData(), part.size()); + } + return res; + } + + size_t FieldRef::numReplaced() const { + size_t res = 0; + for (size_t i = 0; i < _replacements.size(); i++) { + if (!_replacements[i].empty()) { + res++; + } + } + return res; + } + +} // namespace mongo diff --git a/src/mongo/db/field_ref.h b/src/mongo/db/field_ref.h new file mode 100644 index 00000000000..ea7b7dab34d --- /dev/null +++ b/src/mongo/db/field_ref.h @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2012 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 . + */ + +#pragma once + +#include +#include +#include + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/string_data.h" + +namespace mongo { + + /** + * A FieldPath represents a path in a document, starting from the root. The path + * is made of "field parts" separated by dots. The class provides an efficient means to + * "split" the dotted fields in its parts, but no validation is done. + * + * Any field part may be replaced, after the "original" field reference was parsed. Any + * part can be accessed through a StringData object. + * + * The class is not thread safe. + */ + class FieldRef { + MONGO_DISALLOW_COPYING(FieldRef); + public: + FieldRef() : _size(0) {} + + /** + * Field parts accessed through getPart() calls no longer would be valid, after the + * destructor ran. + */ + ~FieldRef() {} + + /** + * Builds a field path out of each field part in 'dottedField'. + */ + void parse(const StringData& dottedField); + + /** + * Sets the 'i-th' field part to point to 'part'. Assumes i < size(). Behavior is + * undefined otherwise. + */ + void setPart(size_t i, const StringData& part); + + /** + * Returns the 'i-th' field part. Assumes i < size(). Behavior is undefined otherwise. + */ + StringData getPart(size_t i) const; + + /** + * Returns a copy of the full dotted field in its current state (i.e., some parts may + * have been replaced since the parse() call). + */ + std::string dottedField() const; + + /** + * Resets the internal state. See note in parse() call. + */ + void clear(); + + // + // accessors + // + + /** + * Returns the number of parts in this FieldRef. + */ + size_t numParts() const { return _size; } + + /** + * Returns the number of fields parts that were replaced so far. Replacing the same + * fields several times only counts for 1. + */ + size_t numReplaced() const; + + private: + // Dotted fields are most often not longer than three parts. We use a mixed structure + // here that will not require any extra memory allocation when that is the case. And + // handle larger dotted fields if it is. The idea is not to penalize the common case + // with allocations. + static const size_t kReserveAhead = 4; + + size_t _size; // # of field parts stored + StringData _fixed[kReserveAhead]; // first kResevedAhead field components + std::vector _variable; // remaining field components + + // Areas that _fixed and _variable point to. + boost::scoped_array _fieldBase; // concatenation of null-terminated parts + std::vector _replacements; // added with the setPart call + + /** Converts the field part index to the variable part equivalent */ + size_t getIndex(size_t i) const { return i-kReserveAhead; } + + /** + * Returns the new number of parts after appending 'part' to this field path. It + * assumes that 'part' is pointing to an internally allocated area. + */ + size_t appendPart(const StringData& part); + + }; + +} // namespace mongo diff --git a/src/mongo/db/field_ref_test.cpp b/src/mongo/db/field_ref_test.cpp new file mode 100644 index 00000000000..e84a9d1158a --- /dev/null +++ b/src/mongo/db/field_ref_test.cpp @@ -0,0 +1,131 @@ +/* Copyright 2012 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "mongo/base/error_codes.h" +#include "mongo/base/status.h" +#include "mongo/base/string_data.h" +#include "mongo/db/field_ref.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/mongoutils/str.h" + +namespace { + + using mongo::FieldRef; + using mongo::StringData; + using mongoutils::str::stream; + using std::string; + + TEST(Empty, NoFields) { + FieldRef fieldRef; + fieldRef.parse(""); + ASSERT_EQUALS(fieldRef.numParts(), 0U); + ASSERT_EQUALS(fieldRef.dottedField(), ""); + } + + TEST(Empty, NoFieldNames) { + string field = "."; + FieldRef fieldRef; + fieldRef.parse(field); + ASSERT_EQUALS(fieldRef.numParts(), 2U); + ASSERT_EQUALS(fieldRef.getPart(0), ""); + ASSERT_EQUALS(fieldRef.getPart(1), ""); + ASSERT_EQUALS(fieldRef.dottedField(), field); + } + + TEST(Empty, EmptyFieldName) { + string field = ".b."; + FieldRef fieldRef; + fieldRef.parse(field); + ASSERT_EQUALS(fieldRef.numParts(), 3U); + ASSERT_EQUALS(fieldRef.getPart(0), ""); + ASSERT_EQUALS(fieldRef.getPart(1), "b"); + ASSERT_EQUALS(fieldRef.getPart(2), ""); + ASSERT_EQUALS(fieldRef.dottedField(), field); + } + + TEST(Normal, SinglePart) { + string field = "a"; + FieldRef fieldRef; + fieldRef.parse(field); + ASSERT_EQUALS(fieldRef.numParts(), 1U); + ASSERT_EQUALS(fieldRef.getPart(0), field); + ASSERT_EQUALS(fieldRef.dottedField(), field); + } + + TEST(Normal, MulitplePartsVariable) { + const char* parts[] = {"a", "b", "c", "d", "e"}; + size_t size = sizeof(parts)/sizeof(char*); + string field(parts[0]); + for (size_t i=1; i 0; len-- ) { + result = ( result * 131 ) + *p++; + } + return result; + } + }; + unordered_set _prefixes; + bool isAscending( const BSONElement& fieldExpression ) const { return ( fieldExpression.isNumber() && fieldExpression.numberInt() == 1 ); } + bool isDescending( const BSONElement& fieldExpression ) const { return ( fieldExpression.isNumber() && fieldExpression.numberInt() == -1 ); } + bool isHashed( const BSONElement& fieldExpression ) const { return mongoutils::str::equals( fieldExpression.valuestrsafe() , "hashed" ); } @@ -178,5 +205,4 @@ namespace mongo { }; - } // namespace mongo diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript deleted file mode 100644 index eb33dfb5680..00000000000 --- a/src/mongo/db/ops/SConscript +++ /dev/null @@ -1,9 +0,0 @@ -# -*- mode: python -*- - -Import("env") - -env.StaticLibrary('update', ['field_ref.cpp'], - LIBDEPS=['$BUILD_DIR/mongo/bson', - '$BUILD_DIR/mongo/foundation']) - -env.CppUnitTest('field_ref_test', ['field_ref_test.cpp'], LIBDEPS=['update']) diff --git a/src/mongo/db/ops/field_ref.cpp b/src/mongo/db/ops/field_ref.cpp deleted file mode 100644 index f4dac8f9097..00000000000 --- a/src/mongo/db/ops/field_ref.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (C) 2012 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 . - */ - -#include "mongo/db/ops/field_ref.h" -#include "mongo/util/assert_util.h" - -namespace mongo { - - void FieldRef::parse(const StringData& dottedField) { - if (dottedField.size() == 0) { - return; - } - - // We guarantee that accesses through getPart() will be valid while 'this' is. So we - // take a copy. We're going to be "chopping" up the copy into c-strings. - _fieldBase.reset(new char[dottedField.size()+1]); - dottedField.copyTo( _fieldBase.get(), true ); - - // Separate the field parts using '.' as a delimiter. - char* beg = _fieldBase.get(); - char* cur = beg; - char* end = beg + dottedField.size(); - while (true) { - if (cur != end && *cur != '.') { - cur++; - continue; - } - - appendPart(StringData(beg, cur - beg)); - - if (cur != end) { - *cur = '\0'; - beg = ++cur; - continue; - } - - break; - } - } - - void FieldRef::setPart(size_t i, const StringData& part) { - dassert(i < _size); - - if (_replacements.size() != _size) { - _replacements.resize(_size); - } - - _replacements[i] = part.toString(); - if (i < kReserveAhead) { - _fixed[i] = _replacements[i]; - } - else { - _variable[getIndex(i)] = _replacements[i]; - } - } - - size_t FieldRef::appendPart(const StringData& part) { - if (_size < kReserveAhead) { - _fixed[_size] = part; - } - else { - _variable.push_back(part); - } - return ++_size; - } - - StringData FieldRef::getPart(size_t i) const { - dassert(i < _size); - - if (i < kReserveAhead) { - return _fixed[i]; - } - else { - return _variable[getIndex(i)]; - } - } - - void FieldRef::clear() { - _size = 0; - _variable.clear(); - _fieldBase.reset(); - _replacements.clear(); - } - - std::string FieldRef::dottedField() const { - std::string res; - if (_size == 0) { - return res; - } - - res.append(_fixed[0].rawData(), _fixed[0].size()); - for (size_t i=1; i<_size; i++) { - res.append(1, '.'); - StringData part = getPart(i); - res.append(part.rawData(), part.size()); - } - return res; - } - - size_t FieldRef::numReplaced() const { - size_t res = 0; - for (size_t i = 0; i < _replacements.size(); i++) { - if (!_replacements[i].empty()) { - res++; - } - } - return res; - } - -} // namespace mongo diff --git a/src/mongo/db/ops/field_ref.h b/src/mongo/db/ops/field_ref.h deleted file mode 100644 index ea7b7dab34d..00000000000 --- a/src/mongo/db/ops/field_ref.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (C) 2012 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 . - */ - -#pragma once - -#include -#include -#include - -#include "mongo/base/disallow_copying.h" -#include "mongo/base/string_data.h" - -namespace mongo { - - /** - * A FieldPath represents a path in a document, starting from the root. The path - * is made of "field parts" separated by dots. The class provides an efficient means to - * "split" the dotted fields in its parts, but no validation is done. - * - * Any field part may be replaced, after the "original" field reference was parsed. Any - * part can be accessed through a StringData object. - * - * The class is not thread safe. - */ - class FieldRef { - MONGO_DISALLOW_COPYING(FieldRef); - public: - FieldRef() : _size(0) {} - - /** - * Field parts accessed through getPart() calls no longer would be valid, after the - * destructor ran. - */ - ~FieldRef() {} - - /** - * Builds a field path out of each field part in 'dottedField'. - */ - void parse(const StringData& dottedField); - - /** - * Sets the 'i-th' field part to point to 'part'. Assumes i < size(). Behavior is - * undefined otherwise. - */ - void setPart(size_t i, const StringData& part); - - /** - * Returns the 'i-th' field part. Assumes i < size(). Behavior is undefined otherwise. - */ - StringData getPart(size_t i) const; - - /** - * Returns a copy of the full dotted field in its current state (i.e., some parts may - * have been replaced since the parse() call). - */ - std::string dottedField() const; - - /** - * Resets the internal state. See note in parse() call. - */ - void clear(); - - // - // accessors - // - - /** - * Returns the number of parts in this FieldRef. - */ - size_t numParts() const { return _size; } - - /** - * Returns the number of fields parts that were replaced so far. Replacing the same - * fields several times only counts for 1. - */ - size_t numReplaced() const; - - private: - // Dotted fields are most often not longer than three parts. We use a mixed structure - // here that will not require any extra memory allocation when that is the case. And - // handle larger dotted fields if it is. The idea is not to penalize the common case - // with allocations. - static const size_t kReserveAhead = 4; - - size_t _size; // # of field parts stored - StringData _fixed[kReserveAhead]; // first kResevedAhead field components - std::vector _variable; // remaining field components - - // Areas that _fixed and _variable point to. - boost::scoped_array _fieldBase; // concatenation of null-terminated parts - std::vector _replacements; // added with the setPart call - - /** Converts the field part index to the variable part equivalent */ - size_t getIndex(size_t i) const { return i-kReserveAhead; } - - /** - * Returns the new number of parts after appending 'part' to this field path. It - * assumes that 'part' is pointing to an internally allocated area. - */ - size_t appendPart(const StringData& part); - - }; - -} // namespace mongo diff --git a/src/mongo/db/ops/field_ref_test.cpp b/src/mongo/db/ops/field_ref_test.cpp deleted file mode 100644 index e6645acffc1..00000000000 --- a/src/mongo/db/ops/field_ref_test.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright 2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "mongo/base/error_codes.h" -#include "mongo/base/status.h" -#include "mongo/base/string_data.h" -#include "mongo/db/ops/field_ref.h" -#include "mongo/unittest/unittest.h" -#include "mongo/util/mongoutils/str.h" - -namespace { - - using mongo::FieldRef; - using mongo::StringData; - using mongoutils::str::stream; - using std::string; - - TEST(Empty, NoFields) { - FieldRef fieldRef; - fieldRef.parse(""); - ASSERT_EQUALS(fieldRef.numParts(), 0U); - ASSERT_EQUALS(fieldRef.dottedField(), ""); - } - - TEST(Empty, NoFieldNames) { - string field = "."; - FieldRef fieldRef; - fieldRef.parse(field); - ASSERT_EQUALS(fieldRef.numParts(), 2U); - ASSERT_EQUALS(fieldRef.getPart(0), ""); - ASSERT_EQUALS(fieldRef.getPart(1), ""); - ASSERT_EQUALS(fieldRef.dottedField(), field); - } - - TEST(Empty, EmptyFieldName) { - string field = ".b."; - FieldRef fieldRef; - fieldRef.parse(field); - ASSERT_EQUALS(fieldRef.numParts(), 3U); - ASSERT_EQUALS(fieldRef.getPart(0), ""); - ASSERT_EQUALS(fieldRef.getPart(1), "b"); - ASSERT_EQUALS(fieldRef.getPart(2), ""); - ASSERT_EQUALS(fieldRef.dottedField(), field); - } - - TEST(Normal, SinglePart) { - string field = "a"; - FieldRef fieldRef; - fieldRef.parse(field); - ASSERT_EQUALS(fieldRef.numParts(), 1U); - ASSERT_EQUALS(fieldRef.getPart(0), field); - ASSERT_EQUALS(fieldRef.dottedField(), field); - } - - TEST(Normal, MulitplePartsVariable) { - const char* parts[] = {"a", "b", "c", "d", "e"}; - size_t size = sizeof(parts)/sizeof(char*); - string field(parts[0]); - for (size_t i=1; i // for max -#include "mongo/db/oplog.h" -#include "mongo/db/ops/field_ref.h" +#include "mongo/db/field_ref.h" #include "mongo/db/jsobjmanipulator.h" #include "mongo/db/pdfile.h" +#include "mongo/db/oplog.h" #include "mongo/util/mongoutils/str.h" #include "update_internal.h" diff --git a/src/mongo/dbtests/keypatterntests.cpp b/src/mongo/dbtests/keypatterntests.cpp index 7d8568c5706..641dea16c8f 100644 --- a/src/mongo/dbtests/keypatterntests.cpp +++ b/src/mongo/dbtests/keypatterntests.cpp @@ -95,6 +95,161 @@ namespace KeyPatternTests { } }; + class HasFieldTests { + public: + void run() { + // TEST(HasField, SameKey) + { + KeyPattern keyPat( BSON( "x" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x" ) ); + } + + // TEST( HasField, DifferentKey ) + { + KeyPattern keyPat( BSON( "x" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "y" ) ); + } + + // TEST( HasField, SameKey2Levels ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x.y" ) ); + } + + // TEST( HasField, SameKeyPartial ) + { + KeyPattern keyPat( BSON( "xyz.a" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "xyz" ) ); + } + + // TEST( HasField, DifferentChildKey ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "x.b" ) ); + } + + // TEST( HasField, DifferentRootKeyDotted ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "a.y" ) ); + } + + // TEST( HasField, SameRootKeyPartial ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x" ) ); + } + + // TEST( HasField, DifferentRootKeyPartial ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "a" ) ); + } + + // TEST( HasField, DifferentMatchingChildKey ) + { + KeyPattern keyPat( BSON( "x.y" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "y" ) ); + } + + // TEST( HasField, SameKey3Level ) + { + KeyPattern keyPat( BSON( "x.y.z" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x.y.z" ) ); + } + + // TEST( HasField, Same3LevelKeyPartialUpto1 ) + { + KeyPattern keyPat( BSON( "x.y.z" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x" ) ); + } + + // TEST( HasField, Same3LevelKeyPartialUpto2 ) + { + KeyPattern keyPat( BSON( "x.y.z" << 1 ) ); + ASSERT_TRUE( keyPat.hasField( "x.y" ) ); + } + + // TEST( HasField, DifferentDottedRoot3Level ) + { + KeyPattern keyPat( BSON( "x.y.z" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "x.b" ) ); + } + + // TEST( HasField, DifferentRoot3Levels ) + { + KeyPattern keyPat( BSON( "x.y.z" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "a" ) ); + } + + // TEST( HasField, SameCompoundHasField ) + { + KeyPattern keyPat( BSON( "x" << 1 << "y" << -1 ) ); + ASSERT_TRUE( keyPat.hasField( "y" ) ); + } + + // TEST( HasField, SameDottedCompoundHasField ) + { + KeyPattern keyPat( BSON( "x.y" << 1 << "a.b" << -1 ) ); + ASSERT_TRUE( keyPat.hasField( "a.b" ) ); + } + + // TEST( HasField, Same3LevelEmbeddedCompoundHasField ) + { + KeyPattern keyPat( BSON( "x.y" << 1 << "a.b.c" << -1 ) ); + ASSERT_TRUE( keyPat.hasField( "a" ) ); + } + + // TEST( HasField, DifferentCompoundHasField ) + { + KeyPattern keyPat( BSON( "x" << 1 << "y" << -1 ) ); + ASSERT_FALSE( keyPat.hasField( "z" ) ); + } + + // TEST( HasField, DifferentDottedCompoundHasField ) + { + KeyPattern keyPat( BSON( "x.y" << 1 << "a.b" << -1 ) ); + ASSERT_FALSE( keyPat.hasField( "a.j" ) ); + } + + // TEST( HasField, SameRootLongerObjKey ) + { + KeyPattern keyPat( BSON( "x" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "x.y.z" ) ); + } + + // TEST( HasField, DifferentRootLongerObjKey ) + { + KeyPattern keyPat( BSON( "x" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "a.b.c" ) ); + } + + // TEST( HasField, DifferentRootPrefixObjKey ) + { + KeyPattern keyPat( BSON( "xyz.a" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "xy" ) ); + } + + // TEST( HasField, DifferentRootPrefixHasField ) + { + KeyPattern keyPat( BSON( "xyz" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "xyzabc" ) ); + } + + // TEST( HasField, DifferentRootPartialPrefixObjKey ) + { + KeyPattern keyPat( BSON( "xyz" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "xy.z" ) ); + } + + // TEST( HasField, DifferentRootPartialPrefixHasField ) + { + KeyPattern keyPat( BSON( "xy.z" << 1 ) ); + ASSERT_FALSE( keyPat.hasField( "xyz" ) ); + } + } + }; + class All : public Suite { public: All() : Suite( "keypattern" ) { @@ -102,8 +257,8 @@ namespace KeyPatternTests { void setupTests() { add< ExtendRangeBoundTests >(); + add< HasFieldTests >(); } } myall; -} // namespace JsobjTests - +} // namespace KeyPatternTests -- cgit v1.2.1