summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Neupauer <martin.neupauer@mongodb.com>2019-07-09 12:30:18 -0400
committerMartin Neupauer <martin.neupauer@mongodb.com>2019-07-22 12:19:43 -0400
commit64bbf8969d59b8461af6d0dd80f5630f6d721e2f (patch)
treebbac96a859a1fbc70453a24dfd6a18c3d4bed37d
parent121972781acf4a5db3cc26e6151044954d612574 (diff)
downloadmongo-64bbf8969d59b8461af6d0dd80f5630f6d721e2f.tar.gz
SERVER-40969 No-op Document/Value to BSON conversion when Document has not been modified.
-rw-r--r--src/mongo/db/pipeline/document.cpp8
-rw-r--r--src/mongo/db/pipeline/document.h2
-rw-r--r--src/mongo/db/pipeline/document_internal.h18
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.cpp4
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.h7
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp4
6 files changed, 36 insertions, 7 deletions
diff --git a/src/mongo/db/pipeline/document.cpp b/src/mongo/db/pipeline/document.cpp
index 64f73e9054d..29af89e0b5b 100644
--- a/src/mongo/db/pipeline/document.cpp
+++ b/src/mongo/db/pipeline/document.cpp
@@ -176,10 +176,12 @@ Position DocumentStorage::findField(StringData requested, LookupPolicy policy) c
}
Position DocumentStorage::constructInCache(const BSONElement& elem) {
+ auto savedModified = _modified;
auto pos = getNextPosition();
const auto fieldName = elem.fieldNameStringData();
const unsigned hash = hashKey(fieldName);
appendField(fieldName, hash, ValueElement::Kind::kCached) = Value(elem);
+ _modified = savedModified;
return pos;
}
@@ -296,7 +298,7 @@ void DocumentStorage::reserveFields(size_t expectedFields) {
}
intrusive_ptr<DocumentStorage> DocumentStorage::clone() const {
- auto out = make_intrusive<DocumentStorage>(_bson, _stripMetadata);
+ auto out = make_intrusive<DocumentStorage>(_bson, _stripMetadata, _modified);
if (_cache) {
// Make a copy of the buffer with the fields.
@@ -453,6 +455,10 @@ void Document::toBson(BSONObjBuilder* builder, size_t recursionLevel) const {
}
BSONObj Document::toBson() const {
+ if (!storage().isModified() && !storage().stripMetadata()) {
+ return storage().bsonObj();
+ }
+
BSONObjBuilder bb;
toBson(&bb);
return bb.obj();
diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h
index 4f63a9fd47e..54629bc0a55 100644
--- a/src/mongo/db/pipeline/document.h
+++ b/src/mongo/db/pipeline/document.h
@@ -623,7 +623,7 @@ public:
* complete list is in Document::allMetadataFieldNames).
*/
DocumentStorage& newStorageWithBson(const BSONObj& bson, bool stripMetadata) {
- reset(make_intrusive<DocumentStorage>(bson, stripMetadata));
+ reset(make_intrusive<DocumentStorage>(bson, stripMetadata, false));
return const_cast<DocumentStorage&>(*storagePtr());
}
diff --git a/src/mongo/db/pipeline/document_internal.h b/src/mongo/db/pipeline/document_internal.h
index cdab670b5a5..843f5d4011d 100644
--- a/src/mongo/db/pipeline/document_internal.h
+++ b/src/mongo/db/pipeline/document_internal.h
@@ -390,10 +390,11 @@ public:
* the document. If we know that the BSON does not contain any metadata fields we can set the
* 'stripMetadata' flag to false that will speed up the field iteration.
*/
- DocumentStorage(const BSONObj& bson, bool stripMetadata) : DocumentStorage() {
+ DocumentStorage(const BSONObj& bson, bool stripMetadata, bool modified) : DocumentStorage() {
_bson = bson.getOwned();
_bsonIt = BSONObjIterator(_bson);
_stripMetadata = stripMetadata;
+ _modified = modified;
}
~DocumentStorage();
@@ -439,10 +440,12 @@ public:
// MutableDocument uses these
ValueElement& getField(Position pos) {
+ _modified = true;
verify(pos.found());
return *(_firstElement->plusBytes(pos.index));
}
Value& getField(StringData name, LookupPolicy policy) {
+ _modified = true;
Position pos = findField(name, policy);
if (!pos.found())
return appendField(name, hashKey(name), ValueElement::Kind::kMaybeInserted);
@@ -630,6 +633,13 @@ public:
Position constructInCache(const BSONElement& elem);
+ auto isModified() const {
+ return _modified;
+ }
+ auto bsonObj() const {
+ return _bson;
+ }
+
private:
/// Returns the position of the named field in the cache or Position()
Position findFieldInCache(StringData name) const;
@@ -711,6 +721,12 @@ private:
// have any metadata we can set _stripMetadata to false that will speed up the iteration.
bool _stripMetadata{false};
+ // This flag is set to true anytime the storage returns a mutable field. It is used to optimize
+ // a conversion to BSON; i.e. if there are not any modifications we can directly return _bson.
+ // Note that an empty (default) document is marked 'modified'. The reason for this is that the
+ // empty _bson is not owned but consumers expect toBson() to return owned BSON.
+ bool _modified{true};
+
// Defined in document.cpp
static const DocumentStorage kEmptyDoc;
diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp
index f4e53f20d16..9c2d124f34d 100644
--- a/src/mongo/db/pipeline/document_source_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_cursor.cpp
@@ -75,7 +75,9 @@ DocumentSource::GetNextResult DocumentSourceCursor::getNext() {
}
Document DocumentSourceCursor::transformBSONObjToDocument(const BSONObj& obj) const {
- return _dependencies ? _dependencies->extractFields(obj) : Document::fromBsonWithMetaData(obj);
+ return _dependencies
+ ? _dependencies->extractFields(obj)
+ : (_inputHasMetadata ? Document::fromBsonWithMetaData(obj) : Document(obj));
}
void DocumentSourceCursor::loadBatch() {
diff --git a/src/mongo/db/pipeline/document_source_cursor.h b/src/mongo/db/pipeline/document_source_cursor.h
index 215d94c3387..815b6635b14 100644
--- a/src/mongo/db/pipeline/document_source_cursor.h
+++ b/src/mongo/db/pipeline/document_source_cursor.h
@@ -119,10 +119,14 @@ public:
*
* @param projection The projection that has been passed down to the query system.
* @param deps The output of DepsTracker::toParsedDeps.
+ * @param inputHasMetadata Indicates whether the input BSON object contains metadata fields.
*/
- void setProjection(const BSONObj& projection, const boost::optional<ParsedDeps>& deps) {
+ void setProjection(const BSONObj& projection,
+ const boost::optional<ParsedDeps>& deps,
+ bool inputHasMetadata) {
_projection = projection;
_dependencies = deps;
+ _inputHasMetadata = inputHasMetadata;
}
/**
@@ -209,6 +213,7 @@ private:
BSONObj _sort;
BSONObj _projection;
bool _shouldProduceEmptyDocs = false;
+ bool _inputHasMetadata = false;
boost::optional<ParsedDeps> _dependencies;
boost::intrusive_ptr<DocumentSourceLimit> _limit;
long long _docsAddedToBatches; // for _limit enforcement
diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp
index dc742e0437c..3a27c6ee73f 100644
--- a/src/mongo/db/pipeline/pipeline_d.cpp
+++ b/src/mongo/db/pipeline/pipeline_d.cpp
@@ -838,7 +838,7 @@ void PipelineD::addCursorSource(Pipeline* pipeline,
}
if (!projectionObj.isEmpty()) {
- cursor->setProjection(projectionObj, boost::none);
+ cursor->setProjection(projectionObj, boost::none, deps.getNeedsAnyMetadata());
} else {
// There may be fewer dependencies now if the sort was covered.
if (!sortObj.isEmpty()) {
@@ -847,7 +847,7 @@ void PipelineD::addCursorSource(Pipeline* pipeline,
: DepsTracker::MetadataAvailable::kNoMetadata);
}
- cursor->setProjection(deps.toProjection(), deps.toParsedDeps());
+ cursor->setProjection(deps.toProjection(), deps.toParsedDeps(), deps.getNeedsAnyMetadata());
}
}