1//
2// Document.h
3//
4// Library: MongoDB
5// Package: MongoDB
6// Module: Document
7//
8// Definition of the Document class.
9//
10// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef MongoDB_Document_INCLUDED
18#define MongoDB_Document_INCLUDED
19
20
21#include "Poco/BinaryReader.h"
22#include "Poco/BinaryWriter.h"
23#include "Poco/MongoDB/MongoDB.h"
24#include "Poco/MongoDB/Element.h"
25#include <algorithm>
26#include <cstdlib>
27
28
29namespace Poco {
30namespace MongoDB {
31
32
33class ElementFindByName
34{
35public:
36 ElementFindByName(const std::string& name):
37 _name(name)
38 {
39 }
40
41 bool operator()(const Element::Ptr& element)
42 {
43 return !element.isNull() && element->name() == _name;
44 }
45
46private:
47 std::string _name;
48};
49
50
51class MongoDB_API Document
52 /// Represents a MongoDB (BSON) document.
53{
54public:
55 typedef SharedPtr<Document> Ptr;
56 typedef std::vector<Document::Ptr> Vector;
57
58 Document();
59 /// Creates an empty Document.
60
61 virtual ~Document();
62 /// Destroys the Document.
63
64 Document& addElement(Element::Ptr element);
65 /// Add an element to the document.
66 ///
67 /// The active document is returned to allow chaining of the add methods.
68
69 template<typename T>
70 Document& add(const std::string& name, T value)
71 /// Creates an element with the given name and value and
72 /// adds it to the document.
73 ///
74 /// The active document is returned to allow chaining of the add methods.
75 {
76 return addElement(new ConcreteElement<T>(name, value));
77 }
78
79 Document& add(const std::string& name, const char* value)
80 /// Creates an element with the given name and value and
81 /// adds it to the document.
82 ///
83 /// The active document is returned to allow chaining of the add methods.
84 {
85 return addElement(new ConcreteElement<std::string>(name, std::string(value)));
86 }
87
88 Document& addNewDocument(const std::string& name);
89 /// Create a new document and add it to this document.
90 /// Unlike the other add methods, this method returns
91 /// a reference to the new document.
92
93 void clear();
94 /// Removes all elements from the document.
95
96 void elementNames(std::vector<std::string>& keys) const;
97 /// Puts all element names into std::vector.
98
99 bool empty() const;
100 /// Returns true if the document doesn't contain any documents.
101
102 bool exists(const std::string& name);
103 /// Returns true if the document has an element with the given name.
104
105 template<typename T>
106 T get(const std::string& name) const
107 /// Returns the element with the given name and tries to convert
108 /// it to the template type. When the element is not found, a
109 /// NotFoundException will be thrown. When the element can't be
110 /// converted a BadCastException will be thrown.
111 {
112 Element::Ptr element = get(name);
113 if (element.isNull())
114 {
115 throw NotFoundException(name);
116 }
117 else
118 {
119 if (ElementTraits<T>::TypeId == element->type())
120 {
121 ConcreteElement<T>* concrete = dynamic_cast<ConcreteElement<T>* >(element.get());
122 if (concrete != 0)
123 {
124 return concrete->value();
125 }
126 }
127 throw BadCastException("Invalid type mismatch!");
128 }
129 }
130
131 template<typename T>
132 T get(const std::string& name, const T& def) const
133 /// Returns the element with the given name and tries to convert
134 /// it to the template type. When the element is not found, or
135 /// has the wrong type, the def argument will be returned.
136 {
137 Element::Ptr element = get(name);
138 if (element.isNull())
139 {
140 return def;
141 }
142
143 if (ElementTraits<T>::TypeId == element->type())
144 {
145 ConcreteElement<T>* concrete = dynamic_cast<ConcreteElement<T>* >(element.get());
146 if (concrete != 0)
147 {
148 return concrete->value();
149 }
150 }
151
152 return def;
153 }
154
155 Element::Ptr get(const std::string& name) const;
156 /// Returns the element with the given name.
157 /// An empty element will be returned when the element is not found.
158
159 Int64 getInteger(const std::string& name) const;
160 /// Returns an integer. Useful when MongoDB returns Int32, Int64
161 /// or double for a number (count for example). This method will always
162 /// return an Int64. When the element is not found, a
163 /// Poco::NotFoundException will be thrown.
164
165 template<typename T>
166 bool isType(const std::string& name) const
167 /// Returns true when the type of the element equals the TypeId of ElementTrait.
168 {
169 Element::Ptr element = get(name);
170 if (element.isNull())
171 {
172 return false;
173 }
174
175 return ElementTraits<T>::TypeId == element->type();
176 }
177
178 void read(BinaryReader& reader);
179 /// Reads a document from the reader
180
181 std::size_t size() const;
182 /// Returns the number of elements in the document.
183
184 virtual std::string toString(int indent = 0) const;
185 /// Returns a String representation of the document.
186
187 void write(BinaryWriter& writer);
188 /// Writes a document to the reader
189
190protected:
191 ElementSet _elements;
192};
193
194
195//
196// inlines
197//
198inline Document& Document::addElement(Element::Ptr element)
199{
200 _elements.push_back(element);
201 return *this;
202}
203
204
205inline Document& Document::addNewDocument(const std::string& name)
206{
207 Document::Ptr newDoc = new Document();
208 add(name, newDoc);
209 return *newDoc;
210}
211
212
213inline void Document::clear()
214{
215 _elements.clear();
216}
217
218
219inline bool Document::empty() const
220{
221 return _elements.empty();
222}
223
224
225inline void Document::elementNames(std::vector<std::string>& keys) const
226{
227 for (ElementSet::const_iterator it = _elements.begin(); it != _elements.end(); ++it)
228 {
229 keys.push_back((*it)->name());
230 }
231}
232
233
234inline bool Document::exists(const std::string& name)
235{
236 return std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name)) != _elements.end();
237}
238
239
240inline std::size_t Document::size() const
241{
242 return _elements.size();
243}
244
245
246// BSON Embedded Document
247// spec: document
248template<>
249struct ElementTraits<Document::Ptr>
250{
251 enum { TypeId = 0x03 };
252
253 static std::string toString(const Document::Ptr& value, int indent = 0)
254 {
255 return value.isNull() ? "null" : value->toString(indent);
256 }
257};
258
259
260template<>
261inline void BSONReader::read<Document::Ptr>(Document::Ptr& to)
262{
263 to->read(_reader);
264}
265
266
267template<>
268inline void BSONWriter::write<Document::Ptr>(Document::Ptr& from)
269{
270 from->write(_writer);
271}
272
273
274} } // namespace Poco::MongoDB
275
276
277#endif // MongoDB_Document_INCLUDED
278