summaryrefslogtreecommitdiff
path: root/src/mongo/bson/mutable/document.h
blob: 293df8638f7df9ee242d69cdd55b7364750cd2d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
/* Copyright 2013 10gen Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *    As a special exception, the copyright holders give permission to link the
 *    code of portions of this program with the OpenSSL library under certain
 *    conditions as described in each individual source file and distribute
 *    linked combinations including the program with the OpenSSL library. You
 *    must comply with the GNU Affero General Public License in all respects
 *    for all of the code used other than as permitted herein. If you modify
 *    file(s) with this exception, you may extend this exception to your
 *    version of the file(s), but you are not obligated to do so. If you do not
 *    wish to do so, delete this exception statement from your version. If you
 *    delete this exception statement from all source files in the program,
 *    then also delete it in the license file.
 */

#pragma once

#include <boost/scoped_ptr.hpp>

#include "mongo/base/disallow_copying.h"
#include "mongo/base/string_data.h"
#include "mongo/bson/mutable/const_element.h"
#include "mongo/bson/mutable/damage_vector.h"
#include "mongo/bson/mutable/element.h"
#include "mongo/db/jsobj.h"
#include "mongo/platform/cstdint.h"
#include "mongo/util/safe_num.h"

namespace mongo {
namespace mutablebson {

