예제 #1
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                coll = db->createCollection(&_txn, ns());
            }

            const int N = 50;

            for (int i = 0; i < N; ++i) {
                // We insert a:1 c:i for i=0..49 but in reverse order for the heck of it.
                insert(BSON("a" << 1 << "c" << N - i - 1));
                insert(BSON("b" << 1 << "c" << i));
            }

            BSONObj firstIndex = BSON("a" << 1 << "c" << -1);
            BSONObj secondIndex = BSON("b" << 1 << "c" << -1);

            addIndex(firstIndex);
            addIndex(secondIndex);

            WorkingSet* ws = new WorkingSet();
            // Sort by c:-1
            MergeSortStageParams msparams;
            msparams.pattern = BSON("c" << -1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            // a:1
            IndexScanParams params;
            params.descriptor = getIndex(firstIndex, coll);
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMaxKey(1);
            params.bounds.endKey = objWithMinKey(1);
            params.bounds.endKeyInclusive = true;
            // This is the direction along the index.
            params.direction = 1;
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));

            // b:1
            params.descriptor = getIndex(secondIndex, coll);
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));
            ctx.commit();

            PlanExecutor runner(ws, new FetchStage(ws, ms, NULL, coll), coll);

            for (int i = 0; i < N; ++i) {
                BSONObj first, second;
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&first, NULL));
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&second, NULL));
                ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
                ASSERT_EQUALS(N - i - 1, first["c"].numberInt());
                ASSERT((first.hasField("a") && second.hasField("b"))
                       || (first.hasField("b") && second.hasField("a")));
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(Runner::RUNNER_EOF, runner.getNext(&foo, NULL));
        }
예제 #2
0
        void run() {
            Client::WriteContext ctx(ns());
            OperationContextImpl txn;
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(ns());
            if (!coll) {
                coll = db->createCollection(&txn, ns());
            }

            const int N = 50;

            for (int i = 0; i < N; ++i) {
                insert(BSON("a" << 1 << "b" << 1 << "c" << i));
            }

            BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
            BSONObj secondIndex = BSON("b" << 1 << "c" << 1);

            addIndex(firstIndex);
            addIndex(secondIndex);

            WorkingSet* ws = new WorkingSet();
            // Sort by c:1
            MergeSortStageParams msparams;
            msparams.dedup = false;
            msparams.pattern = BSON("c" << 1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            // a:1
            IndexScanParams params;
            params.descriptor = getIndex(firstIndex, coll);
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMinKey(1);
            params.bounds.endKey = objWithMaxKey(1);
            params.bounds.endKeyInclusive = true;
            params.direction = 1;
            ms->addChild(new IndexScan(params, ws, NULL));

            // b:1
            params.descriptor = getIndex(secondIndex, coll);
            ms->addChild(new IndexScan(params, ws, NULL));

            PlanExecutor runner(ws, new FetchStage(ws, ms, NULL, coll), coll);

            for (int i = 0; i < N; ++i) {
                BSONObj first, second;
                // We inserted N objects but we get 2 * N from the runner because of dups.
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&first, NULL));
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&second, NULL));
                ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
                ASSERT_EQUALS(i, first["c"].numberInt());
                ASSERT((first.hasField("a") && second.hasField("b"))
                       || (first.hasField("b") && second.hasField("a")));
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(Runner::RUNNER_EOF, runner.getNext(&foo, NULL));
        }
예제 #3
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                coll = db->createCollection(&_txn, ns());
            }

            const int N = 50;

            for (int i = 0; i < N; ++i) {
                insert(BSON("a" << 1 << "c" << i));
                insert(BSON("b" << 1 << "c" << i));
            }

            BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
            BSONObj secondIndex = BSON("b" << 1 << "c" << 1);

            addIndex(firstIndex);
            addIndex(secondIndex);

            WorkingSet* ws = new WorkingSet();
            // Sort by c:1
            MergeSortStageParams msparams;
            msparams.pattern = BSON("c" << 1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            // a:1
            IndexScanParams params;
            params.descriptor = getIndex(firstIndex, coll);
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMinKey(1);
            params.bounds.endKey = objWithMaxKey(1);
            params.bounds.endKeyInclusive = true;
            params.direction = 1;
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));

            // b:51 (EOF)
            params.descriptor = getIndex(secondIndex, coll);
            params.bounds.startKey = BSON("" << 51 << "" << MinKey);
            params.bounds.endKey = BSON("" << 51 << "" << MaxKey);
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));
            ctx.commit();

            PlanExecutor runner(ws, new FetchStage(ws, ms, NULL, coll), coll);

            // Only getting results from the a:1 index scan.
            for (int i = 0; i < N; ++i) {
                BSONObj obj;
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&obj, NULL));
                ASSERT_EQUALS(i, obj["c"].numberInt());
                ASSERT_EQUALS(1, obj["a"].numberInt());
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(Runner::RUNNER_EOF, runner.getNext(&foo, NULL));
        }
