// static void HashAccessMethod::getKeysImpl(const BSONObj& obj, const string& hashedField, HashSeed seed, int hashVersion, bool isSparse, BSONObjSet* keys) { const char* cstr = hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array ); if (!fieldVal.eoo()) { BSONObj key = BSON( "" << makeSingleKey(fieldVal, seed, hashVersion)); keys->insert(key); } else if (!isSparse) { BSONObj nullObj = BSON("" << BSONNULL); keys->insert(BSON("" << makeSingleKey(nullObj.firstElement(), seed, hashVersion))); } }
void HashedIndexType::getKeys( const BSONObj &obj, BSONObjSet &keys ) const { string hashedFieldCopy = string( _hashedField ); const char* hashedFieldCopyPtr = hashedFieldCopy.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray( hashedFieldCopyPtr ); uassert( 16244 , "Error: hashed indexes do not currently support array values" , fieldVal.type() != Array ); if ( ! fieldVal.eoo() ) { BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); keys.insert( key ); } else if (! _isSparse ) { BSONObj nullobj = BSON( _hashedField << BSONNULL ); BSONElement nullElt = nullobj.firstElement(); BSONObj key = BSON( "" << makeSingleKey( nullElt , _seed , _hashVersion ) ); keys.insert( key ); } }
void HashAccessMethod::getKeys(const BSONObj& obj, BSONObjSet* keys) { const char* cstr = _hashedField.c_str(); BSONElement fieldVal = obj.getFieldDottedOrArray(cstr); uassert(16766, "Error: hashed indexes do not currently support array values", fieldVal.type() != Array ); if (!fieldVal.eoo()) { BSONObj key = BSON( "" << makeSingleKey( fieldVal , _seed , _hashVersion ) ); keys->insert( key ); } else if (!_descriptor->isSparse()) { keys->insert( _missingKey.copy() ); } }
shared_ptr<Cursor> HashedIndexType::newCursor( const BSONObj& query , const BSONObj& order , int numWanted ) const { //Use FieldRangeSet to parse the query into a vector of intervals //These should be point-intervals if this cursor is ever used //So the FieldInterval vector will be, e.g. <[1,1], [3,3], [6,6]> FieldRangeSet frs( "" , query , true, true ); const vector<FieldInterval>& intervals = frs.range( _hashedField.c_str() ).intervals(); //Force a match of the query against the actual document by giving //the cursor a matcher with an empty indexKeyPattern. This insures the //index is not used as a covered index. //NOTE: this forcing is necessary due to potential hash collisions const shared_ptr< CoveredIndexMatcher > forceDocMatcher( new CoveredIndexMatcher( query , BSONObj() ) ); //Construct a new query based on the hashes of the previous point-intervals //e.g. {a : {$in : [ hash(1) , hash(3) , hash(6) ]}} BSONObjBuilder newQueryBuilder; BSONObjBuilder inObj( newQueryBuilder.subobjStart( _hashedField ) ); BSONArrayBuilder inArray( inObj.subarrayStart("$in") ); vector<FieldInterval>::const_iterator i; for( i = intervals.begin(); i != intervals.end(); ++i ){ if ( ! i->equality() ){ const shared_ptr< BtreeCursor > exhaustiveCursor( BtreeCursor::make( nsdetails( _spec->getDetails()->parentNS().c_str()), *( _spec->getDetails() ), BSON( "" << MINKEY ) , BSON( "" << MAXKEY ) , true , 1 ) ); exhaustiveCursor->setMatcher( forceDocMatcher ); return exhaustiveCursor; } inArray.append( makeSingleKey( i->_lower._bound , _seed , _hashVersion ) ); } inArray.done(); inObj.done(); BSONObj newQuery = newQueryBuilder.obj(); //Use the point-intervals of the new query to create a Btree cursor FieldRangeSet newfrs( "" , newQuery , true, true ); shared_ptr<FieldRangeVector> newVector( new FieldRangeVector( newfrs , *_spec , 1 ) ); const shared_ptr< BtreeCursor > cursor( BtreeCursor::make( nsdetails( _spec->getDetails()->parentNS().c_str()), *( _spec->getDetails() ), newVector , 1 ) ); cursor->setMatcher( forceDocMatcher ); return cursor; }
HashedIndexType::HashedIndexType( const IndexPlugin* plugin , const IndexSpec* spec ) : IndexType( plugin , spec ) , _keyPattern( spec->keyPattern ) { //change these if single-field limitation lifted later uassert( 16241 , "Currently only single field hashed index supported." , _keyPattern.toBSON().nFields() == 1 ); uassert( 16242 , "Currently hashed indexes cannot guarantee uniqueness. Use a regular index." , ! (spec->info).getField("unique").booleanSafe() ); //Default _seed to 0 if "seed" is not included in the index spec //or if the value of "seed" is not a number _seed = (spec->info).getField("seed").numberInt(); //Default _isSparse to false if "sparse" is not included in the index spec //or if the value of "sparse" is not a boolean _isSparse = (spec->info).getField("sparse").booleanSafe(); //In case we have hashed indexes based on other hash functions in //the future, we store a hashVersion number. If hashVersion changes, // "makeSingleKey" will need to change accordingly. //Defaults to 0 if "hashVersion" is not included in the index spec //or if the value of "hashversion" is not a number _hashVersion = (spec->info).getField("hashVersion").numberInt(); //Get the hashfield name BSONElement firstElt = spec->keyPattern.firstElement(); massert( 16243 , "error: no hashed index field" , firstElt.str().compare( HASHED_INDEX_TYPE_IDENTIFIER ) == 0 ); _hashedField = firstElt.fieldName(); // Explicit null valued fields and missing fields are both represented in hashed indexes // using the hash value of the null BSONElement. This is partly for historical reasons // (hash of null was used in the initial release of hashed indexes and changing would alter // the data format). Additionally, in certain places the hashed index code and the index // bound calculation code assume null and missing are indexed identically. BSONObj nullObj = BSON( "" << BSONNULL ); _missingKey = BSON( "" << makeSingleKey( nullObj.firstElement(), _seed, _hashVersion ) ); }
HashAccessMethod::HashAccessMethod(IndexDescriptor* descriptor) : BtreeBasedAccessMethod(descriptor) { const string HASHED_INDEX_TYPE_IDENTIFIER = "hashed"; //change these if single-field limitation lifted later uassert(16763, "Currently only single field hashed index supported." , 1 == descriptor->getNumFields()); uassert(16764, "Currently hashed indexes cannot guarantee uniqueness. Use a regular index.", !descriptor->unique()); //Default _seed to 0 if "seed" is not included in the index spec //or if the value of "seed" is not a number _seed = descriptor->getInfoElement("seed").numberInt(); //In case we have hashed indexes based on other hash functions in //the future, we store a hashVersion number. If hashVersion changes, // "makeSingleKey" will need to change accordingly. //Defaults to 0 if "hashVersion" is not included in the index spec //or if the value of "hashversion" is not a number _hashVersion = descriptor->getInfoElement("hashVersion").numberInt(); //Get the hashfield name BSONElement firstElt = descriptor->keyPattern().firstElement(); massert(16765, "error: no hashed index field", firstElt.str().compare(HASHED_INDEX_TYPE_IDENTIFIER) == 0); _hashedField = firstElt.fieldName(); // Explicit null valued fields and missing fields are both represented in hashed indexes // using the hash value of the null BSONElement. This is partly for historical reasons // (hash of null was used in the initial release of hashed indexes and changing would alter // the data format). Additionally, in certain places the hashed index code and the index // bound calculation code assume null and missing are indexed identically. BSONObj nullObj = BSON("" << BSONNULL); _missingKey = BSON("" << makeSingleKey(nullObj.firstElement(), _seed, _hashVersion)); }