4781 mongodb remove obsolete protocol and modernise (#5067)

* feature(MongoDB): Remove obsolete legacy wire protocol.

* enh(MongoDB): Use more C++17 features, comments about thread safety.

* enh(MongoDB) Performance improvements (std::set --> std::vector, improved few internal functions).

* enh(MongoDB) Performance improvements (Document member index for fast search, optimised serialisation in OpMsgMessage).

* enh(MongoDB) Performance improvements (move semantics for Document::addElement, improved toString(), use noexcept where appropriate).

* enh(MongoDB): Introduce enums for binary subtypes and more user friendly printout of UUIDs.

* enh(MongoDB) Performance improvements (move semantics in Element, improved implementation of toString()).

* enh(MongoDB) Performance improvements (ObjectId and RegularExpression.).

* enh(MongoDB): add createIndex that uses new wire protocol.
This commit is contained in:
Matej Kenda
2025-11-27 10:24:42 +01:00
committed by GitHub
parent c775d5f3b7
commit 5144cea871
47 changed files with 829 additions and 2553 deletions

View File

@@ -81,19 +81,19 @@ public:
return Document::get<T>(Poco::NumberFormatter::format(pos), deflt); return Document::get<T>(Poco::NumberFormatter::format(pos), deflt);
} }
Element::Ptr get(std::size_t pos) const; [[nodiscard]] Element::Ptr get(std::size_t pos) const;
/// Returns the element at the given index. /// Returns the element at the given index.
/// An empty element will be returned if the element is not found. /// An empty element will be returned if the element is not found.
template<typename T> template<typename T>
bool isType(std::size_t pos) const [[nodiscard]] bool isType(std::size_t pos) const
/// Returns true if the type of the element equals the TypeId of ElementTrait, /// Returns true if the type of the element equals the TypeId of ElementTrait,
/// otherwise false. /// otherwise false.
{ {
return Document::isType<T>(Poco::NumberFormatter::format(pos)); return Document::isType<T>(Poco::NumberFormatter::format(pos));
} }
std::string toString(int indent = 0) const override; [[nodiscard]] std::string toString(int indent = 0) const override;
/// Returns a string representation of the Array. /// Returns a string representation of the Array.
private: private:

View File

@@ -49,7 +49,7 @@ public:
_reader >> t; _reader >> t;
} }
std::string readCString(); [[nodiscard]] std::string readCString();
/// Reads a cstring from the reader. /// Reads a cstring from the reader.
/// A cstring is a string terminated with a 0x00. /// A cstring is a string terminated with a 0x00.

View File

@@ -36,6 +36,19 @@ class MongoDB_API Binary
public: public:
using Ptr = SharedPtr<Binary>; using Ptr = SharedPtr<Binary>;
/// BSON Binary subtypes
enum Subtype
{
SUBTYPE_GENERIC = 0x00, /// Generic binary data
SUBTYPE_FUNCTION = 0x01, /// Function
SUBTYPE_BINARY_OLD = 0x02, /// Binary (Old)
SUBTYPE_UUID_OLD = 0x03, /// UUID (Old)
SUBTYPE_UUID = 0x04, /// UUID
SUBTYPE_MD5 = 0x05, /// MD5
SUBTYPE_ENCRYPTED = 0x06, /// Encrypted BSON value
SUBTYPE_USER_DEFINED = 0x80 /// User defined (start of range)
};
Binary(); Binary();
/// Creates an empty Binary with subtype 0. /// Creates an empty Binary with subtype 0.
@@ -57,19 +70,22 @@ public:
Buffer<unsigned char>& buffer(); Buffer<unsigned char>& buffer();
/// Returns a reference to the internal buffer /// Returns a reference to the internal buffer
unsigned char subtype() const; [[nodiscard]] unsigned char subtype() const;
/// Returns the subtype. /// Returns the subtype.
void subtype(unsigned char type); void subtype(unsigned char type);
/// Sets the subtype. /// Sets the subtype.
std::string toString(int indent = 0) const; [[nodiscard]] std::string toString(int indent = 0) const;
/// Returns the contents of the Binary as Base64-encoded string. /// Returns the contents of the Binary as a string.
/// For UUID subtype (SUBTYPE_UUID), returns a formatted UUID string
/// wrapped in UUID() (e.g., UUID("550e8400-e29b-41d4-a716-446655440000")).
/// For other subtypes, returns Base64-encoded data.
std::string toRawString() const; [[nodiscard]] std::string toRawString() const;
/// Returns the raw content of the Binary as a string. /// Returns the raw content of the Binary as a string.
UUID uuid() const; [[nodiscard]] UUID uuid() const;
/// Returns the UUID when the binary subtype is 0x04. /// Returns the UUID when the binary subtype is 0x04.
/// Otherwise, throws a Poco::BadCastException. /// Otherwise, throws a Poco::BadCastException.

View File