예제 #4
0
    void run() {
        OldClientWriteContext ctx(&_txn, ns());
        Database* db = ctx.db();
        Collection* coll = db->getCollection(ns());
        if (!coll) {
            WriteUnitOfWork wuow(&_txn);
            coll = db->createCollection(&_txn, ns());
            wuow.commit();
        }

        unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
        // Sort by foo:1
        MergeSortStageParams msparams;
        msparams.pattern = BSON("foo" << 1);
        MergeSortStage* ms = new MergeSortStage(&_txn, msparams, ws.get(), coll);

        IndexScanParams params;
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = objWithMinKey(1);
        params.bounds.endKey = objWithMaxKey(1);
        params.bounds.endKeyInclusive = true;
        params.direction = 1;

        int numIndices = 20;
        for (int i = 0; i < numIndices; ++i) {
            // 'a', 'b', ...
            string index(1, 'a' + i);
            insert(BSON(index << 1 << "foo" << i));

            BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
            addIndex(indexSpec);
            params.descriptor = getIndex(indexSpec, coll);
            ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
        }
        unique_ptr<FetchStage> fetchStage =
            make_unique<FetchStage>(&_txn, ws.get(), ms, nullptr, coll);

        auto statusWithPlanExecutor = PlanExecutor::make(
            &_txn, std::move(ws), std::move(fetchStage), coll, PlanExecutor::YIELD_MANUAL);
        ASSERT_OK(statusWithPlanExecutor.getStatus());
        unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());

        for (int i = 0; i < numIndices; ++i) {
            BSONObj obj;
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
            ASSERT_EQUALS(i, obj["foo"].numberInt());
            string index(1, 'a' + i);
            ASSERT_EQUALS(1, obj[index].numberInt());
        }

        // Should be done now.
        BSONObj foo;
        ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
    }
예제 #5
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                WriteUnitOfWork wuow(&_txn);
                coll = db->createCollection(&_txn, ns());
                wuow.commit();
            }

            WorkingSet* ws = new WorkingSet();
            // Sort by foo:1
            MergeSortStageParams msparams;
            msparams.pattern = BSON("foo" << 1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            IndexScanParams params;
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMinKey(1);
            params.bounds.endKey = objWithMaxKey(1);
            params.bounds.endKeyInclusive = true;
            params.direction = 1;

            int numIndices = 20;
            for (int i = 0; i < numIndices; ++i) {
                // 'a', 'b', ...
                string index(1, 'a' + i);
                insert(BSON(index << 1 << "foo" << i));

                BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
                addIndex(indexSpec);
                params.descriptor = getIndex(indexSpec, coll);
                ms->addChild(new IndexScan(&_txn, params, ws, NULL));
            }

            PlanExecutor* rawExec;
            Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
                                               coll, PlanExecutor::YIELD_MANUAL, &rawExec);
            ASSERT_OK(status);
            boost::scoped_ptr<PlanExecutor> exec(rawExec);

            for (int i = 0; i < numIndices; ++i) {
                BSONObj obj;
                ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
                ASSERT_EQUALS(i, obj["foo"].numberInt());
                string index(1, 'a' + i);
                ASSERT_EQUALS(1, obj[index].numberInt());
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
        }
