summaryrefslogtreecommitdiff
path: root/src/mongo/db/timeseries/timeseries_dotted_path_support.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/timeseries/timeseries_dotted_path_support.cpp')
-rw-r--r--src/mongo/db/timeseries/timeseries_dotted_path_support.cpp349
1 files changed, 151 insertions, 198 deletions
diff --git a/src/mongo/db/timeseries/timeseries_dotted_path_support.cpp b/src/mongo/db/timeseries/timeseries_dotted_path_support.cpp
index f666b93f0de..b28e198429e 100644
--- a/src/mongo/db/timeseries/timeseries_dotted_path_support.cpp
+++ b/src/mongo/db/timeseries/timeseries_dotted_path_support.cpp
@@ -61,96 +61,83 @@ boost::optional<std::pair<StringData, StringData>> _splitPath(StringData path) {
}
void _handleElementForExtractAllElementsOnBucketPath(const BSONObj& obj,
+ BSONElement elem,
StringData path,
BSONElementSet& elements,
bool expandArrayOnTrailingField,
BSONDepthIndex depth,
- MultikeyComponents* arrayComponents);
-
-void _handleIntermediateElementForExtractAllElementsOnBucketPath(
- BSONElement elem,
- StringData path,
- BSONElementSet& elements,
- bool expandArrayOnTrailingField,
- BSONDepthIndex depth,
- MultikeyComponents* arrayComponents) {
- if (elem.type() == Object) {
- BSONObj embedded = elem.embeddedObject();
- _handleElementForExtractAllElementsOnBucketPath(
- embedded, path, elements, expandArrayOnTrailingField, depth + 1, arrayComponents);
- } else if (elem.type() == Array) {
- bool allDigits = false;
- if (path.size() > 0 && ctype::isDigit(path[0])) {
- unsigned temp = 1;
- while (temp < path.size() && ctype::isDigit(path[temp]))
- temp++;
- allDigits = temp == path.size() || path[temp] == '.';
- }
- if (allDigits) {
- BSONObj embedded = elem.embeddedObject();
- _handleElementForExtractAllElementsOnBucketPath(
- embedded, path, elements, expandArrayOnTrailingField, depth + 1, arrayComponents);
- } else {
- BSONObjIterator i(elem.embeddedObject());
- while (i.more()) {
- BSONElement e2 = i.next();
- if (e2.type() == Object || e2.type() == Array) {
- BSONObj embedded = e2.embeddedObject();
+ MultikeyComponents* arrayComponents) {
+ if (elem.eoo()) {
+ size_t idx = path.find('.');
+ if (idx != std::string::npos) {
+ invariant(depth != std::numeric_limits<BSONDepthIndex>::max());
+ StringData left = path.substr(0, idx);
+ StringData next = path.substr(idx + 1, path.size());
+
+ BSONElement e = obj.getField(left);
+
+ if (e.type() == Object) {
+ BSONObj embedded = e.embeddedObject();
+ _handleElementForExtractAllElementsOnBucketPath(embedded,
+ embedded.getField(next),
+ next,
+ elements,
+ expandArrayOnTrailingField,
+ depth + 1,
+ arrayComponents);
+ } else if (e.type() == Array) {
+ bool allDigits = false;
+ if (next.size() > 0 && ctype::isDigit(next[0])) {
+ unsigned temp = 1;
+ while (temp < next.size() && ctype::isDigit(next[temp]))
+ temp++;
+ allDigits = temp == next.size() || next[temp] == '.';
+ }
+ if (allDigits) {
+ BSONObj embedded = e.embeddedObject();
_handleElementForExtractAllElementsOnBucketPath(embedded,
- path,
+ embedded.getField(next),
+ next,
elements,
expandArrayOnTrailingField,
depth + 1,
arrayComponents);
+ } else {
+ BSONObjIterator i(e.embeddedObject());
+ while (i.more()) {
+ BSONElement e2 = i.next();
+ if (e2.type() == Object || e2.type() == Array) {
+ BSONObj embedded = e2.embeddedObject();
+ _handleElementForExtractAllElementsOnBucketPath(
+ embedded,
+ embedded.getField(next),
+ next,
+ elements,
+ expandArrayOnTrailingField,
+ depth + 1,
+ arrayComponents);
+ }
+ }
+ if (arrayComponents) {
+ arrayComponents->insert(depth);
+ }
}
+ } else {
+ // do nothing: no match
+ }
+ }
+ } else {
+ if (elem.type() == Array && expandArrayOnTrailingField) {
+ BSONObjIterator i(elem.embeddedObject());
+ while (i.more()) {
+ elements.insert(i.next());
}
if (arrayComponents) {
arrayComponents->insert(depth);
}
+ } else {
+ elements.insert(elem);
}
- } else {
- // do nothing: no match
- }
-}
-
-void _handleTerminalElementForExtractAllElementsOnBucketPath(BSONElement elem,
- BSONElementSet& elements,
- bool expandArrayOnTrailingField,
- BSONDepthIndex depth,
- MultikeyComponents* arrayComponents) {
- if (elem.type() == Array && expandArrayOnTrailingField) {
- BSONObjIterator i(elem.embeddedObject());
- while (i.more()) {
- elements.insert(i.next());
- }
- if (arrayComponents) {
- arrayComponents->insert(depth);
- }
- } else if (!elem.eoo()) {
- elements.insert(elem);
- }
-}
-
-void _handleElementForExtractAllElementsOnBucketPath(const BSONObj& obj,
- StringData path,
- BSONElementSet& elements,
- bool expandArrayOnTrailingField,
- BSONDepthIndex depth,
- MultikeyComponents* arrayComponents) {
- size_t idx = path.find('.');
- if (idx != std::string::npos) {
- invariant(depth != std::numeric_limits<BSONDepthIndex>::max());
- StringData left = path.substr(0, idx);
- StringData next = path.substr(idx + 1, path.size());
-
- BSONElement e = obj.getField(left);
-
- _handleIntermediateElementForExtractAllElementsOnBucketPath(
- e, next, elements, expandArrayOnTrailingField, depth, arrayComponents);
- } else {
- auto elem = obj.getField(path);
- _handleTerminalElementForExtractAllElementsOnBucketPath(
- elem, elements, expandArrayOnTrailingField, depth, arrayComponents);
}
}
@@ -178,21 +165,16 @@ boost::optional<BSONColumn> _extractAllElementsAlongBucketPath(
depth + 1,
arrayComponents);
} else if (isCompressed && e.type() == BinData) {
- // Unbucketing happens here for nested measurement fields (i.e. data.a.b) in
- // compressed buckets. We know that 'e' corresponds to the top-level
- // measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) and do a
- // field lookup to extract the actual field we want (i.e. data.a.1.b).
- // Thanks to the bucket structure, we know there is no literal field with a
- // dot at this depth (e.g. '1.b'), so we can skip that field lookup, but
- // it's possible that the element e2 stores an object, array, or other type,
- // and we need to figure out how to resolve the rest of the path based on
- // the type.
+ // Unbucketing magic happens here for nested measurement fields (i.e.
+ // data.a.b) in compressed buckets.
BSONColumn storage{e};
for (const BSONElement& e2 : storage) {
if (!e2.eoo()) {
- _handleIntermediateElementForExtractAllElementsOnBucketPath(
- e2,
+ BSONObj embedded =
+ e2.isABSONObj() ? e2.embeddedObject() : BSONObj();
+ _handleElementForExtractAllElementsOnBucketPath(
+ embedded,
+ embedded.getField(next),
next,
elements,
expandArrayOnTrailingField,
@@ -200,8 +182,6 @@ boost::optional<BSONColumn> _extractAllElementsAlongBucketPath(
arrayComponents);
}
}
- // Need to pass along the column since it owns the memory referenced by the
- // extracted elements.
return std::move(storage);
}
}
@@ -216,48 +196,43 @@ boost::optional<BSONColumn> _extractAllElementsAlongBucketPath(
depth + 1,
arrayComponents);
} else if (isCompressed && BinData == e.type()) {
- // Unbucketing happens here for top-level measurement fields (i.e. data.a) in
- // compressed buckets. We know that 'e' corresponds to the top-level
- // measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) to extract
- // the actual field we want.
+ // Unbucketing magic happens here for top-level measurement fields (i.e. data.a)
+ // in compressed buckets.
invariant(depth == 1);
BSONColumn storage{e};
for (const BSONElement& e2 : storage) {
if (!e2.eoo()) {
BSONObj embedded = e2.isABSONObj() ? e2.embeddedObject() : BSONObj();
- _handleTerminalElementForExtractAllElementsOnBucketPath(
- e2, elements, expandArrayOnTrailingField, depth, arrayComponents);
+ _handleElementForExtractAllElementsOnBucketPath(
+ embedded,
+ e2,
+ ""_sd,
+ elements,
+ expandArrayOnTrailingField,
+ depth,
+ arrayComponents);
}
}
- // Need to pass along the column since it owns the memory referenced by the
- // extracted elements.
return std::move(storage);
}
}
break;
}
case 2: {
- // Unbucketing happens here for uncompressed buckets. We know that 'obj' corresponds to
- // the top-level measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) to extract the actual
- // field we want. If we are after a top-level field, then we already have the element we
- // want in 'e'. If we are after a nested field, then we need to recurse.
+ // Unbucketing magic happens here for uncompressed buckets.
invariant(!isCompressed);
for (const BSONElement& e : obj) {
- if (path.empty()) {
- // The top-level measurement field (i.e. data.a) is the indexed field we are
- // trying to extract, so we can target it directly.
- _handleTerminalElementForExtractAllElementsOnBucketPath(
- e, elements, expandArrayOnTrailingField, depth, arrayComponents);
- } else {
- // We'll need to inspect at least one more field, but we can take advantage of
- // the bucket structure here to skip checking for any field at this depth with a
- // dot in the name (e.g. '1.b') and instead just look at the next level down
- // (e.g. look within '1' for a field named 'b').
- _handleIntermediateElementForExtractAllElementsOnBucketPath(
- e, path, elements, expandArrayOnTrailingField, depth, arrayComponents);
+ std::string subPath = e.fieldName();
+ if (!path.empty()) {
+ subPath.append("." + path);
}
+ _handleElementForExtractAllElementsOnBucketPath(obj,
+ obj.getField(subPath),
+ subPath,
+ elements,
+ expandArrayOnTrailingField,
+ depth,
+ arrayComponents);
}
break;
}
@@ -270,42 +245,37 @@ boost::optional<BSONColumn> _extractAllElementsAlongBucketPath(
bool _haveArrayAlongBucketDataPath(const BSONObj& obj, StringData path, BSONDepthIndex depth);
bool _handleElementForHaveArrayAlongBucketDataPath(const BSONObj& obj,
- StringData path,
- BSONDepthIndex depth);
-
-bool _handleIntermediateElementForHaveArrayAlongBucketDataPath(BSONElement elem,
- StringData path,
- BSONDepthIndex depth) {
- if (elem.type() == Object) {
- auto embedded = elem.embeddedObject();
- return _handleElementForHaveArrayAlongBucketDataPath(embedded, path, depth + 1);
- } else if (elem.type() == Array) {
- return true;
- }
- // no match
- return false;
-}
-
-bool _handleTerminalElementForHaveArrayAlongBucketDataPath(BSONElement elem) {
- return (elem.type() == Array);
-}
-
-
-bool _handleElementForHaveArrayAlongBucketDataPath(const BSONObj& obj,
+ BSONElement elem,
StringData path,
BSONDepthIndex depth) {
- size_t idx = path.find('.');
- if (idx != std::string::npos) {
- tassert(
- 5930502, "BSON depth too great", depth != std::numeric_limits<BSONDepthIndex>::max());
- StringData left = path.substr(0, idx);
- StringData next = path.substr(idx + 1, path.size());
-
- BSONElement e = obj.getField(left);
-
- return _handleIntermediateElementForHaveArrayAlongBucketDataPath(e, next, depth);
+ if (elem.eoo()) {
+ size_t idx = path.find('.');
+ if (idx != std::string::npos) {
+ tassert(5930502,
+ "BSON depth too great",
+ depth != std::numeric_limits<BSONDepthIndex>::max());
+ StringData left = path.substr(0, idx);
+ StringData next = path.substr(idx + 1, path.size());
+
+ BSONElement e = obj.getField(left);
+
+ if (e.type() == Object) {
+ auto embedded = e.embeddedObject();
+ return _handleElementForHaveArrayAlongBucketDataPath(
+ embedded, embedded.getField(next), next, depth + 1);
+ } else if (e.type() == Array) {
+ return true;
+ } else {
+ // do nothing: no match
+ }
+ }
+ } else {
+ if (elem.type() == Array) {
+ return true;
+ }
}
- return _handleTerminalElementForHaveArrayAlongBucketDataPath(obj.getField(path));
+
+ return false;
}
bool _haveArrayAlongBucketDataPath(const BSONObj& obj,
@@ -323,21 +293,13 @@ bool _haveArrayAlongBucketDataPath(const BSONObj& obj,
return _haveArrayAlongBucketDataPath(
e.embeddedObject(), next, isCompressed, depth + 1);
} else if (isCompressed && BinData == e.type()) {
- // Unbucketing happens here for nested measurement fields (i.e. data.a.b) in
- // compressed buckets. We know that 'e' corresponds to the top-level
- // measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) and do a
- // field lookup to find the actual field we want (i.e. data.a.1.b).
- // Thanks to the bucket structure, we know there is no literal field with a
- // dot at this depth (e.g. '1.b'), so we can skip that field lookup, but
- // it's possible that the element e2 stores an object, array, or other type,
- // and we need to figure out how to resolve the rest of the path based on
- // the type.
+ // Unbucketing magic happens here for nested measurement fields (i.e.
+ // data.a.b) in compressed buckets.
BSONColumn column{e};
for (const BSONElement& e2 : column) {
- const bool foundArray =
- _handleIntermediateElementForHaveArrayAlongBucketDataPath(
- e2, next, depth);
+ BSONObj embedded = e2.isABSONObj() ? e2.embeddedObject() : BSONObj();
+ const bool foundArray = _handleElementForHaveArrayAlongBucketDataPath(
+ embedded, embedded.getField(next), next, depth);
if (foundArray) {
return foundArray;
}
@@ -350,15 +312,12 @@ bool _haveArrayAlongBucketDataPath(const BSONObj& obj,
return _haveArrayAlongBucketDataPath(
e.embeddedObject(), StringData(), isCompressed, depth + 1);
} else if (BinData == e.type()) {
- // Unbucketing happens here for top-level measurement fields (i.e. data.a) in
- // compressed buckets. We know that 'e' corresponds to the top-level
- // measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) to decide whether
- // we have any arrays.
+ // Unbucketing magic happens here for top-level measurement fields (i.e. data.a)
+ // in compressed buckets.
invariant(isCompressed && depth == 1);
BSONColumn column{e};
for (const BSONElement& e2 : column) {
- if (_handleTerminalElementForHaveArrayAlongBucketDataPath(e2)) {
+ if (e2.type() == Array) {
return true;
}
}
@@ -367,35 +326,26 @@ bool _haveArrayAlongBucketDataPath(const BSONObj& obj,
return false;
}
case 2: {
- // Unbucketing happens here for uncompressed buckets. We know that 'obj' corresponds to
- // the top-level measurement field (i.e. data.a) and we need to iterate over each of the
- // numerically-indexed entries (i.e. data.a.1, data.a.5, etc.) to extract the actual
- // field we want. If we are after a top-level field, then we already have the element we
- // want in 'e'. If we are after a nested field, then we need recurse.
+ // Unbucketing magic happens here for uncompressed buckets.
invariant(!isCompressed);
for (const BSONElement& e : obj) {
- bool foundArray = false;
- if (path.empty()) {
- // The top-level measurement field (i.e. data.a) is the field we are trying to
- // check, so we can target it directly.
- if (_handleTerminalElementForHaveArrayAlongBucketDataPath(e)) {
- return true;
- }
- } else {
- // We'll need to inspect at least one more field, but we can take advantage of
- // the bucket structure here to skip checking for any field at this depth with a
- // dot in the name (e.g. '1.b') and instead just look at the next level down
- // (e.g. look within '1' for a field named 'b').
- foundArray =
- _handleIntermediateElementForHaveArrayAlongBucketDataPath(e, path, depth);
+ std::string subPath = e.fieldName();
+ if (!path.empty()) {
+ subPath.append("." + path);
}
+ BSONElement sub = obj.getField(subPath);
+ const bool foundArray =
+ _handleElementForHaveArrayAlongBucketDataPath(obj, sub, subPath, depth);
if (foundArray) {
return foundArray;
}
}
return false;
}
- default: { return _handleElementForHaveArrayAlongBucketDataPath(obj, path, depth); }
+ default: {
+ BSONElement e = obj.getField(path);
+ return _handleElementForHaveArrayAlongBucketDataPath(obj, e, path, depth);
+ }
}
}
@@ -435,8 +385,8 @@ Decision _fieldContainsArrayData(const BSONObj& maxObj, StringData field) {
// When we get here, we know that some prefix value on the control.min path was a non-object
// type < Object. We can also assume that our parent was an Object.
- if (std::string::npos == field.find('.')) {
- auto e = maxObj.getField(field);
+ auto e = maxObj.getField(field);
+ if (!e.eoo()) {
if (e.type() == BSONType::Array) {
return Decision::Yes;
} else if (e.type() > BSONType::Array) {
@@ -447,7 +397,7 @@ Decision _fieldContainsArrayData(const BSONObj& maxObj, StringData field) {
if (auto res = _splitPath(field)) {
auto& [left, next] = *res;
- auto e = maxObj.getField(left);
+ e = maxObj.getField(left);
if (e.type() >= BSONType::Array) {
return e.type() == BSONType::Array ? Decision::Yes : Decision::Maybe;
@@ -475,20 +425,23 @@ Decision _fieldContainsArrayData(const BSONObj& min, const BSONObj& max, StringD
// Let's decide whether we are looking at the terminal field on the dotted path, or if we might
// need to unpack sub-objects.
- if (std::string::npos == field.find('.')) {
- auto [minLit, maxLit] = _getLiteralFields(min, max, field);
- tassert(5993302, "Malformed control summary for bucket", minLit.eoo() == maxLit.eoo());
- if (!minLit.eoo() /* => !maxLit.eoo()*/) {
- return _controlTypesIndicateArrayData(minLit, maxLit, true);
- }
+ const bool terminal = std::string::npos == field.find('.');
+
+ // First lets try to use the field name literally (i.e. treat it as terminal, even if it has an
+ // internal dot).
+ auto [minLit, maxLit] = _getLiteralFields(min, max, field);
+ tassert(5993302, "Malformed control summary for bucket", minLit.eoo() == maxLit.eoo());
+ if (!minLit.eoo() /* => !maxLit.eoo()*/) {
+ return _controlTypesIndicateArrayData(minLit, maxLit, terminal);
+ } else if (terminal) {
// Nothing further to evaluate, the field is missing from min and max, and thus from all
// measurements in this bucket.
return Decision::No;
}
auto [minEl, maxEl, nextField] = _getNextFields(min, max, field);
- invariant(!nextField.empty());
- auto decision = _controlTypesIndicateArrayData(minEl, maxEl, false);
+ invariant(terminal == nextField.empty());
+ auto decision = _controlTypesIndicateArrayData(minEl, maxEl, terminal);
if (decision != Decision::Undecided) {
return decision;
}