0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-31696 Rename path prefixes in applyRenamesToExpression().

Previously, applyRenamesToExpression() would only rename a path if the
entire path matched the rename rule.
This commit is contained in:
Justin Seyster 2017-11-03 21:05:57 -04:00
parent 29769e9514
commit d12505f554
3 changed files with 69 additions and 5 deletions

View File

@ -172,4 +172,30 @@
ixscan = getAggPlanStage(explain, "IXSCAN");
assert.neq(null, ixscan, tojson(explain));
assert.eq({"a.b.c": 1}, ixscan.keyPattern, tojson(ixscan));
// Test that we correctly match on the subfield of a renamed field. Here, a match on "x.b.c"
// follows an "a" to "x" rename. When we move the match stage in front of the rename, the match
// should also get rewritten to use "a.b.c" as its filter.
pipeline = [{$project: {x: "$a"}}, {$match: {"x.b.c": 1}}];
assert.eq([{_id: 0, x: [{b: [{c: 1}, {c: 2}]}, {b: [{c: 3}, {c: 4}]}]}],
coll.aggregate(pipeline).toArray());
explain = coll.explain().aggregate(pipeline);
ixscan = getAggPlanStage(explain, "IXSCAN");
assert.neq(null, ixscan, tojson(explain));
assert.eq({"a.b.c": 1}, ixscan.keyPattern, tojson(ixscan));
// Test that we correctly match on the subfield of a renamed field when the rename results from
// a $map operation. Here, a match on "d.e.c" follows an "a.b" to "d.e" rename. When we move the
// match stage in front of the renaming $map operation, the match should also get rewritten to
// use "a.b.c" as its filter.
pipeline = [
{$project: {d: {$map: {input: "$a", as: "iter", in : {e: "$$iter.b"}}}}},
{$match: {"d.e.c": 7}}
];
assert.eq([{_id: 1, d: [{e: [{c: 5}, {c: 6}]}, {e: [{c: 7}, {c: 8}]}]}],
coll.aggregate(pipeline).toArray());
explain = coll.explain().aggregate(pipeline);
ixscan = getAggPlanStage(explain, "IXSCAN");
assert.neq(null, ixscan, tojson(explain));
assert.eq({"a.b.c": 1}, ixscan.keyPattern, tojson(ixscan));
}());

View File

@ -283,11 +283,8 @@ void applyRenamesToExpression(MatchExpression* expr, const StringMap<std::string
}
if (expr->getCategory() == MatchExpression::MatchCategory::kLeaf) {
auto it = renames.find(expr->path());
if (it != renames.end()) {
LeafMatchExpression* leafExpr = checked_cast<LeafMatchExpression*>(expr);
leafExpr->setPath(it->second).transitional_ignore();
}
LeafMatchExpression* leafExpr = checked_cast<LeafMatchExpression*>(expr);
leafExpr->applyRename(renames);
}
for (size_t i = 0; i < expr->numChildren(); ++i) {

View File

@ -85,6 +85,43 @@ public:
return Status::OK();
}
/**
* Finds an applicable rename from 'renameList' (if one exists) and applies it to the expression
* path. Each pair in 'renameList' specifies a path prefix that should be renamed (as the first
* element) and the path components that should replace the renamed prefix (as the second
* element).
*/
void applyRename(const StringMap<std::string>& renameList) {
FieldRef pathFieldRef(_path);
int renamesFound = 0;
for (auto rename : renameList) {
if (rename.first == _path) {
_rewrittenPath = rename.second;
invariantOK(setPath(_rewrittenPath));
++renamesFound;
}
FieldRef prefixToRename(rename.first);
if (prefixToRename.isPrefixOf(pathFieldRef)) {
// Get the 'pathTail' by chopping off the 'prefixToRename' path components from the
// beginning of the 'pathFieldRef' path.
auto pathTail = pathFieldRef.dottedSubstring(prefixToRename.numParts(),
pathFieldRef.numParts());
// Replace the chopped off components with the component names resulting from the
// rename.
_rewrittenPath = str::stream() << rename.second << "." << pathTail.toString();
invariantOK(setPath(_rewrittenPath));
++renamesFound;
}
}
// There should never be multiple applicable renames.
invariant(renamesFound <= 1);
}
protected:
void _doAddDependencies(DepsTracker* deps) const final {
if (!_path.empty()) {
@ -95,5 +132,9 @@ protected:
private:
StringData _path;
ElementPath _elementPath;
// We use this when we rewrite the value in '_path' and we need a backing store for the
// rewritten string.
std::string _rewrittenPath;
};
} // namespace mongo