예제 #6
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                coll = db->createCollection(&_txn, ns());
            }

            WorkingSet* ws = new WorkingSet();
            // Sort by foo:1
            MergeSortStageParams msparams;
            msparams.pattern = BSON("foo" << 1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            IndexScanParams params;
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMinKey(1);
            params.bounds.endKey = objWithMaxKey(1);
            params.bounds.endKeyInclusive = true;
            params.direction = 1;

            int numIndices = 20;
            for (int i = 0; i < numIndices; ++i) {
                // 'a', 'b', ...
                string index(1, 'a' + i);
                insert(BSON(index << 1 << "foo" << i));

                BSONObj indexSpec = BSON(index << 1 << "foo" << 1);
                addIndex(indexSpec);
                params.descriptor = getIndex(indexSpec, coll);
                ms->addChild(new IndexScan(&_txn, params, ws, NULL));
            }
            ctx.commit();

            PlanExecutor runner(ws, new FetchStage(ws, ms, NULL, coll), coll);

            for (int i = 0; i < numIndices; ++i) {
                BSONObj obj;
                ASSERT_EQUALS(Runner::RUNNER_ADVANCED, runner.getNext(&obj, NULL));
                ASSERT_EQUALS(i, obj["foo"].numberInt());
                string index(1, 'a' + i);
                ASSERT_EQUALS(1, obj[index].numberInt());
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(Runner::RUNNER_EOF, runner.getNext(&foo, NULL));
        }
예제 #7
0
    void run() {
        OldClientWriteContext ctx(&_txn, ns());
        Database* db = ctx.db();
        Collection* coll = db->getCollection(ns());
        if (!coll) {
            WriteUnitOfWork wuow(&_txn);
            coll = db->createCollection(&_txn, ns());
            wuow.commit();
        }

        const int N = 50;

        for (int i = 0; i < N; ++i) {
            insert(BSON("a" << 1 << "c" << i));
            insert(BSON("b" << 1 << "c" << i));
        }

        BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
        BSONObj secondIndex = BSON("b" << 1 << "c" << 1);

        addIndex(firstIndex);
        addIndex(secondIndex);

        unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
        // Sort by c:1
        MergeSortStageParams msparams;
        msparams.pattern = BSON("c" << 1);
        MergeSortStage* ms = new MergeSortStage(&_txn, msparams, ws.get(), coll);

        // a:1
        IndexScanParams params;
        params.descriptor = getIndex(firstIndex, coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = objWithMinKey(1);
        params.bounds.endKey = objWithMaxKey(1);
        params.bounds.endKeyInclusive = true;
        params.direction = 1;
        ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));

        // b:51 (EOF)
        params.descriptor = getIndex(secondIndex, coll);
        params.bounds.startKey = BSON("" << 51 << "" << MinKey);
        params.bounds.endKey = BSON("" << 51 << "" << MaxKey);
        ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
        unique_ptr<FetchStage> fetchStage =
            make_unique<FetchStage>(&_txn, ws.get(), ms, nullptr, coll);

        auto statusWithPlanExecutor = PlanExecutor::make(
            &_txn, std::move(ws), std::move(fetchStage), coll, PlanExecutor::YIELD_MANUAL);
        ASSERT_OK(statusWithPlanExecutor.getStatus());
        unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());

        // Only getting results from the a:1 index scan.
        for (int i = 0; i < N; ++i) {
            BSONObj obj;
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&obj, NULL));
            ASSERT_EQUALS(i, obj["c"].numberInt());
            ASSERT_EQUALS(1, obj["a"].numberInt());
        }

        // Should be done now.
        BSONObj foo;
        ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
    }
예제 #8
0
    void run() {
        OldClientWriteContext ctx(&_txn, ns());
        Database* db = ctx.db();
        Collection* coll = db->getCollection(ns());
        if (!coll) {
            WriteUnitOfWork wuow(&_txn);
            coll = db->createCollection(&_txn, ns());
            wuow.commit();
        }

        const int N = 50;

        for (int i = 0; i < N; ++i) {
            // We insert a:1 c:i for i=0..49 but in reverse order for the heck of it.
            insert(BSON("a" << 1 << "c" << N - i - 1));
            insert(BSON("b" << 1 << "c" << i));
        }

        BSONObj firstIndex = BSON("a" << 1 << "c" << -1);
        BSONObj secondIndex = BSON("b" << 1 << "c" << -1);

        addIndex(firstIndex);
        addIndex(secondIndex);

        unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
        // Sort by c:-1
        MergeSortStageParams msparams;
        msparams.pattern = BSON("c" << -1);
        MergeSortStage* ms = new MergeSortStage(&_txn, msparams, ws.get(), coll);

        // a:1
        IndexScanParams params;
        params.descriptor = getIndex(firstIndex, coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = objWithMaxKey(1);
        params.bounds.endKey = objWithMinKey(1);
        params.bounds.endKeyInclusive = true;
        // This is the direction along the index.
        params.direction = 1;
        ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));

        // b:1
        params.descriptor = getIndex(secondIndex, coll);
        ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
        unique_ptr<FetchStage> fetchStage =
            make_unique<FetchStage>(&_txn, ws.get(), ms, nullptr, coll);

        auto statusWithPlanExecutor = PlanExecutor::make(
            &_txn, std::move(ws), std::move(fetchStage), coll, PlanExecutor::YIELD_MANUAL);
        ASSERT_OK(statusWithPlanExecutor.getStatus());
        unique_ptr<PlanExecutor> exec = std::move(statusWithPlanExecutor.getValue());

        for (int i = 0; i < N; ++i) {
            BSONObj first, second;
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
            first = first.getOwned();
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
            ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
            ASSERT_EQUALS(N - i - 1, first["c"].numberInt());
            ASSERT((first.hasField("a") && second.hasField("b")) ||
                   (first.hasField("b") && second.hasField("a")));
        }

        // Should be done now.
        BSONObj foo;
        ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
    }