@@ -20,8 +20,6 @@
#include "Poco/Net/SocketAddress.h" #include "Poco/Net/SocketAddress.h"
#include "Poco/Net/StreamSocket.h" #include "Poco/Net/StreamSocket.h"
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/MongoDB/ResponseMessage.h"
#include "Poco/MongoDB/OpMsgMessage.h" #include "Poco/MongoDB/OpMsgMessage.h"
@@ -35,6 +33,17 @@ class MongoDB_API Connection
/// ///
/// See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/ /// See https://docs.mongodb.com/manual/reference/mongodb-wire-protocol/
/// for more information on the wire protocol. /// for more information on the wire protocol.
///
/// THREAD SAFETY:
/// This class is NOT thread-safe. A single Connection instance must not be
/// used concurrently from multiple threads without external synchronization.
/// Concurrent calls to sendRequest() will result in interleaved data on the
/// socket and corrupted responses.
///
/// For multi-threaded applications, use one of these patterns:
/// - Each thread has its own Connection instance
/// - Use ObjectPool<Connection> with PooledConnection for connection pooling
/// - Protect shared Connection with external mutex
{ {
public: public:
using Ptr = Poco::SharedPtr<Connection>; using Ptr = Poco::SharedPtr<Connection>;
@@ -86,7 +95,7 @@ public:
virtual ~Connection(); virtual ~Connection();
/// Destroys the Connection. /// Destroys the Connection.
Poco::Net::SocketAddress address() const; [[nodiscard]] Poco::Net::SocketAddress address() const;
/// Returns the address of the MongoDB server. /// Returns the address of the MongoDB server.
void connect(const std::string& hostAndPort); void connect(const std::string& hostAndPort);
@@ -107,8 +116,7 @@ public:
/// a SecureStreamSocket must be supplied. /// a SecureStreamSocket must be supplied.
/// - connectTimeoutMS: Socket connection timeout in milliseconds. /// - connectTimeoutMS: Socket connection timeout in milliseconds.
/// - socketTimeoutMS: Socket send/receive timeout in milliseconds. /// - socketTimeoutMS: Socket send/receive timeout in milliseconds.
/// - authMechanism: Authentication mechanism. Only "SCRAM-SHA-1" (default) /// - authMechanism: Authentication mechanism. Only "SCRAM-SHA-1" is supported.
/// and "MONGODB-CR" are supported.
/// ///
/// Unknown options are silently ignored. /// Unknown options are silently ignored.
/// ///
@@ -129,24 +137,13 @@ public:
void disconnect(); void disconnect();
/// Disconnects from the MongoDB server. /// Disconnects from the MongoDB server.
void sendRequest(RequestMessage& request);
/// Sends a request to the MongoDB server.
///
/// Used for one-way requests without a response.
void sendRequest(RequestMessage& request, ResponseMessage& response);
/// Sends a request to the MongoDB server and receives the response.
///
/// Use this when a response is expected: only a "query" or "getmore"
/// request will return a response.
void sendRequest(OpMsgMessage& request, OpMsgMessage& response); void sendRequest(OpMsgMessage& request, OpMsgMessage& response);
/// Sends a request to the MongoDB server and receives the response /// Sends a request to the MongoDB server and receives the response
/// using newer wire protocol with OP_MSG. /// using OP_MSG wire protocol.
void sendRequest(OpMsgMessage& request); void sendRequest(OpMsgMessage& request);
/// Sends an unacknowledged request to the MongoDB server using newer /// Sends an unacknowledged request to the MongoDB server using
/// wire protocol with OP_MSG. /// OP_MSG wire protocol.
/// No response is sent by the server. /// No response is sent by the server.
void readResponse(OpMsgMessage& response); void readResponse(OpMsgMessage& response);

View File

@@ -1,77 +0,0 @@
//
// Cursor.h
//
// Library: MongoDB
// Package: MongoDB
// Module: Cursor
//
// Definition of the Cursor class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_Cursor_INCLUDED
#define MongoDB_Cursor_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/Connection.h"
#include "Poco/MongoDB/QueryRequest.h"
#include "Poco/MongoDB/ResponseMessage.h"
namespace Poco {
namespace MongoDB {
class POCO_DEPRECATED("Use new wire protocol") Cursor;
class MongoDB_API Cursor: public Document
/// Cursor is an helper class for querying multiple documents.
{
public:
Cursor(const std::string& dbname, const std::string& collectionName, QueryRequest::Flags flags = QueryRequest::QUERY_DEFAULT);
/// Creates a Cursor for the given database and collection, using the specified flags.
Cursor(const std::string& fullCollectionName, QueryRequest::Flags flags = QueryRequest::QUERY_DEFAULT);
/// Creates a Cursor for the given database and collection ("database.collection"), using the specified flags.
Cursor(const Document& aggregationResponse);
/// Creates a Cursor for the given agregation query response.
virtual ~Cursor();
/// Destroys the Cursor.
ResponseMessage& next(Connection& connection);
/// Tries to get the next documents. As long as ResponseMessage has a
/// cursor ID next can be called to retrieve the next bunch of documents.
///
/// The cursor must be killed (see kill()) when not all documents are needed.
QueryRequest& query();
/// Returns the associated query.
void kill(Connection& connection);
/// Kills the cursor and reset it so that it can be reused.
private:
QueryRequest _query;
ResponseMessage _response;
};
//
// inlines
//
inline QueryRequest& Cursor::query()
{
return _query;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_Cursor_INCLUDED

View File

@@ -21,14 +21,10 @@
#include "Poco/MongoDB/MongoDB.h" #include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/Connection.h" #include "Poco/MongoDB/Connection.h"
#include "Poco/MongoDB/Document.h" #include "Poco/MongoDB/Document.h"
#include "Poco/MongoDB/QueryRequest.h"
#include "Poco/MongoDB/InsertRequest.h"
#include "Poco/MongoDB/UpdateRequest.h"
#include "Poco/MongoDB/DeleteRequest.h"
#include "Poco/MongoDB/OpMsgMessage.h" #include "Poco/MongoDB/OpMsgMessage.h"
#include "Poco/MongoDB/OpMsgCursor.h" #include "Poco/MongoDB/OpMsgCursor.h"
namespace Poco { namespace Poco {
namespace MongoDB { namespace MongoDB {
@@ -39,105 +35,75 @@ class MongoDB_API Database
/// the database. /// the database.
{ {
public: public:
enum IndexOptions {
INDEX_UNIQUE = 1 << 0,
INDEX_SPARSE = 1 << 1,
INDEX_BACKGROUND = 1 << 2
};
using FieldIndex = std::tuple<std::string, bool>;
/// name of the field to index, ascending order (true), descending order (false)
using IndexedFields = std::vector<FieldIndex>;
/// Vector of fields to create index on
explicit Database(const std::string& name); explicit Database(const std::string& name);
/// Creates a Database for the database with the given name. /// Creates a Database for the database with the given name.
virtual ~Database(); virtual ~Database();
/// Destroys the Database. /// Destroys the Database.
const std::string& name() const; [[nodiscard]] const std::string& name() const;
/// Database name /// Database name
bool authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method = AUTH_SCRAM_SHA1); bool authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method = AUTH_SCRAM_SHA1);
/// Authenticates against the database using the given connection, /// Authenticates against the database using the given connection,
/// username and password, as well as authentication method. /// username and password, as well as authentication method.
/// ///
/// "MONGODB-CR" (default prior to MongoDB 3.0) and /// "SCRAM-SHA-1" (default starting in MongoDB 3.0) is the only supported
/// "SCRAM-SHA-1" (default starting in 3.0) are the only supported /// authentication method. "MONGODB-CR" is no longer supported as it
/// authentication methods. /// requires the legacy wire protocol.
/// ///
/// Returns true if authentication was successful, otherwise false. /// Returns true if authentication was successful, otherwise false.
/// ///
/// May throw a Poco::ProtocolException if authentication fails for a reason other than /// May throw a Poco::ProtocolException if authentication fails for a reason other than
/// invalid credentials. /// invalid credentials.
Document::Ptr queryBuildInfo(Connection& connection) const; [[nodiscard]] Document::Ptr queryBuildInfo(Connection& connection) const;
/// Queries server build info (all wire protocols) /// Queries server build info using OP_MSG protocol.
Document::Ptr queryServerHello(Connection& connection) const; [[nodiscard]] Document::Ptr queryServerHello(Connection& connection) const;
/// Queries hello response from server (all wire protocols) /// Queries hello response from server using OP_MSG protocol.
Int64 count(Connection& connection, const std::string& collectionName) const; [[nodiscard]] Int64 count(Connection& connection, const std::string& collectionName) const;
/// Sends a count request for the given collection to MongoDB. (old wire protocol) /// Sends a count request for the given collection to MongoDB using OP_MSG protocol.
/// ///
/// If the command fails, -1 is returned. /// If the command fails, -1 is returned.
//[[deprecated]] [[nodiscard]] SharedPtr<OpMsgMessage> createOpMsgMessage(const std::string& collectionName) const;
Poco::SharedPtr<Poco::MongoDB::QueryRequest> createCommand() const; /// Creates OpMsgMessage for the given collection.
/// Creates a QueryRequest for a command. (old wire protocol)
//[[deprecated]] [[nodiscard]] SharedPtr<OpMsgMessage> createOpMsgMessage() const;
Poco::SharedPtr<Poco::MongoDB::QueryRequest> createCountRequest(const std::string& collectionName) const; /// Creates OpMsgMessage for database commands that do not require collection as an argument.
/// Creates a QueryRequest to count the given collection.
/// The collectionname must not contain the database name. (old wire protocol)
POCO_DEPRECATED("Use new wire protocol") [[nodiscard]] SharedPtr<OpMsgCursor> createOpMsgCursor(const std::string& collectionName) const;
Poco::SharedPtr<Poco::MongoDB::DeleteRequest> createDeleteRequest(const std::string& collectionName) const; /// Creates OpMsgCursor for the given collection.
/// Creates a DeleteRequest to delete documents in the given collection.
/// The collectionname must not contain the database name. (old wire protocol)
//[[deprecated]] Document::Ptr createIndex(
Poco::SharedPtr<Poco::MongoDB::InsertRequest> createInsertRequest(const std::string& collectionName) const; Connection& connection,
/// Creates an InsertRequest to insert new documents in the given collection.
/// The collectionname must not contain the database name. (old wire protocol)
//[[deprecated]]
Poco::SharedPtr<Poco::MongoDB::QueryRequest> createQueryRequest(const std::string& collectionName) const;
/// Creates a QueryRequest. (old wire protocol)
/// The collectionname must not contain the database name.
POCO_DEPRECATED("Use new wire protocol")
Poco::SharedPtr<Poco::MongoDB::UpdateRequest> createUpdateRequest(const std::string& collectionName) const;
/// Creates an UpdateRequest. (old wire protocol)
/// The collectionname must not contain the database name.
Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> createOpMsgMessage(const std::string& collectionName) const;
/// Creates OpMsgMessage. (new wire protocol)
Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> createOpMsgMessage() const;
/// Creates OpMsgMessage for database commands that do not require collection as an argument. (new wire protocol)
Poco::SharedPtr<Poco::MongoDB::OpMsgCursor> createOpMsgCursor(const std::string& collectionName) const;
/// Creates OpMsgCursor. (new wire protocol)
Poco::MongoDB::Document::Ptr ensureIndex(Connection& connection,
const std::string& collection, const std::string& collection,
const std::string& indexName, const IndexedFields& indexedFields,
Poco::MongoDB::Document::Ptr keys, const std::string &indexName,
bool unique = false, unsigned long options = 0,
bool background = false, int expirationSeconds = 0,
int version = 0, int version = 0);
int ttl = 0); /// Creates an index. The document returned is the response body..
/// Creates an index. The document returned is the result of a getLastError call. /// For more info look at the createIndex information on the MongoDB website. (new wire protocol)
/// For more info look at the ensureIndex information on the MongoDB website. (old wire protocol)
//[[deprecated]]
Document::Ptr getLastErrorDoc(Connection& connection) const;
/// Sends the getLastError command to the database and returns the error document.
/// (old wire protocol)
//[[deprecated]]
std::string getLastError(Connection& connection) const;
/// Sends the getLastError command to the database and returns the err element
/// from the error document. When err is null, an empty string is returned.
/// (old wire protocol)
static const std::string AUTH_MONGODB_CR;
/// Default authentication mechanism prior to MongoDB 3.0.
static const std::string AUTH_SCRAM_SHA1; static const std::string AUTH_SCRAM_SHA1;
/// Default authentication mechanism for MongoDB 3.0. /// Default authentication mechanism for MongoDB 3.0 and later.
enum WireVersion enum WireVersion
/// Wire version as reported by the command hello. /// Wire version as reported by the command hello.
/// See details in MongoDB github, repository specifications. /// See details in MongoDB github, repository specifications.
@@ -153,14 +119,13 @@ public:
VER_42 = 8, VER_42 = 8,
VER_44 = 9, VER_44 = 9,
VER_50 = 13, VER_50 = 13,
VER_51 = 14, ///< First wire version that supports only OP_MSG VER_51 = 14, ///< First wire version that supports *only* OP_MSG
VER_52 = 15, VER_52 = 15,
VER_53 = 16, VER_53 = 16,
VER_60 = 17 VER_60 = 17
}; };
protected: protected:
bool authCR(Connection& connection, const std::string& username, const std::string& password);
bool authSCRAM(Connection& connection, const std::string& username, const std::string& password); bool authSCRAM(Connection& connection, const std::string& username, const std::string& password);
private: private:
@@ -177,60 +142,25 @@ inline const std::string& Database::name() const
} }
inline Poco::SharedPtr<Poco::MongoDB::QueryRequest> Database::createCommand() const inline SharedPtr<OpMsgMessage>
{
Poco::SharedPtr<Poco::MongoDB::QueryRequest> cmd = createQueryRequest("$cmd");
cmd->setNumberToReturn(1);
return cmd;
}
inline Poco::SharedPtr<Poco::MongoDB::DeleteRequest>
Database::createDeleteRequest(const std::string& collectionName) const
{
return new Poco::MongoDB::DeleteRequest(_dbname + '.' + collectionName);
}
inline Poco::SharedPtr<Poco::MongoDB::InsertRequest>
Database::createInsertRequest(const std::string& collectionName) const
{
return new Poco::MongoDB::InsertRequest(_dbname + '.' + collectionName);
}
inline Poco::SharedPtr<Poco::MongoDB::QueryRequest>
Database::createQueryRequest(const std::string& collectionName) const
{
return new Poco::MongoDB::QueryRequest(_dbname + '.' + collectionName);
}
inline Poco::SharedPtr<Poco::MongoDB::UpdateRequest>
Database::createUpdateRequest(const std::string& collectionName) const
{
return new Poco::MongoDB::UpdateRequest(_dbname + '.' + collectionName);
}
// -- New wire protocol commands
inline Poco::SharedPtr<Poco::MongoDB::OpMsgMessage>
Database::createOpMsgMessage(const std::string& collectionName) const Database::createOpMsgMessage(const std::string& collectionName) const
{ {
return new Poco::MongoDB::OpMsgMessage(_dbname, collectionName); return new OpMsgMessage(_dbname, collectionName);
} }
inline Poco::SharedPtr<Poco::MongoDB::OpMsgMessage>
inline SharedPtr<OpMsgMessage>
Database::createOpMsgMessage() const Database::createOpMsgMessage() const
{ {
// Collection name for database commands is not needed. // Collection name for database commands is not needed.
return createOpMsgMessage(""); return createOpMsgMessage("");
} }
inline Poco::SharedPtr<Poco::MongoDB::OpMsgCursor>
inline SharedPtr<OpMsgCursor>
Database::createOpMsgCursor(const std::string& collectionName) const Database::createOpMsgCursor(const std::string& collectionName) const
{ {
return new Poco::MongoDB::OpMsgCursor(_dbname, collectionName); return new OpMsgCursor(_dbname, collectionName);
} }

View File

@@ -1,114 +0,0 @@
//
// DeleteRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: DeleteRequest
//
// Definition of the DeleteRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_DeleteRequest_INCLUDED
#define MongoDB_DeleteRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/MongoDB/Document.h"
namespace Poco {
namespace MongoDB {
//class POCO_DEPRECATED("Use new wire protocol") DeleteRequest;
class MongoDB_API DeleteRequest: public RequestMessage
/// A DeleteRequest is used to delete one ore more documents from a database.
///
/// Specific flags for this request
/// - DELETE_DEFAULT: default delete operation
/// - DELETE_SINGLE_REMOVE: delete only the first document
{
public:
enum Flags
{
DELETE_DEFAULT = 0,
/// Default
DELETE_SINGLE_REMOVE = 1
/// Delete only the first document.
};
DeleteRequest(const std::string& collectionName, Flags flags = DELETE_DEFAULT);
/// Creates a DeleteRequest for the given collection using the given flags.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar".
DeleteRequest(const std::string& collectionName, bool justOne);
/// Creates a DeleteRequest for the given collection.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar".
///
/// If justOne is true, only the first matching document will
/// be removed (the same as using flag DELETE_SINGLE_REMOVE).
virtual ~DeleteRequest();
/// Destructor
Flags flags() const;
/// Returns the flags.
void flags(Flags flag);
/// Sets the flags.
Document& selector();
/// Returns the selector document.
protected:
void buildRequest(BinaryWriter& writer);
/// Writes the OP_DELETE request to the writer.
private:
Flags _flags;
std::string _fullCollectionName;
Document _selector;
};
///
/// inlines
///
inline DeleteRequest::Flags DeleteRequest::flags() const
{
return _flags;
}
inline void DeleteRequest::flags(DeleteRequest::Flags flags)
{
_flags = flags;
}
inline Document& DeleteRequest::selector()
{
return _selector;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_DeleteRequest_INCLUDED

View File

@@ -24,6 +24,8 @@
#include "Poco/MongoDB/Element.h" #include "Poco/MongoDB/Element.h"
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <unordered_map>
#include <type_traits>
namespace Poco { namespace Poco {
@@ -51,6 +53,13 @@ private:
class MongoDB_API Document class MongoDB_API Document
/// Represents a MongoDB (BSON) document. /// Represents a MongoDB (BSON) document.
///
/// THREAD SAFETY:
/// This class is NOT thread-safe. Document instances must not be accessed
/// concurrently from multiple threads without external synchronization.
/// Concurrent modifications to the element list will cause undefined behavior.
///
/// Each thread should use its own Document instances.
{ {
public: public:
using Ptr = SharedPtr<Document>; using Ptr = SharedPtr<Document>;
@@ -77,6 +86,18 @@ public:
return addElement(new ConcreteElement<T>(name, value)); return addElement(new ConcreteElement<T>(name, value));
} }
template<typename T>
typename std::enable_if<!std::is_same<typename std::decay<T>::type, const char*>::value, Document&>::type
add(std::string&& name, T value)
/// Creates an element with the given name (moved) and value and
/// adds it to the document. Move semantics for efficiency.
/// Disabled for const char* to avoid ambiguity.
///
/// The active document is returned to allow chaining of the add methods.
{
return addElement(new ConcreteElement<T>(std::move(name), value));
}
Document& add(const std::string& name, const char* value) Document& add(const std::string& name, const char* value)
/// Creates an element with the given name and value and /// Creates an element with the given name and value and
/// adds it to the document. /// adds it to the document.
@@ -95,16 +116,16 @@ public:
/// Create a new array and add it to this document. /// Create a new array and add it to this document.
/// Method returns a reference to the new array. /// Method returns a reference to the new array.
void clear(); void clear() noexcept;
/// Removes all elements from the document. /// Removes all elements from the document.
void elementNames(std::vector<std::string>& keys) const; void elementNames(std::vector<std::string>& keys) const;
/// Puts all element names into std::vector. /// Puts all element names into std::vector.
bool empty() const; [[nodiscard]] bool empty() const noexcept;
/// Returns true if the document doesn't contain any documents. /// Returns true if the document doesn't contain any documents.
bool exists(const std::string& name) const; [[nodiscard]] bool exists(const std::string& name) const noexcept;
/// Returns true if the document has an element with the given name. /// Returns true if the document has an element with the given name.
template<typename T> template<typename T>
@@ -157,11 +178,11 @@ public:
return def; return def;
} }
Element::Ptr get(const std::string& name) const; [[nodiscard]] Element::Ptr get(const std::string& name) const;
/// Returns the element with the given name. /// Returns the element with the given name.
/// An empty element will be returned when the element is not found. /// An empty element will be returned when the element is not found.
Int64 getInteger(const std::string& name) const; [[nodiscard]] Int64 getInteger(const std::string& name) const;
/// Returns an integer. Useful when MongoDB returns Int32, Int64 /// Returns an integer. Useful when MongoDB returns Int32, Int64
/// or double for a number (count for example). This method will always /// or double for a number (count for example). This method will always
/// return an Int64. When the element is not found, a /// return an Int64. When the element is not found, a
@@ -186,17 +207,26 @@ public:
void read(BinaryReader& reader); void read(BinaryReader& reader);
/// Reads a document from the reader /// Reads a document from the reader
std::size_t size() const; [[nodiscard]] std::size_t size() const noexcept;
/// Returns the number of elements in the document. /// Returns the number of elements in the document.
virtual std::string toString(int indent = 0) const; [[nodiscard]] virtual std::string toString(int indent = 0) const;
/// Returns a String representation of the document. /// Returns a String representation of the document.
void write(BinaryWriter& writer); void write(BinaryWriter& writer);
/// Writes a document to the reader /// Writes a document to the reader
protected: protected:
const ElementSet& elements() const noexcept;
/// Returns const reference to elements for read-only access by derived classes.
/// Direct modification is not allowed to maintain synchronization with hash map.
private:
ElementSet _elements; ElementSet _elements;
std::unordered_map<std::string, Element::Ptr> _elementMap;
/// Hash map for O(1) element lookups by name.
/// Maintained in sync with _elements for fast access.
/// These are private to ensure derived classes cannot break synchronization.
}; };
@@ -206,6 +236,7 @@ protected:
inline Document& Document::addElement(Element::Ptr element) inline Document& Document::addElement(Element::Ptr element)
{ {
_elements.push_back(element); _elements.push_back(element);
_elementMap[element->name()] = element; // O(1) insert for fast lookups
return *this; return *this;
} }
@@ -218,13 +249,14 @@ inline Document& Document::addNewDocument(const std::string& name)
} }
inline void Document::clear() inline void Document::clear() noexcept
{ {
_elements.clear(); _elements.clear();
_elementMap.clear();
} }
inline bool Document::empty() const inline bool Document::empty() const noexcept
{ {
return _elements.empty(); return _elements.empty();
} }
@@ -232,6 +264,7 @@ inline bool Document::empty() const
inline void Document::elementNames(std::vector<std::string>& keys) const inline void Document::elementNames(std::vector<std::string>& keys) const
{ {
keys.reserve(keys.size() + _elements.size()); // Pre-allocate to avoid reallocations
for (const auto & _element : _elements) for (const auto & _element : _elements)
{ {
keys.push_back(_element->name()); keys.push_back(_element->name());
@@ -239,29 +272,43 @@ inline void Document::elementNames(std::vector<std::string>& keys) const
} }
inline bool Document::exists(const std::string& name) const inline bool Document::exists(const std::string& name) const noexcept
{ {
return std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name)) != _elements.end(); // O(1) lookup using hash map instead of O(n) linear search
return _elementMap.find(name) != _elementMap.end();
} }
inline bool Document::remove(const std::string& name) inline bool Document::remove(const std::string& name)
{ {
auto it = std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name)); // Remove from hash map first (O(1))
if (it == _elements.end()) auto mapIt = _elementMap.find(name);
if (mapIt == _elementMap.end())
return false; return false;
_elements.erase(it); _elementMap.erase(mapIt);
// Then remove from vector (O(n) but unavoidable for order preservation)
auto it = std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name));
if (it != _elements.end())
_elements.erase(it);
return true; return true;
} }
inline std::size_t Document::size() const inline std::size_t Document::size() const noexcept
{ {
return _elements.size(); return _elements.size();
} }
inline const ElementSet& Document::elements() const noexcept
{
return _elements;
}
// BSON Embedded Document // BSON Embedded Document
// spec: document // spec: document
template<> template<>

View File

@@ -47,16 +47,19 @@ public:
explicit Element(const std::string& name); explicit Element(const std::string& name);
/// Creates the Element with the given name. /// Creates the Element with the given name.
explicit Element(std::string&& name);
/// Creates the Element with the given name (move semantics).
virtual ~Element(); virtual ~Element();
/// Destructor /// Destructor
const std::string& name() const; [[nodiscard]] const std::string& name() const noexcept;
/// Returns the name of the element. /// Returns the name of the element.
virtual std::string toString(int indent = 0) const = 0; [[nodiscard]] virtual std::string toString(int indent = 0) const = 0;
/// Returns a string representation of the element. /// Returns a string representation of the element.
virtual int type() const = 0; [[nodiscard]] virtual int type() const noexcept = 0;
/// Returns the MongoDB type of the element. /// Returns the MongoDB type of the element.
private: private:
@@ -71,13 +74,13 @@ private:
// //
// inlines // inlines
// //
inline const std::string& Element::name() const inline const std::string& Element::name() const noexcept
{ {
return _name; return _name;
} }
using ElementSet = std::list<Element::Ptr>; using ElementSet = std::vector<Element::Ptr>;
template<typename T> template<typename T>
@@ -110,51 +113,76 @@ struct ElementTraits<std::string>
static std::string toString(const std::string& value, int indent = 0) static std::string toString(const std::string& value, int indent = 0)
{ {
std::ostringstream oss; // Fast path: check if escaping is needed at all
bool needsEscaping = false;
oss << '"'; for (char c : value)
for (char it : value)
{ {
switch (it) if (c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t' || (c > 0 && c <= 0x1F))
{ {
case '"': needsEscaping = true;
oss << "\\\"";
break; break;
case '\\':
oss << "\\\\";
break;
case '\b':
oss << "\\b";
break;
case '\f':
oss << "\\f";
break;
case '\n':
oss << "\\n";
break;
case '\r':
oss << "\\r";
break;
case '\t':
oss << "\\t";
break;
default:
{
if ( it > 0 && it <= 0x1F )
{
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(it);
}
else
{
oss << it;
}
break;
}
} }
} }
oss << '"';
return oss.str(); // Fast path: no escaping needed - just wrap in quotes
if (!needsEscaping)
{
std::string result;
result.reserve(value.size() + 2);
result += '"';
result += value;
result += '"';
return result;
}
// Slow path: escaping needed
std::string result;
result.reserve(value.size() * 2 + 2); // Pessimistic estimate
result += '"';
for (char c : value)
{
switch (c)
{
case '"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
if (c > 0 && c <= 0x1F)
{
// Unicode escape sequence
char buf[7]; // "\uXXXX" + null terminator
std::snprintf(buf, sizeof(buf), "\\u%04X", static_cast<unsigned char>(c));
result += buf;
}
else
{
result += c;
}
break;
}
}
result += '"';
return result;
} }
}; };
@@ -233,9 +261,10 @@ struct ElementTraits<Timestamp>
static std::string toString(const Timestamp& value, int indent = 0) static std::string toString(const Timestamp& value, int indent = 0)
{ {
std::string result; std::string result;
result.append(1, '"'); result.reserve(32); // Pre-allocate for typical timestamp string length
result.append(DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z")); result += '"';
result.append(1, '"'); result += DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z");
result += '"';
return result; return result;
} }
}; };
@@ -304,11 +333,12 @@ struct ElementTraits<BSONTimestamp>
static std::string toString(const BSONTimestamp& value, int indent = 0) static std::string toString(const BSONTimestamp& value, int indent = 0)
{ {
std::string result; std::string result;
result.append(1, '"'); result.reserve(48); // Pre-allocate for timestamp + space + increment + quotes
result.append(DateTimeFormatter::format(value.ts, "%Y-%m-%dT%H:%M:%s%z")); result += '"';
result.append(1, ' '); result += DateTimeFormatter::format(value.ts, "%Y-%m-%dT%H:%M:%s%z");
result.append(NumberFormatter::format(value.inc)); result += ' ';
result.append(1, '"'); result += NumberFormatter::format(value.inc);
result += '"';
return result; return result;
} }
}; };
@@ -359,22 +389,28 @@ public:
{ {
} }
ConcreteElement(std::string&& name, T&& init):
Element(std::move(name)),
_value(std::move(init))
{
}
~ConcreteElement() override = default; ~ConcreteElement() override = default;
const T& value() const const T& value() const noexcept
{ {
return _value; return _value;
} }
std::string toString(int indent = 0) const override [[nodiscard]] std::string toString(int indent = 0) const override
{ {
return ElementTraits<T>::toString(_value, indent); return ElementTraits<T>::toString(_value, indent);
} }
int type() const override [[nodiscard]] int type() const noexcept override
{ {
return ElementTraits<T>::TypeId; return ElementTraits<T>::TypeId;
} }

