void run() { dblock lk; const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeGap"; { DBDirectClient c; for( int i = 0; i < 10; ++i ) c.insert( ns, BSON( "a" << i ) ); for( int i = 100; i < 110; ++i ) c.insert( ns, BSON( "a" << i ) ); ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) ); } BoundList b; b.push_back( pair< BSONObj, BSONObj >( BSON( "" << -50 ), BSON( "" << 2 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 40 ), BSON( "" << 60 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 109 ), BSON( "" << 200 ) ) ); Client::Context ctx( ns ); BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->idx(1), b, 1 ); ASSERT_EQUALS( "BtreeCursor a_1 multi", c.toString() ); double expected[] = { 0, 1, 2, 109 }; for( int i = 0; i < 4; ++i ) { ASSERT( c.ok() ); ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() ); c.advance(); } ASSERT( !c.ok() ); }
void run() { dblock lk; const char *ns = "unittests.cursortests.BtreeCursorTests.MultiRangeReverse"; { DBDirectClient c; for( int i = 0; i < 10; ++i ) c.insert( ns, BSON( "a" << i ) ); ASSERT( c.ensureIndex( ns, BSON( "a" << 1 ) ) ); } BoundList b; b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 6 ), BSON( "" << 4 ) ) ); b.push_back( pair< BSONObj, BSONObj >( BSON( "" << 2 ), BSON( "" << 1 ) ) ); setClient( ns ); BtreeCursor c( nsdetails( ns ), 1, nsdetails( ns )->indexes[ 1 ], b, -1 ); ASSERT_EQUALS( "BtreeCursor a_1 reverse multi", c.toString() ); double expected[] = { 6, 5, 4, 2, 1 }; for( int i = 0; i < 5; ++i ) { ASSERT( c.ok() ); ASSERT_EQUALS( expected[ i ], c.currKey().firstElement().number() ); c.advance(); } ASSERT( !c.ok() ); }
BoundList ShardKeyPattern::flattenBounds(const IndexBounds& indexBounds) const { invariant(indexBounds.fields.size() == (size_t)_keyPattern.toBSON().nFields()); // If any field is unsatisfied, return empty bound list. for (vector<OrderedIntervalList>::const_iterator it = indexBounds.fields.begin(); it != indexBounds.fields.end(); it++) { if (it->intervals.size() == 0) { return BoundList(); } } // To construct our bounds we will generate intervals based on bounds for // the first field, then compound intervals based on constraints for the first // 2 fields, then compound intervals for the first 3 fields, etc. // As we loop through the fields, we start generating new intervals that will later // get extended in another iteration of the loop. We define these partially constructed // intervals using pairs of BSONObjBuilders (shared_ptrs, since after one iteration of the // loop they still must exist outside their scope). 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 keyIter(_keyPattern.toBSON()); // until equalityOnly is false, we are just dealing with equality (no range or $in queries). bool equalityOnly = true; for (size_t i = 0; i < indexBounds.fields.size(); i++) { BSONElement e = keyIter.next(); StringData fieldName = e.fieldNameStringData(); // get the relevant intervals for this field, but we may have to transform the // list of what's relevant according to the expression for this field const OrderedIntervalList& oil = indexBounds.fields[i]; const vector<Interval>& intervals = oil.intervals; if (equalityOnly) { if (intervals.size() == 1 && intervals.front().isPoint()) { // this field is only a single point-interval BoundBuilders::const_iterator j; for (j = builders.begin(); j != builders.end(); ++j) { j->first->appendAs(intervals.front().start, fieldName); j->second->appendAs(intervals.front().end, fieldName); } } else { // This clause is the first to generate more than a single point. // We only execute this clause once. After that, we simplify the bound // extensions to prevent combinatorial explosion. equalityOnly = false; BoundBuilders newBuilders; for (BoundBuilders::const_iterator it = builders.begin(); it != builders.end(); ++it) { BSONObj first = it->first->obj(); BSONObj second = it->second->obj(); for (vector<Interval>::const_iterator interval = intervals.begin(); interval != intervals.end(); ++interval) { uassert(17439, "combinatorial limit of $in partitioning of results exceeded", newBuilders.size() < kMaxFlattenedInCombinations); 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(interval->start, fieldName); newBuilders.back().second->appendAs(interval->end, fieldName); } } builders = newBuilders; } } else { // if we've already generated a range or multiple point-intervals // just extend what we've generated with min/max bounds for this field BoundBuilders::const_iterator j; for (j = builders.begin(); j != builders.end(); ++j) { j->first->appendAs(intervals.front().start, fieldName); j->second->appendAs(intervals.back().end, fieldName); } } } 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; }