예제 #9
0
        void run() {
            Client::WriteContext ctx(&_txn, ns());
            Database* db = ctx.ctx().db();
            Collection* coll = db->getCollection(&_txn, ns());
            if (!coll) {
                WriteUnitOfWork wuow(&_txn);
                coll = db->createCollection(&_txn, ns());
                wuow.commit();
            }

            const int N = 50;

            for (int i = 0; i < N; ++i) {
                insert(BSON("a" << 1 << "b" << 1 << "c" << i));
            }

            BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
            BSONObj secondIndex = BSON("b" << 1 << "c" << 1);

            addIndex(firstIndex);
            addIndex(secondIndex);

            WorkingSet* ws = new WorkingSet();
            // Sort by c:1
            MergeSortStageParams msparams;
            msparams.dedup = false;
            msparams.pattern = BSON("c" << 1);
            MergeSortStage* ms = new MergeSortStage(msparams, ws, coll);

            // a:1
            IndexScanParams params;
            params.descriptor = getIndex(firstIndex, coll);
            params.bounds.isSimpleRange = true;
            params.bounds.startKey = objWithMinKey(1);
            params.bounds.endKey = objWithMaxKey(1);
            params.bounds.endKeyInclusive = true;
            params.direction = 1;
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));

            // b:1
            params.descriptor = getIndex(secondIndex, coll);
            ms->addChild(new IndexScan(&_txn, params, ws, NULL));

            PlanExecutor* rawExec;
            Status status = PlanExecutor::make(&_txn, ws, new FetchStage(&_txn, ws, ms, NULL, coll),
                                               coll, PlanExecutor::YIELD_MANUAL, &rawExec);
            ASSERT_OK(status);
            boost::scoped_ptr<PlanExecutor> exec(rawExec);

            for (int i = 0; i < N; ++i) {
                BSONObj first, second;
                // We inserted N objects but we get 2 * N from the runner because of dups.
                ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
                ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
                ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
                ASSERT_EQUALS(i, first["c"].numberInt());
                ASSERT((first.hasField("a") && second.hasField("b"))
                       || (first.hasField("b") && second.hasField("a")));
            }

            // Should be done now.
            BSONObj foo;
            ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
        }