    /** Mutable BSON Overview
     *
     *  Mutable BSON provides classes to facilitate the manipulation of existing BSON objects
     *  or the construction of new BSON objects from scratch in an incremental fashion. The
     *  operations (including additions, deletions, renamings, type changes and value
     *  modification) that are to be performed do not need to be known ahead of time, and do
     *  not need to occur in any particular order. This is in contrast to BSONObjBuilder and
     *  BSONArrayBuilder which offer only serialization and cannot revise already serialized
     *  data. If you need to build a BSONObj but you know upfront what you need to build then
     *  you should use BSONObjBuilder and BSONArrayBuilder directly as they will be faster and
     *  less resource intensive.
     *
     *  The classes in this library (Document, Element, and ConstElement) present a tree-like
     *  (or DOM like) interface. Elements are logically equivalent to BSONElements: they carry
     *  a type, a field name, and a value. Every Element belongs to a Document, which roots the
     *  tree, and Elements of proper type (mongo::Object or mongo::Array) may have child
     *  Elements of their own. Given an Element, you may navigate to the Element's parent, to
     *  its siblings to the left or right of the Element in the tree, and to the leftmost or
     *  rightmost children of the Element. Note that some Elements may not offer all of these
     *  relationships: An Element that represents a terminal BSON value (like an integer) will
     *  not have children (though it may well have siblings). Similarly, an Element that is an
     *  'only child' will not have any left or right siblings. Given a Document, you may begin
     *  navigating by obtaining the root Element of the tree by calling Document::root. See the
     *  documentation for the Element class for the specific navigation methods that will be
     *  available from the root Element.
     *
     *  Elements within the Document may be modified in various ways: the value of the Element
     *  may be changed, the Element may be removed, it may be renamed, and if it is eligible
     *  for children (i.e. it represents a mongo::Array or mongo::Object) it may have child
     *  Elements added to it. Once you have completed building or modifying the Document, you
     *  may write it back out to a BSONObjBuilder by calling Document::writeTo. You may also
     *  serialize individual Elements within the Document to BSONObjBuilder or BSONArrayBuilder
     *  objects by calling Element::writeTo or Element::writeArrayTo.
     *
     *  In addition to the above capabilities, there are algorithms provided in 'algorithm.h'
     *  to help with tasks like searching for Elements that match a predicate or for sorting
     *  the children of an Object Element.
     *
     *  Example 1: Building up a document from scratch, reworking it, and then serializing it:

         namespace mmb = mongo::mutablebson;
         // Create a new document
         mmb::Document doc;
         // doc contents: '{}'

         // Get the root of the document.
         mmb::Element root = doc.root();

         // Create a new mongo::NumberInt typed Element to represent life, the universe, and
         // everything, then push that Element into the root object, making it a child of root.
         mmb::Element e0 = doc.makeElementInt("ltuae", 42);
         root.pushBack(e0);
         // doc contents: '{ ltuae : 42 }'

         // Create a new empty mongo::Object-typed Element named 'magic', and push it back as a
         // child of the root, making it a sibling of e0.
         mmb::Element e1 = doc.makeElementObject("magic");
         root.pushBack(e1);
         // doc contents: '{ ltuae : 42, magic : {} }'

         // Create a new mongo::NumberDouble typed Element to represent Pi, and insert it as child
         // of the new object we just created.
         mmb::Element e3 = doc.makeElementDouble("pi", 3.14);
         e1.pushBack(e3);
         // doc contents: '{ ltuae : 42, magic : { pi : 3.14 } }'

         // Create a new mongo::NumberDouble to represent Plancks constant in electrovolt
         // micrometers, and add it as a child of the 'magic' object.
         mmb::Element e4 = doc.makeElementDouble("hbar", 1.239);
         e1.pushBack(e4);
         // doc contents: '{ ltuae : 42, magic : { pi : 3.14, hbar : 1.239 } }'

         // Rename the parent element of 'hbar' to be 'constants'.
         e4.parent().rename("constants");
         // doc contents: '{ ltuae : 42, constants : { pi : 3.14, hbar : 1.239 } }'

         // Rename 'ltuae' to 'answer' by accessing it as the root objects left child.
         doc.root().leftChild().rename("answer");
         // doc contents: '{ answer : 42, constants : { pi : 3.14, hbar : 1.239 } }'

         // Sort the constants by name.
         mmb::sortChildren(doc.root().rightChild(), mmb::FieldNameLessThan());
         // doc contents: '{ answer : 42, constants : { hbar : 1.239, pi : 3.14 } }'

         mongo::BSONObjBuilder builder;
         doc.writeTo(&builder);
         mongo::BSONObj result = builder.obj();
         // result contents: '{ answer : 42, constants : { hbar : 1.239, pi : 3.14 } }'

     *  While you can use this library to build Documents from scratch, its real purpose is to
     *  manipulate existing BSONObjs. A BSONObj may be passed to the Document constructor or to
     *  Document::make[Object|Array]Element, in which case the Document or Element will reflect
     *  the values contained within the provided BSONObj. Modifications will not alter the
     *  underlying BSONObj: they are held off to the side within the Document. However, when
     *  the Document is subsequently written back out to a BSONObjBuilder, the modifications
     *  applied to the Document will be reflected in the serialized version.
     *
     *  Example 2: Modifying an existing BSONObj (some error handling removed for length)

         namespace mmb = mongo::mutablebson;

         static const char inJson[] =
             "{"
             "  'whale': { 'alive': true, 'dv': -9.8, 'height': 50, attrs : [ 'big' ] },"
             "  'petunias': { 'alive': true, 'dv': -9.8, 'height': 50 } "
             "}";
         mongo::BSONObj obj = mongo::fromjson(inJson);

         // Create a new document representing the BSONObj with the above contents.
         mmb::Document doc(obj);

         // The whale hits the planet and dies.
         mmb::Element whale = mmb::findFirstChildNamed(doc.root(), "whale");
         // Find the 'dv' field in the whale.
         mmb::Element whale_deltav = mmb::findFirstChildNamed(whale, "dv");
         // Set the dv field to zero.
         whale_deltav.setValueDouble(0.0);
         // Find the 'height' field in the whale.
         mmb::Element whale_height = mmb::findFirstChildNamed(whale, "height");
         // Set the height field to zero.
         whale_deltav.setValueDouble(0);
         // Find the 'alive' field, and set it to false.
         mmb::Element whale_alive = mmb::findFirstChildNamed(whale, "alive");
         whale_alive.setValueBool(false);

         // The petunias survive, update its fields much like we did above.
         mmb::Element petunias = mmb::findFirstChildNamed(doc.root(), "petunias");
         mmb::Element petunias_deltav = mmb::findFirstChildNamed(petunias, "dv");
         petunias_deltav.setValueDouble(0.0);
         mmb::Element petunias_height = mmb::findFirstChildNamed(petunias, "height");
         petunias_deltav.setValueDouble(0);

         // Replace the whale by its wreckage, saving only its attributes:
         // Construct a new mongo::Object element for the ex-whale.
         mmb::Element ex_whale = doc.makeElementObject("ex-whale");
         doc.root().pushBack(ex_whale);
         // Find the attributes of the old 'whale' element.
         mmb::Element whale_attrs = mmb::findFirstChildNamed(whale, "attrs");
         // Remove the attributes from the whale (they remain valid, but detached).
         whale_attrs.remove();
         // Add the attributes into the ex-whale.
         ex_whale.pushBack(whale_attrs);
         // Remove the whale object.
         whale.remove();

         // Current state of document:
         "{"
         "    'petunias': { 'alive': true, 'dv': 0.0, 'height': 50 },"
         "    'ex-whale': { 'attrs': [ 'big' ] } })"
         "}";

     * Both of the above examples are derived from tests in mutable_bson_test.cpp, see the
     * tests Example1 and Example2 if you would like to play with the code.
     *
     * Additional details on Element and Document are available in their class and member
     * comments.
     */

