first commit

This commit is contained in:
bing
2026-04-03 11:32:07 +08:00
commit 003be19522
1142 changed files with 185854 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
cmake_minimum_required(VERSION 3.1)
project(qxBlogMongoDB LANGUAGES CXX)
include(../../QxOrm.cmake)
if(_QX_ENABLE_MONGODB)
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_DEBUG_POSTFIX "d")
set(HEADERS
./include/precompiled.h
./include/export.h
./include/author.h
./include/blog.h
./include/category.h
./include/comment.h
./include/TestQtProperty.h
)
set(SRCS
./src/author.cpp
./src/blog.cpp
./src/category.cpp
./src/comment.cpp
./src/TestQtProperty.cpp
./src/main.cpp
)
add_executable(qxBlogMongoDB ${SRCS} ${HEADERS})
target_compile_definitions(qxBlogMongoDB PRIVATE -D_BUILDING_QX_BLOG)
if(COMMAND target_precompile_headers)
target_precompile_headers(qxBlogMongoDB PRIVATE ./include/precompiled.h)
endif() # (COMMAND target_precompile_headers)
target_link_libraries(qxBlogMongoDB ${QX_LIBRARIES} QxOrm)
set_target_properties(qxBlogMongoDB PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
ARCHIVE_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../_bin"
)
set_target_properties(qxBlogMongoDB PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
else() # _QX_ENABLE_MONGODB
message(STATUS "qxBlogMongoDB project not loaded because _QX_ENABLE_MONGODB option not enabled (QxOrm MongoDB database driver is not available)")
endif() # _QX_ENABLE_MONGODB

7
test/qxBlogMongoDB/debug/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
# git does not allow empty directories.
# Yet, we need to add this empty directory on git.
# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it.
# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this.
*
# And then add an exception for this specifc file (so that we can commit it).
!.gitignore

View File

@@ -0,0 +1,55 @@
#ifndef _QX_TEST_QT_META_PROPERTY_H_
#define _QX_TEST_QT_META_PROPERTY_H_
#ifdef _MSC_VER
#pragma once
#endif
#ifdef _QX_NO_PRECOMPILED_HEADER
#ifndef Q_MOC_RUN
#include "../include/precompiled.h" // Need to include precompiled header for the generated moc file
#endif // Q_MOC_RUN
#endif // _QX_NO_PRECOMPILED_HEADER
class QX_BLOG_DLL_EXPORT TestQtProperty : public QObject, public qx::IxPersistable
{
Q_OBJECT
QX_PERSISTABLE_HPP(TestQtProperty)
Q_PROPERTY(QString id READ id WRITE setId)
Q_PROPERTY(long number READ number WRITE setNumber)
Q_PROPERTY(QString desc READ desc WRITE setDesc)
Q_PROPERTY(QDateTime birthDate READ birthDate WRITE setBirthDate)
Q_PROPERTY(QVariant photo READ photo WRITE setPhoto)
protected:
QString m_id;
long m_number;
QString m_desc;
QDateTime m_birthDate;
QVariant m_photo;
public:
TestQtProperty() : QObject(), m_number(0) { ; }
virtual ~TestQtProperty() { ; }
QString id() const { return m_id; }
long number() const { return m_number; }
QString desc() const { return m_desc; }
QDateTime birthDate() const { return m_birthDate; }
QVariant photo() const { return m_photo; }
void setId(const QString & id) { m_id = id; }
void setNumber(long l) { m_number = l; }
void setDesc(const QString & s) { m_desc = s; }
void setBirthDate(const QDateTime & dt) { m_birthDate = dt; }
void setPhoto(const QVariant & v) { m_photo = v; }
};
QX_REGISTER_PRIMARY_KEY(TestQtProperty, QString)
QX_REGISTER_HPP_QX_BLOG(TestQtProperty, QObject, 0)
#endif // _QX_TEST_QT_META_PROPERTY_H_

View File

@@ -0,0 +1,33 @@
#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
class blog;
class QX_BLOG_DLL_EXPORT author
{
public:
// -- typedef
typedef std::shared_ptr<blog> blog_ptr;
typedef std::vector<blog_ptr> list_blog;
// -- enum
enum enum_sex { male, female, unknown };
// -- properties
QString m_id;
QString m_name;
QDate m_birthdate;
enum_sex m_sex;
list_blog m_blogX;
// -- contructor, virtual destructor
author() : m_sex(unknown) { ; }
virtual ~author() { ; }
// -- methods
int age() const;
};
QX_REGISTER_PRIMARY_KEY(author, QString)
QX_REGISTER_HPP_QX_BLOG(author, qx::trait::no_base_class_defined, 0)
typedef std::shared_ptr<author> author_ptr;
typedef qx::QxCollection<QString, author_ptr> list_author;
#endif // _QX_BLOG_AUTHOR_H_

View File

@@ -0,0 +1,29 @@
#ifndef _QX_BLOG_BLOG_H_
#define _QX_BLOG_BLOG_H_
#include "author.h"
#include "comment.h"
#include "category.h"
class QX_BLOG_DLL_EXPORT blog
{
public:
// -- properties
QString m_id;
QString m_text;
QDateTime m_dt_creation;
author_ptr m_author;
list_comment m_commentX;
list_category m_categoryX;
// -- contructor, virtual destructor
blog() { ; }
virtual ~blog() { ; }
};
QX_REGISTER_PRIMARY_KEY(blog, QString)
QX_REGISTER_HPP_QX_BLOG(blog, qx::trait::no_base_class_defined, 0)
typedef std::shared_ptr<blog> blog_ptr;
typedef std::vector<blog_ptr> list_blog;
#endif // _QX_BLOG_BLOG_H_

View File

@@ -0,0 +1,28 @@
#ifndef _QX_BLOG_CATEGORY_H_
#define _QX_BLOG_CATEGORY_H_
class blog;
class QX_BLOG_DLL_EXPORT category
{
public:
// -- typedef
typedef std::shared_ptr<blog> blog_ptr;
typedef qx::QxCollection<long, blog_ptr> list_blog;
// -- properties
QString m_id;
QString m_name;
QString m_desc;
list_blog m_blogX;
// -- contructor, virtual destructor
category() { ; }
virtual ~category() { ; }
};
QX_REGISTER_PRIMARY_KEY(category, QString)
QX_REGISTER_HPP_QX_BLOG(category, qx::trait::no_base_class_defined, 0)
typedef QSharedPointer<category> category_ptr;
typedef qx::QxCollection<QString, category_ptr> list_category;
#endif // _QX_BLOG_CATEGORY_H_

View File

@@ -0,0 +1,27 @@
#ifndef _QX_BLOG_COMMENT_H_
#define _QX_BLOG_COMMENT_H_
class blog;
class QX_BLOG_DLL_EXPORT comment
{
public:
// -- typedef
typedef std::shared_ptr<blog> blog_ptr;
// -- properties
QString m_id;
QString m_text;
QDateTime m_dt_create;
blog_ptr m_blog;
// -- contructor, virtual destructor
comment() { ; }
virtual ~comment() { ; }
};
QX_REGISTER_PRIMARY_KEY(comment, QString)
QX_REGISTER_HPP_QX_BLOG(comment, qx::trait::no_base_class_defined, 0)
typedef std::shared_ptr<comment> comment_ptr;
typedef QList<comment_ptr> list_comment;
#endif // _QX_BLOG_COMMENT_H_

View File

@@ -0,0 +1,18 @@
#ifndef _QX_BLOG_EXPORT_H_
#define _QX_BLOG_EXPORT_H_
#ifdef _BUILDING_QX_BLOG
#define QX_BLOG_DLL_EXPORT QX_DLL_EXPORT_HELPER
#else // _BUILDING_QX_BLOG
#define QX_BLOG_DLL_EXPORT QX_DLL_IMPORT_HELPER
#endif // _BUILDING_QX_BLOG
#ifdef _BUILDING_QX_BLOG
#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_EXPORT_DLL
#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_EXPORT_DLL
#else // _BUILDING_QX_BLOG
#define QX_REGISTER_HPP_QX_BLOG QX_REGISTER_HPP_IMPORT_DLL
#define QX_REGISTER_CPP_QX_BLOG QX_REGISTER_CPP_IMPORT_DLL
#endif // _BUILDING_QX_BLOG
#endif // _QX_BLOG_EXPORT_H_

View File

@@ -0,0 +1,8 @@
#ifndef _QX_BLOG_PRECOMPILED_HEADER_H_
#define _QX_BLOG_PRECOMPILED_HEADER_H_
#include <QxOrm.h>
#include "export.h"
#endif // _QX_BLOG_PRECOMPILED_HEADER_H_

7
test/qxBlogMongoDB/qt/moc/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
# git does not allow empty directories.
# Yet, we need to add this empty directory on git.
# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it.
# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this.
*
# And then add an exception for this specifc file (so that we can commit it).
!.gitignore

View File

@@ -0,0 +1,40 @@
include(../../QxOrm.pri)
!contains(DEFINES, _QX_ENABLE_MONGODB) {
error(unable to use QxOrm MongoDB database driver : please define _QX_ENABLE_MONGODB compilation option in QxOrm.pri configuration file)
} # !contains(DEFINES, _QX_ENABLE_MONGODB)
TEMPLATE = app
DEFINES += _BUILDING_QX_BLOG
INCLUDEPATH += ../../../QxOrm/include/
DESTDIR = ../../../QxOrm/test/_bin/
LIBS += -L"../../../QxOrm/lib"
!contains(DEFINES, _QX_NO_PRECOMPILED_HEADER) {
PRECOMPILED_HEADER = ./include/precompiled.h
} # !contains(DEFINES, _QX_NO_PRECOMPILED_HEADER)
macx:CONFIG-=app_bundle
CONFIG(debug, debug|release) {
TARGET = qxBlogMongoDBd
LIBS += -l"QxOrmd"
} else {
TARGET = qxBlogMongoDB
LIBS += -l"QxOrm"
} # CONFIG(debug, debug|release)
HEADERS += ./include/precompiled.h
HEADERS += ./include/export.h
HEADERS += ./include/author.h
HEADERS += ./include/blog.h
HEADERS += ./include/category.h
HEADERS += ./include/comment.h
HEADERS += ./include/TestQtProperty.h
SOURCES += ./src/author.cpp
SOURCES += ./src/blog.cpp
SOURCES += ./src/category.cpp
SOURCES += ./src/comment.cpp
SOURCES += ./src/TestQtProperty.cpp
SOURCES += ./src/main.cpp

7
test/qxBlogMongoDB/release/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
# git does not allow empty directories.
# Yet, we need to add this empty directory on git.
# To achieve that, we created this .gitignore file, so that the directory will not be empty thus enabling us to commit it.
# Since we want all generated files/folders in this directory to be ignored by git, we add a rule for this.
*
# And then add an exception for this specifc file (so that we can commit it).
!.gitignore

View File

@@ -0,0 +1,25 @@
#include "../include/precompiled.h"
#include "../include/TestQtProperty.h"
#include <QxOrm_Impl.h>
QX_REGISTER_CPP_QX_BLOG(TestQtProperty)
QX_REGISTER_ALL_QT_PROPERTIES(TestQtProperty, "id")
QX_PERSISTABLE_CPP(TestQtProperty)
/*
Instead of using 'QX_REGISTER_ALL_QT_PROPERTIES(...)' macro, it's also
possible to write classic 'void qx::register_class<T>(...)' function, like this :
namespace qx {
template <> void register_class(QxClass<MyQObject> & t)
{ qx::register_all_qt_properties<MyQObject>(t, "my_id"); }
} // namespace qx
So, you can mix Qt meta-properties and classic registration data-member.
All is stored using the same interface : 'qx::IxDataMember *'.
To more details about advantages and disadvantages of using Qt meta-property,
go to the FAQ of QxOrm library :
- How to register automatically Qt meta-properties to QxOrm context ?
*/

View File

@@ -0,0 +1,28 @@
#include "../include/precompiled.h"
#include "../include/author.h"
#include "../include/blog.h"
#include <QxOrm_Impl.h>
QX_REGISTER_CPP_QX_BLOG(author)
namespace qx {
template <> void register_class(QxClass<author> & t)
{
t.id(& author::m_id, "author_id");
t.data(& author::m_name, "name");
t.data(& author::m_birthdate, "birthdate")->setIsIndex(true); // Index created automatically with qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes() function
t.data(& author::m_sex, "sex");
t.relationOneToMany(& author::m_blogX, "list_blog", "author_id");
t.fct_0<int>(std::mem_fn(& author::age), "age"); // using std::mem_fn() here is just a workaround for an issue with some versions of MSVC, it is not required with a full compliant C++11 compiler (http://stackoverflow.com/questions/23778883/vs2013-stdfunction-with-member-function)
}}
int author::age() const
{
if (! m_birthdate.isValid()) { return -1; }
return (QDate::currentDate().year() - m_birthdate.year());
}

View File

@@ -0,0 +1,20 @@
#include "../include/precompiled.h"
#include "../include/blog.h"
#include <QxOrm_Impl.h>
QX_REGISTER_CPP_QX_BLOG(blog)
namespace qx {
template <> void register_class(QxClass<blog> & t)
{
t.id(& blog::m_id, "blog_id");
t.data(& blog::m_text, "blog_text");
t.data(& blog::m_dt_creation, "date_creation");
t.data(& blog::m_categoryX, "list_category");
t.relationManyToOne(& blog::m_author, "author_id");
t.relationOneToMany(& blog::m_commentX, "list_comment", "blog_id");
}}

View File

@@ -0,0 +1,19 @@
#include "../include/precompiled.h"
#include "../include/category.h"
#include "../include/blog.h"
#include <QxOrm_Impl.h>
QX_REGISTER_CPP_QX_BLOG(category)
namespace qx {
template <> void register_class(QxClass<category> & t)
{
t.id(& category::m_id, "category_id");
t.data(& category::m_name, "name");
t.data(& category::m_desc, "description");
t.relationManyToMany(& category::m_blogX, "list_blog", "category_blog", "category_id", "blog_id");
}}

View File

@@ -0,0 +1,19 @@
#include "../include/precompiled.h"
#include "../include/comment.h"
#include "../include/blog.h"
#include <QxOrm_Impl.h>
QX_REGISTER_CPP_QX_BLOG(comment)
namespace qx {
template <> void register_class(QxClass<comment> & t)
{
t.id(& comment::m_id, "comment_id");
t.data(& comment::m_text, "comment_text");
t.data(& comment::m_dt_create, "date_creation");
t.relationManyToOne(& comment::m_blog, "blog_id");
}}

View File

@@ -0,0 +1,480 @@
#include "../include/precompiled.h"
#include <QtCore/qcoreapplication.h>
#include "../include/blog.h"
#include "../include/author.h"
#include "../include/comment.h"
#include "../include/category.h"
#include "../include/TestQtProperty.h"
#include <QxOrm_Impl.h>
void basicCRUDOnAuthor(bool withDelete);
void basicCRUDOnCategory();
void basicCRUDOnBlog();
void complexQueriesOnBlog();
void miscellaneousOperations();
void testQtProperty();
int main(int argc, char * argv[])
{
// Qt application
QCoreApplication app(argc, argv);
// Following command is recommanded to initialize QxOrm introspection engine
qx::QxClassX::registerAllClasses();
// Parameters to connect to MongoDB database
qx::QxSqlDatabase * pDatabase = qx::QxSqlDatabase::getSingleton();
pDatabase->setDriverName("QXMONGODB");
pDatabase->setDatabaseName("qxBlog");
pDatabase->setHostName("localhost");
pDatabase->setPort(27017);
pDatabase->setUserName("");
pDatabase->setPassword("");
// For debug purpose : log all replies from MongoDB database
qx::dao::mongodb::QxMongoDB_Helper::setLogDatabaseReply(true);
// Clear previous sample database
qx_query dropDB("{ \"dropDatabase\" : 1 }");
QSqlError daoError = qx::dao::call_query(dropDB); qAssert(! daoError.isValid());
// To optimize queries : create automatically indexes based on relationships and properties marked as 'index'
daoError = qx::dao::mongodb::QxMongoDB_Helper::autoCreateIndexes(true); qAssert(! daoError.isValid());
// Basic CRUD operations
basicCRUDOnAuthor(true);
basicCRUDOnAuthor(false);
// Populate category collection
basicCRUDOnCategory();
// Populate blog collection
basicCRUDOnBlog();
// Process some complex queries on blog collection (with relationships)
complexQueriesOnBlog();
// Process some others operations on database
miscellaneousOperations();
// Test Qt properties registered via QX_REGISTER_ALL_QT_PROPERTIES() macro (https://www.qxorm.com/qxorm_en/manual.html#manual_520)
testQtProperty();
return 0;
}
void basicCRUDOnAuthor(bool withDelete)
{
// Test delete all author
QSqlError daoError = qx::dao::delete_all<author>(); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 0);
// Test insert one without id
author_ptr author_1 = std::make_shared<author>();
author_1->m_name = "author_1";
author_1->m_sex = author::male;
author_1->m_birthdate = QDate(1998, 07, 12);
daoError = qx::dao::insert(author_1); qAssert(! daoError.isValid());
qAssert(! author_1->m_id.isEmpty());
qAssert(qx::dao::count<author>() == 1);
// Test insert one author with a custom id
author_ptr author_2 = std::make_shared<author>();
author_2->m_id = "my_custom_id_author_2";
author_2->m_name = "author_2";
author_2->m_sex = author::female;
author_2->m_birthdate = QDate(2003, 02, 28);
daoError = qx::dao::insert(author_2); qAssert(! daoError.isValid());
qAssert(author_2->m_id == "my_custom_id_author_2");
qAssert(qx::dao::count<author>() == 2);
// Test insert many author with/without ids
QList<author> authorX;
author author_3; author_3.m_name = "author_3"; author_3.m_sex = author::female; author_3.m_birthdate = QDate(1968, 05, 01); authorX.append(author_3);
author author_4; author_4.m_id = "my_custom_id_author_4"; author_4.m_name = "author_4"; author_4.m_sex = author::male; author_4.m_birthdate = QDate(1980, 12, 19); authorX.append(author_4);
author author_5; author_5.m_name = "author_5"; author_5.m_sex = author::female; author_5.m_birthdate = QDate(1978, 03, 03); authorX.append(author_5);
daoError = qx::dao::insert(authorX); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 5);
qAssert(authorX.at(1).m_id == "my_custom_id_author_4");
QString author_3_id = authorX.at(0).m_id; qAssert(! author_3_id.isEmpty());
QString author_5_id = authorX.at(2).m_id; qAssert(! author_5_id.isEmpty());
// Test update one author
author_3 = authorX.at(0);
author_3.m_name = "author_3_modified";
daoError = qx::dao::update(author_3); qAssert(! daoError.isValid());
// Test update many author
authorX.clear();
author_3.m_name = "author_3_modified_twice"; authorX.append(author_3);
author_2->m_name = "author_2_modified"; authorX.append(* author_2);
author_1->m_name = "author_1_modified"; authorX.append(* author_1);
daoError = qx::dao::update(authorX); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 5);
// Test fetch all author (inside QList)
authorX.clear();
daoError = qx::dao::fetch_all(authorX); qAssert(! daoError.isValid());
qAssert(authorX.count() == 5);
qx::dump(authorX);
// Test fetch all author (inside QHash by id)
QHash<QString, author_ptr> authorXById;
daoError = qx::dao::fetch_all(authorXById); qAssert(! daoError.isValid());
qAssert(authorXById.count() == 5);
qx::dump(authorXById);
// Test fetch author by query (only female)
list_author list_of_female_author;
qx_query query("{ \"sex\" : " + QString::number(static_cast<int>(author::female)) + " }");
daoError = qx::dao::fetch_by_query(query, list_of_female_author); qAssert(! daoError.isValid());
qAssert(list_of_female_author.count() == 3);
qx::dump(list_of_female_author);
// Test fetch by query using MongoDB aggregation framework (only female)
list_author list_of_female_author_other;
qx_query queryAggregate("aggregate", "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast<int>(author::female)) + " } } ]");
daoError = qx::dao::fetch_by_query(queryAggregate, list_of_female_author_other); qAssert(! daoError.isValid());
qAssert(list_of_female_author_other.count() == 3);
qx::dump(list_of_female_author_other);
// Test fetch by query (only female) adding 'sort', 'limit', 'skip', etc... commands (see second query QStringList parameter)
list_of_female_author_other.clear();
qx_query queryOpts(QStringList() << "{ \"sex\" : " + QString::number(static_cast<int>(author::female)) + " }"
<< "{ \"sort\" : { \"sex\" : -1 }, \"limit\" : 2 }");
daoError = qx::dao::fetch_by_query(queryOpts, list_of_female_author_other); qAssert(! daoError.isValid());
qAssert(list_of_female_author_other.count() == 2);
qx::dump(list_of_female_author_other);
// Test update one author after fetch
author_5 = authorX.at(4);
author_5.m_name = "author_5_modified_after_fetch";
daoError = qx::dao::update(author_5); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 5);
// Test update many author after fetch
author_ptr pAuthor = list_of_female_author.getByIndex(0);
pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch";
pAuthor = list_of_female_author.getByIndex(1);
pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch";
pAuthor = list_of_female_author.getByIndex(2);
pAuthor->m_name = pAuthor->m_name + "_female_modified_after_fetch";
daoError = qx::dao::update(list_of_female_author); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 5);
// Test fetch one author by id
pAuthor.reset(new author());
pAuthor->m_id = "my_custom_id_author_2";
daoError = qx::dao::fetch_by_id(pAuthor); qAssert(! daoError.isValid());
qAssert(pAuthor->m_id == "my_custom_id_author_2");
qAssert(! pAuthor->m_birthdate.isNull());
// Test fetch many author by id
authorX.clear();
QHashIterator<QString, author_ptr> itr(authorXById);
while (itr.hasNext()) { itr.next(); author author_tmp; author_tmp.m_id = itr.key(); authorX.append(author_tmp); }
daoError = qx::dao::fetch_by_id(authorX); qAssert(! daoError.isValid());
qAssert(authorX.count() == 5);
qAssert(! authorX.at(2).m_birthdate.isNull());
qx::dump(authorX);
if (! withDelete) { return; }
// Test delete one author by id
pAuthor.reset(new author());
pAuthor->m_id = "my_custom_id_author_4";
daoError = qx::dao::delete_by_id(pAuthor); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 4);
// Test delete many author by id
authorX.clear();
{ author author_tmp; author_tmp.m_id = author_3_id; authorX.append(author_tmp); }
{ author author_tmp; author_tmp.m_id = author_5_id; authorX.append(author_tmp); }
daoError = qx::dao::delete_by_id(authorX); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 2);
// Test delete author by query (all male)
#ifdef Q_COMPILER_INITIALIZER_LISTS
int iMale = static_cast<int>(author::male);
query = qx_query{ { "sex", iMale } };
#else // Q_COMPILER_INITIALIZER_LISTS
query = qx_query("{ \"sex\" : " + QString::number(static_cast<int>(author::male)) + " }");
#endif // Q_COMPILER_INITIALIZER_LISTS
daoError = qx::dao::delete_by_query<author>(query); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 1);
// Now delete all author
daoError = qx::dao::delete_all<author>(); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 0);
}
void basicCRUDOnCategory()
{
// Create 3 categories
category_ptr category_1 = category_ptr(new category());
category_ptr category_2 = category_ptr(new category());
category_ptr category_3 = category_ptr(new category());
category_1->m_name = "category_1"; category_1->m_desc = "desc_1";
category_2->m_name = "category_2"; category_2->m_desc = "desc_2";
category_3->m_name = "category_3"; category_3->m_desc = "desc_3";
// Insert 3 categories into database, use 'db' parameter for the transaction
QSqlError daoError = qx::dao::insert(category_1); qAssert(! daoError.isValid());
daoError = qx::dao::insert(category_2); qAssert(! daoError.isValid());
daoError = qx::dao::insert(category_3); qAssert(! daoError.isValid());
qAssert(qx::dao::count<category>() == 3);
}
void basicCRUDOnBlog()
{
// Search author_1
author_ptr author_1 = std::make_shared<author>();
qx_query query("{ \"name\" : \"author_1_modified\" }");
QSqlError daoError = qx::dao::fetch_by_query<author>(query, (* author_1)); qAssert(! daoError.isValid());
qAssert(author_1 && ! author_1->m_id.isEmpty());
// Search author_2
author_ptr author_2 = std::make_shared<author>();
author_2->m_id = "my_custom_id_author_2";
daoError = qx::dao::fetch_by_id(author_2); qAssert(! daoError.isValid());
qAssert(author_2 && ! author_2->m_id.isEmpty());
// Create a blog from class name (factory)
qx::any blog_any = qx::create("blog");
blog_ptr blog_1;
try { blog_1 = qx::any_cast<blog_ptr>(blog_any); }
catch (...) { blog_1.reset(new blog()); }
blog_1->m_text = "blog_text_1";
blog_1->m_dt_creation = QDateTime::currentDateTime();
blog_1->m_author = author_1;
// Insert 'blog_1' into database with 'save()' method
daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid());
qAssert(qx::dao::count<blog>() == 1);
// Modify 'blog_1' properties and save into database
blog_1->m_text = "update blog_text_1";
blog_1->m_author = author_2;
daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid());
qAssert(qx::dao::count<blog>() == 1);
// Add 2 comments to 'blog_1'
comment_ptr comment_1; comment_1.reset(new comment());
comment_ptr comment_2; comment_2.reset(new comment());
comment_1->m_text = "comment_1 text";
comment_1->m_dt_create = QDateTime::currentDateTime();
comment_1->m_blog = blog_1;
comment_2->m_text = "comment_2 text";
comment_2->m_dt_create = QDateTime::currentDateTime();
comment_2->m_blog = blog_1;
daoError = qx::dao::insert(comment_1); qAssert(! daoError.isValid());
daoError = qx::dao::insert(comment_2); qAssert(! daoError.isValid());
qAssert(qx::dao::count<comment>() == 2);
// Add 2 categories to 'blog_1' => 'list_category' is not defined as relationship in 'blog' class => so all categories are embedded in 'blog' collection (instead of referenced for relationships)
list_category categories;
daoError = qx::dao::fetch_all(categories); qAssert(! daoError.isValid());
qAssert(categories.count() == 3);
category_ptr category_1 = categories.getByIndex(0);
category_ptr category_3 = categories.getByIndex(2);
blog_1->m_categoryX.insert(category_1->m_id, category_1);
blog_1->m_categoryX.insert(category_3->m_id, category_3);
daoError = qx::dao::save(blog_1); qAssert(! daoError.isValid());
qAssert(qx::dao::count<blog>() == 1);
// Push a second blog
blog_ptr blog_2 = std::make_shared<blog>();
blog_2->m_text = "blog_text_2";
daoError = qx::dao::save(blog_2); qAssert(! daoError.isValid());
}
void complexQueriesOnBlog()
{
// Fetch all blogs (without relation)
list_blog allBlogs;
QSqlError daoError = qx::dao::fetch_all(allBlogs); qAssert(! daoError.isValid());
qx::dump(allBlogs);
qAssert(allBlogs.size() == 2);
// Fetch blog into a new variable with all relations : 'author', 'comment' and 'category' (MongoDB version 3.6+ is required for relationships)
blog_ptr blog_tmp = std::make_shared<blog>();
blog_tmp->m_id = allBlogs[0]->m_id;
daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); qAssert(! daoError.isValid());
qx::dump(blog_tmp);
qAssert(blog_tmp->m_commentX.count() == 2);
qAssert(blog_tmp->m_categoryX.count() == 2);
qAssert(blog_tmp->m_text == "update blog_text_1");
qAssert(blog_tmp->m_author && (blog_tmp->m_author->m_id == "my_custom_id_author_2"));
// Fetch blog into a new variable with many relations using "*->*->*->*" (4 levels of relationships)
blog_tmp.reset(new blog());
blog_tmp->m_id = allBlogs[0]->m_id;
daoError = qx::dao::fetch_by_id_with_relation("*->*->*->*", blog_tmp); qAssert(! daoError.isValid());
qx::dump(blog_tmp);
qAssert(blog_tmp->m_commentX.count() == 2);
qAssert(blog_tmp->m_categoryX.count() == 2);
qAssert(blog_tmp->m_text == "update blog_text_1");
qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "my_custom_id_author_2");
qAssert(blog_tmp->m_author->m_blogX[0]->m_commentX.count() > 0);
// Fetch blog into a new variable with many relations using "*->*" (2 levels of relationships)
blog_tmp.reset(new blog());
blog_tmp->m_id = allBlogs[0]->m_id;
daoError = qx::dao::fetch_all_with_relation("*->*", blog_tmp); qAssert(! daoError.isValid());
qx::dump(blog_tmp);
qAssert(blog_tmp->m_commentX.count() == 2);
qAssert(blog_tmp->m_categoryX.count() == 2);
qAssert(blog_tmp->m_text == "update blog_text_1");
qAssert(blog_tmp->m_author && blog_tmp->m_author->m_id == "my_custom_id_author_2");
qAssert(blog_tmp->m_author->m_blogX[0]->m_commentX.count() == 0);
// Fetch only property 'm_dt_creation' of blog
QStringList lstColumns = QStringList() << "date_creation";
list_blog lst_blog_with_only_date_creation;
daoError = qx::dao::fetch_all(lst_blog_with_only_date_creation, NULL, lstColumns);
qAssert(! daoError.isValid() && (lst_blog_with_only_date_creation.size() > 0));
if ((lst_blog_with_only_date_creation.size() > 0) && (lst_blog_with_only_date_creation[0].get() != NULL))
{ qAssert(lst_blog_with_only_date_creation[0]->m_text.isEmpty()); }
qx::dump(lst_blog_with_only_date_creation);
// Fetch relations defining fields to fetch with syntax { col_1, col_2, etc... }
list_blog lstBlogComplexRelation;
daoError = qx::dao::fetch_all_with_relation(QStringList() << "{ blog_text }" << "author_id { name, birthdate }" << "list_comment { comment_text } -> blog_id -> *", lstBlogComplexRelation); qAssert(! daoError.isValid());
qx::dump(lstBlogComplexRelation);
qAssert(lstBlogComplexRelation.size() > 0);
qAssert(lstBlogComplexRelation[0]->m_text != ""); // Fetched
qAssert(lstBlogComplexRelation[0]->m_dt_creation.isNull()); // Not fetched
qAssert(lstBlogComplexRelation[0]->m_author->m_sex == author::unknown); // Not fetched
qAssert(lstBlogComplexRelation[0]->m_author->m_name != ""); // Fetched
qAssert(lstBlogComplexRelation[0]->m_commentX.size() > 0);
qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_dt_create.isNull()); // Not fetched
qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_text != ""); // Fetched
qAssert(lstBlogComplexRelation[0]->m_commentX[0]->m_blog);
// Fetch relations defining columns to remove before fetching with syntax -{ col_1, col_2, etc... }
list_blog lstBlogComplexRelation2;
daoError = qx::dao::fetch_all_with_relation(QStringList() << "- { blog_text }" << "author_id -{ name, birthdate }" << "list_comment -{ comment_text } -> blog_id -> *", lstBlogComplexRelation2); qAssert(! daoError.isValid());
qx::dump(lstBlogComplexRelation2);
qAssert(lstBlogComplexRelation2.size() > 0);
qAssert(lstBlogComplexRelation2[0]->m_text == ""); // Not fetched
qAssert(! lstBlogComplexRelation2[0]->m_dt_creation.isNull()); // Fetched
qAssert(lstBlogComplexRelation2[0]->m_author->m_sex != author::unknown); // Fetched
qAssert(lstBlogComplexRelation2[0]->m_author->m_name == ""); // Not fetched
qAssert(lstBlogComplexRelation2[0]->m_commentX.size() > 0);
qAssert(! lstBlogComplexRelation2[0]->m_commentX[0]->m_dt_create.isNull()); // Fetched
qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_text == ""); // Not fetched
qAssert(lstBlogComplexRelation2[0]->m_commentX[0]->m_blog);
// Check qx::dao::save_with_relation_recursive() function
daoError = qx::dao::save_with_relation_recursive(blog_tmp); qAssert(! daoError.isValid());
daoError = qx::dao::save_with_relation_recursive(blog_tmp, qx::dao::save_mode::e_update_only); qAssert(! daoError.isValid());
// Fetch the second blog stored in database
blog_tmp = std::make_shared<blog>();
blog_tmp->m_id = allBlogs[1]->m_id;
daoError = qx::dao::fetch_by_id_with_all_relation(blog_tmp); qAssert(! daoError.isValid());
qx::dump(blog_tmp);
qAssert(blog_tmp->m_text == "blog_text_2");
qAssert(blog_tmp->m_author == NULL);
qAssert(blog_tmp->m_categoryX.size() == 0);
qAssert(blog_tmp->m_commentX.size() == 0);
// Fetch 2 blogs by id with their relationships
QList<blog_ptr> lstBlogById;
blog_tmp = std::make_shared<blog>(); blog_tmp->m_id = allBlogs[1]->m_id; lstBlogById.append(blog_tmp);
blog_tmp = std::make_shared<blog>(); blog_tmp->m_id = allBlogs[0]->m_id; lstBlogById.append(blog_tmp);
daoError = qx::dao::fetch_by_id_with_all_relation(lstBlogById); qAssert(! daoError.isValid());
qx::dump(lstBlogById);
}
void miscellaneousOperations()
{
// Dump all registered classes into QxOrm context (introspection engine)
qx::QxClassX::dumpAllClasses();
// Call 'age()' method with class name and method name (reflexion)
author author_1; qx_bool bInvokeOk = qx::QxClassX::invoke("author", "age", author_1); qAssert(bInvokeOk);
// Fetch all blogs (without relation)
list_blog allBlogs; QSqlError daoError = qx::dao::fetch_all(allBlogs); qAssert(! daoError.isValid());
qx::dump(allBlogs); qAssert(allBlogs.size() == 2);
// Test 'isDirty()' method
qx::dao::ptr<blog> blog_isdirty = qx::dao::ptr<blog>(new blog());
blog_isdirty->m_id = allBlogs[0]->m_id;
daoError = qx::dao::fetch_by_id(blog_isdirty);
qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
blog_isdirty->m_text = "blog property 'text' modified => blog is dirty !!!";
QStringList lstDiff; bool bDirty = blog_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 1) && (lstDiff.at(0) == "blog_text"));
if (bDirty) { qDebug("[QxOrm] test dirty 1 : blog is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
// Update only property 'm_text' of 'blog_isdirty'
daoError = qx::dao::update_optimized(blog_isdirty);
qAssert(! daoError.isValid() && ! blog_isdirty.isDirty());
qx::dump(blog_isdirty);
// Test 'isDirty()' method with a container
typedef qx::dao::ptr< QList<author_ptr> > type_lst_author_test_is_dirty;
type_lst_author_test_is_dirty container_isdirty = type_lst_author_test_is_dirty(new QList<author_ptr>());
daoError = qx::dao::fetch_all(container_isdirty);
qAssert(! daoError.isValid() && ! container_isdirty.isDirty() && (container_isdirty->count() == qx::dao::count<author>()));
author_ptr author_ptr_dirty = container_isdirty->at(1);
author_ptr_dirty->m_name = "author name modified at index 1 => container is dirty !!!";
bDirty = container_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 1));
if (bDirty) { qDebug("[QxOrm] test dirty 2 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
author_ptr_dirty = container_isdirty->at(2);
author_ptr_dirty->m_birthdate = QDate(1998, 03, 06);
bDirty = container_isdirty.isDirty(lstDiff);
qAssert(bDirty && (lstDiff.count() == 2));
if (bDirty) { qDebug("[QxOrm] test dirty 3 : container is dirty => '%s'", qPrintable(lstDiff.join("|"))); }
// Update only property 'm_name' at position 1, only property 'm_birthdate' at position 2 and nothing at position 0
daoError = qx::dao::update_optimized(container_isdirty);
qAssert(! daoError.isValid() && ! container_isdirty.isDirty());
qx::dump(container_isdirty);
// Call a custom query and get JSON response as QVariantMap
qx_query customQuery("{ \"find\": \"author\", \"filter\": { } }");
daoError = qx::dao::call_query(customQuery); qAssert(! daoError.isValid());
QString responseCustomQuery = customQuery.response().toString();
QVariantMap responseCustomQueryAsJson;
qx::serialization::json::from_string(responseCustomQueryAsJson, responseCustomQuery);
qx::dump(responseCustomQueryAsJson);
// Call a custom query with cursor and get JSON response as QList<QVariantMap>
qx_query customQueryCursor("cursor", "{ \"find\": \"author\", \"filter\": { } }");
daoError = qx::dao::call_query(customQueryCursor); qAssert(! daoError.isValid());
QString responseCustomQueryCursor = customQueryCursor.response().toString();
QList<QVariantMap> responseCustomQueryCursorAsJson;
qx::serialization::json::from_string(responseCustomQueryCursorAsJson, responseCustomQueryCursor);
qx::dump(responseCustomQueryCursorAsJson);
qAssert(responseCustomQueryCursorAsJson.count() == qx::dao::count<author>());
// Test delete by query using MongoDB aggregation framework (delete all female)
qx_query queryAggregate("aggregate", "[ { \"$match\" : { \"sex\" : " + QString::number(static_cast<int>(author::female)) + " } } ]");
daoError = qx::dao::delete_by_query<author>(queryAggregate); qAssert(! daoError.isValid());
qAssert(qx::dao::count<author>() == 2);
}
void testQtProperty()
{
TestQtProperty tst;
tst.setDesc("tst");
QSqlError daoError = qx::dao::insert(tst); qAssert(! daoError.isValid());
tst.setDesc("tst update");
daoError = qx::dao::update(tst); qAssert(! daoError.isValid());
daoError = qx::dao::fetch_by_id(tst); qAssert(! daoError.isValid());
std::shared_ptr<qx::IxPersistableCollection> lst = qx::IxPersistable::qxFetchAll("TestQtProperty"); qAssert(lst->__count() == 1);
}