diff options
author | Martin Neupauer <martin.neupauer@mongodb.com> | 2019-07-09 12:30:18 -0400 |
---|---|---|
committer | Martin Neupauer <martin.neupauer@mongodb.com> | 2019-07-22 12:19:43 -0400 |
commit | 64bbf8969d59b8461af6d0dd80f5630f6d721e2f (patch) | |
tree | bbac96a859a1fbc70453a24dfd6a18c3d4bed37d | |
parent | 121972781acf4a5db3cc26e6151044954d612574 (diff) | |
download | mongo-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.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document.h | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_internal.h | 18 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_cursor.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_cursor.h | 7 | ||||
-rw-r--r-- | src/mongo/db/pipeline/pipeline_d.cpp | 4 |
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()); } } |