    /** Document is the entry point into the mutable BSON system. It has a fairly simple
     *  API. It acts as an owner for the Element resources of the document, provides a
     *  pre-constructed designated root Object Element, and acts as a factory for new Elements,
     *  which may then be attached to the root or to other Elements by calling the appropriate
     *  topology mutation methods in Element.
     *
     *  The default constructor builds an empty Document which you may then extend by creating
     *  new Elements and manipulating the tree topology. It is also possible to build a
     *  Document that derives its initial values from a BSONObj. The given BSONObj will not be
     *  modified, but it also must not be modified elsewhere while Document is using it. Unlike
     *  all other calls in this library where a BSONObj is passed in, the one argument Document
     *  constructor *does not copy* the BSONObj's contents, so they must remain valid for the
     *  duration of Documents lifetime. Document does hold a copy of the BSONObj itself, so it
     *  will up the refcount if the BSONObj internals are counted.
     *
     *  Newly constructed Elements formed by calls to 'makeElement[Type]' methods are not
     *  attached to the root of the document. You must explicitly attach them somewhere. If you
     *  lose the Element value that is returned to you from a 'makeElement' call before you
     *  attach it to the tree then the value will be unreachable. Elements in a document do not
     *  outlive the Document.
     *
     *  Document provides a convenience method to serialize all of the Elements in the tree
     *  that are reachable from the root element to a BSONObjBuilder. In general you should use
     *  this in preference to root().writeTo() if you mean to write the entire
     *  Document. Similarly, Document provides wrappers for comparisons that simply delegate to
     *  comparison operations on the root Element.
     *
     *  A 'const Document' is very limited: you may only write its contents out or obtain a
     *  ConstElement for the root. ConstElement is much like Element, but does not permit
     *  mutations. See the class comment for ConstElement for more information.
     */
    class Document {

        // TODO: In principle there is nothing that prevents implementing a deep copy for
        // Document, but for now it is not permitted.
        MONGO_DISALLOW_COPYING(Document);

    public:

        //
        // Lifecycle
        //

        /** Construct a new empty document. */
        Document();

        enum InPlaceMode {
            kInPlaceDisabled = 0,
            kInPlaceEnabled  = 1,
        };

        /** Construct new document for the given BSONObj. The data in 'value' is NOT copied. By
         *  default, queueing of in-place modifications against the underlying document is
         *  permitted. To disable this behavior, explicitly pass kInPlaceDisabled.
         */
        explicit Document(const BSONObj& value, InPlaceMode inPlaceMode = kInPlaceEnabled);

        /** Abandon all internal state associated with this Document, and return to a state
         *  semantically equivalent to that yielded by a call to the default constructor. All
         *  objects associated with the current document state are invalidated (e.g. Elements,
         *  BSONElements, BSONObj's values, field names, etc.). This method is useful because
         *  it may (though it is not required to) preserve the memory allocation of the
         *  internal data structures of Document. If you need to logically create and destroy
         *  many Documents in serial, it may be faster to reset.
         */
        void reset();

        /** As the no argument 'reset', but returns to a state semantically equivalent to that
         *  yielded by a call to the two argument constructor with the arguments provided
         *  here. As with the other 'reset' call, all associated objects are invalidated. */
        void reset(const BSONObj& value, InPlaceMode inPlaceMode = kInPlaceEnabled);