예제 #10
0
    void run() {
        OldClientWriteContext ctx(&_opCtx, ns());
        Database* db = ctx.db();
        Collection* coll = db->getCollection(&_opCtx, ns());
        if (!coll) {
            WriteUnitOfWork wuow(&_opCtx);
            coll = db->createCollection(&_opCtx, ns());
            wuow.commit();
        }

        const int N = 50;

        for (int i = 0; i < N; ++i) {
            insert(BSON("a" << 1 << "c" << i << "d"
                            << "abc"));
            insert(BSON("b" << 1 << "c" << i << "d"
                            << "cba"));
        }

        BSONObj firstIndex = BSON("a" << 1 << "c" << 1 << "d" << 1);
        BSONObj secondIndex = BSON("b" << 1 << "c" << 1 << "d" << 1);

        addIndex(firstIndex);
        addIndex(secondIndex);

        unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
        // Sort by c:1, d:1
        MergeSortStageParams msparams;
        msparams.pattern = BSON("c" << 1 << "d" << 1);
        CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
        msparams.collator = &collator;
        MergeSortStage* ms = new MergeSortStage(&_opCtx, msparams, ws.get(), coll);

        // a:1
        IndexScanParams params;
        params.descriptor = getIndex(firstIndex, coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = objWithMinKey(1);
        params.bounds.endKey = objWithMaxKey(1);
        params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
        params.direction = 1;
        ms->addChild(new IndexScan(&_opCtx, params, ws.get(), NULL));

        // b:1
        params.descriptor = getIndex(secondIndex, coll);
        ms->addChild(new IndexScan(&_opCtx, params, ws.get(), NULL));

        unique_ptr<FetchStage> fetchStage =
            make_unique<FetchStage>(&_opCtx, ws.get(), ms, nullptr, coll);
        // Must fetch if we want to easily pull out an obj.
        auto statusWithPlanExecutor = PlanExecutor::make(
            &_opCtx, std::move(ws), std::move(fetchStage), coll, PlanExecutor::NO_YIELD);
        ASSERT_OK(statusWithPlanExecutor.getStatus());
        auto exec = std::move(statusWithPlanExecutor.getValue());

        for (int i = 0; i < N; ++i) {
            BSONObj first, second;
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
            first = first.getOwned();
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
            ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
            ASSERT_EQUALS(i, first["c"].numberInt());
            // {b: 1, c: i, d: "cba"} should precede {a: 1, c: i, d: "abc"}.
            ASSERT(first.hasField("b") && second.hasField("a"));
        }

        // Should be done now.
        BSONObj foo;
        ASSERT_NOT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&foo, NULL));
    }
예제 #11
0
    void run() {
        OldClientWriteContext ctx(&_opCtx, ns());
        Database* db = ctx.db();
        Collection* coll = db->getCollection(&_opCtx, ns());
        if (!coll) {
            WriteUnitOfWork wuow(&_opCtx);
            coll = db->createCollection(&_opCtx, ns());
            wuow.commit();
        }

        const int N = 50;

        for (int i = 0; i < N; ++i) {
            insert(BSON("a" << 1 << "b" << 1 << "c" << i));
        }

        BSONObj firstIndex = BSON("a" << 1 << "c" << 1);
        BSONObj secondIndex = BSON("b" << 1 << "c" << 1);

        addIndex(firstIndex);
        addIndex(secondIndex);

        unique_ptr<WorkingSet> ws = make_unique<WorkingSet>();
        // Sort by c:1
        MergeSortStageParams msparams;
        msparams.dedup = false;
        msparams.pattern = BSON("c" << 1);
        MergeSortStage* ms = new MergeSortStage(&_opCtx, msparams, ws.get(), coll);

        // a:1
        IndexScanParams params;
        params.descriptor = getIndex(firstIndex, coll);
        params.bounds.isSimpleRange = true;
        params.bounds.startKey = objWithMinKey(1);
        params.bounds.endKey = objWithMaxKey(1);
        params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
        params.direction = 1;
        ms->addChild(new IndexScan(&_opCtx, params, ws.get(), NULL));

        // b:1
        params.descriptor = getIndex(secondIndex, coll);
        ms->addChild(new IndexScan(&_opCtx, params, ws.get(), NULL));
        unique_ptr<FetchStage> fetchStage =
            make_unique<FetchStage>(&_opCtx, ws.get(), ms, nullptr, coll);

        auto statusWithPlanExecutor = PlanExecutor::make(
            &_opCtx, std::move(ws), std::move(fetchStage), coll, PlanExecutor::NO_YIELD);
        ASSERT_OK(statusWithPlanExecutor.getStatus());
        auto exec = std::move(statusWithPlanExecutor.getValue());

        for (int i = 0; i < N; ++i) {
            BSONObj first, second;
            // We inserted N objects but we get 2 * N from the runner because of dups.
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&first, NULL));
            first = first.getOwned();
            ASSERT_EQUALS(PlanExecutor::ADVANCED, exec->getNext(&second, NULL));
            ASSERT_EQUALS(first["c"].numberInt(), second["c"].numberInt());
            ASSERT_EQUALS(i, first["c"].numberInt());
            ASSERT((first.hasField("a") && second.hasField("b")) ||
                   (first.hasField("b") && second.hasField("a")));
        }

        // Should be done now.
        BSONObj foo;
        ASSERT_EQUALS(PlanExecutor::IS_EOF, exec->getNext(&foo, NULL));
    }