Exemplo n.º 1
0
 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() );
 }
Exemplo n.º 2
0
void ChunkManager::getShardIdsForQuery(OperationContext* txn,
                                       const BSONObj& query,
                                       set<ShardId>* shardIds) const {
    auto statusWithCQ =
        CanonicalQuery::canonicalize(NamespaceString(_ns), query, ExtensionsCallbackNoop());

    uassertStatusOK(statusWithCQ.getStatus());
    unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());

    // Query validation
    if (QueryPlannerCommon::hasNode(cq->root(), MatchExpression::GEO_NEAR)) {
        uassert(13501, "use geoNear command rather than $near query", false);
    }

    // Fast path for targeting equalities on the shard key.
    auto shardKeyToFind = _keyPattern.extractShardKeyFromQuery(*cq);
    if (shardKeyToFind.isOK() && !shardKeyToFind.getValue().isEmpty()) {
        auto chunk = findIntersectingChunk(txn, shardKeyToFind.getValue());
        shardIds->insert(chunk->getShardId());
        return;
    }

    // Transforms query into bounds for each field in the shard key
    // for example :
    //   Key { a: 1, b: 1 },
    //   Query { a : { $gte : 1, $lt : 2 },
    //            b : { $gte : 3, $lt : 4 } }
    //   => Bounds { a : [1, 2), b : [3, 4) }
    IndexBounds bounds = getIndexBoundsForQuery(_keyPattern.toBSON(), *cq);

    // Transforms bounds for each shard key field into full shard key ranges
    // for example :
    //   Key { a : 1, b : 1 }
    //   Bounds { a : [1, 2), b : [3, 4) }
    //   => Ranges { a : 1, b : 3 } => { a : 2, b : 4 }
    BoundList ranges = _keyPattern.flattenBounds(bounds);

    for (BoundList::const_iterator it = ranges.begin(); it != ranges.end(); ++it) {
        getShardIdsForRange(*shardIds, it->first /*min*/, it->second /*max*/);

        // once we know we need to visit all shards no need to keep looping
        if (shardIds->size() == _shardIds.size())
            break;
    }

    // SERVER-4914 Some clients of getShardIdsForQuery() assume at least one shard will be
    // returned.  For now, we satisfy that assumption by adding a shard with no matches rather
    // than return an empty set of shards.
    if (shardIds->empty()) {
        massert(16068, "no chunk ranges available", !_chunkRanges.ranges().empty());
        shardIds->insert(_chunkRanges.ranges().begin()->second->getShardId());
    }
}
Exemplo n.º 3
0
    void ChunkManager::getShardIdsForQuery(set<ShardId>& shardIds, const BSONObj& query) const {
        CanonicalQuery* canonicalQuery = NULL;
        Status status = CanonicalQuery::canonicalize(
                            _ns,
                            query,
                            &canonicalQuery,
                            WhereCallbackNoop());
                            
        boost::scoped_ptr<CanonicalQuery> canonicalQueryPtr(canonicalQuery);
        
        uassert(status.code(), status.reason(), status.isOK());

        // Query validation
        if (QueryPlannerCommon::hasNode(canonicalQuery->root(), MatchExpression::GEO_NEAR)) {
            uassert(13501, "use geoNear command rather than $near query", false);
        }

        // Transforms query into bounds for each field in the shard key
        // for example :
        //   Key { a: 1, b: 1 },
        //   Query { a : { $gte : 1, $lt : 2 },
        //            b : { $gte : 3, $lt : 4 } }
        //   => Bounds { a : [1, 2), b : [3, 4) }
        IndexBounds bounds = getIndexBoundsForQuery(_keyPattern.toBSON(), canonicalQuery);

        // Transforms bounds for each shard key field into full shard key ranges
        // for example :
        //   Key { a : 1, b : 1 }
        //   Bounds { a : [1, 2), b : [3, 4) }
        //   => Ranges { a : 1, b : 3 } => { a : 2, b : 4 }
        BoundList ranges = _keyPattern.flattenBounds(bounds);

        for (BoundList::const_iterator it = ranges.begin(); it != ranges.end();
            ++it) {

            getShardIdsForRange(shardIds, it->first /*min*/, it->second /*max*/);

            // once we know we need to visit all shards no need to keep looping
            if(shardIds.size() == _shardIds.size()) break;
        }

        // SERVER-4914 Some clients of getShardIdsForQuery() assume at least one shard will be
        // returned.  For now, we satisfy that assumption by adding a shard with no matches rather
        // than return an empty set of shards.
        if (shardIds.empty()) {
            massert( 16068, "no chunk ranges available", !_chunkRanges.ranges().empty() );
            shardIds.insert(_chunkRanges.ranges().begin()->second->getShardId());
        }
    }
Exemplo n.º 4
0
 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() );
 }
Exemplo n.º 5
0
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;
}