        /** Destroy this document permanently */
        ~Document();


        //
        // Comparison API
        //

        /** Compare this Document to 'other' with the semantics of BSONObj::woCompare. */
        inline int compareWith(const Document& other, bool considerFieldName = true) const;

        /** Compare this Document to 'other' with the semantics of BSONObj::woCompare. */
        inline int compareWithBSONObj(const BSONObj& other, bool considerFieldName = true) const;


        //
        // Serialization API
        //

        /** Serialize the Elements reachable from the root Element of this Document to the
         *  provided builder.
         */
        inline void writeTo(BSONObjBuilder* builder) const;

        /** Serialize the Elements reachable from the root Element of this Document and return
         *  the result as a BSONObj.
         */
        inline BSONObj getObject() const;


        //
        // Element creation API.
        //
        // Newly created elements are not attached to the tree (effectively, they are
        // 'roots'). You must call one of the topology management methods in 'Element' to
        // connect the newly created Element to another Element in the Document, possibly the
        // Element referenced by Document::root. Elements do not outlive the Document.
        //

        /** Create a new double Element with the given value and field name. */
        Element makeElementDouble(StringData fieldName, double value);

        /** Create a new std::string Element with the given value and field name. */
        Element makeElementString(StringData fieldName, StringData value);

        /** Create a new empty object Element with the given field name. */
        Element makeElementObject(StringData fieldName);

        /** Create a new object Element with the given field name. The data in 'value' is
         *  copied.
         */
        Element makeElementObject(StringData fieldName, const BSONObj& value);

        /** Create a new empty array Element with the given field name. */
        Element makeElementArray(StringData fieldName);

        /** Create a new array Element with the given field name. The data in 'value' is
         *  copied.
         */
        Element makeElementArray(StringData fieldName, const BSONObj& value);

        /** Create a new binary Element with the given data and field name. */
        Element makeElementBinary(
            StringData fieldName, uint32_t len, BinDataType binType, const void* data);

        /** Create a new undefined Element with the given field name. */
        Element makeElementUndefined(StringData fieldName);

        /** Create a new OID + Element with the given field name. */
        Element makeElementNewOID(StringData fieldName);

        /** Create a new OID Element with the given value and field name. */
        Element makeElementOID(StringData fieldName, mongo::OID value);

        /** Create a new bool Element with the given value and field name. */
        Element makeElementBool(StringData fieldName, bool value);

        /** Create a new date Element with the given value and field name. */
        Element makeElementDate(StringData fieldName, Date_t value);

        /** Create a new null Element with the given field name. */
        Element makeElementNull(StringData fieldName);

        /** Create a new regex Element with the given data and field name. */
        Element makeElementRegex(
            StringData fieldName, StringData regex, StringData flags);

        /** Create a new DBRef Element with the given data and field name. */
        Element makeElementDBRef(
            StringData fieldName, StringData ns, mongo::OID oid);

        /** Create a new code Element with the given value and field name. */
        Element makeElementCode(StringData fieldName, StringData value);

        /** Create a new symbol Element with the given value and field name. */
        Element makeElementSymbol(StringData fieldName, StringData value);

        /** Create a new scoped code Element with the given data and field name. */
        Element makeElementCodeWithScope(
            StringData fieldName, StringData code, const BSONObj& scope);

        /** Create a new integer Element with the given value and field name. */
        Element makeElementInt(StringData fieldName, int32_t value);

        /** Create a new timetamp Element with the given value and field name. */
        Element makeElementTimestamp(StringData fieldName, OpTime value);

        /** Create a new long integer Element with the given value and field name. */
        Element makeElementLong(StringData fieldName, int64_t value);

        /** Create a new min key Element with the given field name. */
        Element makeElementMinKey(StringData fieldName);

        /** Create a new max key Element with the given field name. */
        Element makeElementMaxKey(StringData fieldName);


        //
        // Element creation methods from variant types
        //

        /** Construct a new Element with the same name, type, and value as the provided
         *  BSONElement. The value is copied.
         */
        Element makeElement(const BSONElement& elt);

        /** Construct a new Element with the same type and value as the provided BSONElement,
         *  but with a new name. The value is copied.
         */
        Element makeElementWithNewFieldName(StringData fieldName, const BSONElement& elt);