View File

@@ -1,91 +0,0 @@
//
// GetMoreRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: GetMoreRequest
//
// Definition of the GetMoreRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_GetMoreRequest_INCLUDED
#define MongoDB_GetMoreRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
namespace Poco {
namespace MongoDB {
//class [[deprecated]] GetMoreRequest;
class GetMoreRequest;
class MongoDB_API GetMoreRequest: public RequestMessage
/// A GetMoreRequest is used to query the database for more documents in a collection
/// after a query request is send (OP_GETMORE).
{
public:
GetMoreRequest(const std::string& collectionName, Int64 cursorID);
/// Creates a GetMoreRequest for the give collection and cursor.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar". The cursorID has been returned by the response on the query request.
/// By default the numberToReturn is set to 100.
virtual ~GetMoreRequest();
/// Destroys the GetMoreRequest.
Int32 getNumberToReturn() const;
/// Returns the limit of returned documents.
void setNumberToReturn(Int32 n);
/// Sets the limit of returned documents.
Int64 cursorID() const;
/// Returns the cursor ID.
protected:
void buildRequest(BinaryWriter& writer);
private:
std::string _fullCollectionName;
Int32 _numberToReturn;
Int64 _cursorID;
};
//
// inlines
//
inline Int32 GetMoreRequest::getNumberToReturn() const
{
return _numberToReturn;
}
inline void GetMoreRequest::setNumberToReturn(Int32 n)
{
_numberToReturn = n;
}
inline Int64 GetMoreRequest::cursorID() const
{
return _cursorID;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_GetMoreRequest_INCLUDED

View File

@@ -1,99 +0,0 @@
//
// InsertRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: InsertRequest
//
// Definition of the InsertRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_InsertRequest_INCLUDED
#define MongoDB_InsertRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/MongoDB/Document.h"
namespace Poco {
namespace MongoDB {
//class [[deprecated]] InsertRequest;
class InsertRequest;
class MongoDB_API InsertRequest: public RequestMessage
/// A request for inserting one or more documents to the database
/// (OP_INSERT).
{
public:
enum Flags
{
INSERT_DEFAULT = 0,
/// If specified, perform a normal insert operation.
INSERT_CONTINUE_ON_ERROR = 1
/// If set, the database will not stop processing a bulk insert if one
/// fails (e.g. due to duplicate IDs). This makes bulk insert behave similarly
/// to a series of single inserts, except lastError will be set if any insert
/// fails, not just the last one. If multiple errors occur, only the most
/// recent will be reported.
};
InsertRequest(const std::string& collectionName, Flags flags = INSERT_DEFAULT);
/// Creates an InsertRequest.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar".
virtual ~InsertRequest();
/// Destroys the InsertRequest.
Document& addNewDocument();
/// Adds a new document for insertion. A reference to the empty document is
/// returned. InsertRequest is the owner of the Document and will free it
/// on destruction.
Document::Vector& documents();
/// Returns the documents to insert into the database.
protected:
void buildRequest(BinaryWriter& writer);
private:
Int32 _flags;
std::string _fullCollectionName;
Document::Vector _documents;
};
//
// inlines
//
inline Document& InsertRequest::addNewDocument()
{
Document::Ptr doc = new Document();
_documents.push_back(doc);
return *doc;
}
inline Document::Vector& InsertRequest::documents()
{
return _documents;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_InsertRequest_INCLUDED

View File

@@ -44,7 +44,7 @@ public:
void setCode(const std::string& code); void setCode(const std::string& code);
/// Sets the JavaScript code. /// Sets the JavaScript code.
std::string getCode() const; [[nodiscard]] std::string getCode() const;
/// Returns the JavaScript code. /// Returns the JavaScript code.
private: private:

View File

@@ -1,62 +0,0 @@
//
// KillCursorsRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: KillCursorsRequest
//
// Definition of the KillCursorsRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_KillCursorsRequest_INCLUDED
#define MongoDB_KillCursorsRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
namespace Poco {
namespace MongoDB {
class MongoDB_API KillCursorsRequest: public RequestMessage
/// Class for creating an OP_KILL_CURSORS client request. This
/// request is used to kill cursors, which are still open,
/// returned by query requests.
{
public:
KillCursorsRequest();
/// Creates a KillCursorsRequest.
virtual ~KillCursorsRequest();
/// Destroys the KillCursorsRequest.
std::vector<Int64>& cursors();
/// The internal list of cursors.
protected:
void buildRequest(BinaryWriter& writer);
std::vector<Int64> _cursors;
};
//
// inlines
//
inline std::vector<Int64>& KillCursorsRequest::cursors()
{
return _cursors;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_KillCursorsRequest_INCLUDED

View File

@@ -40,7 +40,7 @@ public:
virtual ~Message(); virtual ~Message();
/// Destructor /// Destructor
MessageHeader& header(); [[nodiscard]] MessageHeader& header();
/// Returns the message header /// Returns the message header
protected: protected:

View File

@@ -38,25 +38,6 @@ public:
enum OpCode enum OpCode
{ {
#if false
// Opcodes deprecated in MongoDB 5.0
OP_REPLY [[deprecated]] = 1,
OP_UPDATE [[deprecated]] = 2001,
OP_INSERT [[deprecated]] = 2002,
OP_QUERY [[deprecated]] = 2004,
OP_GET_MORE [[deprecated]] = 2005,
OP_DELETE [[deprecated]] = 2006,
OP_KILL_CURSORS [[deprecated]] = 2007,
#else
OP_REPLY = 1,
OP_UPDATE = 2001,
OP_INSERT = 2002,
OP_QUERY = 2004,
OP_GET_MORE = 2005,
OP_DELETE = 2006,
OP_KILL_CURSORS = 2007,
#endif
/// Opcodes supported in MongoDB 5.1 and later /// Opcodes supported in MongoDB 5.1 and later
OP_COMPRESSED = 2012, OP_COMPRESSED = 2012,
OP_MSG = 2013 OP_MSG = 2013
@@ -74,19 +55,19 @@ public:
void write(BinaryWriter& writer); void write(BinaryWriter& writer);
/// Writes the header using the given BinaryWriter. /// Writes the header using the given BinaryWriter.
Int32 getMessageLength() const; [[nodiscard]] Int32 getMessageLength() const;
/// Returns the message length. /// Returns the message length.
OpCode opCode() const; [[nodiscard]] OpCode opCode() const;
/// Returns the OpCode. /// Returns the OpCode.
Int32 getRequestID() const; [[nodiscard]] Int32 getRequestID() const;
/// Returns the request ID of the current message. /// Returns the request ID of the current message.
void setRequestID(Int32 id); void setRequestID(Int32 id);
/// Sets the request ID of the current message. /// Sets the request ID of the current message.
Int32 responseTo() const; [[nodiscard]] Int32 responseTo() const;
/// Returns the request id from the original request. /// Returns the request id from the original request.
private: private:

View File

@@ -50,16 +50,25 @@ public:
/// The string must contain a hexadecimal representation /// The string must contain a hexadecimal representation
/// of an object ID. This means a string of 24 characters. /// of an object ID. This means a string of 24 characters.
ObjectId(const ObjectId& copy); ObjectId(const ObjectId& copy) noexcept;
/// Creates an ObjectId by copying another one. /// Creates an ObjectId by copying another one.
ObjectId(ObjectId&& other) noexcept;
/// Creates an ObjectId by moving another one.
ObjectId& operator=(const ObjectId& copy) noexcept;
/// Assigns another ObjectId.
ObjectId& operator=(ObjectId&& other) noexcept;
/// Move-assigns another ObjectId.
virtual ~ObjectId(); virtual ~ObjectId();
/// Destroys the ObjectId. /// Destroys the ObjectId.
Timestamp timestamp() const; [[nodiscard]] Timestamp timestamp() const noexcept;
/// Returns the timestamp which is stored in the first four bytes of the id /// Returns the timestamp which is stored in the first four bytes of the id
std::string toString(const std::string& fmt = "%02x") const; [[nodiscard]] std::string toString(const std::string& fmt = "%02x") const;
/// Returns the id in string format. The fmt parameter /// Returns the id in string format. The fmt parameter
/// specifies the formatting used for individual members /// specifies the formatting used for individual members
/// of the ID char array. /// of the ID char array.
@@ -67,8 +76,8 @@ public:
private: private:
ObjectId(); ObjectId();
static int fromHex(char c); static constexpr int fromHex(char c) noexcept;
static char fromHex(const char* c); static constexpr char fromHex(const char* c) noexcept;
unsigned char _id[12]; unsigned char _id[12];
@@ -81,19 +90,19 @@ private:
// //
// inlines // inlines
// //
inline Timestamp ObjectId::timestamp() const inline Timestamp ObjectId::timestamp() const noexcept
{ {
int time; int time;
char* T = (char *) &time; char* T = reinterpret_cast<char*>(&time);
T[0] = _id[3]; T[0] = _id[3];
T[1] = _id[2]; T[1] = _id[2];
T[2] = _id[1]; T[2] = _id[1];
T[3] = _id[0]; T[3] = _id[0];
return Timestamp::fromEpochTime((time_t) time); return Timestamp::fromEpochTime(static_cast<time_t>(time));
} }
inline int ObjectId::fromHex(char c) constexpr inline int ObjectId::fromHex(char c) noexcept
{ {
if ( '0' <= c && c <= '9' ) if ( '0' <= c && c <= '9' )
return c - '0'; return c - '0';
@@ -105,9 +114,9 @@ inline int ObjectId::fromHex(char c)
} }
inline char ObjectId::fromHex(const char* c) constexpr inline char ObjectId::fromHex(const char* c) noexcept
{ {
return (char)((fromHex(c[0]) << 4 ) | fromHex(c[1])); return static_cast<char>((fromHex(c[0]) << 4) | fromHex(c[1]));
} }
@@ -130,14 +139,14 @@ struct ElementTraits<ObjectId::Ptr>
template<> template<>
inline void BSONReader::read<ObjectId::Ptr>(ObjectId::Ptr& to) inline void BSONReader::read<ObjectId::Ptr>(ObjectId::Ptr& to)
{ {
_reader.readRaw((char*) to->_id, 12); _reader.readRaw(reinterpret_cast<char*>(to->_id), 12);
} }
template<> template<>
inline void BSONWriter::write<ObjectId::Ptr>(ObjectId::Ptr& from) inline void BSONWriter::write<ObjectId::Ptr>(ObjectId::Ptr& from)
{ {
_writer.writeRaw((char*) from->_id, 12); _writer.writeRaw(reinterpret_cast<char*>(from->_id), 12);
} }

View File

@@ -29,6 +29,16 @@ namespace MongoDB {
class MongoDB_API OpMsgCursor: public Document class MongoDB_API OpMsgCursor: public Document
/// OpMsgCursor is an helper class for querying multiple documents using OpMsgMessage. /// OpMsgCursor is an helper class for querying multiple documents using OpMsgMessage.
/// Once all of the data is read with the cursor (see isActive()) it can't be reused. /// Once all of the data is read with the cursor (see isActive()) it can't be reused.
///
/// RESOURCE MANAGEMENT:
/// When a cursor is no longer needed, you should call kill() to release server-side
/// resources. If kill() is not called explicitly, the server will keep the cursor
/// open until it times out.
///
/// THREAD SAFETY:
/// This class is NOT thread-safe. A cursor must not be used concurrently from
/// multiple threads. Each thread should have its own cursor instances.
/// The next() method modifies internal state and is not safe for concurrent access.
{ {
public: public:
OpMsgCursor(const std::string& dbname, const std::string& collectionName); OpMsgCursor(const std::string& dbname, const std::string& collectionName);
@@ -37,20 +47,20 @@ public:
virtual ~OpMsgCursor(); virtual ~OpMsgCursor();
/// Destroys the OpMsgCursor. /// Destroys the OpMsgCursor.
void setEmptyFirstBatch(bool empty); void setEmptyFirstBatch(bool empty) noexcept;
/// Empty first batch is used to get error response faster with little server processing /// Empty first batch is used to get error response faster with little server processing
bool emptyFirstBatch() const; [[nodiscard]] bool emptyFirstBatch() const noexcept;
void setBatchSize(Int32 batchSize); void setBatchSize(Int32 batchSize) noexcept;
/// Set non-default batch size /// Set non-default batch size
Int32 batchSize() const; [[nodiscard]] Int32 batchSize() const noexcept;
/// Current batch size (zero or negative number indicates default batch size) /// Current batch size (zero or negative number indicates default batch size)
Int64 cursorID() const; [[nodiscard]] Int64 cursorID() const noexcept;
bool isActive() const; [[nodiscard]] bool isActive() const noexcept;
/// Is there more data to acquire with this cursor? /// Is there more data to acquire with this cursor?
OpMsgMessage& next(Connection& connection); OpMsgMessage& next(Connection& connection);
@@ -85,7 +95,7 @@ inline OpMsgMessage& OpMsgCursor::query()
return _query; return _query;
} }
inline Int64 OpMsgCursor::cursorID() const inline Int64 OpMsgCursor::cursorID() const noexcept
{ {
return _cursorID; return _cursorID;
} }

View File

@@ -30,6 +30,11 @@ namespace MongoDB {
class MongoDB_API OpMsgMessage: public Message class MongoDB_API OpMsgMessage: public Message
/// This class represents a request/response (OP_MSG) to send requests and receive responses to/from MongoDB. /// This class represents a request/response (OP_MSG) to send requests and receive responses to/from MongoDB.
///
/// THREAD SAFETY:
/// This class is NOT thread-safe. OpMsgMessage instances must not be accessed
/// concurrently from multiple threads without external synchronization.
/// Each thread should use its own message instances.
{ {
public: public:
@@ -90,38 +95,38 @@ public:
virtual ~OpMsgMessage(); virtual ~OpMsgMessage();
const std::string& databaseName() const; [[nodiscard]] const std::string& databaseName() const;
const std::string& collectionName() const; [[nodiscard]] const std::string& collectionName() const;
void setCommandName(const std::string& command); void setCommandName(const std::string& command);
/// Sets the command name and clears the command document /// Sets the command name and clears the command document
const std::string& commandName() const; [[nodiscard]] const std::string& commandName() const;
/// Current command name. /// Current command name.
void setAcknowledgedRequest(bool ack); void setAcknowledgedRequest(bool ack);
/// Set false to create request that does not return response. /// Set false to create request that does not return response.
/// It has effect only for commands that write or delete documents. /// It has effect only for commands that write or delete documents.
/// Default is true (request returns acknowledge response). /// Default is true (request returns acknowledge response).
bool acknowledgedRequest() const; [[nodiscard]] bool acknowledgedRequest() const;
UInt32 flags() const; [[nodiscard]] UInt32 flags() const;
Document& body(); Document& body();
/// Access to body document. /// Access to body document.
/// Additional query arguments shall be added after setting the command name. /// Additional query arguments shall be added after setting the command name.
const Document& body() const; [[nodiscard]] const Document& body() const;
Document::Vector& documents(); Document::Vector& documents();
/// Documents prepared for request or retrieved in response. /// Documents prepared for request or retrieved in response.
const Document::Vector& documents() const; [[nodiscard]] const Document::Vector& documents() const;
/// Documents prepared for request or retrieved in response. /// Documents prepared for request or retrieved in response.
bool responseOk() const; [[nodiscard]] bool responseOk() const;
/// Reads "ok" status from the response message. /// Reads "ok" status from the response message.
void clear(); void clear();

View File

@@ -1,189 +0,0 @@
//
// QueryRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: QueryRequest
//
// Definition of the QueryRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_QueryRequest_INCLUDED
#define MongoDB_QueryRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/MongoDB/Document.h"
namespace Poco {
namespace MongoDB {
//class [[deprecated]] QueryRequest;
class QueryRequest;
class MongoDB_API QueryRequest: public RequestMessage
/// A request to query documents in a MongoDB database
/// using an OP_QUERY request.
{
public:
enum Flags
{
QUERY_DEFAULT = 0,
/// Do not set any flags.
QUERY_TAILABLE_CURSOR = 2,
/// Tailable means cursor is not closed when the last data is retrieved.
/// Rather, the cursor marks the final objects position.
/// You can resume using the cursor later, from where it was located,
/// if more data were received. Like any "latent cursor", the cursor may
/// become invalid at some point (CursorNotFound) for example if the final
/// object it references were deleted.
QUERY_SLAVE_OK = 4,
/// Allow query of replica slave. Normally these return an error except
/// for namespace "local".
// QUERY_OPLOG_REPLAY = 8 (internal replication use only - drivers should not implement)
QUERY_NO_CURSOR_TIMEOUT = 16,
/// The server normally times out idle cursors after an inactivity period
/// (10 minutes) to prevent excess memory use. Set this option to prevent that.
QUERY_AWAIT_DATA = 32,
/// Use with QUERY_TAILABLECURSOR. If we are at the end of the data, block for
/// a while rather than returning no data. After a timeout period, we do
/// return as normal.
QUERY_EXHAUST = 64,
/// Stream the data down full blast in multiple "more" packages, on the
/// assumption that the client will fully read all data queried.
/// Faster when you are pulling a lot of data and know you want to pull
/// it all down.
/// Note: the client is not allowed to not read all the data unless it
/// closes the connection.
QUERY_PARTIAL = 128
/// Get partial results from a mongos if some shards are down
/// (instead of throwing an error).
};
QueryRequest(const std::string& collectionName, Flags flags = QUERY_DEFAULT);
/// Creates a QueryRequest.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar".
virtual ~QueryRequest();
/// Destroys the QueryRequest.
Flags getFlags() const;
/// Returns the flags.
void setFlags(Flags flag);
/// Set the flags.
std::string fullCollectionName() const;
/// Returns the <db>.<collection> used for this query.
Int32 getNumberToSkip() const;
/// Returns the number of documents to skip.
void setNumberToSkip(Int32 n);
/// Sets the number of documents to skip.
Int32 getNumberToReturn() const;
/// Returns the number of documents to return.
void setNumberToReturn(Int32 n);
/// Sets the number of documents to return (limit).
Document& selector();
/// Returns the selector document.
Document& returnFieldSelector();
/// Returns the field selector document.
protected:
void buildRequest(BinaryWriter& writer);
private:
Flags _flags;
std::string _fullCollectionName;
Int32 _numberToSkip;
Int32 _numberToReturn;
Document _selector;
Document _returnFieldSelector;
};
//
// inlines
//
inline QueryRequest::Flags QueryRequest::getFlags() const
{
return _flags;
}
inline void QueryRequest::setFlags(QueryRequest::Flags flags)
{
_flags = flags;
}
inline std::string QueryRequest::fullCollectionName() const
{
return _fullCollectionName;
}
inline Document& QueryRequest::selector()
{
return _selector;
}
inline Document& QueryRequest::returnFieldSelector()
{
return _returnFieldSelector;
}
inline Int32 QueryRequest::getNumberToSkip() const
{
return _numberToSkip;
}
inline void QueryRequest::setNumberToSkip(Int32 n)
{
_numberToSkip = n;
}
inline Int32 QueryRequest::getNumberToReturn() const
{
return _numberToReturn;
}
inline void QueryRequest::setNumberToReturn(Int32 n)
{
_numberToReturn = n;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_QueryRequest_INCLUDED

View File

@@ -39,24 +39,45 @@ public:
RegularExpression(const std::string& pattern, const std::string& options); RegularExpression(const std::string& pattern, const std::string& options);
/// Creates a RegularExpression using the given pattern and options. /// Creates a RegularExpression using the given pattern and options.
RegularExpression(std::string&& pattern, std::string&& options);
/// Creates a RegularExpression using the given pattern and options (move semantics).
RegularExpression(const RegularExpression& copy) = default;
/// Creates a RegularExpression by copying another one.
RegularExpression(RegularExpression&& other) noexcept = default;
/// Creates a RegularExpression by moving another one.
RegularExpression& operator=(const RegularExpression& copy) = default;
/// Assigns another RegularExpression.
RegularExpression& operator=(RegularExpression&& other) noexcept = default;
/// Move-assigns another RegularExpression.
virtual ~RegularExpression(); virtual ~RegularExpression();
/// Destroys the RegularExpression. /// Destroys the RegularExpression.
SharedPtr<Poco::RegularExpression> createRE() const; [[nodiscard]] SharedPtr<Poco::RegularExpression> createRE() const;
/// Tries to create a Poco::RegularExpression from the MongoDB regular expression. /// Tries to create a Poco::RegularExpression from the MongoDB regular expression.
std::string getOptions() const; [[nodiscard]] const std::string& getOptions() const noexcept;
/// Returns the options string. /// Returns the options string.
void setOptions(const std::string& options); void setOptions(const std::string& options);
/// Sets the options string. /// Sets the options string.
std::string getPattern() const; void setOptions(std::string&& options) noexcept;
/// Sets the options string (move semantics).
[[nodiscard]] const std::string& getPattern() const noexcept;
/// Returns the pattern. /// Returns the pattern.
void setPattern(const std::string& pattern); void setPattern(const std::string& pattern);
/// Sets the pattern. /// Sets the pattern.
void setPattern(std::string&& pattern) noexcept;
/// Sets the pattern (move semantics).
private: private:
std::string _pattern; std::string _pattern;
std::string _options; std::string _options;
@@ -66,7 +87,7 @@ private:
/// ///
/// inlines /// inlines
/// ///
inline std::string RegularExpression::getPattern() const inline const std::string& RegularExpression::getPattern() const noexcept
{ {
return _pattern; return _pattern;
} }
@@ -78,7 +99,13 @@ inline void RegularExpression::setPattern(const std::string& pattern)
} }
inline std::string RegularExpression::getOptions() const inline void RegularExpression::setPattern(std::string&& pattern) noexcept
{
_pattern = std::move(pattern);
}
inline const std::string& RegularExpression::getOptions() const noexcept
{ {
return _options; return _options;
} }
@@ -90,6 +117,12 @@ inline void RegularExpression::setOptions(const std::string& options)
} }
inline void RegularExpression::setOptions(std::string&& options) noexcept
{
_options = std::move(options);
}
// BSON Regex // BSON Regex
// spec: cstring cstring // spec: cstring cstring
template<> template<>
@@ -99,8 +132,19 @@ struct ElementTraits<RegularExpression::Ptr>
static std::string toString(const RegularExpression::Ptr& value, int indent = 0) static std::string toString(const RegularExpression::Ptr& value, int indent = 0)
{ {
//TODO if (value.isNull())
return "RE: not implemented yet"; {
return "null";
}
// Format as /pattern/options similar to MongoDB shell
std::string result;
result.reserve(value->getPattern().size() + value->getOptions().size() + 3);
result += '/';
result += value->getPattern();
result += '/';
result += value->getOptions();
return result;
} }
}; };

View File

@@ -37,7 +37,7 @@ public:
virtual ~ReplicaSet(); virtual ~ReplicaSet();
/// Destroys the ReplicaSet. /// Destroys the ReplicaSet.
Connection::Ptr findMaster(); [[nodiscard]] Connection::Ptr findMaster();
/// Tries to find the master MongoDB instance from the addresses /// Tries to find the master MongoDB instance from the addresses
/// passed to the constructor. /// passed to the constructor.
/// ///

View File

@@ -1,53 +0,0 @@
//
// RequestMessage.h
//
// Library: MongoDB
// Package: MongoDB
// Module: RequestMessage
//
// Definition of the RequestMessage class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_RequestMessage_INCLUDED
#define MongoDB_RequestMessage_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/Message.h"
#include <ostream>
namespace Poco {
namespace MongoDB {
//class [[deprecated]] RequestMessage;
class RequestMessage;
class MongoDB_API RequestMessage: public Message
/// Base class for a request sent to the MongoDB server.
{
public:
explicit RequestMessage(MessageHeader::OpCode opcode);
/// Creates a RequestMessage using the given opcode.
virtual ~RequestMessage();
/// Destroys the RequestMessage.
void send(std::ostream& ostr);
/// Writes the request to stream.
protected:
virtual void buildRequest(BinaryWriter& ss) = 0;
};
} } // namespace Poco::MongoDB
#endif // MongoDB_RequestMessage_INCLUDED

View File

@@ -1,113 +0,0 @@
//
// ResponseMessage.h
//
// Library: MongoDB
// Package: MongoDB
// Module: ResponseMessage
//
// Definition of the ResponseMessage class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_ResponseMessage_INCLUDED
#define MongoDB_ResponseMessage_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/Message.h"
#include "Poco/MongoDB/Document.h"
#include <istream>
#include <cstdlib>
namespace Poco {
namespace MongoDB {
//class [[deprecated]] ResponseMessage;
class ResponseMessage;
class MongoDB_API ResponseMessage: public Message
/// This class represents a response (OP_REPLY) from MongoDB.
{
public:
ResponseMessage();
/// Creates an empty ResponseMessage.
ResponseMessage(const Int64& cursorID);
/// Creates an ResponseMessage for existing cursor ID.
virtual ~ResponseMessage();
/// Destroys the ResponseMessage.
Int64 cursorID() const;
/// Returns the cursor ID.
void clear();
/// Clears the response.
std::size_t count() const;
/// Returns the number of documents in the response.
Document::Vector& documents();
/// Returns a vector containing the received documents.
bool empty() const;
/// Returns true if the response does not contain any documents.
bool hasDocuments() const;
/// Returns true if there is at least one document in the response.
void read(std::istream& istr);
/// Reads the response from the stream.
private:
Int32 _responseFlags;
Int64 _cursorID;
Int32 _startingFrom;
Int32 _numberReturned;
Document::Vector _documents;
};
//
// inlines
//
inline std::size_t ResponseMessage::count() const
{
return _documents.size();
}
inline bool ResponseMessage::empty() const
{
return _documents.size() == 0;
}
inline Int64 ResponseMessage::cursorID() const
{
return _cursorID;
}
inline Document::Vector& ResponseMessage::documents()
{
return _documents;
}
inline bool ResponseMessage::hasDocuments() const
{
return _documents.size() > 0;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_ResponseMessage_INCLUDED

View File

@@ -1,115 +0,0 @@
//
// UpdateRequest.h
//
// Library: MongoDB
// Package: MongoDB
// Module: UpdateRequest
//
// Definition of the UpdateRequest class.
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef MongoDB_UpdateRequest_INCLUDED
#define MongoDB_UpdateRequest_INCLUDED
#include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/MongoDB/Document.h"
namespace Poco {
namespace MongoDB {
//class POCO_DEPRECATED("Use new wire protocol") UpdateRequest;
class MongoDB_API UpdateRequest: public RequestMessage
/// This request is used to update a document in a database
/// using the OP_UPDATE client request.
{
public:
enum Flags
{
UPDATE_DEFAULT = 0,
/// If set, the database will insert the supplied object into the
/// collection if no matching document is found.
UPDATE_UPSERT = 1,
/// If set, the database will update all matching objects in the collection.
/// Otherwise only updates first matching doc.
UPDATE_MULTIUPDATE = 2
/// If set to, updates multiple documents that meet the query criteria.
/// Otherwise only updates one document.
};
UpdateRequest(const std::string& collectionName, Flags flags = UPDATE_DEFAULT);
/// Creates the UpdateRequest.
///
/// The full collection name is the concatenation of the database
/// name with the collection name, using a "." for the concatenation. For example,
/// for the database "foo" and the collection "bar", the full collection name is
/// "foo.bar".
virtual ~UpdateRequest();
/// Destroys the UpdateRequest.
Document& selector();
/// Returns the selector document.
Document& update();
/// Returns the document to update.
Flags flags() const;
/// Returns the flags
void flags(Flags flags);
/// Sets the flags
protected:
void buildRequest(BinaryWriter& writer);
private:
Flags _flags;
std::string _fullCollectionName;
Document _selector;
Document _update;
};
//
// inlines
//
inline UpdateRequest::Flags UpdateRequest::flags() const
{
return _flags;
}
inline void UpdateRequest::flags(UpdateRequest::Flags flags)
{
_flags = flags;
}
inline Document& UpdateRequest::selector()
{
return _selector;
}
inline Document& UpdateRequest::update()
{
return _update;
}
} } // namespace Poco::MongoDB
#endif // MongoDB_UpdateRequest_INCLUDED

View File

@@ -1,7 +1,7 @@
// //
// main.cpp // main.cpp
// //
// This sample shows SQL to mongo Shell to C++ examples. // This sample shows SQL to MongoDB Shell to C++ examples using OP_MSG wire protocol.
// //
// Copyright (c) 2013, Applied Informatics Software Engineering GmbH. // Copyright (c) 2013, Applied Informatics Software Engineering GmbH.
// and Contributors. // and Contributors.
@@ -13,7 +13,8 @@
#include "Poco/MongoDB/MongoDB.h" #include "Poco/MongoDB/MongoDB.h"
#include "Poco/MongoDB/Connection.h" #include "Poco/MongoDB/Connection.h"
#include "Poco/MongoDB/Database.h" #include "Poco/MongoDB/Database.h"
#include "Poco/MongoDB/Cursor.h" #include "Poco/MongoDB/OpMsgMessage.h"
#include "Poco/MongoDB/OpMsgCursor.h"
#include "Poco/MongoDB/Array.h" #include "Poco/MongoDB/Array.h"
@@ -24,117 +25,119 @@ void sample1(Poco::MongoDB::Connection& connection)
std::cout << "*** SAMPLE 1 ***" << std::endl; std::cout << "*** SAMPLE 1 ***" << std::endl;
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::InsertRequest> insertPlayerRequest = db.createInsertRequest("players"); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_INSERT);
// With one insert request, we can add multiple documents // Add multiple documents
insertPlayerRequest->addNewDocument() auto& docs = request->documents();
.add("lastname", "Valdes")
.add("firstname", "Victor")
.add("birthyear", 1982);
insertPlayerRequest->addNewDocument()
.add("lastname", "Alves")
.add("firstname", "Daniel")
.add("birthyear", 1983);
insertPlayerRequest->addNewDocument()
.add("lastname", "Bartra")
.add("firstname", "Marc")
.add("birthyear", 1991);
insertPlayerRequest->addNewDocument()
.add("lastname", "Alba")
.add("firstname", "Jordi")
.add("birthyear", 1989);
insertPlayerRequest->addNewDocument()
.add("lastname", "Montoya")
.add("firstname", "Martin")
.add("birthyear", 1991);
insertPlayerRequest->addNewDocument()
.add("lastname", "Abidal")
.add("firstname", "Eric")
.add("birthyear", 1979);
insertPlayerRequest->addNewDocument()
.add("lastname", "Fontas")
.add("firstname", "Andreu")
.add("birthyear", 1989);
insertPlayerRequest->addNewDocument()
.add("lastname", "Messi")
.add("firstname", "Lionel")
.add("birthyear", 1987);
insertPlayerRequest->addNewDocument()
.add("lastname", "Puyol")
.add("firstname", "Carles")
.add("birthyear", 1978);
insertPlayerRequest->addNewDocument()
.add("lastname", "Piqué")
.add("firstname", "Gerard")
.add("birthyear", 1987);
insertPlayerRequest->addNewDocument()
.add("lastname", "Muniesa")
.add("firstname", "Marc")
.add("birthyear", 1992);
insertPlayerRequest->addNewDocument()
.add("lastname", "Fabrégas")
.add("firstname", "Cesc")
.add("birthyear", 1987);
insertPlayerRequest->addNewDocument()
.add("lastname", "Hernandez")
.add("firstname", "Xavi")
.add("birthyear", 1980);
insertPlayerRequest->addNewDocument()
.add("lastname", "Iniesta")
.add("firstname", "Andres")
.add("birthyear", 1984);
insertPlayerRequest->addNewDocument()
.add("lastname", "Alcantara")
.add("firstname", "Thiago")
.add("birthyear", 1991);
insertPlayerRequest->addNewDocument()
.add("lastname", "Dos Santos")
.add("firstname", "Jonathan")
.add("birthyear", 1990);
insertPlayerRequest->addNewDocument()
.add("lastname", "Mascherano")
.add("firstname", "Javier")
.add("birthyear", 1984);
insertPlayerRequest->addNewDocument()
.add("lastname", "Busquets")
.add("firstname", "Sergio")
.add("birthyear", 1988);
insertPlayerRequest->addNewDocument()
.add("lastname", "Adriano")
.add("firstname", "")
.add("birthyear", 1984);
insertPlayerRequest->addNewDocument()
.add("lastname", "Song")
.add("firstname", "Alex")
.add("birthyear", 1987);
insertPlayerRequest->addNewDocument()
.add("lastname", "Villa")
.add("firstname", "David")
.add("birthyear", 1981);
insertPlayerRequest->addNewDocument()
.add("lastname", "Sanchez")
.add("firstname", "Alexis")
.add("birthyear", 1988);
insertPlayerRequest->addNewDocument()
.add("lastname", "Pedro")
.add("firstname", "")
.add("birthyear", 1987);
insertPlayerRequest->addNewDocument()
.add("lastname", "Cuenca")
.add("firstname", "Isaac")
.add("birthyear", 1991);
insertPlayerRequest->addNewDocument()
.add("lastname", "Tello")
.add("firstname", "Cristian")
.add("birthyear", 1991);
std::cout << insertPlayerRequest->documents().size() << std::endl; Poco::MongoDB::Document::Ptr doc = new Poco::MongoDB::Document();
doc->add("lastname", "Valdes").add("firstname", "Victor").add("birthyear", 1982);
docs.push_back(doc);
connection.sendRequest(*insertPlayerRequest); doc = new Poco::MongoDB::Document();
std::string lastError = db.getLastError(connection); doc->add("lastname", "Alves").add("firstname", "Daniel").add("birthyear", 1983);
if (!lastError.empty()) docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Bartra").add("firstname", "Marc").add("birthyear", 1991);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Alba").add("firstname", "Jordi").add("birthyear", 1989);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Montoya").add("firstname", "Martin").add("birthyear", 1991);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Abidal").add("firstname", "Eric").add("birthyear", 1979);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Fontas").add("firstname", "Andreu").add("birthyear", 1989);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Messi").add("firstname", "Lionel").add("birthyear", 1987);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Puyol").add("firstname", "Carles").add("birthyear", 1978);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Piqué").add("firstname", "Gerard").add("birthyear", 1987);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Muniesa").add("firstname", "Marc").add("birthyear", 1992);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Fabrégas").add("firstname", "Cesc").add("birthyear", 1987);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Hernandez").add("firstname", "Xavi").add("birthyear", 1980);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Iniesta").add("firstname", "Andres").add("birthyear", 1984);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Alcantara").add("firstname", "Thiago").add("birthyear", 1991);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Dos Santos").add("firstname", "Jonathan").add("birthyear", 1990);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Mascherano").add("firstname", "Javier").add("birthyear", 1984);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Busquets").add("firstname", "Sergio").add("birthyear", 1988);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Adriano").add("firstname", "").add("birthyear", 1984);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Song").add("firstname", "Alex").add("birthyear", 1987);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Villa").add("firstname", "David").add("birthyear", 1981);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Sanchez").add("firstname", "Alexis").add("birthyear", 1988);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Pedro").add("firstname", "").add("birthyear", 1987);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Cuenca").add("firstname", "Isaac").add("birthyear", 1991);
docs.push_back(doc);
doc = new Poco::MongoDB::Document();
doc->add("lastname", "Tello").add("firstname", "Cristian").add("birthyear", 1991);
docs.push_back(doc);
std::cout << request->documents().size() << std::endl;
Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*request, response);
if (!response.responseOk())
{ {
std::cout << "Last Error: " << db.getLastError(connection) << std::endl; std::cout << "Error: " << response.body().toString(2) << std::endl;
} }
} }
@@ -144,26 +147,20 @@ void sample2(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 2 ***" << std::endl; std::cout << "*** SAMPLE 2 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
// Selecting fields is done by adding them to the returnFieldSelector cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
// Use 1 as value of the element. // Selecting fields is done by adding a projection document
cursor.query().returnFieldSelector().add("lastname", 1); cursor.query().body().addNewDocument("projection")
cursor.query().returnFieldSelector().add("birthyear", 1); .add("lastname", 1)
Poco::MongoDB::ResponseMessage& response = cursor.next(connection); .add("birthyear", 1);
for (;;)
Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
while (cursor.isActive())
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
} }
} }
@@ -174,24 +171,18 @@ void sample3(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 3 ***" << std::endl; std::cout << "*** SAMPLE 3 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
Poco::MongoDB::ResponseMessage& response = cursor.next(connection); cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
for (;;)
Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
while (cursor.isActive())
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << ' ' << (*it)->get<std::string>("firstname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << ' ' << doc->get<std::string>("firstname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
}; }
} }
@@ -200,26 +191,19 @@ void sample4(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 4 ***" << std::endl; std::cout << "*** SAMPLE 4 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
cursor.query().selector().add("birthyear", 1978); cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
cursor.query().body().addNewDocument("filter").add("birthyear", 1978);
Poco::MongoDB::ResponseMessage& response = cursor.next(connection); Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
for (;;) while (cursor.isActive())
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << ' ' << (*it)->get<std::string>("firstname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << ' ' << doc->get<std::string>("firstname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
}; }
} }
@@ -228,29 +212,20 @@ void sample5(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 5 ***" << std::endl; std::cout << "*** SAMPLE 5 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
cursor.query().body().addNewDocument("filter").add("birthyear", 1987);
cursor.query().body().addNewDocument("sort").add("lastname", 1);
// When orderby is needed, use 2 separate documents in the query selector Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
cursor.query().selector().addNewDocument("$query").add("birthyear", 1987); while (cursor.isActive())
cursor.query().selector().addNewDocument("$orderby").add("lastname", 1);
Poco::MongoDB::ResponseMessage& response = cursor.next(connection);
for (;;)
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << ' ' << (*it)->get<std::string>("firstname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << ' ' << doc->get<std::string>("firstname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
}; }
} }
@@ -259,29 +234,22 @@ void sample6(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 6 ***" << std::endl; std::cout << "*** SAMPLE 6 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
cursor.query().body().addNewDocument("filter")
.addNewDocument("birthyear")
.add("$gt", 1969)
.add("$lte", 1980);
cursor.query().selector().addNewDocument("birthyear") Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
.add("$gt", 1969) while (cursor.isActive())
.add("$lte", 1980);
Poco::MongoDB::ResponseMessage& response = cursor.next(connection);
for (;;)
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << ' ' << (*it)->get<std::string>("firstname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << ' ' << doc->get<std::string>("firstname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
}; }
} }
@@ -294,20 +262,9 @@ void sample7(Poco::MongoDB::Connection& connection)
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::MongoDB::Document::Ptr keys = new Poco::MongoDB::Document(); Poco::MongoDB::Document::Ptr keys = new Poco::MongoDB::Document();
keys->add("lastname", 1); keys->add("lastname", 1);
Poco::MongoDB::Document::Ptr errorDoc = db.ensureIndex(connection, "players", "lastname", keys); Poco::MongoDB::Document::Ptr resultDoc = db.ensureIndex(connection, "players", "lastname", keys);
/* Sample above is the same as the following code: std::cout << resultDoc->toString(2);
Poco::MongoDB::Document::Ptr index = new Poco::MongoDB::Document();
index->add("ns", "sample.players");
index->add("name", "lastname");
index->addNewDocument("key").add("lastname", 1);
Poco::SharedPtr<Poco::MongoDB::InsertRequest> insertRequest = db.createInsertRequest("system.indexes");
insertRequest->documents().push_back(index);
connection.sendRequest(*insertRequest);
Poco::MongoDB::Document::Ptr errorDoc = db.getLastErrorDoc(connection);
*/
std::cout << errorDoc->toString(2);
} }
@@ -316,134 +273,132 @@ void sample8(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 8 ***" << std::endl; std::cout << "*** SAMPLE 8 ***" << std::endl;
Poco::MongoDB::Cursor cursor("sample", "players"); Poco::MongoDB::OpMsgCursor cursor("sample", "players");
cursor.query().setNumberToReturn(10); cursor.query().setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
cursor.query().setNumberToSkip(20); cursor.query().body()
Poco::MongoDB::ResponseMessage& response = cursor.next(connection); .add("limit", 10)
for (;;) .add("skip", 20);
Poco::MongoDB::OpMsgMessage& response = cursor.next(connection);
while (cursor.isActive())
{ {
for (Poco::MongoDB::Document::Vector::const_iterator it = response.documents().begin(); it != response.documents().end(); ++it) for (const auto& doc : response.documents())
{ {
std::cout << (*it)->get<std::string>("lastname") << ' ' << (*it)->get<std::string>("firstname") << " (" << (*it)->get<int>("birthyear") << ')' << std::endl; std::cout << doc->get<std::string>("lastname") << ' ' << doc->get<std::string>("firstname") << " (" << doc->get<int>("birthyear") << ')' << std::endl;
} }
// When the cursorID is 0, there are no documents left, so break out ...
if (response.cursorID() == 0)
{
break;
}
// Get the next bunch of documents
response = cursor.next(connection); response = cursor.next(connection);
}; }
} }
// SELECT * FROM players LIMIT 1 // SELECT * FROM players LIMIT 1
void sample9(Poco::MongoDB::Connection& connection) void sample9(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 9 ***" << std::endl; std::cout << "*** SAMPLE 9 ***" << std::endl;
// QueryRequest can be used directly
Poco::MongoDB::QueryRequest query("sample.players");
query.setNumberToReturn(1);
Poco::MongoDB::ResponseMessage response;
connection.sendRequest(query, response);
if (response.hasDocuments())
{
std::cout << response.documents()[0]->toString(2) << std::endl;
}
// QueryRequest can be created using the Database class
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> queryPtr = db.createQueryRequest("players"); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
queryPtr->setNumberToReturn(1); request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_FIND);
connection.sendRequest(*queryPtr, response); request->body().add("limit", 1);
if (response.hasDocuments())
Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*request, response);
if (!response.documents().empty())
{ {
std::cout << response.documents()[0]->toString(2) << std::endl; std::cout << response.documents()[0]->toString(2) << std::endl;
} }
} }
// SELECT DISTINCT birthyear FROM players WHERE birthyear > 1980 // SELECT DISTINCT birthyear FROM players WHERE birthyear > 1980
void sample10(Poco::MongoDB::Connection& connection) void sample10(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 10 ***" << std::endl; std::cout << "*** SAMPLE 10 ***" << std::endl;
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> command = db.createCommand(); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_DISTINCT);
command->selector() request->body()
.add("distinct", "players")
.add("key", "birthyear") .add("key", "birthyear")
.addNewDocument("query") .addNewDocument("query")
.addNewDocument("birthyear") .addNewDocument("birthyear")
.add("$gt", 1980); .add("$gt", 1980);
Poco::MongoDB::ResponseMessage response; Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*command, response); connection.sendRequest(*request, response);
if (response.hasDocuments()) if (response.responseOk())
{ {
Poco::MongoDB::Array::Ptr values = response.documents()[0]->get<Poco::MongoDB::Array::Ptr>("values"); Poco::MongoDB::Array::Ptr values = response.body().get<Poco::MongoDB::Array::Ptr>("values");
for (int i = 0; i < values->size(); ++i ) for (std::size_t i = 0; i < values->size(); ++i)
{ {
std::cout << values->get<int>(i) << std::endl; std::cout << values->get<int>(i) << std::endl;
} }
} }
} }
// SELECT COUNT(*) FROM players WHERE birthyear > 1980 // SELECT COUNT(*) FROM players WHERE birthyear > 1980
void sample11(Poco::MongoDB::Connection& connection) void sample11(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 11 ***" << std::endl; std::cout << "*** SAMPLE 11 ***" << std::endl;
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> count = db.createCountRequest("players"); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
count->selector().addNewDocument("query") request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_COUNT);
.addNewDocument("birthyear") request->body()
.add("$gt", 1980); .addNewDocument("query")
.addNewDocument("birthyear")
.add("$gt", 1980);
Poco::MongoDB::ResponseMessage response; Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*count, response); connection.sendRequest(*request, response);
if (response.hasDocuments()) if (response.responseOk())
{ {
std::cout << "Count: " << response.documents()[0]->getInteger("n") << std::endl; std::cout << "Count: " << response.body().getInteger("n") << std::endl;
} }
} }
//UPDATE players SET birthyear = birthyear + 1 WHERE firstname = 'Victor' // UPDATE players SET birthyear = birthyear + 1 WHERE firstname = 'Victor'
void sample12(Poco::MongoDB::Connection& connection) void sample12(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 12 ***" << std::endl; std::cout << "*** SAMPLE 12 ***" << std::endl;
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::UpdateRequest> request = db.createUpdateRequest("players"); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
request->selector().add("firstname", "Victor"); request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_UPDATE);
request->update().addNewDocument("$inc").add("birthyear", 1); Poco::MongoDB::Document::Ptr update = new Poco::MongoDB::Document();
update->addNewDocument("q").add("firstname", "Victor");
update->addNewDocument("u").addNewDocument("$inc").add("birthyear", 1);
request->documents().push_back(update);
connection.sendRequest(*request); Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*request, response);
Poco::MongoDB::Document::Ptr lastError = db.getLastErrorDoc(connection); std::cout << "Response: " << response.body().toString(2) << std::endl;
std::cout << "LastError: " << lastError->toString(2) << std::endl;
} }
//DELETE players WHERE firstname = 'Victor' // DELETE players WHERE firstname = 'Victor'
void sample13(Poco::MongoDB::Connection& connection) void sample13(Poco::MongoDB::Connection& connection)
{ {
std::cout << "*** SAMPLE 13 ***" << std::endl; std::cout << "*** SAMPLE 13 ***" << std::endl;
Poco::MongoDB::Database db("sample"); Poco::MongoDB::Database db("sample");
Poco::SharedPtr<Poco::MongoDB::DeleteRequest> request = db.createDeleteRequest("players"); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = db.createOpMsgMessage("players");
request->selector().add("firstname", "Victor"); request->setCommandName(Poco::MongoDB::OpMsgMessage::CMD_DELETE);
connection.sendRequest(*request); Poco::MongoDB::Document::Ptr del = new Poco::MongoDB::Document();
del->addNewDocument("q").add("firstname", "Victor");
del->add("limit", 0); // 0 = delete all matching
request->documents().push_back(del);
Poco::MongoDB::Document::Ptr lastError = db.getLastErrorDoc(connection); Poco::MongoDB::OpMsgMessage response;
std::cout << "LastError: " << lastError->toString(2) << std::endl; connection.sendRequest(*request, response);
std::cout << "Response: " << response.body().toString(2) << std::endl;
} }

