diff options
Diffstat (limited to 'src/mongo/db/query/query_planner_test_lib.cpp')
-rw-r--r-- | src/mongo/db/query/query_planner_test_lib.cpp | 803 |
1 files changed, 429 insertions, 374 deletions
diff --git a/src/mongo/db/query/query_planner_test_lib.cpp b/src/mongo/db/query/query_planner_test_lib.cpp index d81fc0f27e9..6ff080a767f 100644 --- a/src/mongo/db/query/query_planner_test_lib.cpp +++ b/src/mongo/db/query/query_planner_test_lib.cpp @@ -43,465 +43,520 @@ namespace { - using namespace mongo; +using namespace mongo; - using std::string; +using std::string; - bool filterMatches(const BSONObj& testFilter, - const QuerySolutionNode* trueFilterNode) { - if (NULL == trueFilterNode->filter) { return false; } - StatusWithMatchExpression swme = MatchExpressionParser::parse(testFilter); - if (!swme.isOK()) { - return false; - } - const std::unique_ptr<MatchExpression> root(swme.getValue()); - CanonicalQuery::sortTree(root.get()); - std::unique_ptr<MatchExpression> trueFilter(trueFilterNode->filter->shallowClone()); - CanonicalQuery::sortTree(trueFilter.get()); - return trueFilter->equivalent(root.get()); +bool filterMatches(const BSONObj& testFilter, const QuerySolutionNode* trueFilterNode) { + if (NULL == trueFilterNode->filter) { + return false; } - - void appendIntervalBound(BSONObjBuilder& bob, BSONElement& el) { - if (el.type() == String) { - std::string data = el.String(); - if (data == "MaxKey") { - bob.appendMaxKey(""); - } - else if (data == "MinKey") { - bob.appendMinKey(""); - } - else { - bob.appendAs(el, ""); - } - } - else { + StatusWithMatchExpression swme = MatchExpressionParser::parse(testFilter); + if (!swme.isOK()) { + return false; + } + const std::unique_ptr<MatchExpression> root(swme.getValue()); + CanonicalQuery::sortTree(root.get()); + std::unique_ptr<MatchExpression> trueFilter(trueFilterNode->filter->shallowClone()); + CanonicalQuery::sortTree(trueFilter.get()); + return trueFilter->equivalent(root.get()); +} + +void appendIntervalBound(BSONObjBuilder& bob, BSONElement& el) { + if (el.type() == String) { + std::string data = el.String(); + if (data == "MaxKey") { + bob.appendMaxKey(""); + } else if (data == "MinKey") { + bob.appendMinKey(""); + } else { bob.appendAs(el, ""); } + } else { + bob.appendAs(el, ""); } +} - bool intervalMatches(const BSONObj& testInt, const Interval trueInt) { - BSONObjIterator it(testInt); - if (!it.more()) { return false; } - BSONElement low = it.next(); - if (!it.more()) { return false; } - BSONElement high = it.next(); - if (!it.more()) { return false; } - bool startInclusive = it.next().Bool(); - if (!it.more()) { return false; } - bool endInclusive = it.next().Bool(); - if (it.more()) { return false; } - - BSONObjBuilder bob; - appendIntervalBound(bob, low); - appendIntervalBound(bob, high); - Interval toCompare(bob.obj(), startInclusive, endInclusive); - - return Interval::INTERVAL_EQUALS == trueInt.compare(toCompare); +bool intervalMatches(const BSONObj& testInt, const Interval trueInt) { + BSONObjIterator it(testInt); + if (!it.more()) { + return false; + } + BSONElement low = it.next(); + if (!it.more()) { + return false; + } + BSONElement high = it.next(); + if (!it.more()) { + return false; + } + bool startInclusive = it.next().Bool(); + if (!it.more()) { + return false; + } + bool endInclusive = it.next().Bool(); + if (it.more()) { + return false; } - /** - * Returns whether the BSON representation of the index bounds in - * 'testBounds' matches 'trueBounds'. - * - * 'testBounds' should be of the following format: - * {<field 1>: <oil 1>, <field 2>: <oil 2>, ...} - * Each ordered interval list (e.g. <oil 1>) is an array of arrays of - * the format: - * [[<low 1>,<high 1>,<lowInclusive 1>,<highInclusive 1>], ...] - * - * For example, - * {a: [[1,2,true,false], [3,4,false,true]], b: [[-Infinity, Infinity]]} - * Means that the index bounds on field 'a' consist of the two intervals - * [1, 2) and (3, 4] and the index bounds on field 'b' are [-Infinity, Infinity]. - */ - bool boundsMatch(const BSONObj& testBounds, const IndexBounds trueBounds) { - // Iterate over the fields on which we have index bounds. - BSONObjIterator fieldIt(testBounds); - int fieldItCount = 0; - while (fieldIt.more()) { - BSONElement arrEl = fieldIt.next(); - if (arrEl.type() != Array) { + BSONObjBuilder bob; + appendIntervalBound(bob, low); + appendIntervalBound(bob, high); + Interval toCompare(bob.obj(), startInclusive, endInclusive); + + return Interval::INTERVAL_EQUALS == trueInt.compare(toCompare); +} + +/** + * Returns whether the BSON representation of the index bounds in + * 'testBounds' matches 'trueBounds'. + * + * 'testBounds' should be of the following format: + * {<field 1>: <oil 1>, <field 2>: <oil 2>, ...} + * Each ordered interval list (e.g. <oil 1>) is an array of arrays of + * the format: + * [[<low 1>,<high 1>,<lowInclusive 1>,<highInclusive 1>], ...] + * + * For example, + * {a: [[1,2,true,false], [3,4,false,true]], b: [[-Infinity, Infinity]]} + * Means that the index bounds on field 'a' consist of the two intervals + * [1, 2) and (3, 4] and the index bounds on field 'b' are [-Infinity, Infinity]. + */ +bool boundsMatch(const BSONObj& testBounds, const IndexBounds trueBounds) { + // Iterate over the fields on which we have index bounds. + BSONObjIterator fieldIt(testBounds); + int fieldItCount = 0; + while (fieldIt.more()) { + BSONElement arrEl = fieldIt.next(); + if (arrEl.type() != Array) { + return false; + } + // Iterate over an ordered interval list for + // a particular field. + BSONObjIterator oilIt(arrEl.Obj()); + int oilItCount = 0; + while (oilIt.more()) { + BSONElement intervalEl = oilIt.next(); + if (intervalEl.type() != Array) { return false; } - // Iterate over an ordered interval list for - // a particular field. - BSONObjIterator oilIt(arrEl.Obj()); - int oilItCount = 0; - while (oilIt.more()) { - BSONElement intervalEl = oilIt.next(); - if (intervalEl.type() != Array) { - return false; - } - Interval trueInt = trueBounds.getInterval(fieldItCount, oilItCount); - if (!intervalMatches(intervalEl.Obj(), trueInt)) { - return false; - } - ++oilItCount; + Interval trueInt = trueBounds.getInterval(fieldItCount, oilItCount); + if (!intervalMatches(intervalEl.Obj(), trueInt)) { + return false; } - ++fieldItCount; + ++oilItCount; } - - return true; + ++fieldItCount; } -} // namespace + return true; +} + +} // namespace namespace mongo { - /** - * Looks in the children stored in the 'nodes' field of 'testSoln' - * to see if thet match the 'children' field of 'trueSoln'. - * - * This does an unordered comparison, i.e. childrenMatch returns - * true as long as the set of subtrees in testSoln's 'nodes' matches - * the set of subtrees in trueSoln's 'children' vector. - */ - static bool childrenMatch(const BSONObj& testSoln, const QuerySolutionNode* trueSoln) { - BSONElement children = testSoln["nodes"]; - if (children.eoo() || !children.isABSONObj()) { return false; } - - // The order of the children array in testSoln might not match - // the order in trueSoln, so we have to check all combos with - // these nested loops. - BSONObjIterator i(children.Obj()); - while (i.more()) { - BSONElement child = i.next(); - if (child.eoo() || !child.isABSONObj()) { return false; } - - // try to match against one of the QuerySolutionNode's children - bool found = false; - for (size_t j = 0; j < trueSoln->children.size(); ++j) { - if (QueryPlannerTestLib::solutionMatches(child.Obj(), trueSoln->children[j])) { - found = true; - break; - } - } +/** + * Looks in the children stored in the 'nodes' field of 'testSoln' + * to see if thet match the 'children' field of 'trueSoln'. + * + * This does an unordered comparison, i.e. childrenMatch returns + * true as long as the set of subtrees in testSoln's 'nodes' matches + * the set of subtrees in trueSoln's 'children' vector. + */ +static bool childrenMatch(const BSONObj& testSoln, const QuerySolutionNode* trueSoln) { + BSONElement children = testSoln["nodes"]; + if (children.eoo() || !children.isABSONObj()) { + return false; + } - // we couldn't match child - if (!found) { return false; } + // The order of the children array in testSoln might not match + // the order in trueSoln, so we have to check all combos with + // these nested loops. + BSONObjIterator i(children.Obj()); + while (i.more()) { + BSONElement child = i.next(); + if (child.eoo() || !child.isABSONObj()) { + return false; } - return true; + // try to match against one of the QuerySolutionNode's children + bool found = false; + for (size_t j = 0; j < trueSoln->children.size(); ++j) { + if (QueryPlannerTestLib::solutionMatches(child.Obj(), trueSoln->children[j])) { + found = true; + break; + } + } + + // we couldn't match child + if (!found) { + return false; + } } - // static - bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln, - const QuerySolutionNode* trueSoln) { - // - // leaf nodes - // - if (STAGE_COLLSCAN == trueSoln->getType()) { - const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(trueSoln); - BSONElement el = testSoln["cscan"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj csObj = el.Obj(); - - BSONElement dir = csObj["dir"]; - if (dir.eoo() || !dir.isNumber()) { return false; } - if (dir.numberInt() != csn->direction) { return false; } - - BSONElement filter = csObj["filter"]; - if (filter.eoo()) { - return true; - } - else if (filter.isNull()) { - return NULL == csn->filter; - } - else if (!filter.isABSONObj()) { - return false; - } - return filterMatches(filter.Obj(), trueSoln); + return true; +} + +// static +bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln, + const QuerySolutionNode* trueSoln) { + // + // leaf nodes + // + if (STAGE_COLLSCAN == trueSoln->getType()) { + const CollectionScanNode* csn = static_cast<const CollectionScanNode*>(trueSoln); + BSONElement el = testSoln["cscan"]; + if (el.eoo() || !el.isABSONObj()) { + return false; } - else if (STAGE_IXSCAN == trueSoln->getType()) { - const IndexScanNode* ixn = static_cast<const IndexScanNode*>(trueSoln); - BSONElement el = testSoln["ixscan"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj ixscanObj = el.Obj(); + BSONObj csObj = el.Obj(); - BSONElement pattern = ixscanObj["pattern"]; - if (pattern.eoo() || !pattern.isABSONObj()) { return false; } - if (pattern.Obj() != ixn->indexKeyPattern) { return false; } + BSONElement dir = csObj["dir"]; + if (dir.eoo() || !dir.isNumber()) { + return false; + } + if (dir.numberInt() != csn->direction) { + return false; + } - BSONElement bounds = ixscanObj["bounds"]; - if (!bounds.eoo()) { - if (!bounds.isABSONObj()) { - return false; - } - else if (!boundsMatch(bounds.Obj(), ixn->bounds)) { - return false; - } - } + BSONElement filter = csObj["filter"]; + if (filter.eoo()) { + return true; + } else if (filter.isNull()) { + return NULL == csn->filter; + } else if (!filter.isABSONObj()) { + return false; + } + return filterMatches(filter.Obj(), trueSoln); + } else if (STAGE_IXSCAN == trueSoln->getType()) { + const IndexScanNode* ixn = static_cast<const IndexScanNode*>(trueSoln); + BSONElement el = testSoln["ixscan"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj ixscanObj = el.Obj(); - BSONElement dir = ixscanObj["dir"]; - if (!dir.eoo() && NumberInt == dir.type()) { - if (dir.numberInt() != ixn->direction) { - return false; - } - } + BSONElement pattern = ixscanObj["pattern"]; + if (pattern.eoo() || !pattern.isABSONObj()) { + return false; + } + if (pattern.Obj() != ixn->indexKeyPattern) { + return false; + } - BSONElement filter = ixscanObj["filter"]; - if (filter.eoo()) { - return true; - } - else if (filter.isNull()) { - return NULL == ixn->filter; + BSONElement bounds = ixscanObj["bounds"]; + if (!bounds.eoo()) { + if (!bounds.isABSONObj()) { + return false; + } else if (!boundsMatch(bounds.Obj(), ixn->bounds)) { + return false; } - else if (!filter.isABSONObj()) { + } + + BSONElement dir = ixscanObj["dir"]; + if (!dir.eoo() && NumberInt == dir.type()) { + if (dir.numberInt() != ixn->direction) { return false; } - return filterMatches(filter.Obj(), trueSoln); - } - else if (STAGE_GEO_NEAR_2D == trueSoln->getType()) { - const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(trueSoln); - BSONElement el = testSoln["geoNear2d"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj geoObj = el.Obj(); - return geoObj == node->indexKeyPattern; - } - else if (STAGE_GEO_NEAR_2DSPHERE == trueSoln->getType()) { - const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(trueSoln); - BSONElement el = testSoln["geoNear2dsphere"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj geoObj = el.Obj(); - return geoObj == node->indexKeyPattern; - } - else if (STAGE_TEXT == trueSoln->getType()) { - // {text: {search: "somestr", language: "something", filter: {blah: 1}}} - const TextNode* node = static_cast<const TextNode*>(trueSoln); - BSONElement el = testSoln["text"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj textObj = el.Obj(); - - BSONElement searchElt = textObj["search"]; - if (!searchElt.eoo()) { - if (searchElt.String() != node->query) { - return false; - } + } + + BSONElement filter = ixscanObj["filter"]; + if (filter.eoo()) { + return true; + } else if (filter.isNull()) { + return NULL == ixn->filter; + } else if (!filter.isABSONObj()) { + return false; + } + return filterMatches(filter.Obj(), trueSoln); + } else if (STAGE_GEO_NEAR_2D == trueSoln->getType()) { + const GeoNear2DNode* node = static_cast<const GeoNear2DNode*>(trueSoln); + BSONElement el = testSoln["geoNear2d"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj geoObj = el.Obj(); + return geoObj == node->indexKeyPattern; + } else if (STAGE_GEO_NEAR_2DSPHERE == trueSoln->getType()) { + const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(trueSoln); + BSONElement el = testSoln["geoNear2dsphere"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj geoObj = el.Obj(); + return geoObj == node->indexKeyPattern; + } else if (STAGE_TEXT == trueSoln->getType()) { + // {text: {search: "somestr", language: "something", filter: {blah: 1}}} + const TextNode* node = static_cast<const TextNode*>(trueSoln); + BSONElement el = testSoln["text"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj textObj = el.Obj(); + + BSONElement searchElt = textObj["search"]; + if (!searchElt.eoo()) { + if (searchElt.String() != node->query) { + return false; } + } - BSONElement languageElt = textObj["language"]; - if (!languageElt.eoo()) { - if (languageElt.String() != node->language) { - return false; - } + BSONElement languageElt = textObj["language"]; + if (!languageElt.eoo()) { + if (languageElt.String() != node->language) { + return false; } + } - BSONElement caseSensitiveElt = textObj["caseSensitive"]; - if (!caseSensitiveElt.eoo()) { - if (caseSensitiveElt.trueValue() != node->caseSensitive) { - return false; - } + BSONElement caseSensitiveElt = textObj["caseSensitive"]; + if (!caseSensitiveElt.eoo()) { + if (caseSensitiveElt.trueValue() != node->caseSensitive) { + return false; } + } - BSONElement indexPrefix = textObj["prefix"]; - if (!indexPrefix.eoo()) { - if (!indexPrefix.isABSONObj()) { - return false; - } + BSONElement indexPrefix = textObj["prefix"]; + if (!indexPrefix.eoo()) { + if (!indexPrefix.isABSONObj()) { + return false; + } - if (0 != indexPrefix.Obj().woCompare(node->indexPrefix)) { - return false; - } + if (0 != indexPrefix.Obj().woCompare(node->indexPrefix)) { + return false; } + } - BSONElement filter = textObj["filter"]; - if (!filter.eoo()) { - if (filter.isNull()) { - if (NULL != node->filter) { return false; } - } - else if (!filter.isABSONObj()) { - return false; - } - else if (!filterMatches(filter.Obj(), trueSoln)) { + BSONElement filter = textObj["filter"]; + if (!filter.eoo()) { + if (filter.isNull()) { + if (NULL != node->filter) { return false; } + } else if (!filter.isABSONObj()) { + return false; + } else if (!filterMatches(filter.Obj(), trueSoln)) { + return false; } - - return true; } - // - // internal nodes - // - if (STAGE_FETCH == trueSoln->getType()) { - const FetchNode* fn = static_cast<const FetchNode*>(trueSoln); + return true; + } - BSONElement el = testSoln["fetch"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj fetchObj = el.Obj(); + // + // internal nodes + // + if (STAGE_FETCH == trueSoln->getType()) { + const FetchNode* fn = static_cast<const FetchNode*>(trueSoln); - BSONElement filter = fetchObj["filter"]; - if (!filter.eoo()) { - if (filter.isNull()) { - if (NULL != fn->filter) { return false; } - } - else if (!filter.isABSONObj()) { - return false; - } - else if (!filterMatches(filter.Obj(), trueSoln)) { - return false; - } - } + BSONElement el = testSoln["fetch"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj fetchObj = el.Obj(); - BSONElement child = fetchObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } - return solutionMatches(child.Obj(), fn->children[0]); - } - else if (STAGE_OR == trueSoln->getType()) { - const OrNode * orn = static_cast<const OrNode*>(trueSoln); - BSONElement el = testSoln["or"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj orObj = el.Obj(); - return childrenMatch(orObj, orn); - } - else if (STAGE_AND_HASH == trueSoln->getType()) { - const AndHashNode* ahn = static_cast<const AndHashNode*>(trueSoln); - BSONElement el = testSoln["andHash"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj andHashObj = el.Obj(); - - BSONElement filter = andHashObj["filter"]; - if (!filter.eoo()) { - if (filter.isNull()) { - if (NULL != ahn->filter) { return false; } - } - else if (!filter.isABSONObj()) { - return false; - } - else if (!filterMatches(filter.Obj(), trueSoln)) { + BSONElement filter = fetchObj["filter"]; + if (!filter.eoo()) { + if (filter.isNull()) { + if (NULL != fn->filter) { return false; } + } else if (!filter.isABSONObj()) { + return false; + } else if (!filterMatches(filter.Obj(), trueSoln)) { + return false; } + } - return childrenMatch(andHashObj, ahn); + BSONElement child = fetchObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; + } + return solutionMatches(child.Obj(), fn->children[0]); + } else if (STAGE_OR == trueSoln->getType()) { + const OrNode* orn = static_cast<const OrNode*>(trueSoln); + BSONElement el = testSoln["or"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj orObj = el.Obj(); + return childrenMatch(orObj, orn); + } else if (STAGE_AND_HASH == trueSoln->getType()) { + const AndHashNode* ahn = static_cast<const AndHashNode*>(trueSoln); + BSONElement el = testSoln["andHash"]; + if (el.eoo() || !el.isABSONObj()) { + return false; } - else if (STAGE_AND_SORTED == trueSoln->getType()) { - const AndSortedNode* asn = static_cast<const AndSortedNode*>(trueSoln); - BSONElement el = testSoln["andSorted"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj andSortedObj = el.Obj(); + BSONObj andHashObj = el.Obj(); - BSONElement filter = andSortedObj["filter"]; - if (!filter.eoo()) { - if (filter.isNull()) { - if (NULL != asn->filter) { return false; } - } - else if (!filter.isABSONObj()) { - return false; - } - else if (!filterMatches(filter.Obj(), trueSoln)) { + BSONElement filter = andHashObj["filter"]; + if (!filter.eoo()) { + if (filter.isNull()) { + if (NULL != ahn->filter) { return false; } + } else if (!filter.isABSONObj()) { + return false; + } else if (!filterMatches(filter.Obj(), trueSoln)) { + return false; } - - return childrenMatch(andSortedObj, asn); } - else if (STAGE_PROJECTION == trueSoln->getType()) { - const ProjectionNode* pn = static_cast<const ProjectionNode*>(trueSoln); - BSONElement el = testSoln["proj"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj projObj = el.Obj(); + return childrenMatch(andHashObj, ahn); + } else if (STAGE_AND_SORTED == trueSoln->getType()) { + const AndSortedNode* asn = static_cast<const AndSortedNode*>(trueSoln); + BSONElement el = testSoln["andSorted"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj andSortedObj = el.Obj(); - BSONElement projType = projObj["type"]; - if (!projType.eoo()) { - string projTypeStr = projType.str(); - if (!((pn->projType == ProjectionNode::DEFAULT && projTypeStr == "default") || - (pn->projType == ProjectionNode::SIMPLE_DOC && projTypeStr == "simple") || - (pn->projType == ProjectionNode::COVERED_ONE_INDEX && - projTypeStr == "coveredIndex"))) { + BSONElement filter = andSortedObj["filter"]; + if (!filter.eoo()) { + if (filter.isNull()) { + if (NULL != asn->filter) { return false; } + } else if (!filter.isABSONObj()) { + return false; + } else if (!filterMatches(filter.Obj(), trueSoln)) { + return false; } + } - BSONElement spec = projObj["spec"]; - if (spec.eoo() || !spec.isABSONObj()) { return false; } - BSONElement child = projObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + return childrenMatch(andSortedObj, asn); + } else if (STAGE_PROJECTION == trueSoln->getType()) { + const ProjectionNode* pn = static_cast<const ProjectionNode*>(trueSoln); - return (spec.Obj() == pn->projection) - && solutionMatches(child.Obj(), pn->children[0]); + BSONElement el = testSoln["proj"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj projObj = el.Obj(); + + BSONElement projType = projObj["type"]; + if (!projType.eoo()) { + string projTypeStr = projType.str(); + if (!((pn->projType == ProjectionNode::DEFAULT && projTypeStr == "default") || + (pn->projType == ProjectionNode::SIMPLE_DOC && projTypeStr == "simple") || + (pn->projType == ProjectionNode::COVERED_ONE_INDEX && + projTypeStr == "coveredIndex"))) { + return false; + } } - else if (STAGE_SORT == trueSoln->getType()) { - const SortNode* sn = static_cast<const SortNode*>(trueSoln); - BSONElement el = testSoln["sort"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj sortObj = el.Obj(); - BSONElement patternEl = sortObj["pattern"]; - if (patternEl.eoo() || !patternEl.isABSONObj()) { return false; } - BSONElement limitEl = sortObj["limit"]; - if (!limitEl.isNumber()) { return false; } - BSONElement child = sortObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + BSONElement spec = projObj["spec"]; + if (spec.eoo() || !spec.isABSONObj()) { + return false; + } + BSONElement child = projObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; + } - size_t expectedLimit = limitEl.numberInt(); - return (patternEl.Obj() == sn->pattern) - && (expectedLimit == sn->limit) - && solutionMatches(child.Obj(), sn->children[0]); + return (spec.Obj() == pn->projection) && solutionMatches(child.Obj(), pn->children[0]); + } else if (STAGE_SORT == trueSoln->getType()) { + const SortNode* sn = static_cast<const SortNode*>(trueSoln); + BSONElement el = testSoln["sort"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj sortObj = el.Obj(); + + BSONElement patternEl = sortObj["pattern"]; + if (patternEl.eoo() || !patternEl.isABSONObj()) { + return false; } - else if (STAGE_SORT_MERGE == trueSoln->getType()) { - const MergeSortNode* msn = static_cast<const MergeSortNode*>(trueSoln); - BSONElement el = testSoln["mergeSort"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj mergeSortObj = el.Obj(); - return childrenMatch(mergeSortObj, msn); + BSONElement limitEl = sortObj["limit"]; + if (!limitEl.isNumber()) { + return false; + } + BSONElement child = sortObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; } - else if (STAGE_SKIP == trueSoln->getType()) { - const SkipNode* sn = static_cast<const SkipNode*>(trueSoln); - BSONElement el = testSoln["skip"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj sortObj = el.Obj(); - BSONElement skipEl = sortObj["n"]; - if (!skipEl.isNumber()) { return false; } - BSONElement child = sortObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + size_t expectedLimit = limitEl.numberInt(); + return (patternEl.Obj() == sn->pattern) && (expectedLimit == sn->limit) && + solutionMatches(child.Obj(), sn->children[0]); + } else if (STAGE_SORT_MERGE == trueSoln->getType()) { + const MergeSortNode* msn = static_cast<const MergeSortNode*>(trueSoln); + BSONElement el = testSoln["mergeSort"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj mergeSortObj = el.Obj(); + return childrenMatch(mergeSortObj, msn); + } else if (STAGE_SKIP == trueSoln->getType()) { + const SkipNode* sn = static_cast<const SkipNode*>(trueSoln); + BSONElement el = testSoln["skip"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj sortObj = el.Obj(); - return (skipEl.numberInt() == sn->skip) - && solutionMatches(child.Obj(), sn->children[0]); + BSONElement skipEl = sortObj["n"]; + if (!skipEl.isNumber()) { + return false; + } + BSONElement child = sortObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; } - else if (STAGE_LIMIT == trueSoln->getType()) { - const LimitNode* ln = static_cast<const LimitNode*>(trueSoln); - BSONElement el = testSoln["limit"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj sortObj = el.Obj(); - BSONElement limitEl = sortObj["n"]; - if (!limitEl.isNumber()) { return false; } - BSONElement child = sortObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + return (skipEl.numberInt() == sn->skip) && solutionMatches(child.Obj(), sn->children[0]); + } else if (STAGE_LIMIT == trueSoln->getType()) { + const LimitNode* ln = static_cast<const LimitNode*>(trueSoln); + BSONElement el = testSoln["limit"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj sortObj = el.Obj(); - return (limitEl.numberInt() == ln->limit) - && solutionMatches(child.Obj(), ln->children[0]); + BSONElement limitEl = sortObj["n"]; + if (!limitEl.isNumber()) { + return false; + } + BSONElement child = sortObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; } - else if (STAGE_KEEP_MUTATIONS == trueSoln->getType()) { - const KeepMutationsNode* kn = static_cast<const KeepMutationsNode*>(trueSoln); - BSONElement el = testSoln["keep"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj keepObj = el.Obj(); + return (limitEl.numberInt() == ln->limit) && solutionMatches(child.Obj(), ln->children[0]); + } else if (STAGE_KEEP_MUTATIONS == trueSoln->getType()) { + const KeepMutationsNode* kn = static_cast<const KeepMutationsNode*>(trueSoln); - // Doesn't have any parameters really. - BSONElement child = keepObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + BSONElement el = testSoln["keep"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj keepObj = el.Obj(); - return solutionMatches(child.Obj(), kn->children[0]); + // Doesn't have any parameters really. + BSONElement child = keepObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; } - else if (STAGE_SHARDING_FILTER == trueSoln->getType()) { - const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(trueSoln); - BSONElement el = testSoln["sharding_filter"]; - if (el.eoo() || !el.isABSONObj()) { return false; } - BSONObj keepObj = el.Obj(); + return solutionMatches(child.Obj(), kn->children[0]); + } else if (STAGE_SHARDING_FILTER == trueSoln->getType()) { + const ShardingFilterNode* fn = static_cast<const ShardingFilterNode*>(trueSoln); - BSONElement child = keepObj["node"]; - if (child.eoo() || !child.isABSONObj()) { return false; } + BSONElement el = testSoln["sharding_filter"]; + if (el.eoo() || !el.isABSONObj()) { + return false; + } + BSONObj keepObj = el.Obj(); - return solutionMatches(child.Obj(), fn->children[0]); + BSONElement child = keepObj["node"]; + if (child.eoo() || !child.isABSONObj()) { + return false; } - return false; + return solutionMatches(child.Obj(), fn->children[0]); } + return false; +} + } // namespace mongo |