diff options
author | Kevin Matulef <matulef@gmail.com> | 2012-10-15 02:25:12 -0400 |
---|---|---|
committer | Kevin Matulef <matulef@gmail.com> | 2012-10-15 02:39:24 -0400 |
commit | 16a8e0484cfbe69c674ed6f3b7609fdc1518b50e (patch) | |
tree | f1bffbb58be9225fccc1d7b9d42b7620b04097d8 /src/mongo | |
parent | 02e0dc4015276b787dcfc448304d978a06bec51d (diff) | |
download | mongo-16a8e0484cfbe69c674ed6f3b7609fdc1518b50e.tar.gz |
SERVER-2001 KeyPattern class; utilities for more general index & shard key specs
The KeyPattern class is an abstraction for defining more general
expression-based keys (both index keys and shard keys). This class
provide some utility functions for extracting keys based on an
expression, and computing range bounds based on an expression.
This patch lays the groundwork and begins to make use of KeyPatterns.
The idea is that to implement more general key expressions, we will only
need to enhance the functions in this class.
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/hashindex.cpp | 13 | ||||
-rw-r--r-- | src/mongo/db/hashindex.h | 3 | ||||
-rw-r--r-- | src/mongo/db/keypattern.cpp | 117 | ||||
-rw-r--r-- | src/mongo/db/keypattern.h | 108 | ||||
-rw-r--r-- | src/mongo/db/queryutil.cpp | 69 | ||||
-rw-r--r-- | src/mongo/db/queryutil.h | 33 | ||||
-rw-r--r-- | src/mongo/s/chunk.cpp | 4 | ||||
-rw-r--r-- | src/mongo/s/shardkey.cpp | 24 | ||||
-rw-r--r-- | src/mongo/s/shardkey.h | 41 |
10 files changed, 271 insertions, 142 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 435a048d79f..ba7377c0762 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -129,6 +129,7 @@ env.StaticLibrary("coredb", [ "db/pipeline/pipeline.cpp", "db/dbcommands_generic.cpp", "db/dbwebserver.cpp", + "db/keypattern.cpp", "db/matcher.cpp", "db/pipeline/accumulator.cpp", "db/pipeline/accumulator_add_to_set.cpp", diff --git a/src/mongo/db/hashindex.cpp b/src/mongo/db/hashindex.cpp index 034306bf7fd..c33c9fd4515 100644 --- a/src/mongo/db/hashindex.cpp +++ b/src/mongo/db/hashindex.cpp @@ -26,13 +26,11 @@ namespace mongo { const string HashedIndexType::HASHED_INDEX_TYPE_IDENTIFIER = "hashed"; HashedIndexType::HashedIndexType( const IndexPlugin* plugin , const IndexSpec* spec ) : - IndexType( plugin , spec ) { - - _keyPattern = spec->keyPattern; + IndexType( plugin , spec ) , _keyPattern( spec->keyPattern ) { //change these if single-field limitation lifted later uassert( 16241 , "Currently only single field hashed index supported." , - _keyPattern.nFields() == 1 ); + _keyPattern.toBSON().nFields() == 1 ); uassert( 16242 , "Currently hashed indexes cannot guarantee uniqueness. Use a regular index." , ! (spec->info).getField("unique").booleanSafe() ); @@ -52,7 +50,7 @@ namespace mongo { _hashVersion = (spec->info).getField("hashVersion").numberInt(); //Get the hashfield name - BSONElement firstElt = _keyPattern.firstElement(); + BSONElement firstElt = spec->keyPattern.firstElement(); massert( 16243 , "error: no hashed index field" , firstElt.str().compare( HASHED_INDEX_TYPE_IDENTIFIER ) == 0 ); _hashedField = firstElt.fieldName(); @@ -75,13 +73,12 @@ namespace mongo { uassert( 16244 , "Error: hashed indexes do not currently support array values" , fieldVal.type() != Array ); if ( ! fieldVal.eoo() ) { - BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); + BSONObj key = _keyPattern.extractSingleKey( obj ) ; keys.insert( key ); } else if (! _isSparse ) { BSONObj nullobj = BSON( _hashedField << BSONNULL ); - BSONElement nullElt = nullobj.firstElement(); - BSONObj key = BSON( "" << makeSingleKey( nullElt , _seed , _hashVersion ) ); + BSONObj key = _keyPattern.extractSingleKey( nullobj ); keys.insert( key ); } } diff --git a/src/mongo/db/hashindex.h b/src/mongo/db/hashindex.h index a360ea8669a..3a96f82178c 100644 --- a/src/mongo/db/hashindex.h +++ b/src/mongo/db/hashindex.h @@ -19,6 +19,7 @@ #include "mongo/db/btree.h" #include "mongo/db/hasher.h" #include "mongo/db/index.h" +#include "mongo/db/keypattern.h" #include "mongo/db/matcher.h" #include "mongo/db/namespace-inl.h" #include "mongo/db/pdfile.h" @@ -110,7 +111,7 @@ namespace mongo { private: string _hashedField; - BSONObj _keyPattern; + KeyPattern _keyPattern; HashSeed _seed; //defaults to zero if not in the IndexSpec HashVersion _hashVersion; //defaults to zero if not in the IndexSpec bool _isSparse; diff --git a/src/mongo/db/keypattern.cpp b/src/mongo/db/keypattern.cpp new file mode 100644 index 00000000000..93b4ab050fc --- /dev/null +++ b/src/mongo/db/keypattern.cpp @@ -0,0 +1,117 @@ +// @file keypattern.cpp + +/** +* 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 <http://www.gnu.org/licenses/>. +*/ + +#include "mongo/db/keypattern.h" +#include "mongo/util/mongoutils/str.h" +#include "mongo/db/hasher.h" +#include "mongo/db/queryutil.h" + +namespace mongo { + + BSONObj KeyPattern::extractSingleKey(const BSONObj& doc ) const { + if ( _pattern.isEmpty() ) + return BSONObj(); + + if ( mongoutils::str::equals( _pattern.firstElement().valuestrsafe() , "hashed" ) ){ + BSONElement fieldVal = doc.getFieldDotted( _pattern.firstElementFieldName() ); + return BSON( _pattern.firstElementFieldName() << + BSONElementHasher::hash64( fieldVal , + BSONElementHasher::DEFAULT_HASH_SEED ) ); + } + + return doc.extractFields( _pattern ); + } + + bool KeyPattern::isSpecial() const { + BSONForEach(e, _pattern) { + int fieldVal = e.numberInt(); + if ( fieldVal != 1 && fieldVal != -1 ){ + return true; + } + } + return false; + } + + BoundList KeyPattern::keyBounds( const FieldRangeSet& queryConstraints ) const { + typedef vector< pair< shared_ptr<BSONObjBuilder> , + shared_ptr<BSONObjBuilder> > > BoundBuilders; + BoundBuilders builders; + builders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), + shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); + BSONObjIterator i( _pattern ); + // until equalityOnly is false, we are just dealing with equality (no range or $in querys). + bool equalityOnly = true; + while( i.more() ) { + BSONElement e = i.next(); + const FieldRange &fr = queryConstraints.range( e.fieldName() ); + int number = (int) e.number(); // returns 0.0 if not numeric + bool forward = ( number >= 0 ); + if ( equalityOnly ) { + if ( fr.equality() ) { + for( BoundBuilders::const_iterator j = builders.begin(); j != builders.end(); ++j ) { + j->first->appendAs( fr.min(), "" ); + j->second->appendAs( fr.min(), "" ); + } + } + else { + equalityOnly = false; + + BoundBuilders newBuilders; + const vector<FieldInterval> &intervals = fr.intervals(); + for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) { + BSONObj first = i->first->obj(); + BSONObj second = i->second->obj(); + + if ( forward ) { + for( vector<FieldInterval>::const_iterator j = intervals.begin(); j != intervals.end(); ++j ) { + uassert( 16449, "combinatorial limit of $in partitioning of result set exceeded", newBuilders.size() < MAX_IN_COMBINATIONS ); + newBuilders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); + newBuilders.back().first->appendElements( first ); + newBuilders.back().second->appendElements( second ); + newBuilders.back().first->appendAs( j->_lower._bound, "" ); + newBuilders.back().second->appendAs( j->_upper._bound, "" ); + } + } + else { + for( vector<FieldInterval>::const_reverse_iterator j = intervals.rbegin(); j != intervals.rend(); ++j ) { + uassert( 16450, "combinatorial limit of $in partitioning of result set exceeded", newBuilders.size() < MAX_IN_COMBINATIONS ); + newBuilders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); + newBuilders.back().first->appendElements( first ); + newBuilders.back().second->appendElements( second ); + newBuilders.back().first->appendAs( j->_upper._bound, "" ); + newBuilders.back().second->appendAs( j->_lower._bound, "" ); + } + } + } + builders = newBuilders; + } + } + else { + for( BoundBuilders::const_iterator j = builders.begin(); j != builders.end(); ++j ) { + j->first->appendAs( forward ? fr.min() : fr.max(), "" ); + j->second->appendAs( forward ? fr.max() : fr.min(), "" ); + } + } + } + BoundList ret; + for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) + ret.push_back( make_pair( i->first->obj(), i->second->obj() ) ); + return ret; + } + +} // namespace mongo diff --git a/src/mongo/db/keypattern.h b/src/mongo/db/keypattern.h new file mode 100644 index 00000000000..afb7a04af76 --- /dev/null +++ b/src/mongo/db/keypattern.h @@ -0,0 +1,108 @@ +// @file keypattern.h - Utilities for manipulating index/shard key patterns. + +/** +* 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 <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "mongo/db/jsobj.h" + +namespace mongo { + + class FieldRangeSet; + + /** + * A BoundList contains intervals specified by inclusive start + * and end bounds. The intervals should be nonoverlapping and occur in + * the specified direction of traversal. For example, given a simple index {i:1} + * and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList + * would be valid for index {i:-1} with direction -1. + */ + typedef vector<pair<BSONObj,BSONObj> > BoundList; + + /** A KeyPattern is an expression describing a transformation of a document into a + * document key. Document keys are used to store documents in indices and to target + * sharded queries. + * + * Examples: + * { a : 1 } + * { a : 1 , b : -1 } + * { a : "hashed" } + */ + class KeyPattern { + public: + KeyPattern( const BSONObj& pattern ): _pattern( pattern ) {} + + /* + * Returns a BSON representation of this KeyPattern. + */ + BSONObj toBSON() const { return _pattern; } + + /* + * Returns true if the given fieldname is the name of one element of the (potentially) + * compound key described by this KeyPattern. + */ + bool hasField( const char* fieldname ) const { return _pattern.hasField( fieldname ); } + + /* + * Returns true if the key described by this KeyPattern is a prefix of + * the (potentially) compound key described by 'other' + */ + bool isPrefixOf( const KeyPattern& other ) const { + return _pattern.isPrefixOf( other.toBSON() ); + } + + /** + * Returns true if this KeyPattern contains any computed values, (e.g. {a : "hashed"}), + * and false if this KeyPattern consists of only ascending/descending fields + * (e.g. {a : 1, b : -1}). With our current index expression language, "special" patterns + * are any patterns that are not a simple list of field names and 1/-1 values. + */ + bool isSpecial() const; + + string toString() const{ return toBSON().toString(); } + + /* Given a document, extracts the index key corresponding to this KeyPattern + * Warning: assumes that there is a *single* key to be extracted! + * + * Examples: + * If 'this' KeyPattern is { a : 1 } + * { a: "hi" , b : 4} --> returns { a : "hi" } + * { c : 4 , a : 2 } --> returns { a : 2 } + * { b : 2 } (bad input, don't call with this) + * { a : [1,2] } (bad input, don't call with this) + * + * If 'this' KeyPattern is { a : "hashed" } + * { a: 1 } --> returns { a : NumberLong("5902408780260971510") } + */ + BSONObj extractSingleKey( const BSONObj& doc ) const; + + /**@param fromQuery a FieldRangeSet formed from parsing a query + * @return an ordered list of bounds generated using this KeyPattern + * and the constraints from the FieldRangeSet + * + * The value of frsp->matchPossibleForSingleKeyFRS(fromQuery) should be true, + * otherwise this function could throw. + * + */ + BoundList keyBounds( const FieldRangeSet& queryConstraints ) const; + + private: + BSONObj _pattern; + }; + + +} // namespace mongo diff --git a/src/mongo/db/queryutil.cpp b/src/mongo/db/queryutil.cpp index b926160664e..bdfc561ac90 100644 --- a/src/mongo/db/queryutil.cpp +++ b/src/mongo/db/queryutil.cpp @@ -25,8 +25,6 @@ namespace mongo { - static const unsigned maxCombinations = 4000000; - ParsedQuery::ParsedQuery( QueryMessage& qm ) : _ns( qm.ns ) , _ntoskip( qm.ntoskip ) , _ntoreturn( qm.ntoreturn ) , _options( qm.queryOptions ) { init( qm.query ); @@ -1163,7 +1161,7 @@ namespace mongo { verify( !_ranges.back().empty() ); } uassert( 13385, "combinatorial limit of $in partitioning of result set exceeded", - size() < maxCombinations ); + size() < MAX_IN_COMBINATIONS ); } BSONObj FieldRangeVector::startKey() const { @@ -1260,71 +1258,6 @@ namespace mongo { return QueryPattern( *this, sort ); } - // TODO get rid of this - BoundList FieldRangeSet::indexBounds( const BSONObj &keyPattern, int direction ) const { - typedef vector<pair<shared_ptr<BSONObjBuilder>, shared_ptr<BSONObjBuilder> > > BoundBuilders; - BoundBuilders builders; - builders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); - BSONObjIterator i( keyPattern ); - bool equalityOnly = true; // until equalityOnly is false, we are just dealing with equality (no range or $in querys). - while( i.more() ) { - BSONElement e = i.next(); - const FieldRange &fr = range( e.fieldName() ); - int number = (int) e.number(); // returns 0.0 if not numeric - bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction >= 0 ? 1 : -1 ) > 0 ); - if ( equalityOnly ) { - if ( fr.equality() ) { - for( BoundBuilders::const_iterator j = builders.begin(); j != builders.end(); ++j ) { - j->first->appendAs( fr.min(), "" ); - j->second->appendAs( fr.min(), "" ); - } - } - else { - equalityOnly = false; - - BoundBuilders newBuilders; - const vector<FieldInterval> &intervals = fr.intervals(); - for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) { - BSONObj first = i->first->obj(); - BSONObj second = i->second->obj(); - - if ( forward ) { - for( vector<FieldInterval>::const_iterator j = intervals.begin(); j != intervals.end(); ++j ) { - uassert( 13303, "combinatorial limit of $in partitioning of result set exceeded", newBuilders.size() < maxCombinations ); - newBuilders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); - newBuilders.back().first->appendElements( first ); - newBuilders.back().second->appendElements( second ); - newBuilders.back().first->appendAs( j->_lower._bound, "" ); - newBuilders.back().second->appendAs( j->_upper._bound, "" ); - } - } - else { - for( vector<FieldInterval>::const_reverse_iterator j = intervals.rbegin(); j != intervals.rend(); ++j ) { - uassert( 13304, "combinatorial limit of $in partitioning of result set exceeded", newBuilders.size() < maxCombinations ); - newBuilders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); - newBuilders.back().first->appendElements( first ); - newBuilders.back().second->appendElements( second ); - newBuilders.back().first->appendAs( j->_upper._bound, "" ); - newBuilders.back().second->appendAs( j->_lower._bound, "" ); - } - } - } - builders = newBuilders; - } - } - else { - for( BoundBuilders::const_iterator j = builders.begin(); j != builders.end(); ++j ) { - j->first->appendAs( forward ? fr.min() : fr.max(), "" ); - j->second->appendAs( forward ? fr.max() : fr.min(), "" ); - } - } - } - BoundList ret; - for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) - ret.push_back( make_pair( i->first->obj(), i->second->obj() ) ); - return ret; - } - int FieldRangeSet::numNonUniversalRanges() const { int count = 0; for( map<string,FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { diff --git a/src/mongo/db/queryutil.h b/src/mongo/db/queryutil.h index bdea78b7361..32bf3541e88 100644 --- a/src/mongo/db/queryutil.h +++ b/src/mongo/db/queryutil.h @@ -25,6 +25,9 @@ namespace mongo { extern const int MaxBytesToReturnToClientAtOnce; + //maximum number of intervals produced by $in queries. + static const unsigned MAX_IN_COMBINATIONS = 4000000; + /* This is for languages whose "objects" are not well ordered (JSON is well ordered). [ { a : ... } , { b : ... } ] -> { a : ..., b : ... } */ @@ -381,15 +384,6 @@ namespace mongo { // _elemMatchContext for the FieldRange on 'a.b' is the query // element having field name '$elemMatch'. }; - - /** - * A BoundList contains intervals specified by inclusive start - * and end bounds. The intervals should be nonoverlapping and occur in - * the specified direction of traversal. For example, given a simple index {i:1} - * and direction +1, one valid BoundList is: (1, 2); (4, 6). The same BoundList - * would be valid for index {i:-1} with direction -1. - */ - typedef vector<pair<BSONObj,BSONObj> > BoundList; class QueryPattern; @@ -477,18 +471,6 @@ namespace mongo { const FieldRangeSet &operator-=( const FieldRangeSet &other ); /** @return intersection of 'this' with 'other'. */ const FieldRangeSet &operator&=( const FieldRangeSet &other ); - - /** - * @return an ordered list of bounds generated using an index key pattern - * and traversal direction. - * - * The value of matchPossible() should be true, otherwise this function - * may @throw. - * - * NOTE This function is deprecated in the query optimizer and only - * currently used by sharding code. - */ - BoundList indexBounds( const BSONObj &keyPattern, int direction ) const; /** * @return - A new FieldRangeSet based on this FieldRangeSet, but with only @@ -597,17 +579,16 @@ namespace mongo { * already been scanned. */ FieldRangeSetPair &operator-=( const FieldRangeSet &scanned ); - - BoundList shardKeyIndexBounds( const BSONObj &keyPattern ) const { - return _singleKey.indexBounds( keyPattern, 1 ); - } - bool matchPossibleForShardKey( const BSONObj &keyPattern ) const { + bool matchPossibleForSingleKeyFRS( const BSONObj &keyPattern ) const { return _singleKey.matchPossibleForIndex( keyPattern ); } BSONObj originalQuery() const { return _singleKey.originalQuery(); } + const FieldRangeSet getSingleKeyFRS() const { return _singleKey; } + const FieldRangeSet getMultiKeyFRS() const { return _singleKey; } + string toString() const; private: FieldRangeSetPair( const FieldRangeSet &singleKey, const FieldRangeSet &multiKey ) diff --git a/src/mongo/s/chunk.cpp b/src/mongo/s/chunk.cpp index cec8cdde8ad..d1fd4b7899d 100644 --- a/src/mongo/s/chunk.cpp +++ b/src/mongo/s/chunk.cpp @@ -1101,8 +1101,8 @@ namespace mongo { return; } - if ( frsp->matchPossibleForShardKey( _key.key() ) ) { - BoundList ranges = frsp->shardKeyIndexBounds(_key.key()); + if ( frsp->matchPossibleForSingleKeyFRS( _key.key() ) ) { + BoundList ranges = _key.keyBounds( frsp->getSingleKeyFRS() ); for (BoundList::const_iterator it=ranges.begin(), end=ranges.end(); it != end; ++it) { diff --git a/src/mongo/s/shardkey.cpp b/src/mongo/s/shardkey.cpp index 83338a71fdc..a2ad2ce02f2 100644 --- a/src/mongo/s/shardkey.cpp +++ b/src/mongo/s/shardkey.cpp @@ -26,7 +26,7 @@ namespace mongo { ShardKeyPattern::ShardKeyPattern( BSONObj p ) : pattern( p.getOwned() ) { - pattern.getFieldNames(patternfields); + pattern.toBSON().getFieldNames( patternfields ); BSONObjBuilder min; BSONObjBuilder max; @@ -57,26 +57,16 @@ namespace mongo { return true; } - bool ShardKeyPattern::isPrefixOf( const BSONObj& otherPattern ) const { + bool ShardKeyPattern::isPrefixOf( const KeyPattern& otherPattern ) const { return pattern.isPrefixOf( otherPattern ); } - bool ShardKeyPattern::isUniqueIndexCompatible( const BSONObj& uniqueIndexPattern ) const { - if ( ! uniqueIndexPattern.isEmpty() && - str::equals( uniqueIndexPattern.firstElementFieldName(), "_id" ) ){ + bool ShardKeyPattern::isUniqueIndexCompatible( const KeyPattern& uniqueIndexPattern ) const { + if ( ! uniqueIndexPattern.toBSON().isEmpty() && + str::equals( uniqueIndexPattern.toBSON().firstElementFieldName(), "_id" ) ){ return true; } - return pattern.isFieldNamePrefixOf( uniqueIndexPattern ); - } - - bool ShardKeyPattern::isSpecial() const { - BSONForEach(e, pattern) { - int fieldVal = e.numberInt(); - if ( fieldVal != 1 && fieldVal != -1 ){ - return true; - } - } - return false; + return pattern.toBSON().isFieldNamePrefixOf( uniqueIndexPattern.toBSON() ); } string ShardKeyPattern::toString() const { @@ -86,7 +76,7 @@ namespace mongo { BSONObj ShardKeyPattern::moveToFront(const BSONObj& obj) const { vector<const char*> keysToMove; keysToMove.push_back("_id"); - BSONForEach(e, pattern) { + BSONForEach(e, pattern.toBSON()) { if (strchr(e.fieldName(), '.') == NULL && strcmp(e.fieldName(), "_id") != 0) keysToMove.push_back(e.fieldName()); } diff --git a/src/mongo/s/shardkey.h b/src/mongo/s/shardkey.h index c67cd2820ab..efbf82d52fe 100644 --- a/src/mongo/s/shardkey.h +++ b/src/mongo/s/shardkey.h @@ -18,9 +18,12 @@ #pragma once +#include "mongo/db/keypattern.h" + namespace mongo { class Chunk; + class FieldRangeSet; /* A ShardKeyPattern is a pattern indicating what data to extract from the object to make the shard key from. Analogous to an index key pattern. @@ -66,7 +69,7 @@ namespace mongo { */ bool hasShardKey( const BSONObj& obj ) const; - BSONObj key() const { return pattern; } + BSONObj key() const { return pattern.toBSON(); } string toString() const; @@ -83,7 +86,7 @@ namespace mongo { * @return * true if 'this' is a prefix (not necessarily contained) of 'otherPattern'. */ - bool isPrefixOf( const BSONObj& otherPattern ) const; + bool isPrefixOf( const KeyPattern& otherPattern ) const; /** * @return @@ -106,7 +109,7 @@ namespace mongo { * is never capable of being a unique index, and thus is an invalid setting * for the 'uniqueIndexPattern' argument. */ - bool isUniqueIndexCompatible( const BSONObj& uniqueIndexPattern ) const; + bool isUniqueIndexCompatible( const KeyPattern& uniqueIndexPattern ) const; /** * @return @@ -115,15 +118,27 @@ namespace mongo { * With our current index expression language, "special" shard keys are any keys * that are not a simple list of field names and 1/-1 values. */ - bool isSpecial() const; + bool isSpecial() const { return pattern.isSpecial(); } /** * @return BSONObj with _id and shardkey at front. May return original object. */ BSONObj moveToFront(const BSONObj& obj) const; + /**@param queryConstraints a FieldRangeSet formed from parsing a query + * @return an ordered list of bounds generated using this KeyPattern + * and the constraints from the FieldRangeSet + * + * The value of frsp->matchPossibleForSingleKeyFRS(fromQuery) should be true, + * otherwise this function could throw. + * + */ + BoundList keyBounds( const FieldRangeSet& queryConstraints ) const{ + return pattern.keyBounds( queryConstraints ); + } + private: - BSONObj pattern; + KeyPattern pattern; BSONObj gMin; BSONObj gMax; @@ -132,21 +147,7 @@ namespace mongo { }; inline BSONObj ShardKeyPattern::extractKey(const BSONObj& from) const { - BSONObj k = from; - bool needExtraction = false; - - BSONObjIterator a(from); - BSONObjIterator b(pattern); - while (a.more() && b.more()){ - if (strcmp(a.next().fieldName(), b.next().fieldName()) != 0){ - needExtraction = true; - break; - } - } - - if (needExtraction || a.more() != b.more()) - k = from.extractFields(pattern); - + BSONObj k = pattern.extractSingleKey( from ); uassert(13334, "Shard Key must be less than 512 bytes", k.objsize() < 512); return k; } |