diff options
Diffstat (limited to 'src/mongo/db/pipeline/document.h')
-rw-r--r-- | src/mongo/db/pipeline/document.h | 953 |
1 files changed, 502 insertions, 451 deletions
diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h index 491b9c050d3..5010f69b5fa 100644 --- a/src/mongo/db/pipeline/document.h +++ b/src/mongo/db/pipeline/document.h @@ -36,529 +36,580 @@ #include "mongo/bson/util/builder.h" namespace mongo { - class BSONObj; - class FieldIterator; - class FieldPath; - class Value; - class MutableDocument; +class BSONObj; +class FieldIterator; +class FieldPath; +class Value; +class MutableDocument; - /** An internal class that represents the position of a field in a document. +/** An internal class that represents the position of a field in a document. + * + * This is a low-level class that you usually don't need to worry about. + * + * The main use of this class for clients is to allow refetching or + * setting a field without looking it up again. It has a default + * constructor that represents a field not being in a document. It also + * has a method 'bool found()' that tells you if a field was found. + * + * For more details see document_internal.h + */ +class Position; + +/** A Document is similar to a BSONObj but with a different in-memory representation. + * + * A Document can be treated as a const std::map<std::string, const Value> that is + * very cheap to copy and is Assignable. Therefore, it is acceptable to + * pass and return by Value. Note that the data in a Document is + * immutable, but you can replace a Document instance with assignment. + * + * See Also: Value class in Value.h + */ +class Document { +public: + /// Empty Document (does no allocation) + Document() {} + + /// Create a new Document deep-converted from the given BSONObj. + explicit Document(const BSONObj& bson); + + void swap(Document& rhs) { + _storage.swap(rhs._storage); + } + + /// Look up a field by key name. Returns Value() if no such field. O(1) + const Value operator[](StringData key) const { + return getField(key); + } + const Value getField(StringData key) const { + return storage().getField(key); + } + + /// Look up a field by Position. See positionOf and getNestedField. + const Value operator[](Position pos) const { + return getField(pos); + } + const Value getField(Position pos) const { + return storage().getField(pos).val; + } + + /** Similar to BSONObj::getFieldDotted, but using FieldPath rather than a dotted string. + * If you pass a non-NULL positions vector, you get back a path suitable + * to pass to MutableDocument::setNestedField. + * + * TODO a version that doesn't use FieldPath + */ + const Value getNestedField(const FieldPath& fieldNames, + std::vector<Position>* positions = NULL) const; + + /// Number of fields in this document. O(n) + size_t size() const { + return storage().size(); + } + + /// True if this document has no fields. + bool empty() const { + return !_storage || storage().iterator().atEnd(); + } + + /// Create a new FieldIterator that can be used to examine the Document's fields in order. + FieldIterator fieldIterator() const; + + /// Convenience type for dealing with fields. Used by FieldIterator. + typedef std::pair<StringData, Value> FieldPair; + + /** Get the approximate storage size of the document and sub-values in bytes. + * Note: Some memory may be shared with other Documents or between fields within + * a single Document so this can overestimate usage. + */ + size_t getApproximateSize() const; + + /** Compare two documents. * - * This is a low-level class that you usually don't need to worry about. + * BSON document field order is significant, so this just goes through + * the fields in order. The comparison is done in roughly the same way + * as strings are compared, but comparing one field at a time instead + * of one character at a time. * - * The main use of this class for clients is to allow refetching or - * setting a field without looking it up again. It has a default - * constructor that represents a field not being in a document. It also - * has a method 'bool found()' that tells you if a field was found. + * Note: This does not consider metadata when comparing documents. * - * For more details see document_internal.h + * @returns an integer less than zero, zero, or an integer greater than + * zero, depending on whether lhs < rhs, lhs == rhs, or lhs > rhs + * Warning: may return values other than -1, 0, or 1 */ - class Position; + static int compare(const Document& lhs, const Document& rhs); - /** A Document is similar to a BSONObj but with a different in-memory representation. - * - * A Document can be treated as a const std::map<std::string, const Value> that is - * very cheap to copy and is Assignable. Therefore, it is acceptable to - * pass and return by Value. Note that the data in a Document is - * immutable, but you can replace a Document instance with assignment. + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& out, const Document& doc) { + return out << doc.toString(); + } + + /** Calculate a hash value. * - * See Also: Value class in Value.h + * Meant to be used to create composite hashes suitable for + * hashed container classes such as unordered_map. */ - class Document { - public: + void hash_combine(size_t& seed) const; - /// Empty Document (does no allocation) - Document() {} - - /// Create a new Document deep-converted from the given BSONObj. - explicit Document(const BSONObj& bson); - - void swap(Document& rhs) { _storage.swap(rhs._storage); } - - /// Look up a field by key name. Returns Value() if no such field. O(1) - const Value operator[] (StringData key) const { return getField(key); } - const Value getField(StringData key) const { return storage().getField(key); } - - /// Look up a field by Position. See positionOf and getNestedField. - const Value operator[] (Position pos) const { return getField(pos); } - const Value getField(Position pos) const { return storage().getField(pos).val; } - - /** Similar to BSONObj::getFieldDotted, but using FieldPath rather than a dotted string. - * If you pass a non-NULL positions vector, you get back a path suitable - * to pass to MutableDocument::setNestedField. - * - * TODO a version that doesn't use FieldPath - */ - const Value getNestedField(const FieldPath& fieldNames, - std::vector<Position>* positions=NULL) const; - - /// Number of fields in this document. O(n) - size_t size() const { return storage().size(); } - - /// True if this document has no fields. - bool empty() const { return !_storage || storage().iterator().atEnd(); } - - /// Create a new FieldIterator that can be used to examine the Document's fields in order. - FieldIterator fieldIterator() const; - - /// Convenience type for dealing with fields. Used by FieldIterator. - typedef std::pair<StringData, Value> FieldPair; - - /** Get the approximate storage size of the document and sub-values in bytes. - * Note: Some memory may be shared with other Documents or between fields within - * a single Document so this can overestimate usage. - */ - size_t getApproximateSize() const; - - /** Compare two documents. - * - * BSON document field order is significant, so this just goes through - * the fields in order. The comparison is done in roughly the same way - * as strings are compared, but comparing one field at a time instead - * of one character at a time. - * - * Note: This does not consider metadata when comparing documents. - * - * @returns an integer less than zero, zero, or an integer greater than - * zero, depending on whether lhs < rhs, lhs == rhs, or lhs > rhs - * Warning: may return values other than -1, 0, or 1 - */ - static int compare(const Document& lhs, const Document& rhs); - - std::string toString() const; - - friend - std::ostream& operator << (std::ostream& out, const Document& doc) { return out << doc.toString(); } - - /** Calculate a hash value. - * - * Meant to be used to create composite hashes suitable for - * hashed container classes such as unordered_map. - */ - void hash_combine(size_t &seed) const; - - /** - * Add this document to the BSONObj under construction with the given BSONObjBuilder. - * Does not include metadata. - */ - void toBson(BSONObjBuilder *pBsonObjBuilder) const; - BSONObj toBson() const; - - /** - * Like toBson, but includes metadata at the top-level. - * Output is parseable by fromBsonWithMetaData - */ - BSONObj toBsonWithMetaData() const; - - /** - * Like Document(BSONObj) but treats top-level fields with special names as metadata. - * Special field names are available as static constants on this class with names starting - * with metaField. - */ - static Document fromBsonWithMetaData(const BSONObj& bson); - - // Support BSONObjBuilder and BSONArrayBuilder "stream" API - friend BSONObjBuilder& operator << (BSONObjBuilderValueStream& builder, const Document& d); - - /** Return the abstract Position of a field, suitable to pass to operator[] or getField(). - * This can potentially save time if you need to refer to a field multiple times. - */ - Position positionOf(StringData fieldName) const { return storage().findField(fieldName); } - - /** Clone a document. - * - * This should only be called by MutableDocument and tests - * - * The new document shares all the fields' values with the original. - * This is not a deep copy. Only the fields on the top-level document - * are cloned. - */ - Document clone() const { return Document(storage().clone().get()); } - - static const StringData metaFieldTextScore; // "$textScore" - bool hasTextScore() const { return storage().hasTextScore(); } - double getTextScore() const { return storage().getTextScore(); } - - /// members for Sorter - struct SorterDeserializeSettings {}; // unused - void serializeForSorter(BufBuilder& buf) const; - static Document deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&); - int memUsageForSorter() const { return getApproximateSize(); } - Document getOwned() const { return *this; } - - /// only for testing - const void* getPtr() const { return _storage.get(); } - - private: - friend class FieldIterator; - friend class ValueStorage; - friend class MutableDocument; - friend class MutableValue; - - explicit Document(const DocumentStorage* ptr) : _storage(ptr) {}; - - const DocumentStorage& storage() const { - return (_storage ? *_storage : DocumentStorage::emptyDoc()); - } - boost::intrusive_ptr<const DocumentStorage> _storage; - }; + /** + * Add this document to the BSONObj under construction with the given BSONObjBuilder. + * Does not include metadata. + */ + void toBson(BSONObjBuilder* pBsonObjBuilder) const; + BSONObj toBson() const; + + /** + * Like toBson, but includes metadata at the top-level. + * Output is parseable by fromBsonWithMetaData + */ + BSONObj toBsonWithMetaData() const; + + /** + * Like Document(BSONObj) but treats top-level fields with special names as metadata. + * Special field names are available as static constants on this class with names starting + * with metaField. + */ + static Document fromBsonWithMetaData(const BSONObj& bson); + + // Support BSONObjBuilder and BSONArrayBuilder "stream" API + friend BSONObjBuilder& operator<<(BSONObjBuilderValueStream& builder, const Document& d); - inline bool operator== (const Document& l, const Document& r) { - return Document::compare(l, r) == 0; + /** Return the abstract Position of a field, suitable to pass to operator[] or getField(). + * This can potentially save time if you need to refer to a field multiple times. + */ + Position positionOf(StringData fieldName) const { + return storage().findField(fieldName); } - inline bool operator!= (const Document& l, const Document& r) { - return Document::compare(l, r) != 0; + + /** Clone a document. + * + * This should only be called by MutableDocument and tests + * + * The new document shares all the fields' values with the original. + * This is not a deep copy. Only the fields on the top-level document + * are cloned. + */ + Document clone() const { + return Document(storage().clone().get()); } - inline bool operator< (const Document& l, const Document& r) { - return Document::compare(l, r) < 0; + + static const StringData metaFieldTextScore; // "$textScore" + bool hasTextScore() const { + return storage().hasTextScore(); } - inline bool operator<= (const Document& l, const Document& r) { - return Document::compare(l, r) <= 0; + double getTextScore() const { + return storage().getTextScore(); } - inline bool operator> (const Document& l, const Document& r) { - return Document::compare(l, r) > 0; + + /// members for Sorter + struct SorterDeserializeSettings {}; // unused + void serializeForSorter(BufBuilder& buf) const; + static Document deserializeForSorter(BufReader& buf, const SorterDeserializeSettings&); + int memUsageForSorter() const { + return getApproximateSize(); } - inline bool operator>= (const Document& l, const Document& r) { - return Document::compare(l, r) >= 0; + Document getOwned() const { + return *this; } + /// only for testing + const void* getPtr() const { + return _storage.get(); + } - /** This class is returned by MutableDocument to allow you to modify its values. - * You are not allowed to hold variables of this type (enforced by the type system). - */ - class MutableValue { - public: - void operator= (const Value& v) { _val = v; } - - /** These are designed to allow things like mutDoc["a"]["b"]["c"] = Value(10); - * It is safe to use even on nonexistent fields. - */ - MutableValue operator[] (StringData key) { return getField(key); } - MutableValue operator[] (Position pos) { return getField(pos); } - - MutableValue getField(StringData key); - MutableValue getField(Position pos); - - private: - friend class MutableDocument; - - /// can only be constructed or copied by self and friends - MutableValue(const MutableValue& other): _val(other._val) {} - explicit MutableValue(Value& val): _val(val) {} - - /// Used by MutableDocument(MutableValue) - const RefCountable*& getDocPtr() { - if (_val.getType() != Object || _val._storage.genericRCPtr == NULL) { - // If the current value isn't an object we replace it with a Object-typed Value. - // Note that we can't just use Document() here because that is a NULL pointer and - // Value doesn't refcount NULL pointers. This led to a memory leak (SERVER-10554) - // because MutableDocument::newStorage() would set a non-NULL pointer into the Value - // without setting the refCounter bit. While allocating a DocumentStorage here could - // result in an allocation where none is needed, in practice this is only called - // when we are about to add a field to the sub-document so this just changes where - // the allocation is done. - _val = Value(Document(new DocumentStorage())); - } - - return _val._storage.genericRCPtr; - } +private: + friend class FieldIterator; + friend class ValueStorage; + friend class MutableDocument; + friend class MutableValue; - MutableValue& operator= (const MutableValue&); // not assignable with another MutableValue + explicit Document(const DocumentStorage* ptr) : _storage(ptr){}; - Value& _val; - }; + const DocumentStorage& storage() const { + return (_storage ? *_storage : DocumentStorage::emptyDoc()); + } + boost::intrusive_ptr<const DocumentStorage> _storage; +}; - /** MutableDocument is a Document builder that supports both adding and updating fields. - * - * This class fills a similar role to BSONObjBuilder, but allows you to - * change existing fields and more easily write to sub-Documents. - * - * To preserve the immutability of Documents, MutableDocument will - * shallow-clone its storage on write (COW) if it is shared with any other - * Documents. +inline bool operator==(const Document& l, const Document& r) { + return Document::compare(l, r) == 0; +} +inline bool operator!=(const Document& l, const Document& r) { + return Document::compare(l, r) != 0; +} +inline bool operator<(const Document& l, const Document& r) { + return Document::compare(l, r) < 0; +} +inline bool operator<=(const Document& l, const Document& r) { + return Document::compare(l, r) <= 0; +} +inline bool operator>(const Document& l, const Document& r) { + return Document::compare(l, r) > 0; +} +inline bool operator>=(const Document& l, const Document& r) { + return Document::compare(l, r) >= 0; +} + + +/** This class is returned by MutableDocument to allow you to modify its values. + * You are not allowed to hold variables of this type (enforced by the type system). + */ +class MutableValue { +public: + void operator=(const Value& v) { + _val = v; + } + + /** These are designed to allow things like mutDoc["a"]["b"]["c"] = Value(10); + * It is safe to use even on nonexistent fields. */ - class MutableDocument { - MONGO_DISALLOW_COPYING(MutableDocument); - public: + MutableValue operator[](StringData key) { + return getField(key); + } + MutableValue operator[](Position pos) { + return getField(pos); + } - /** Create a new empty Document. - * - * @param expectedFields a hint at what the number of fields will be, if known. - * this can be used to increase memory allocation efficiency. There is - * no impact on correctness if this field over or under estimates. - * - * TODO: find some way to convey field-name sizes to make even more efficient - */ - MutableDocument() :_storageHolder(NULL), _storage(_storageHolder) {} - explicit MutableDocument(size_t expectedFields); - - /// No copy yet. Copy-on-write. See storage() - explicit MutableDocument(const Document& d) : _storageHolder(NULL) - , _storage(_storageHolder) { - reset(d); + MutableValue getField(StringData key); + MutableValue getField(Position pos); + +private: + friend class MutableDocument; + + /// can only be constructed or copied by self and friends + MutableValue(const MutableValue& other) : _val(other._val) {} + explicit MutableValue(Value& val) : _val(val) {} + + /// Used by MutableDocument(MutableValue) + const RefCountable*& getDocPtr() { + if (_val.getType() != Object || _val._storage.genericRCPtr == NULL) { + // If the current value isn't an object we replace it with a Object-typed Value. + // Note that we can't just use Document() here because that is a NULL pointer and + // Value doesn't refcount NULL pointers. This led to a memory leak (SERVER-10554) + // because MutableDocument::newStorage() would set a non-NULL pointer into the Value + // without setting the refCounter bit. While allocating a DocumentStorage here could + // result in an allocation where none is needed, in practice this is only called + // when we are about to add a field to the sub-document so this just changes where + // the allocation is done. + _val = Value(Document(new DocumentStorage())); } - ~MutableDocument() { - if (_storageHolder) - intrusive_ptr_release(_storageHolder); - } + return _val._storage.genericRCPtr; + } - /** Replace the current base Document with the argument - * - * All Positions from the passed in Document are valid and refer to the - * same field in this MutableDocument. - */ - void reset(const Document& d=Document()) { reset(d._storage.get()); } - - /** Add the given field to the Document. - * - * BSON documents' fields are ordered; the new Field will be - * appended to the current list of fields. - * - * Unlike getField/setField, addField does not look for a field with the - * same name and therefore cannot be used to update fields. - * - * It is an error to add a field that has the same name as another field. - * - * TODO: This is currently allowed but getField only gets first field. - * Decide what level of support is needed for duplicate fields. - * If duplicates are not allowed, consider removing this method. - */ - void addField(StringData fieldName, const Value& val) { - storage().appendField(fieldName) = val; - } + MutableValue& operator=(const MutableValue&); // not assignable with another MutableValue - /** Update field by key. If there is no field with that key, add one. - * - * If the new value is missing(), the field is logically removed. - */ - MutableValue operator[] (StringData key) { return getField(key); } - void setField(StringData key, const Value& val) { getField(key) = val; } - MutableValue getField(StringData key) { - return MutableValue(storage().getField(key)); - } + Value& _val; +}; - /// Update field by Position. Must already be a valid Position. - MutableValue operator[] (Position pos) { return getField(pos); } - void setField(Position pos, const Value& val) { getField(pos) = val; } - MutableValue getField(Position pos) { - return MutableValue(storage().getField(pos).val); - } +/** MutableDocument is a Document builder that supports both adding and updating fields. + * + * This class fills a similar role to BSONObjBuilder, but allows you to + * change existing fields and more easily write to sub-Documents. + * + * To preserve the immutability of Documents, MutableDocument will + * shallow-clone its storage on write (COW) if it is shared with any other + * Documents. + */ +class MutableDocument { + MONGO_DISALLOW_COPYING(MutableDocument); - /// Logically remove a field. Note that memory usage does not decrease. - void remove(StringData key) { getField(key) = Value(); } - - /** Gets/Sets a nested field given a path. - * - * All fields along path are created as empty Documents if they don't exist - * or are any other type. - */ - MutableValue getNestedField(const FieldPath& dottedField); - void setNestedField(const FieldPath& dottedField, const Value& val) { - getNestedField(dottedField) = val; - } +public: + /** Create a new empty Document. + * + * @param expectedFields a hint at what the number of fields will be, if known. + * this can be used to increase memory allocation efficiency. There is + * no impact on correctness if this field over or under estimates. + * + * TODO: find some way to convey field-name sizes to make even more efficient + */ + MutableDocument() : _storageHolder(NULL), _storage(_storageHolder) {} + explicit MutableDocument(size_t expectedFields); - /// Takes positions vector from Document::getNestedField. All fields in path must exist. - MutableValue getNestedField(const std::vector<Position>& positions); - void setNestedField(const std::vector<Position>& positions, const Value& val) { - getNestedField(positions) = val; - } + /// No copy yet. Copy-on-write. See storage() + explicit MutableDocument(const Document& d) : _storageHolder(NULL), _storage(_storageHolder) { + reset(d); + } - /** - * Copies all metadata from source if it has any. - * Note: does not clear metadata from this. - */ - void copyMetaDataFrom(const Document& source) { - storage().copyMetaDataFrom(source.storage()); - } + ~MutableDocument() { + if (_storageHolder) + intrusive_ptr_release(_storageHolder); + } - void setTextScore(double score) { storage().setTextScore(score); } - - /** Convert to a read-only document and release reference. - * - * Call this to indicate that you are done with this Document and will - * not be making further changes from this MutableDocument. - * - * TODO: there are some optimizations that may make sense at freeze time. - */ - Document freeze() { - // This essentially moves _storage into a new Document by way of temp. - Document ret; - boost::intrusive_ptr<const DocumentStorage> temp (storagePtr(), /*inc_ref_count=*/false); - temp.swap(ret._storage); - _storage = NULL; - return ret; - } + /** Replace the current base Document with the argument + * + * All Positions from the passed in Document are valid and refer to the + * same field in this MutableDocument. + */ + void reset(const Document& d = Document()) { + reset(d._storage.get()); + } - /// Used to simplify the common pattern of creating a value of the document. - Value freezeToValue() { - return Value(freeze()); - } + /** Add the given field to the Document. + * + * BSON documents' fields are ordered; the new Field will be + * appended to the current list of fields. + * + * Unlike getField/setField, addField does not look for a field with the + * same name and therefore cannot be used to update fields. + * + * It is an error to add a field that has the same name as another field. + * + * TODO: This is currently allowed but getField only gets first field. + * Decide what level of support is needed for duplicate fields. + * If duplicates are not allowed, consider removing this method. + */ + void addField(StringData fieldName, const Value& val) { + storage().appendField(fieldName) = val; + } - /** Borrow a readable reference to this Document. - * - * Note that unlike freeze(), this indicates intention to continue - * modifying this document. The returned Document will not observe - * future changes to this MutableDocument. - */ - Document peek() { - return Document(storagePtr()); - } + /** Update field by key. If there is no field with that key, add one. + * + * If the new value is missing(), the field is logically removed. + */ + MutableValue operator[](StringData key) { + return getField(key); + } + void setField(StringData key, const Value& val) { + getField(key) = val; + } + MutableValue getField(StringData key) { + return MutableValue(storage().getField(key)); + } - private: - friend class MutableValue; // for access to next constructor - explicit MutableDocument(MutableValue mv) - : _storageHolder(NULL) - , _storage(mv.getDocPtr()) - {} - - void reset(const DocumentStorage* ds) { - if (_storage) intrusive_ptr_release(_storage); - _storage = ds; - if (_storage) intrusive_ptr_add_ref(_storage); - } + /// Update field by Position. Must already be a valid Position. + MutableValue operator[](Position pos) { + return getField(pos); + } + void setField(Position pos, const Value& val) { + getField(pos) = val; + } + MutableValue getField(Position pos) { + return MutableValue(storage().getField(pos).val); + } - // This is split into 3 functions to speed up the fast-path - DocumentStorage& storage() { - if (MONGO_unlikely( !_storage )) - return newStorage(); + /// Logically remove a field. Note that memory usage does not decrease. + void remove(StringData key) { + getField(key) = Value(); + } - if (MONGO_unlikely( _storage->isShared() )) - return clonedStorage(); + /** Gets/Sets a nested field given a path. + * + * All fields along path are created as empty Documents if they don't exist + * or are any other type. + */ + MutableValue getNestedField(const FieldPath& dottedField); + void setNestedField(const FieldPath& dottedField, const Value& val) { + getNestedField(dottedField) = val; + } - // This function exists to ensure this is safe - return const_cast<DocumentStorage&>(*storagePtr()); - } - DocumentStorage& newStorage() { - reset(new DocumentStorage); - return const_cast<DocumentStorage&>(*storagePtr()); - } - DocumentStorage& clonedStorage() { - reset(storagePtr()->clone().get()); - return const_cast<DocumentStorage&>(*storagePtr()); - } + /// Takes positions vector from Document::getNestedField. All fields in path must exist. + MutableValue getNestedField(const std::vector<Position>& positions); + void setNestedField(const std::vector<Position>& positions, const Value& val) { + getNestedField(positions) = val; + } - // recursive helpers for same-named public methods - MutableValue getNestedFieldHelper(const FieldPath& dottedField, size_t level); - MutableValue getNestedFieldHelper(const std::vector<Position>& positions, size_t level); + /** + * Copies all metadata from source if it has any. + * Note: does not clear metadata from this. + */ + void copyMetaDataFrom(const Document& source) { + storage().copyMetaDataFrom(source.storage()); + } - // this should only be called by storage methods and peek/freeze - const DocumentStorage* storagePtr() const { - dassert(!_storage || typeid(*_storage) == typeid(const DocumentStorage)); - return static_cast<const DocumentStorage*>(_storage); - } + void setTextScore(double score) { + storage().setTextScore(score); + } - // These are both const to prevent modifications bypassing storage() method. - // They always point to NULL or an object with dynamic type DocumentStorage. - const RefCountable* _storageHolder; // Only used in constructors and destructor - const RefCountable*& _storage; // references either above member or genericRCPtr in a Value - }; + /** Convert to a read-only document and release reference. + * + * Call this to indicate that you are done with this Document and will + * not be making further changes from this MutableDocument. + * + * TODO: there are some optimizations that may make sense at freeze time. + */ + Document freeze() { + // This essentially moves _storage into a new Document by way of temp. + Document ret; + boost::intrusive_ptr<const DocumentStorage> temp(storagePtr(), /*inc_ref_count=*/false); + temp.swap(ret._storage); + _storage = NULL; + return ret; + } - /// This is the public iterator over a document - class FieldIterator { - public: - explicit FieldIterator(const Document& doc) - : _doc(doc) - , _it(_doc.storage().iterator()) - {} + /// Used to simplify the common pattern of creating a value of the document. + Value freezeToValue() { + return Value(freeze()); + } - /// Ask if there are more fields to return. - bool more() const { return !_it.atEnd(); } + /** Borrow a readable reference to this Document. + * + * Note that unlike freeze(), this indicates intention to continue + * modifying this document. The returned Document will not observe + * future changes to this MutableDocument. + */ + Document peek() { + return Document(storagePtr()); + } - /// Get next item and advance iterator - Document::FieldPair next() { - verify(more()); +private: + friend class MutableValue; // for access to next constructor + explicit MutableDocument(MutableValue mv) : _storageHolder(NULL), _storage(mv.getDocPtr()) {} - Document::FieldPair fp (_it->nameSD(), _it->val); - _it.advance(); - return fp; - } + void reset(const DocumentStorage* ds) { + if (_storage) + intrusive_ptr_release(_storage); + _storage = ds; + if (_storage) + intrusive_ptr_add_ref(_storage); + } - private: - // We'll hang on to the original document to ensure we keep its storage alive - Document _doc; - DocumentStorageIterator _it; - }; + // This is split into 3 functions to speed up the fast-path + DocumentStorage& storage() { + if (MONGO_unlikely(!_storage)) + return newStorage(); - /// Macro to create Document literals. Syntax is the same as the BSON("name" << 123) macro. -#define DOC(fields) ((DocumentStream() << fields).done()) + if (MONGO_unlikely(_storage->isShared())) + return clonedStorage(); - /** Macro to create Array-typed Value literals. - * Syntax is the same as the BSON_ARRAY(123 << "foo") macro. - */ -#define DOC_ARRAY(fields) ((ValueArrayStream() << fields).done()) + // This function exists to ensure this is safe + return const_cast<DocumentStorage&>(*storagePtr()); + } + DocumentStorage& newStorage() { + reset(new DocumentStorage); + return const_cast<DocumentStorage&>(*storagePtr()); + } + DocumentStorage& clonedStorage() { + reset(storagePtr()->clone().get()); + return const_cast<DocumentStorage&>(*storagePtr()); + } + // recursive helpers for same-named public methods + MutableValue getNestedFieldHelper(const FieldPath& dottedField, size_t level); + MutableValue getNestedFieldHelper(const std::vector<Position>& positions, size_t level); - // These classes are only for the implementation of the DOC and DOC_ARRAY macros. - // They should not be used for any other reason. - class DocumentStream { - // The stream alternates between DocumentStream taking a fieldname - // and ValueStream taking a Value. - class ValueStream { - public: - ValueStream(DocumentStream& builder) :builder(builder) {} + // this should only be called by storage methods and peek/freeze + const DocumentStorage* storagePtr() const { + dassert(!_storage || typeid(*_storage) == typeid(const DocumentStorage)); + return static_cast<const DocumentStorage*>(_storage); + } - DocumentStream& operator << (const Value& val) { - builder._md[name] = val; - return builder; - } + // These are both const to prevent modifications bypassing storage() method. + // They always point to NULL or an object with dynamic type DocumentStorage. + const RefCountable* _storageHolder; // Only used in constructors and destructor + const RefCountable*& _storage; // references either above member or genericRCPtr in a Value +}; - /// support anything directly supported by a value constructor - template <typename T> - DocumentStream& operator << (const T& val) { - return *this << Value(val); - } +/// This is the public iterator over a document +class FieldIterator { +public: + explicit FieldIterator(const Document& doc) : _doc(doc), _it(_doc.storage().iterator()) {} - StringData name; - DocumentStream& builder; - }; + /// Ask if there are more fields to return. + bool more() const { + return !_it.atEnd(); + } - public: - DocumentStream() :_stream(*this) {} + /// Get next item and advance iterator + Document::FieldPair next() { + verify(more()); - ValueStream& operator << (StringData name) { - _stream.name = name; - return _stream; - } + Document::FieldPair fp(_it->nameSD(), _it->val); + _it.advance(); + return fp; + } - Document done() { return _md.freeze(); } +private: + // We'll hang on to the original document to ensure we keep its storage alive + Document _doc; + DocumentStorageIterator _it; +}; + +/// Macro to create Document literals. Syntax is the same as the BSON("name" << 123) macro. +#define DOC(fields) ((DocumentStream() << fields).done()) + +/** Macro to create Array-typed Value literals. + * Syntax is the same as the BSON_ARRAY(123 << "foo") macro. + */ +#define DOC_ARRAY(fields) ((ValueArrayStream() << fields).done()) - private: - ValueStream _stream; - MutableDocument _md; - }; - class ValueArrayStream { +// These classes are only for the implementation of the DOC and DOC_ARRAY macros. +// They should not be used for any other reason. +class DocumentStream { + // The stream alternates between DocumentStream taking a fieldname + // and ValueStream taking a Value. + class ValueStream { public: - ValueArrayStream& operator << (const Value& val) { - _array.push_back(val); - return *this; + ValueStream(DocumentStream& builder) : builder(builder) {} + + DocumentStream& operator<<(const Value& val) { + builder._md[name] = val; + return builder; } /// support anything directly supported by a value constructor template <typename T> - ValueArrayStream& operator << (const T& val) { + DocumentStream& operator<<(const T& val) { return *this << Value(val); } - Value done() { return Value(std::move(_array)); } - - private: - std::vector<Value> _array; + StringData name; + DocumentStream& builder; }; - inline void swap(mongo::Document& lhs, mongo::Document& rhs) { lhs.swap(rhs); } +public: + DocumentStream() : _stream(*this) {} -/* ======================= INLINED IMPLEMENTATIONS ========================== */ + ValueStream& operator<<(StringData name) { + _stream.name = name; + return _stream; + } - inline FieldIterator Document::fieldIterator() const { - return FieldIterator(*this); + Document done() { + return _md.freeze(); } - inline MutableValue MutableValue::getField(Position pos) { - return MutableDocument(*this).getField(pos); +private: + ValueStream _stream; + MutableDocument _md; +}; + +class ValueArrayStream { +public: + ValueArrayStream& operator<<(const Value& val) { + _array.push_back(val); + return *this; } - inline MutableValue MutableValue::getField(StringData key) { - return MutableDocument(*this).getField(key); + + /// support anything directly supported by a value constructor + template <typename T> + ValueArrayStream& operator<<(const T& val) { + return *this << Value(val); + } + + Value done() { + return Value(std::move(_array)); } + +private: + std::vector<Value> _array; +}; + +inline void swap(mongo::Document& lhs, mongo::Document& rhs) { + lhs.swap(rhs); +} + +/* ======================= INLINED IMPLEMENTATIONS ========================== */ + +inline FieldIterator Document::fieldIterator() const { + return FieldIterator(*this); +} + +inline MutableValue MutableValue::getField(Position pos) { + return MutableDocument(*this).getField(pos); +} +inline MutableValue MutableValue::getField(StringData key) { + return MutableDocument(*this).getField(key); +} } |