View File

@@ -46,9 +46,11 @@ std::string Array::toString(int indent) const
if (indent > 0) oss << std::endl; if (indent > 0) oss << std::endl;
for (auto it = _elements.begin(), total = _elements.end(); it != total; ++it) // Use protected accessor instead of direct _elements access to maintain encapsulation
const ElementSet& elems = elements();
for (auto it = elems.begin(), total = elems.end(); it != total; ++it)
{ {
if (it != _elements.begin()) if (it != elems.begin())
{ {
oss << ","; oss << ",";
if (indent > 0) oss << std::endl; if (indent > 0) oss << std::endl;

View File

@@ -39,7 +39,7 @@ Binary::Binary(Poco::Int32 size, unsigned char subtype):
Binary::Binary(const UUID& uuid): Binary::Binary(const UUID& uuid):
_buffer(128 / 8), _buffer(128 / 8),
_subtype(0x04) _subtype(SUBTYPE_UUID)
{ {
unsigned char szUUID[16]; unsigned char szUUID[16];
uuid.copyTo((char*) szUUID); uuid.copyTo((char*) szUUID);
@@ -69,6 +69,20 @@ Binary::~Binary()
std::string Binary::toString(int indent) const std::string Binary::toString(int indent) const
{ {
// Special handling for UUID subtype - format like MongoDB tools: UUID("...")
if (_subtype == SUBTYPE_UUID && _buffer.size() == 16)
{
try
{
return "UUID(\"" + uuid().toString() + "\")";
}
catch (...)
{
// Fall through to Base64 encoding if UUID extraction fails
}
}
// Default: Base64 encode the binary data
std::ostringstream oss; std::ostringstream oss;
Base64Encoder encoder(oss); Base64Encoder encoder(oss);
MemoryInputStream mis((const char*) _buffer.begin(), _buffer.size()); MemoryInputStream mis((const char*) _buffer.begin(), _buffer.size());
@@ -80,7 +94,7 @@ std::string Binary::toString(int indent) const
UUID Binary::uuid() const UUID Binary::uuid() const
{ {
if (_subtype == 0x04 && _buffer.size() == 16) if (_subtype == SUBTYPE_UUID && _buffer.size() == 16)
{ {
UUID uuid; UUID uuid;
uuid.copyFrom((const char*) _buffer.begin()); uuid.copyFrom((const char*) _buffer.begin());

View File

@@ -216,22 +216,6 @@ void Connection::disconnect()
} }
void Connection::sendRequest(RequestMessage& request)
{
Poco::Net::SocketOutputStream sos(_socket);
request.send(sos);
}
void Connection::sendRequest(RequestMessage& request, ResponseMessage& response)
{
sendRequest(request);
Poco::Net::SocketInputStream sis(_socket);
response.read(sis);
}
void Connection::sendRequest(OpMsgMessage& request, OpMsgMessage& response) void Connection::sendRequest(OpMsgMessage& request, OpMsgMessage& response)
{ {
Poco::Net::SocketOutputStream sos(_socket); Poco::Net::SocketOutputStream sos(_socket);

View File

@@ -1,83 +0,0 @@
//
// Cursor.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: Cursor
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/Cursor.h"
#include "Poco/MongoDB/GetMoreRequest.h"
#include "Poco/MongoDB/KillCursorsRequest.h"
namespace Poco {
namespace MongoDB {
Cursor::Cursor(const std::string& db, const std::string& collection, QueryRequest::Flags flags):
_query(db + '.' + collection, flags)
{
}
Cursor::Cursor(const std::string& fullCollectionName, QueryRequest::Flags flags):
_query(fullCollectionName, flags)
{
}
Cursor::Cursor(const Document& aggregationResponse) :
_query(aggregationResponse.get<Poco::MongoDB::Document::Ptr>("cursor")->get<std::string>("ns")),
_response(aggregationResponse.get<Poco::MongoDB::Document::Ptr>("cursor")->get<Int64>("id"))
{
}
Cursor::~Cursor()
{
try
{
poco_assert_dbg(!_response.cursorID());
}
catch (...)
{
}
}
ResponseMessage& Cursor::next(Connection& connection)
{
if (_response.cursorID() == 0)
{
connection.sendRequest(_query, _response);
}
else
{
Poco::MongoDB::GetMoreRequest getMore(_query.fullCollectionName(), _response.cursorID());
getMore.setNumberToReturn(_query.getNumberToReturn());
_response.clear();
connection.sendRequest(getMore, _response);
}
return _response;
}
void Cursor::kill(Connection& connection)
{
if (_response.cursorID() != 0)
{
KillCursorsRequest killRequest;
killRequest.cursors().push_back(_response.cursorID());
connection.sendRequest(killRequest);
}
_response.clear();
}
} } // Namespace Poco::MongoDB

View File

@@ -13,8 +13,9 @@
#include "Poco/MongoDB/Database.h" #include "Poco/MongoDB/Database.h"
#include "Poco/Base64Encoder.h" #include "Poco/MongoDB/Array.h"
#include "Poco/MongoDB/Binary.h" #include "Poco/MongoDB/Binary.h"
#include "Poco/Base64Encoder.h"
#include "Poco/MD5Engine.h" #include "Poco/MD5Engine.h"
#include "Poco/SHA1Engine.h" #include "Poco/SHA1Engine.h"
#include "Poco/PBKDF2Engine.h" #include "Poco/PBKDF2Engine.h"
@@ -35,7 +36,6 @@ namespace Poco {
namespace MongoDB { namespace MongoDB {
const std::string Database::AUTH_MONGODB_CR("MONGODB-CR");
const std::string Database::AUTH_SCRAM_SHA1("SCRAM-SHA-1"); const std::string Database::AUTH_SCRAM_SHA1("SCRAM-SHA-1");
@@ -44,17 +44,24 @@ namespace
std::map<std::string, std::string> parseKeyValueList(const std::string& str) std::map<std::string, std::string> parseKeyValueList(const std::string& str)
{ {
std::map<std::string, std::string> kvm; std::map<std::string, std::string> kvm;
std::string::const_iterator it = str.begin(); std::string::size_type pos = 0;
std::string::const_iterator end = str.end(); while (pos < str.size())
while (it != end)
{ {
std::string k; // Find key-value separators using find() instead of character-by-character iteration
std::string v; std::string::size_type eqPos = str.find('=', pos);
while (it != end && *it != '=') k += *it++; if (eqPos == std::string::npos)
if (it != end) ++it; break;
while (it != end && *it != ',') v += *it++;
if (it != end) ++it; std::string::size_type commaPos = str.find(',', eqPos);
kvm[k] = v; if (commaPos == std::string::npos)
commaPos = str.length();
// Extract key and value using substr (single allocation each)
std::string key = str.substr(pos, eqPos - pos);
std::string value = str.substr(eqPos + 1, commaPos - eqPos - 1);
kvm[std::move(key)] = std::move(value);
pos = commaPos + 1;
} }
return kvm; return kvm;
} }
@@ -135,93 +142,40 @@ bool Database::authenticate(Connection& connection, const std::string& username,
if (username.empty()) throw Poco::InvalidArgumentException("empty username"); if (username.empty()) throw Poco::InvalidArgumentException("empty username");
if (password.empty()) throw Poco::InvalidArgumentException("empty password"); if (password.empty()) throw Poco::InvalidArgumentException("empty password");
if (method == AUTH_MONGODB_CR) if (method == AUTH_SCRAM_SHA1)
return authCR(connection, username, password);
else if (method == AUTH_SCRAM_SHA1)
return authSCRAM(connection, username, password); return authSCRAM(connection, username, password);
else else
throw Poco::InvalidArgumentException("authentication method", method); throw Poco::InvalidArgumentException("authentication method", method);
} }
bool Database::authCR(Connection& connection, const std::string& username, const std::string& password)
{
std::string nonce;
Poco::SharedPtr<QueryRequest> pCommand = createCommand();
pCommand->selector().add<Poco::Int32>("getnonce", 1);
ResponseMessage response;
connection.sendRequest(*pCommand, response);
if (response.documents().empty())
{
throw Poco::ProtocolException("empty response for getnonce");
}
{
Document::Ptr pDoc = response.documents()[0];
if (pDoc->getInteger("ok") != 1) return false;
nonce = pDoc->get<std::string>("nonce", "");
if (nonce.empty()) throw Poco::ProtocolException("no nonce received");
}
std::string credsDigest = hashCredentials(username, password);
Poco::MD5Engine md5;
md5.update(nonce);
md5.update(username);
md5.update(credsDigest);
std::string key = digestToHexString(md5);
pCommand = createCommand();
pCommand->selector()
.add<Poco::Int32>("authenticate", 1)
.add<std::string>("user", username)
.add<std::string>("nonce", nonce)
.add<std::string>("key", key);
connection.sendRequest(*pCommand, response);
if (!response.documents().empty())
{
Document::Ptr pDoc = response.documents()[0];
return pDoc->getInteger("ok") == 1;
}
throw Poco::ProtocolException("empty response for authenticate");
}
bool Database::authSCRAM(Connection& connection, const std::string& username, const std::string& password) bool Database::authSCRAM(Connection& connection, const std::string& username, const std::string& password)
{ {
std::string clientNonce(createNonce()); std::string clientNonce(createNonce());
std::string clientFirstMsg = Poco::format("n=%s,r=%s", username, clientNonce); std::string clientFirstMsg = Poco::format("n=%s,r=%s", username, clientNonce);
Poco::SharedPtr<QueryRequest> pCommand = createCommand(); Poco::SharedPtr<OpMsgMessage> pCommand = createOpMsgMessage("$cmd");
pCommand->selector() pCommand->setCommandName("saslStart");
.add<Poco::Int32>("saslStart", 1) pCommand->body()
.add<std::string>("mechanism", AUTH_SCRAM_SHA1) .add<std::string>("mechanism", AUTH_SCRAM_SHA1)
.add<Binary::Ptr>("payload", new Binary(Poco::format("n,,%s", clientFirstMsg))) .add<Binary::Ptr>("payload", new Binary(Poco::format("n,,%s", clientFirstMsg)))
.add<bool>("autoAuthorize", true); .add<bool>("autoAuthorize", true);
ResponseMessage response; OpMsgMessage response;
connection.sendRequest(*pCommand, response); connection.sendRequest(*pCommand, response);
Int32 conversationId = 0; Int32 conversationId = 0;
std::string serverFirstMsg; std::string serverFirstMsg;
if (response.documents().empty()) if (!response.responseOk())
{ {
throw Poco::ProtocolException("empty response for saslStart"); return false;
} }
{ {
Document::Ptr pDoc = response.documents()[0]; Document::Ptr pDoc = new Document(response.body());
if (pDoc->getInteger("ok") == 1) Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
{ serverFirstMsg = pPayload->toRawString();
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload"); conversationId = pDoc->get<Int32>("conversationId");
serverFirstMsg = pPayload->toRawString();
conversationId = pDoc->get<Int32>("conversationId");
}
else
{
return false;
}
} }
std::map<std::string, std::string> kvm = parseKeyValueList(serverFirstMsg); std::map<std::string, std::string> kvm = parseKeyValueList(serverFirstMsg);
@@ -259,29 +213,23 @@ bool Database::authSCRAM(Connection& connection, const std::string& username, co
std::string clientFinal = Poco::format("%s,p=%s", clientFinalNoProof, encodeBase64(clientProof)); std::string clientFinal = Poco::format("%s,p=%s", clientFinalNoProof, encodeBase64(clientProof));
pCommand = createCommand(); pCommand = createOpMsgMessage("$cmd");
pCommand->selector() pCommand->setCommandName("saslContinue");
.add<Poco::Int32>("saslContinue", 1) pCommand->body()
.add<Poco::Int32>("conversationId", conversationId) .add<Poco::Int32>("conversationId", conversationId)
.add<Binary::Ptr>("payload", new Binary(clientFinal)); .add<Binary::Ptr>("payload", new Binary(clientFinal));
std::string serverSecondMsg; std::string serverSecondMsg;
connection.sendRequest(*pCommand, response); connection.sendRequest(*pCommand, response);
if (response.documents().empty())
if (!response.responseOk())
{ {
throw Poco::ProtocolException("empty response for saslContinue"); return false;
} }
{ {
Document::Ptr pDoc = response.documents()[0]; Document::Ptr pDoc = new Document(response.body());
if (pDoc->getInteger("ok") == 1) Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
{ serverSecondMsg = pPayload->toRawString();
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
serverSecondMsg = pPayload->toRawString();
}
else
{
return false;
}
} }
Poco::HMACEngine<Poco::SHA1Engine> hmacSKey(saltedPassword); Poco::HMACEngine<Poco::SHA1Engine> hmacSKey(saltedPassword);
@@ -298,39 +246,33 @@ bool Database::authSCRAM(Connection& connection, const std::string& username, co
if (serverSignature != serverSignatureReceived) if (serverSignature != serverSignatureReceived)
throw Poco::ProtocolException("server signature verification failed"); throw Poco::ProtocolException("server signature verification failed");
pCommand = createCommand(); pCommand = createOpMsgMessage("$cmd");
pCommand->selector() pCommand->setCommandName("saslContinue");
.add<Poco::Int32>("saslContinue", 1) pCommand->body()
.add<Poco::Int32>("conversationId", conversationId) .add<Poco::Int32>("conversationId", conversationId)
.add<Binary::Ptr>("payload", new Binary); .add<Binary::Ptr>("payload", new Binary);
connection.sendRequest(*pCommand, response); connection.sendRequest(*pCommand, response);
if (!response.documents().empty()) return response.responseOk();
{
Document::Ptr pDoc = response.documents()[0];
return pDoc->getInteger("ok") == 1;
}
throw Poco::ProtocolException("empty response for saslContinue");
} }
Document::Ptr Database::queryBuildInfo(Connection& connection) const Document::Ptr Database::queryBuildInfo(Connection& connection) const
{ {
// build info can be issued on "config" system database Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = createOpMsgMessage();
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = createCommand(); request->setCommandName(OpMsgMessage::CMD_BUILD_INFO);
request->selector().add("buildInfo", 1);
Poco::MongoDB::ResponseMessage response; Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*request, response); connection.sendRequest(*request, response);
Document::Ptr buildInfo; Document::Ptr buildInfo;
if ( !response.documents().empty() ) if (response.responseOk())
{ {
buildInfo = response.documents()[0]; buildInfo = new Document(response.body());
} }
else else
{ {
throw Poco::ProtocolException("Didn't get a response from the buildinfo command"); throw Poco::ProtocolException("Didn't get a response from the buildInfo command");
} }
return buildInfo; return buildInfo;
} }
@@ -338,17 +280,16 @@ Document::Ptr Database::queryBuildInfo(Connection& connection) const
Document::Ptr Database::queryServerHello(Connection& connection) const Document::Ptr Database::queryServerHello(Connection& connection) const
{ {
// hello can be issued on "config" system database Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = createOpMsgMessage();
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = createCommand(); request->setCommandName(OpMsgMessage::CMD_HELLO);
request->selector().add("hello", 1);
Poco::MongoDB::ResponseMessage response; Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*request, response); connection.sendRequest(*request, response);
Document::Ptr hello; Document::Ptr hello;
if ( response.documents().size() > 0 ) if (response.responseOk())
{ {
hello = response.documents()[0]; hello = new Document(response.body());
} }
else else
{ {
@@ -360,94 +301,77 @@ Document::Ptr Database::queryServerHello(Connection& connection) const
Int64 Database::count(Connection& connection, const std::string& collectionName) const Int64 Database::count(Connection& connection, const std::string& collectionName) const
{ {
Poco::SharedPtr<Poco::MongoDB::QueryRequest> countRequest = createCountRequest(collectionName); Poco::SharedPtr<Poco::MongoDB::OpMsgMessage> request = createOpMsgMessage(collectionName);
request->setCommandName(OpMsgMessage::CMD_COUNT);
Poco::MongoDB::ResponseMessage response; Poco::MongoDB::OpMsgMessage response;
connection.sendRequest(*countRequest, response); connection.sendRequest(*request, response);
if (!response.documents().empty()) if (response.responseOk())
{ {
Poco::MongoDB::Document::Ptr doc = response.documents()[0]; const auto& body = response.body();
return doc->getInteger("n"); return body.getInteger("n");
} }
return -1; return -1;
} }
Poco::MongoDB::Document::Ptr Database::ensureIndex(Connection& connection, const std::string& collection, const std::string& indexName, Poco::MongoDB::Document::Ptr keys, bool unique, bool background, int version, int ttl) Poco::MongoDB::Document::Ptr Database::createIndex(
Connection& connection,
const std::string& collection,
const IndexedFields& indexedFields,
const std::string &indexName,
unsigned long options,
int expirationSeconds,
int version)
{ {
Poco::MongoDB::Document::Ptr index = new Poco::MongoDB::Document(); // https://www.mongodb.com/docs/manual/reference/command/createIndexes/
MongoDB::Document::Ptr keys = new MongoDB::Document();
for (const auto& [name, ascending]: indexedFields) {
keys->add(name, ascending ? 1 : -1);
}
MongoDB::Document::Ptr index = new MongoDB::Document();
if (!indexName.empty())
{
index->add("name", indexName);
}
index->add("key", keys);
index->add("ns", _dbname + "." + collection); index->add("ns", _dbname + "." + collection);
index->add("name", indexName); index->add("name", indexName);
index->add("key", keys);
if (version > 0) if (options & INDEX_UNIQUE) {
{
index->add("version", version);
}
if (unique)
{
index->add("unique", true); index->add("unique", true);
} }
if (options & INDEX_BACKGROUND) {
if (background)
{
index->add("background", true); index->add("background", true);
} }
if (options & INDEX_SPARSE) {
if (ttl > 0) index->add("sparse", true);
{ }
index->add("expireAfterSeconds", ttl); if (expirationSeconds > 0) {
index->add("expireAfterSeconds", static_cast<Poco::Int32>(expirationSeconds));
}
if (version > 0) {
index->add("version", static_cast<Poco::Int32>(version));
} }
Poco::SharedPtr<Poco::MongoDB::InsertRequest> insertRequest = createInsertRequest("system.indexes"); MongoDB::Array::Ptr indexes = new MongoDB::Array();
insertRequest->documents().push_back(index); indexes->add(index);
connection.sendRequest(*insertRequest);
return getLastErrorDoc(connection); auto request = createOpMsgMessage(collection);
} request->setCommandName(OpMsgMessage::CMD_CREATE_INDEXES);
request->body().add("indexes", indexes);
OpMsgMessage response;
Document::Ptr Database::getLastErrorDoc(Connection& connection) const
{
Document::Ptr errorDoc;
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = createCommand();
request->setNumberToReturn(1);
request->selector().add("getLastError", 1);
Poco::MongoDB::ResponseMessage response;
connection.sendRequest(*request, response); connection.sendRequest(*request, response);
if (!response.documents().empty()) MongoDB::Document::Ptr result = new MongoDB::Document(response.body());
{
errorDoc = response.documents()[0];
}
return errorDoc; return result;
}
std::string Database::getLastError(Connection& connection) const
{
Document::Ptr errorDoc = getLastErrorDoc(connection);
if (!errorDoc.isNull() && errorDoc->isType<std::string>("err"))
{
return errorDoc->get<std::string>("err");
}
return "";
}
Poco::SharedPtr<Poco::MongoDB::QueryRequest> Database::createCountRequest(const std::string& collectionName) const
{
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = createCommand();
request->setNumberToReturn(1);
request->selector().add("count", collectionName);
return request;
} }

View File

@@ -1,54 +0,0 @@
//
// DeleteRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: DeleteRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/DeleteRequest.h"
namespace Poco {
namespace MongoDB {
DeleteRequest::DeleteRequest(const std::string& collectionName, DeleteRequest::Flags flags):
RequestMessage(MessageHeader::OP_DELETE),
_flags(flags),
_fullCollectionName(collectionName),
_selector()
{
}
DeleteRequest::DeleteRequest(const std::string& collectionName, bool justOne):
RequestMessage(MessageHeader::OP_DELETE),
_flags(justOne ? DELETE_SINGLE_REMOVE : DELETE_DEFAULT),
_fullCollectionName(collectionName),
_selector()
{
}
DeleteRequest::~DeleteRequest()
{
}
void DeleteRequest::buildRequest(BinaryWriter& writer)
{
writer << 0; // 0 - reserved for future use
BSONWriter(writer).writeCString(_fullCollectionName);
writer << _flags;
_selector.write(writer);
}
} } // namespace Poco::MongoDB

View File

@@ -45,15 +45,14 @@ Array& Document::addNewArray(const std::string& name)
Element::Ptr Document::get(const std::string& name) const Element::Ptr Document::get(const std::string& name) const
{ {
Element::Ptr element; // O(1) hash map lookup instead of O(n) linear search
auto it = _elementMap.find(name);
auto it = std::find_if(_elements.begin(), _elements.end(), ElementFindByName(name)); if (it != _elementMap.end())
if (it != _elements.end())
{ {
return *it; return it->second;
} }
return element; return Element::Ptr(); // Return empty pointer if not found
} }
@@ -64,18 +63,19 @@ Int64 Document::getInteger(const std::string& name) const
if (ElementTraits<double>::TypeId == element->type()) if (ElementTraits<double>::TypeId == element->type())
{ {
ConcreteElement<double>* concrete = dynamic_cast<ConcreteElement<double>*>(element.get()); // Type is already verified, static_cast is safe and ~10x faster than dynamic_cast
if (concrete) return static_cast<Int64>(concrete->value()); auto* concrete = static_cast<ConcreteElement<double>*>(element.get());
return static_cast<Int64>(concrete->value());
} }
else if (ElementTraits<Int32>::TypeId == element->type()) else if (ElementTraits<Int32>::TypeId == element->type())
{ {
ConcreteElement<Int32>* concrete = dynamic_cast<ConcreteElement<Int32>*>(element.get()); auto* concrete = static_cast<ConcreteElement<Int32>*>(element.get());
if (concrete) return concrete->value(); return concrete->value();
} }
else if (ElementTraits<Int64>::TypeId == element->type()) else if (ElementTraits<Int64>::TypeId == element->type())
{ {
ConcreteElement<Int64>* concrete = dynamic_cast<ConcreteElement<Int64>*>(element.get()); auto* concrete = static_cast<ConcreteElement<Int64>*>(element.get());
if (concrete) return concrete->value(); return concrete->value();
} }
throw Poco::BadCastException("Invalid type mismatch!"); throw Poco::BadCastException("Invalid type mismatch!");
} }
@@ -152,6 +152,7 @@ void Document::read(BinaryReader& reader)
element->read(reader); element->read(reader);
_elements.push_back(element); _elements.push_back(element);
_elementMap[element->name()] = element; // Populate hash map for O(1) lookups
reader >> type; reader >> type;
} }
@@ -161,12 +162,13 @@ void Document::read(BinaryReader& reader)
std::string Document::toString(int indent) const std::string Document::toString(int indent) const
{ {
std::ostringstream oss; std::ostringstream oss;
// Pre-reserve reasonable capacity for small-medium documents to reduce reallocations
oss.str().reserve(256);
oss << '{'; oss << '{';
if (indent > 0) oss << std::endl; if (indent > 0) oss << std::endl;
for (auto it = _elements.begin(), total = _elements.end(); it != total; ++it) for (auto it = _elements.begin(), total = _elements.end(); it != total; ++it)
{ {
if (it != _elements.begin()) if (it != _elements.begin())
@@ -175,7 +177,11 @@ std::string Document::toString(int indent) const
if (indent > 0) oss << std::endl; if (indent > 0) oss << std::endl;
} }
for (int i = 0; i < indent; ++i) oss << ' '; if (indent > 0)
{
const std::string indentStr(indent, ' ');
oss << indentStr;
}
oss << '"' << (*it)->name() << '"'; oss << '"' << (*it)->name() << '"';
oss << (indent > 0 ? " : " : ":"); oss << (indent > 0 ? " : " : ":");
@@ -188,7 +194,8 @@ std::string Document::toString(int indent) const
oss << std::endl; oss << std::endl;
if (indent >= 2) indent -= 2; if (indent >= 2) indent -= 2;
for (int i = 0; i < indent; ++i) oss << ' '; const std::string indentStr(indent, ' ');
oss << indentStr;
} }
oss << '}'; oss << '}';

View File

@@ -24,6 +24,11 @@ Element::Element(const std::string& name) : _name(name)
} }
Element::Element(std::string&& name) : _name(std::move(name))
{
}
Element::~Element() Element::~Element()
{ {
} }

View File

@@ -1,46 +0,0 @@
//
// GetMoreRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: GetMoreRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/GetMoreRequest.h"
#include "Poco/MongoDB/Element.h"
namespace Poco {
namespace MongoDB {
GetMoreRequest::GetMoreRequest(const std::string& collectionName, Int64 cursorID):
RequestMessage(MessageHeader::OP_GET_MORE),
_fullCollectionName(collectionName),
_numberToReturn(100),
_cursorID(cursorID)
{
}
GetMoreRequest::~GetMoreRequest()
{
}
void GetMoreRequest::buildRequest(BinaryWriter& writer)
{
writer << 0; // 0 - reserved for future use
BSONWriter(writer).writeCString(_fullCollectionName);
writer << _numberToReturn;
writer << _cursorID;
}
} } // namespace Poco::MongoDB

View File

@@ -1,49 +0,0 @@
//
// InsertRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: InsertRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/InsertRequest.h"
namespace Poco {
namespace MongoDB {
InsertRequest::InsertRequest(const std::string& collectionName, Flags flags):
RequestMessage(MessageHeader::OP_INSERT),
_flags(flags),
_fullCollectionName(collectionName)
{
}
InsertRequest::~InsertRequest()
{
}
void InsertRequest::buildRequest(BinaryWriter& writer)
{
poco_assert (!_documents.empty());
writer << _flags;
BSONWriter bsonWriter(writer);
bsonWriter.writeCString(_fullCollectionName);
for (Document::Vector::iterator it = _documents.begin(); it != _documents.end(); ++it)
{
bsonWriter.write(*it);
}
}
} } // namespace Poco::MongoDB

View File

@@ -1,44 +0,0 @@
//
// KillCursorsRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: KillCursorsRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/KillCursorsRequest.h"
namespace Poco {
namespace MongoDB {
KillCursorsRequest::KillCursorsRequest():
RequestMessage(MessageHeader::OP_KILL_CURSORS)
{
}
KillCursorsRequest::~KillCursorsRequest()
{
}
void KillCursorsRequest::buildRequest(BinaryWriter& writer)
{
writer << 0; // 0 - reserved for future use
writer << static_cast<Poco::UInt64>(_cursors.size());
for (std::vector<Int64>::iterator it = _cursors.begin(); it != _cursors.end(); ++it)
{
writer << *it;
}
}
} } // namespace Poco::MongoDB

View File

@@ -40,12 +40,38 @@ ObjectId::ObjectId(const std::string& id)
} }
ObjectId::ObjectId(const ObjectId& copy) ObjectId::ObjectId(const ObjectId& copy) noexcept
{ {
std::memcpy(_id, copy._id, sizeof(_id)); std::memcpy(_id, copy._id, sizeof(_id));
} }
ObjectId::ObjectId(ObjectId&& other) noexcept
{
std::memcpy(_id, other._id, sizeof(_id));
}
ObjectId& ObjectId::operator=(const ObjectId& copy) noexcept
{
if (this != &copy)
{
std::memcpy(_id, copy._id, sizeof(_id));
}
return *this;
}
ObjectId& ObjectId::operator=(ObjectId&& other) noexcept
{
if (this != &other)
{
std::memcpy(_id, other._id, sizeof(_id));
}
return *this;
}
ObjectId::~ObjectId() ObjectId::~ObjectId()
{ {
} }
@@ -54,10 +80,11 @@ ObjectId::~ObjectId()
std::string ObjectId::toString(const std::string& fmt) const std::string ObjectId::toString(const std::string& fmt) const
{ {
std::string s; std::string s;
s.reserve(24); // Pre-allocate for 12 bytes * 2 hex chars
for (int i = 0; i < 12; ++i) for (std::size_t i = 0; i < 12; ++i)
{ {
s += format(fmt, (unsigned int) _id[i]); s += format(fmt, static_cast<unsigned int>(_id[i]));
} }
return s; return s;
} }

View File

@@ -65,7 +65,7 @@ OpMsgCursor::~OpMsgCursor()
{ {
try try
{ {
poco_assert_dbg(_cursorID == 0); poco_assert_msg_dbg(_cursorID == 0, "OpMsgCursor destroyed with active cursor - call kill() before destruction");
} }
catch (...) catch (...)
{ {
@@ -73,31 +73,31 @@ OpMsgCursor::~OpMsgCursor()
} }
void OpMsgCursor::setEmptyFirstBatch(bool empty) void OpMsgCursor::setEmptyFirstBatch(bool empty) noexcept
{ {
_emptyFirstBatch = empty; _emptyFirstBatch = empty;
} }
bool OpMsgCursor::emptyFirstBatch() const bool OpMsgCursor::emptyFirstBatch() const noexcept
{ {
return _emptyFirstBatch; return _emptyFirstBatch;
} }
void OpMsgCursor::setBatchSize(Int32 batchSize) void OpMsgCursor::setBatchSize(Int32 batchSize) noexcept
{ {
_batchSize = batchSize; _batchSize = batchSize;
} }
Int32 OpMsgCursor::batchSize() const Int32 OpMsgCursor::batchSize() const noexcept
{ {
return _batchSize; return _batchSize;
} }
bool OpMsgCursor::isActive() const bool OpMsgCursor::isActive() const noexcept
{ {
const auto& cmd {_query.commandName()}; const auto& cmd {_query.commandName()};
return ( _cursorID > 0 || (!cmd.empty() && cmd != OpMsgMessage::CMD_GET_MORE) ); return ( _cursorID > 0 || (!cmd.empty() && cmd != OpMsgMessage::CMD_GET_MORE) );

View File

@@ -242,8 +242,10 @@ void OpMsgMessage::send(std::ostream& ostr)
if (!_documents.empty()) if (!_documents.empty())
{ {
// Serialise attached documents // Serialise attached documents directly to main stream to avoid extra buffer copy
const std::string& identifier = commandIdentifier(_commandName);
// Write documents to temporary buffer (still needed to calculate size)
std::stringstream ssdoc; std::stringstream ssdoc;
BinaryWriter wdoc(ssdoc, BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER); BinaryWriter wdoc(ssdoc, BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
for (auto& doc: _documents) for (auto& doc: _documents)
@@ -252,12 +254,14 @@ void OpMsgMessage::send(std::ostream& ostr)
} }
wdoc.flush(); wdoc.flush();
const std::string& identifier = commandIdentifier(_commandName);
const Poco::Int32 size = static_cast<Poco::Int32>(sizeof(size) + identifier.size() + 1 + ssdoc.tellp()); const Poco::Int32 size = static_cast<Poco::Int32>(sizeof(size) + identifier.size() + 1 + ssdoc.tellp());
writer << PAYLOAD_TYPE_1; writer << PAYLOAD_TYPE_1;
writer << size; writer << size;
writer.writeCString(identifier.c_str()); writer.writeCString(identifier.c_str());
StreamCopier::copyStream(ssdoc, ss);
// Use writeRaw instead of copyStream for better performance
const std::string& docData = ssdoc.str();
ss.write(docData.data(), docData.size());
} }
writer.flush(); writer.flush();
@@ -271,8 +275,10 @@ void OpMsgMessage::send(std::ostream& ostr)
messageLength(static_cast<Poco::Int32>(ss.tellp())); messageLength(static_cast<Poco::Int32>(ss.tellp()));
_header.write(socketWriter); _header.write(socketWriter);
StreamCopier::copyStream(ss, ostr);
// Write directly instead of using StreamCopier for better performance
const std::string& msgData = ss.str();
ostr.write(msgData.data(), msgData.size());
ostr.flush(); ostr.flush();
} }
@@ -377,6 +383,9 @@ void OpMsgMessage::read(std::istream& istr)
} }
if (batch) if (batch)
{ {
// Reserve space to avoid reallocations
_documents.reserve(_documents.size() + batch->size());
for(std::size_t i = 0; i < batch->size(); i++) for(std::size_t i = 0; i < batch->size(); i++)
{ {
const auto& d = batch->get<MongoDB::Document::Ptr>(i, nullptr); const auto& d = batch->get<MongoDB::Document::Ptr>(i, nullptr);

View File

@@ -1,54 +0,0 @@
//
// QueryRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: QueryRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/QueryRequest.h"
namespace Poco {
namespace MongoDB {
QueryRequest::QueryRequest(const std::string& collectionName, QueryRequest::Flags flags):
RequestMessage(MessageHeader::OP_QUERY),
_flags(flags),
_fullCollectionName(collectionName),
_numberToSkip(0),
_numberToReturn(100),
_selector(),
_returnFieldSelector()
{
}
QueryRequest::~QueryRequest()
{
}
void QueryRequest::buildRequest(BinaryWriter& writer)
{
writer << _flags;
BSONWriter(writer).writeCString(_fullCollectionName);
writer << _numberToSkip;
writer << _numberToReturn;
_selector.write(writer);
if (!_returnFieldSelector.empty())
{
_returnFieldSelector.write(writer);
}
}
} } // namespace Poco::MongoDB

View File

@@ -32,6 +32,13 @@ RegularExpression::RegularExpression(const std::string& pattern, const std::stri
} }
RegularExpression::RegularExpression(std::string&& pattern, std::string&& options):
_pattern(std::move(pattern)),
_options(std::move(options))
{
}
RegularExpression::~RegularExpression() RegularExpression::~RegularExpression()
{ {
} }
@@ -40,9 +47,9 @@ RegularExpression::~RegularExpression()
SharedPtr<Poco::RegularExpression> RegularExpression::createRE() const SharedPtr<Poco::RegularExpression> RegularExpression::createRE() const
{ {
int options = 0; int options = 0;
for (std::string::const_iterator optIt = _options.begin(); optIt != _options.end(); ++optIt) for (char opt : _options)
{ {
switch (*optIt) switch (opt)
{ {
case 'i': // Case Insensitive case 'i': // Case Insensitive
options |= Poco::RegularExpression::RE_CASELESS; options |= Poco::RegularExpression::RE_CASELESS;

View File

@@ -13,8 +13,7 @@
#include "Poco/MongoDB/ReplicaSet.h" #include "Poco/MongoDB/ReplicaSet.h"
#include "Poco/MongoDB/QueryRequest.h" #include "Poco/MongoDB/OpMsgMessage.h"
#include "Poco/MongoDB/ResponseMessage.h"
namespace Poco { namespace Poco {
@@ -57,23 +56,22 @@ Connection::Ptr ReplicaSet::isMaster(const Net::SocketAddress& address)
{ {
conn->connect(address); conn->connect(address);
QueryRequest request("admin.$cmd"); OpMsgMessage request("admin", "");
request.setNumberToReturn(1); request.setCommandName(OpMsgMessage::CMD_HELLO);
request.selector().add("isMaster", 1);
ResponseMessage response; OpMsgMessage response;
conn->sendRequest(request, response); conn->sendRequest(request, response);
if (response.documents().size() > 0) if (response.responseOk())
{ {
Document::Ptr doc = response.documents()[0]; const Document& doc = response.body();
if (doc->get<bool>("ismaster")) if (doc.get<bool>("isWritablePrimary", false) || doc.get<bool>("ismaster", false))
{ {
return conn; return conn;
} }
else if (doc->exists("primary")) else if (doc.exists("primary"))
{ {
return isMaster(Net::SocketAddress(doc->get<std::string>("primary"))); return isMaster(Net::SocketAddress(doc.get<std::string>("primary")));
} }
} }
} }

View File

@@ -1,51 +0,0 @@
//
// RequestMessage.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: RequestMessage
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/RequestMessage.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/StreamCopier.h"
namespace Poco {
namespace MongoDB {
RequestMessage::RequestMessage(MessageHeader::OpCode opcode):
Message(opcode)
{
}
RequestMessage::~RequestMessage()
{
}
void RequestMessage::send(std::ostream& ostr)
{
std::stringstream ss;
BinaryWriter requestWriter(ss, BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
buildRequest(requestWriter);
requestWriter.flush();
messageLength(static_cast<Poco::Int32>(ss.tellp()));
BinaryWriter socketWriter(ostr, BinaryWriter::LITTLE_ENDIAN_BYTE_ORDER);
_header.write(socketWriter);
StreamCopier::copyStream(ss, ostr);
ostr.flush();
}
} } // namespace Poco::MongoDB

View File

@@ -1,80 +0,0 @@
//
// ResponseMessage.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: ResponseMessage
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/ResponseMessage.h"
#include "Poco/Net/SocketStream.h"
namespace Poco {
namespace MongoDB {
ResponseMessage::ResponseMessage():
Message(MessageHeader::OP_REPLY),
_responseFlags(0),
_cursorID(0),
_startingFrom(0),
_numberReturned(0)
{
}
ResponseMessage::ResponseMessage(const Int64& cursorID):
Message(MessageHeader::OP_REPLY),
_responseFlags(0),
_cursorID(cursorID),
_startingFrom(0),
_numberReturned(0)
{
}
ResponseMessage::~ResponseMessage()
{
}
void ResponseMessage::clear()
{
_responseFlags = 0;
_startingFrom = 0;
_cursorID = 0;
_numberReturned = 0;
_documents.clear();
}
void ResponseMessage::read(std::istream& istr)
{
clear();
BinaryReader reader(istr, BinaryReader::LITTLE_ENDIAN_BYTE_ORDER);
_header.read(reader);
reader >> _responseFlags;
reader >> _cursorID;
reader >> _startingFrom;
reader >> _numberReturned;
for (int i = 0; i < _numberReturned; ++i)
{
Document::Ptr doc = new Document();
doc->read(reader);
_documents.push_back(doc);
}
}
} } // namespace Poco::MongoDB

View File

@@ -1,47 +0,0 @@
//
// UpdateRequest.cpp
//
// Library: MongoDB
// Package: MongoDB
// Module: UpdateRequest
//
// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/MongoDB/UpdateRequest.h"
namespace Poco {
namespace MongoDB {
UpdateRequest::UpdateRequest(const std::string& collectionName, UpdateRequest::Flags flags):
RequestMessage(MessageHeader::OP_UPDATE),
_flags(flags),
_fullCollectionName(collectionName),
_selector(),
_update()
{
}
UpdateRequest::~UpdateRequest()
{
}
void UpdateRequest::buildRequest(BinaryWriter& writer)
{
writer << 0; // 0 - reserved for future use
BSONWriter(writer).writeCString(_fullCollectionName);
writer << _flags;
_selector.write(writer);
_update.write(writer);
}
} } // namespace Poco::MongoDB

View File

@@ -11,14 +11,11 @@
#include "Poco/DateTime.h" #include "Poco/DateTime.h"
#include "Poco/ObjectPool.h" #include "Poco/ObjectPool.h"
#include "Poco/MongoDB/Array.h" #include "Poco/MongoDB/Array.h"
#include "Poco/MongoDB/InsertRequest.h"
#include "Poco/MongoDB/QueryRequest.h"
#include "Poco/MongoDB/DeleteRequest.h"
#include "Poco/MongoDB/GetMoreRequest.h"
#include "Poco/MongoDB/PoolableConnectionFactory.h" #include "Poco/MongoDB/PoolableConnectionFactory.h"
#include "Poco/MongoDB/Database.h" #include "Poco/MongoDB/Database.h"
#include "Poco/MongoDB/Connection.h" #include "Poco/MongoDB/Connection.h"
#include "Poco/MongoDB/Cursor.h" #include "Poco/MongoDB/OpMsgMessage.h"
#include "Poco/MongoDB/OpMsgCursor.h"
#include "Poco/MongoDB/ObjectId.h" #include "Poco/MongoDB/ObjectId.h"
#include "Poco/MongoDB/Binary.h" #include "Poco/MongoDB/Binary.h"
#include "Poco/Net/NetException.h" #include "Poco/Net/NetException.h"
@@ -57,29 +54,6 @@ void MongoDBTest::tearDown()
} }
void MongoDBTest::testInsertRequest()
{
Poco::MongoDB::Document::Ptr player = new Poco::MongoDB::Document();
player->add("lastname", std::string("Braem"));
player->add("firstname", std::string("Franky"));
Poco::DateTime birthdate;
birthdate.assign(1969, 3, 9);
player->add("birthdate", birthdate.timestamp());
player->add("start", 1993);
player->add("active", false);
Poco::DateTime now;
player->add("lastupdated", now.timestamp());
player->add("unknown", NullValue());
Poco::MongoDB::InsertRequest request("team.players");
request.documents().push_back(player);
_mongo->sendRequest(request);
}
void MongoDBTest::testArray() void MongoDBTest::testArray()
{ {
Poco::MongoDB::Array::Ptr arr = new Poco::MongoDB::Array(); Poco::MongoDB::Array::Ptr arr = new Poco::MongoDB::Array();
@@ -117,190 +91,8 @@ void MongoDBTest::testArray()
} }
void MongoDBTest::testQueryRequest()
{
Poco::MongoDB::QueryRequest request("team.players");
request.selector().add("lastname" , std::string("Braem"));
request.setNumberToReturn(1);
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
try
{
std::string lastname = doc->get<std::string>("lastname");
assertTrue (lastname.compare("Braem") == 0);
std::string firstname = doc->get<std::string>("firstname");
assertTrue (firstname.compare("Franky") == 0);
Poco::Timestamp birthDateTimestamp = doc->get<Poco::Timestamp>("birthdate");
Poco::DateTime birthDate(birthDateTimestamp);
assertTrue (birthDate.year() == 1969 && birthDate.month() == 3 && birthDate.day() == 9);
Poco::Timestamp lastupdatedTimestamp = doc->get<Poco::Timestamp>("lastupdated");
assertTrue (doc->isType<NullValue>("unknown"));
bool active = doc->get<bool>("active");
assertTrue (!active);
std::string id = doc->get("_id")->toString();
}
catch(Poco::NotFoundException& nfe)
{
fail(nfe.message() + " not found.");
}
}
else
{
fail("No document returned");
}
}
void MongoDBTest::testDBQueryRequest()
{
Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = db.createQueryRequest("players");
request->selector().add("lastname" , std::string("Braem"));
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(*request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
try
{
std::string lastname = doc->get<std::string>("lastname");
assertTrue (lastname.compare("Braem") == 0);
std::string firstname = doc->get<std::string>("firstname");
assertTrue (firstname.compare("Franky") == 0);
Poco::Timestamp birthDateTimestamp = doc->get<Poco::Timestamp>("birthdate");
Poco::DateTime birthDate(birthDateTimestamp);
assertTrue (birthDate.year() == 1969 && birthDate.month() == 3 && birthDate.day() == 9);
Poco::Timestamp lastupdatedTimestamp = doc->get<Poco::Timestamp>("lastupdated");
assertTrue (doc->isType<NullValue>("unknown"));
std::string id = doc->get("_id")->toString();
}
catch(Poco::NotFoundException& nfe)
{
fail(nfe.message() + " not found.");
}
}
else
{
fail("No document returned");
}
}
void MongoDBTest::testCountCommand()
{
Poco::MongoDB::QueryRequest request("team.$cmd");
request.setNumberToReturn(1);
request.selector().add("count", std::string("players"));
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
assertTrue (doc->getInteger("n") == 1);
}
else
{
fail("Didn't get a response from the count command");
}
}
void MongoDBTest::testDBCountCommand()
{
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> request = db.createCountRequest("players");
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(*request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
assertTrue (doc->getInteger("n") == 1);
}
else
{
fail("Didn't get a response from the count command");
}
}
void MongoDBTest::testDBCount2Command()
{
Poco::MongoDB::Database db("team");
Poco::Int64 count = db.count(*_mongo, "players");
assertTrue (count == 1);
}
void MongoDBTest::testDeleteRequest()
{
Poco::MongoDB::DeleteRequest request("team.players");
request.selector().add("lastname", std::string("Braem"));
_mongo->sendRequest(request);
}
void MongoDBTest::testCursorRequest()
{
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::DeleteRequest> deleteRequest = db.createDeleteRequest("numbers");
_mongo->sendRequest(*deleteRequest);
Poco::SharedPtr<Poco::MongoDB::InsertRequest> insertRequest = db.createInsertRequest("numbers");
for(int i = 0; i < 10000; ++i)
{
Document::Ptr doc = new Document();
doc->add("number", i);
insertRequest->documents().push_back(doc);
}
_mongo->sendRequest(*insertRequest);
Poco::Int64 count = db.count(*_mongo, "numbers");
assertTrue (count == 10000);
Poco::MongoDB::Cursor cursor("team", "numbers");
int n = 0;
Poco::MongoDB::ResponseMessage& response = cursor.next(*_mongo);
while(1)
{
n += static_cast<int>(response.documents().size());
if ( response.cursorID() == 0 )
break;
response = cursor.next(*_mongo);
}
assertTrue (n == 10000);
Poco::MongoDB::QueryRequest drop("team.$cmd");
drop.setNumberToReturn(1);
drop.selector().add("drop", std::string("numbers"));
Poco::MongoDB::ResponseMessage responseDrop;
_mongo->sendRequest(drop, responseDrop);
}
void MongoDBTest::testBuildInfo() void MongoDBTest::testBuildInfo()
{ {
// build info can be issued on "config" system database
Poco::MongoDB::Database db("config"); Poco::MongoDB::Database db("config");
try try
{ {
@@ -328,39 +120,6 @@ void MongoDBTest::testHello()
} }
void MongoDBTest::testConnectionPool()
{
#if POCO_OS == POCO_OS_ANDROID
std::string host = "10.0.2.2";
#else
std::string host = "127.0.0.1";
#endif
Poco::Net::SocketAddress sa(host, 27017);
Poco::PoolableObjectFactory<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> factory(sa);
Poco::ObjectPool<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> pool(factory, 10, 15);
Poco::MongoDB::PooledConnection pooledConnection(pool);
Poco::MongoDB::QueryRequest request("team.$cmd");
request.setNumberToReturn(1);
request.selector().add("count", std::string("players"));
Poco::MongoDB::ResponseMessage response;
((Connection::Ptr) pooledConnection)->sendRequest(request, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
assertTrue (doc->getInteger("n") == 1);
}
else
{
fail("Didn't get a response from the count command");
}
}
void MongoDBTest::testObjectID() void MongoDBTest::testObjectID()
{ {
ObjectId oid("536aeebba081de6815000002"); ObjectId oid("536aeebba081de6815000002");
@@ -369,77 +128,6 @@ void MongoDBTest::testObjectID()
} }
void MongoDBTest::testCommand() {
Poco::MongoDB::Database db("team");
Poco::SharedPtr<Poco::MongoDB::QueryRequest> command = db.createCommand();
command->selector().add("create", "fixCol")
.add("capped", true)
.add("max", 1024*1024)
.add("size", 1024);
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(*command, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
}
else
{
Poco::MongoDB::Document::Ptr lastError = db.getLastErrorDoc(*_mongo);
fail(lastError->toString(2));
}
}
void MongoDBTest::testUUID()
{
Poco::MongoDB::Document::Ptr club = new Poco::MongoDB::Document();
club->add("name", std::string("Barcelona"));
Poco::UUIDGenerator generator;
Poco::UUID uuid = generator.create();
Poco::MongoDB::Binary::Ptr uuidBinary = new Poco::MongoDB::Binary(uuid);
club->add("uuid", uuidBinary);
Poco::MongoDB::InsertRequest request("team.club");
request.documents().push_back(club);
_mongo->sendRequest(request);
Poco::MongoDB::QueryRequest queryReq("team.club");
queryReq.selector().add("name" , std::string("Barcelona"));
Poco::MongoDB::ResponseMessage response;
_mongo->sendRequest(queryReq, response);
if ( response.documents().size() > 0 )
{
Poco::MongoDB::Document::Ptr doc = response.documents()[0];
try
{
std::string name = doc->get<std::string>("name");
assertTrue (name.compare("Barcelona") == 0);
Poco::MongoDB::Binary::Ptr uuidBinary = doc->get<Binary::Ptr>("uuid");
assertTrue (uuid == uuidBinary->uuid());
}
catch(Poco::NotFoundException& nfe)
{
fail(nfe.message() + " not found.");
}
}
else
{
fail("No document returned");
}
Poco::MongoDB::DeleteRequest delRequest("team.club");
delRequest.selector().add("name", std::string("Barcelona"));
_mongo->sendRequest(delRequest);
}
void MongoDBTest::testConnectURI() void MongoDBTest::testConnectURI()
{ {
Poco::MongoDB::Connection conn; Poco::MongoDB::Connection conn;
@@ -491,6 +179,36 @@ void MongoDBTest::testConnectURI()
} }
void MongoDBTest::testDBCount()
{
// First insert some documents
Database db("team");
Poco::SharedPtr<OpMsgMessage> request = db.createOpMsgMessage("players");
request->setCommandName(OpMsgMessage::CMD_INSERT);
Document::Ptr player = new Document();
player->add("lastname", std::string("TestPlayer"));
player->add("firstname", std::string("Test"));
request->documents().push_back(player);
OpMsgMessage response;
_mongo->sendRequest(*request, response);
assertTrue(response.responseOk());
// Now test the count method
Poco::Int64 count = db.count(*_mongo, "players");
assertTrue (count >= 1);
// Cleanup
request = db.createOpMsgMessage("players");
request->setCommandName(OpMsgMessage::CMD_DELETE);
Document::Ptr del = new Document();
del->add("limit", 0).addNewDocument("q").add("lastname" , std::string("TestPlayer"));
request->documents().push_back(del);
_mongo->sendRequest(*request, response);
}
CppUnit::Test* MongoDBTest::suite() CppUnit::Test* MongoDBTest::suite()
{ {
#if POCO_OS == POCO_OS_ANDROID #if POCO_OS == POCO_OS_ANDROID
@@ -522,23 +240,6 @@ CppUnit::Test* MongoDBTest::suite()
CppUnit_addTest(pSuite, MongoDBTest, testHello); CppUnit_addTest(pSuite, MongoDBTest, testHello);
CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo); CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo);
if (_wireVersion < Poco::MongoDB::Database::VER_51)
{
// Database supports old wire protocol
CppUnit_addTest(pSuite, MongoDBTest, testInsertRequest);
CppUnit_addTest(pSuite, MongoDBTest, testQueryRequest);
CppUnit_addTest(pSuite, MongoDBTest, testDBQueryRequest);
CppUnit_addTest(pSuite, MongoDBTest, testCountCommand);
CppUnit_addTest(pSuite, MongoDBTest, testDBCountCommand);
CppUnit_addTest(pSuite, MongoDBTest, testDBCount2Command);
CppUnit_addTest(pSuite, MongoDBTest, testConnectionPool);
CppUnit_addTest(pSuite, MongoDBTest, testDeleteRequest);
CppUnit_addTest(pSuite, MongoDBTest, testCursorRequest);
CppUnit_addTest(pSuite, MongoDBTest, testCommand);
CppUnit_addTest(pSuite, MongoDBTest, testUUID);
}
if (_wireVersion >= Poco::MongoDB::Database::VER_36) if (_wireVersion >= Poco::MongoDB::Database::VER_36)
{ {
// Database supports OP_MSG wire protocol // Database supports OP_MSG wire protocol
@@ -560,6 +261,8 @@ CppUnit::Test* MongoDBTest::suite()
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdUUID); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdUUID);
CppUnit_addTest(pSuite, MongoDBTest, testDBCount);
CppUnit_addTest(pSuite, MongoDBTest, testOpCmdDropDatabase); CppUnit_addTest(pSuite, MongoDBTest, testOpCmdDropDatabase);
} }

View File

@@ -35,21 +35,7 @@ public:
void testHello(); void testHello();
void testConnectURI(); void testConnectURI();
// Old wire protocol // OP_MSG wire protocol
void testInsertRequest();
void testQueryRequest();
void testDBQueryRequest();
void testCountCommand();
void testDBCountCommand();
void testDBCount2Command();
void testDeleteRequest();
void testConnectionPool();
void testCursorRequest();
void testCommand();
void testUUID();
// New wire protocol using OP_CMD
void testOpCmdUUID(); void testOpCmdUUID();
void testOpCmdHello(); void testOpCmdHello();
void testOpCmdWriteRead(); void testOpCmdWriteRead();
@@ -64,6 +50,7 @@ public:
void testOpCmdUnaknowledgedInsert(); void testOpCmdUnaknowledgedInsert();
void testOpCmdConnectionPool(); void testOpCmdConnectionPool();
void testOpCmdDropDatabase(); void testOpCmdDropDatabase();
void testDBCount();
static CppUnit::Test* suite(); static CppUnit::Test* suite();