// static void QueryPlannerIXSelect::rateIndices(MatchExpression* node, string prefix, const vector<IndexEntry>& indices) { // Do not traverse tree beyond negation node MatchExpression::MatchType exprtype = node->matchType(); if (exprtype == MatchExpression::NOT || exprtype == MatchExpression::NOR) { return; } // Every indexable node is tagged even when no compatible index is // available. if (Indexability::nodeCanUseIndexOnOwnField(node)) { string fullPath = prefix + node->path().toString(); verify(NULL == node->getTag()); RelevantTag* rt = new RelevantTag(); node->setTag(rt); rt->path = fullPath; // TODO: This is slow, with all the string compares. for (size_t i = 0; i < indices.size(); ++i) { BSONObjIterator it(indices[i].keyPattern); BSONElement elt = it.next(); if (elt.fieldName() == fullPath && compatible(elt, indices[i], node)) { rt->first.push_back(i); } while (it.more()) { elt = it.next(); if (elt.fieldName() == fullPath && compatible(elt, indices[i], node)) { rt->notFirst.push_back(i); } } } } else if (Indexability::arrayUsesIndexOnChildren(node)) { // See comment in getFields about all/elemMatch and paths. if (!node->path().empty()) { prefix += node->path().toString() + "."; } for (size_t i = 0; i < node->numChildren(); ++i) { rateIndices(node->getChild(i), prefix, indices); } } else if (node->isLogical()) { for (size_t i = 0; i < node->numChildren(); ++i) { rateIndices(node->getChild(i), prefix, indices); } } }
// static void QueryPlanner::plan(const CanonicalQuery& query, const vector<BSONObj>& indexKeyPatterns, vector<QuerySolution*>* out) { // XXX: If pq.hasOption(QueryOption_OplogReplay) use FindingStartCursor equivalent which // must be translated into stages. // // Planner Section 1: Calculate predicate/index data. // // Get all the predicates (and their fields). PredicateMap predicates; makePredicateMap(query.root(), &predicates); // If the query requests a tailable cursor, the only solution is a collscan + filter with // tailable set on the collscan. TODO: This is a policy departure. Previously I think you // could ask for a tailable cursor and it just tried to give you one. Now, we fail if we // can't provide one. Is this what we want? if (query.getParsed().hasOption(QueryOption_CursorTailable)) { if (!hasPredicate(predicates, MatchExpression::GEO_NEAR)) { out->push_back(makeCollectionScan(query, true)); } return; } // NOR and NOT we can't handle well with indices. If we see them here, they weren't // rewritten. Just output a collscan for those. if (hasPredicate(predicates, MatchExpression::NOT) || hasPredicate(predicates, MatchExpression::NOR)) { // If there's a near predicate, we can't handle this. // TODO: Should canonicalized query detect this? if (hasPredicate(predicates, MatchExpression::GEO_NEAR)) { warning() << "Can't handle NOT/NOR with GEO_NEAR"; return; } out->push_back(makeCollectionScan(query, false)); return; } // Filter our indices so we only look at indices that are over our predicates. vector<BSONObj> relevantIndices; findRelevantIndices(predicates, indexKeyPatterns, &relevantIndices); // No indices, no work to do. if (0 == relevantIndices.size()) { return; } // Figure out how useful each index is to each predicate. rateIndices(relevantIndices, &predicates); // // Planner Section 2: Use predicate/index data to output sets of indices that we can use. // PlanEnumerator isp(&query, &predicates, &relevantIndices); MatchExpression* rawTree; while (isp.getNext(&rawTree)) { QuerySolutionNode* solutionRoot = NULL; // // Planner Section 3: Logical Rewrite. Use the index selection and the tree structure // to try to rewrite the tree. TODO: Do this for real. We treat the tree as static. // // // Planner Section 4: Covering. If we're projecting, See if we get any covering from // this plan. If not, add a fetch. // if (!query.getParsed().getProj().isEmpty()) { warning() << "Can't deal with proj yet" << endl; } else { // Note that we need a fetch, possibly tack on to end? } // // Planner Section 5: Sort. If we're sorting, see if the plan gives us a sort for free. // If not, add a sort. // if (!query.getParsed().getSort().isEmpty()) { } else { // Note that we need a sort, possibly tack on to end? may want to see if sort is // covered and then tack fetch on after the covered sort... } // // Planner Section 6: Final check. Make sure that we build a valid solution. // TODO: Validate. // if (NULL != solutionRoot) { QuerySolution* qs = new QuerySolution(); qs->root.reset(solutionRoot); out->push_back(qs); } } // TODO: Do we always want to offer a collscan solution? if (!hasPredicate(predicates, MatchExpression::GEO_NEAR)) { out->push_back(makeCollectionScan(query, false)); } }