        /** Create a new element of the appopriate type to hold the given value, with the given
         *  field name.
         */
        Element makeElementSafeNum(StringData fieldName, SafeNum value);

        /** Construct a new element with the same name, type, and value as the provided mutable
         *  Element. The data is copied from the given Element. Unlike most methods in this
         *  class the provided Element may be from a different Document.
         */
        Element makeElement(ConstElement elt);

        /** Construct a new Element with the same type and value as the provided mutable
         *  Element, but with a new field name. The data is copied from the given
         *  Element. Unlike most methods in this class the provided Element may be from a
         *  different Document.
         */
        Element makeElementWithNewFieldName(StringData fieldName, ConstElement elt);

        //
        // Accessors
        //

        /** Returns the root element for this document. */
        inline Element root();

        /** Returns the root element for this document. */
        inline ConstElement root() const;

        /** Returns an element that will compare equal to a non-ok element. */
        inline Element end();

        /** Returns an element that will compare equal to a non-ok element. */
        inline ConstElement end() const;

        inline std::string toString() const;

        //
        // In-place API.
        //

        /** Ensure that at least 'expectedEvents' damage events can be recorded for in-place
         *  mutations without reallocation. This call is ignored if damage events are disabled.
         */
        void reserveDamageEvents(size_t expectedEvents);

        /** Request a vector of damage events describing in-place updates to this Document. If
         *  the modifications to this Document were not all able to be achieved in-place, then
         *  a non-OK Status is returned, and the provided damage vector will be made empty and
         *  *source set equal to NULL. Otherwise, the provided damage vector is populated, and
         *  the 'source' argument is set to point to a region from which bytes can be read. The
         *  'source' offsets in the damage vector are to be interpreted as offsets within this
         *  region. If the 'size' parameter is non-null and 'source' is set to a non-NULL
         *  value, then size will be filled in with the size of the 'source' region to
         *  facilitate making an owned copy of the source data, in the event that that is
         *  needed.
         *
         *  The lifetime of the source region should be considered to extend only from the
         *  return from this call to before the next API call on this Document or any of its
         *  member Elements. That is almost certainly overly conservative: some read only calls
         *  are undoubtedly fine. But it is very easy to invalidate 'source' by calling any
         *  mutating operation, so proceed with due caution.
         *
         *  It is expected, though, that in normal modes of operation obtainin the damage
         *  vector is one of the last operations performed on a Document before its
         *  destruction, so this is not so great a restriction.
         *
         *  The destination offsets in the damage events are implicitly offsets into the
         *  BSONObj used to construct this Document.
         */
        bool getInPlaceUpdates(DamageVector* damages,
                               const char** source,
                               size_t* size = NULL);

        /** Drop the queue of in-place update damage events, and do not queue new operations
         *  that would otherwise have been in-place. Use this if you know that in-place updates
         *  will not continue to be possible and do not want to pay the overhead of
         *  speculatively queueing them. After calling this method, getInPlaceUpdates will
         *  return a non-OK Status. It is not possible to re-enable in-place updates once
         *  disabled.
         */
        void disableInPlaceUpdates();

        /** Returns the current in-place mode for the document. Note that for some documents,
         *  like those created without any backing BSONObj, this will always return kForbidden,
         *  since in-place updates make no sense for such an object. In other cases, an object
         *  which started in kInPlacePermitted mode may transition to kInPlaceForbidden if a
         *  topology mutating operation is applied.
         */
        InPlaceMode getCurrentInPlaceMode() const;

        /** A convenience routine, this returns true if the current in-place mode is
         *  kInPlaceEnabled, and false otherwise.
         */
        inline bool isInPlaceModeEnabled() const;

    private:
        friend class Element;

        // For now, the implementation of Document is firewalled.
        class Impl;
        inline Impl& getImpl();
        inline const Impl& getImpl() const;

        Element makeRootElement();
        Element makeRootElement(const BSONObj& value);
        Element makeElement(ConstElement element, const StringData* fieldName);

        const boost::scoped_ptr<Impl> _impl;

        // The root element of this document.
        const Element _root;
    };

} // namespace mutablebson
} // namespace mongo

#include "mongo/bson/mutable/document-inl.h"