コード例 #1
0
ファイル: get_runner.cpp プロジェクト: pkranas/mongo
Status getRunnerDistinct(Collection* collection,
                         const BSONObj& query,
                         const string& field,
                         Runner** out) {
    // This should'a been checked by the distinct command.
    verify(collection);

    // TODO: check for idhack here?

    // When can we do a fast distinct hack?
    // 1. There is a plan with just one leaf and that leaf is an ixscan.
    // 2. The ixscan indexes the field we're interested in.
    // 2a: We are correct if the index contains the field but for now we look for prefix.
    // 3. The query is covered/no fetch.
    //
    // We go through normal planning (with limited parameters) to see if we can produce
    // a soln with the above properties.

    QueryPlannerParams plannerParams;
    plannerParams.options = QueryPlannerParams::NO_TABLE_SCAN;

    IndexCatalog::IndexIterator ii = collection->getIndexCatalog()->getIndexIterator(false);
    while (ii.more()) {
        const IndexDescriptor* desc = ii.next();
        // The distinct hack can work if any field is in the index but it's not always clear
        // if it's a win unless it's the first field.
        if (desc->keyPattern().firstElement().fieldName() == field) {
            plannerParams.indices.push_back(IndexEntry(desc->keyPattern(),
                                            desc->isMultikey(),
                                            desc->isSparse(),
                                            desc->indexName(),
                                            desc->infoObj()));
        }
    }

    // If there are no suitable indices for the distinct hack and the _id index is the only
    // index in the catalog, a collection scan is the only possible solution. Bail out now
    // into regular planning with no projection. Projection is unnecessary because there
    // is no possibility of a covered index scan.
    if (plannerParams.indices.empty() &&
            collection->getIndexCatalog()->numIndexesTotal() == 1 &&
            collection->getIndexCatalog()->haveIdIndex()) {
        CanonicalQuery* cq;
        Status status = CanonicalQuery::canonicalize(collection->ns().ns(), query, BSONObj(),
                        BSONObj(), &cq);
        if (!status.isOK()) {
            return status;
        }
        // Takes ownership of cq.
        return getRunner(cq, out);
    }

    // We only care about the field that we're projecting over.  Have to drop the _id field
    // explicitly because those are .find() semantics.
    //
    // Applying a projection allows the planner to try to give us covered plans.
    BSONObj projection;
    if ("_id" == field) {
        projection = BSON("_id" << 1);
    }
    else {
        projection = BSON("_id" << 0 << field << 1);
    }

    // Apply a projection of the key.  Empty BSONObj() is for the sort.
    CanonicalQuery* cq;
    Status status = CanonicalQuery::canonicalize(collection->ns().ns(), query, BSONObj(), projection, &cq);
    if (!status.isOK()) {
        return status;
    }

    // No index has the field we're looking for.  Punt to normal planning.
    if (plannerParams.indices.empty()) {
        // Takes ownership of cq.
        return getRunner(cq, out);
    }

    // If we're here, we have an index prefixed by the field we're distinct-ing over.

    // If there's no query, we can just distinct-scan one of the indices.
    // Not every index in plannerParams.indices may be suitable. Refer to
    // getDistinctNodeIndex().
    size_t distinctNodeIndex = 0;
    if (query.isEmpty() &&
            getDistinctNodeIndex(plannerParams.indices, &distinctNodeIndex)) {
        DistinctNode* dn = new DistinctNode();
        dn->indexKeyPattern = plannerParams.indices[distinctNodeIndex].keyPattern;
        dn->direction = 1;
        IndexBoundsBuilder::allValuesBounds(dn->indexKeyPattern, &dn->bounds);
        dn->fieldNo = 0;

        QueryPlannerParams params;

        // Takes ownership of 'dn'.
        QuerySolution* soln = QueryPlannerAnalysis::analyzeDataAccess(*cq, params, dn);
        verify(soln);

        WorkingSet* ws;
        PlanStage* root;
        verify(StageBuilder::build(*soln, &root, &ws));
        *out = new SingleSolutionRunner(collection, cq, soln, root, ws);
        return Status::OK();
    }

    // See if we can answer the query in a fast-distinct compatible fashion.
    vector<QuerySolution*> solutions;
    status = QueryPlanner::plan(*cq, plannerParams, &solutions);
    if (!status.isOK()) {
        return getRunner(cq, out);
    }

    // XXX: why do we need to do this?  planner should prob do this internally
    cq->root()->resetTag();

    // We look for a solution that has an ixscan we can turn into a distinctixscan
    for (size_t i = 0; i < solutions.size(); ++i) {
        if (turnIxscanIntoDistinctIxscan(solutions[i], field)) {
            // Great, we can use solutions[i].  Clean up the other QuerySolution(s).
            for (size_t j = 0; j < solutions.size(); ++j) {
                if (j != i) {
                    delete solutions[j];
                }
            }

            // Build and return the SSR over solutions[i].
            WorkingSet* ws;
            PlanStage* root;
            verify(StageBuilder::build(*solutions[i], &root, &ws));
            *out = new SingleSolutionRunner(collection, cq, solutions[i], root, ws);
            return Status::OK();
        }
    }

    // If we're here, the planner made a soln with the restricted index set but we couldn't
    // translate any of them into a distinct-compatible soln.  So, delete the solutions and just
    // go through normal planning.
    for (size_t i = 0; i < solutions.size(); ++i) {
        delete solutions[i];
    }

    return getRunner(cq, out);
}
コード例 #2
0
ファイル: get_executor.cpp プロジェクト: MohdVara/mongo
    Status getExecutorDistinct(OperationContext* txn,
                               Collection* collection,
                               const BSONObj& query,
                               const std::string& field,
                               PlanExecutor** out) {
        // This should'a been checked by the distinct command.
        invariant(collection);

        // TODO: check for idhack here?

        // When can we do a fast distinct hack?
        // 1. There is a plan with just one leaf and that leaf is an ixscan.
        // 2. The ixscan indexes the field we're interested in.
        // 2a: We are correct if the index contains the field but for now we look for prefix.
        // 3. The query is covered/no fetch.
        //
        // We go through normal planning (with limited parameters) to see if we can produce
        // a soln with the above properties.

        QueryPlannerParams plannerParams;
        plannerParams.options = QueryPlannerParams::NO_TABLE_SCAN;

        IndexCatalog::IndexIterator ii = collection->getIndexCatalog()->getIndexIterator(false);
        while (ii.more()) {
            const IndexDescriptor* desc = ii.next();
            // The distinct hack can work if any field is in the index but it's not always clear
            // if it's a win unless it's the first field.
            if (desc->keyPattern().firstElement().fieldName() == field) {
                plannerParams.indices.push_back(IndexEntry(desc->keyPattern(),
                                                           desc->getAccessMethodName(),
                                                           desc->isMultikey(),
                                                           desc->isSparse(),
                                                           desc->indexName(),
                                                           desc->infoObj()));
            }
        }

        const WhereCallbackReal whereCallback(collection->ns().db());

        // If there are no suitable indices for the distinct hack bail out now into regular planning
        // with no projection.
        if (plannerParams.indices.empty()) {
            CanonicalQuery* cq;
            Status status = CanonicalQuery::canonicalize(
                                collection->ns().ns(), query, &cq, whereCallback);
            if (!status.isOK()) {
                return status;
            }

            // Takes ownership of 'cq'.
            return getExecutor(txn, collection, cq, out);
        }

        //
        // If we're here, we have an index prefixed by the field we're distinct-ing over.
        //

        // Applying a projection allows the planner to try to give us covered plans that we can turn
        // into the projection hack.  getDistinctProjection deals with .find() projection semantics
        // (ie _id:1 being implied by default).
        BSONObj projection = getDistinctProjection(field);

        // Apply a projection of the key.  Empty BSONObj() is for the sort.
        CanonicalQuery* cq;
        Status status = CanonicalQuery::canonicalize(collection->ns().ns(),
                                                     query,
                                                     BSONObj(),
                                                     projection,
                                                     &cq,
                                                     whereCallback);
        if (!status.isOK()) {
            return status;
        }

        auto_ptr<CanonicalQuery> autoCq(cq);

        // If there's no query, we can just distinct-scan one of the indices.
        // Not every index in plannerParams.indices may be suitable. Refer to
        // getDistinctNodeIndex().
        size_t distinctNodeIndex = 0;
        if (query.isEmpty() &&
            getDistinctNodeIndex(plannerParams.indices, field, &distinctNodeIndex)) {
            DistinctNode* dn = new DistinctNode();
            dn->indexKeyPattern = plannerParams.indices[distinctNodeIndex].keyPattern;
            dn->direction = 1;
            IndexBoundsBuilder::allValuesBounds(dn->indexKeyPattern, &dn->bounds);
            dn->fieldNo = 0;

            QueryPlannerParams params;

            // Takes ownership of 'dn'.
            QuerySolution* soln = QueryPlannerAnalysis::analyzeDataAccess(*cq, params, dn);
            invariant(soln);

            LOG(2) << "Using fast distinct: " << cq->toStringShort()
                   << ", planSummary: " << getPlanSummary(*soln);

            WorkingSet* ws = new WorkingSet();
            PlanStage* root;
            verify(StageBuilder::build(txn, collection, *soln, ws, &root));
            // Takes ownership of its arguments (except for 'collection').
            *out = new PlanExecutor(ws, root, soln, autoCq.release(), collection);
            return Status::OK();
        }

        // See if we can answer the query in a fast-distinct compatible fashion.
        vector<QuerySolution*> solutions;
        status = QueryPlanner::plan(*cq, plannerParams, &solutions);
        if (!status.isOK()) {
            return getExecutor(txn, collection, autoCq.release(), out);
        }

        // We look for a solution that has an ixscan we can turn into a distinctixscan
        for (size_t i = 0; i < solutions.size(); ++i) {
            if (turnIxscanIntoDistinctIxscan(solutions[i], field)) {
                // Great, we can use solutions[i].  Clean up the other QuerySolution(s).
                for (size_t j = 0; j < solutions.size(); ++j) {
                    if (j != i) {
                        delete solutions[j];
                    }
                }

                LOG(2) << "Using fast distinct: " << cq->toStringShort()
                       << ", planSummary: " << getPlanSummary(*solutions[i]);

                // Build and return the SSR over solutions[i].
                WorkingSet* ws = new WorkingSet();
                PlanStage* root;
                verify(StageBuilder::build(txn, collection, *solutions[i], ws, &root));
                // Takes ownership of its arguments (except for 'collection').
                *out = new PlanExecutor(ws, root, solutions[i], autoCq.release(), collection);
                return Status::OK();
            }
        }

        // If we're here, the planner made a soln with the restricted index set but we couldn't
        // translate any of them into a distinct-compatible soln.  So, delete the solutions and just
        // go through normal planning.
        for (size_t i = 0; i < solutions.size(); ++i) {
            delete solutions[i];
        }

        // We drop the projection from the 'cq'.  Unfortunately this is not trivial.
        status = CanonicalQuery::canonicalize(collection->ns().ns(), query, &cq, whereCallback);
        if (!status.isOK()) {
            return status;
        }

        autoCq.reset(cq);

        // Takes ownership of 'autoCq'.
        return getExecutor(txn, collection, autoCq.release(), out);
    }