Skip to main content
Code Review

Return to Question

Became Hot Network Question
added 488 characters in body
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341
 /*
 * query-comparison
 * https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
 * Equal => Eq
 * Not Equal => Ne
 * Greater => Gt
 * Greater or Equal => Gte
 * Less => Lt
 * Less or Equal => Lte
 * Matches a value in array => In
 * Matches no values in array => Nin
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/eq/
 * https://www.mongodb.com/docs/manual/reference/operator/query/ne/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gte/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nin/
 */ template<typename T>
 struct Eq {
 using CType = ConstructorType<T>;
 Eq(CType init): $eq{std::move(init)} {}
 T $eq;
 };
 // Same pattern for Ne, Gt, Gte, Lt, Lte (are basically the same as Eq)
 template<typename T>
 struct In
 {
 using CType = std::vector<T>;
 In(CType init): $in{std::move(init)} {}
 std::vector<T> $in;
 }
 // Same pattern for Nin (is basically the same as In)
 /*
 * query-logical
 * https://www.mongodb.com/docs/manual/reference/operator/query-logical/
 * Logical AND of two Values => And
 * Logical OR of two Values => Or
 * Logical NOR of two Values => Nor => NOR(A, B) => !(OR(A,B))
 * Logical NOT => Not
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/and/
 * https://www.mongodb.com/docs/manual/reference/operator/query/or/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nor/
 * https://www.mongodb.com/docs/manual/reference/operator/query/not/
 */
 template<typename LHS, typename RHS>
 struct And
 {
 using CType = And<LHS, RHS>;
 using LP = ConstructorType<LHS>;
 using RP = ConstructorType<RHS>;
 And(LP lhs, RP rhs)
 : $and(std::move(lhs), std::move(rhs))
 {}
 std::tuple<LHS, RHS> $and;
 };
 // Same pattern for Or and Nor
 template<typename T>
 struct Not
 {
 using CType = ConstructorType<T>;
 Not(CType init)
 : $not(std::move(init))
 {}
 T $not;
 };
 /*
 * query-element
 * https://www.mongodb.com/docs/manual/reference/operator/query-element/
 * An element exists in the object => Exists
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 * An element has a specific Type => Type
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 */
 struct Exists
 {
 using CType = bool;
 Exists(bool init)
 : $exists(init)
 {}
 bool $exists;
 };

 enum class BsonType { Double = 1, String = 2, Object = 3, Array = 4,
 BinaryData = 5, ObjectId = 7, Boolean = 8, Date = 9,
 Null = 10, RegularExpression = 11, JavaScript = 13, Int32 = 16,
 Timestamp = 17, Int64 = 18, Decimal128 = 19, MinKey = -1,
 MaxKey = 127
 };
 struct Type
 {
 using CType = BsonType;
 Type(BsonType type)
 : $type(static_cast<std::int32_t>(type))
 {}
 std::int32_t $type;
 };
 /*
 * query-evaluation
 * https://www.mongodb.com/docs/manual/reference/operator/query-evaluation/
 * Mod field and test remainder => Mod
 * https://www.mongodb.com/docs/manual/reference/operator/query/mod/
 * Regular Expression => RegEx
 * https://www.mongodb.com/docs/manual/reference/operator/query/regex/
 * Text search of whole object => Text
 * https://www.mongodb.com/docs/manual/reference/operator/query/text/
 * Can't seem to get his working.
 *
 */
 struct Mod
 {
 using CType = std::pair<std::uint32_t, std::uint32_t>;
 Mod(CType init)
 : $mod({init.first, init.second})
 {}
 std::array<std::uint32_t, 2> $mod;
 };
 struct RegEx
 {
 using CType = std::pair<std::string, std::string>;
 RegEx(CType init)
 : $regex(std::move(init.first))
 , $options(std::move(init.second))
 {}
 std::string $regex;
 std::string $options;
 };
 struct TextSearch
 {
 std::string $search;
 std::optional<std::string> $language;
 std::optional<bool> $caseSensitive;
 std::optional<bool> $diacriticSensitive;
 };
 struct Text
 {
 Text(std::string search, std::optional<std::string> language = {}, std::optional<bool> cs = {}, std::optional<bool> ds = {})
 : $text{std::move(search), std::move(language), std::move(cs), std::move(ds)}
 {}
 TextSearch $text;
 };
 /*
 * query-array
 * https://www.mongodb.com/docs/manual/reference/operator/query-array/
 * Has all the following elements: => All
 * An array has an element that matches multiple considitions: => ElemMatch
 * An array has a specific size: => Size
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/all/
 * https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/
 * https://www.mongodb.com/docs/manual/reference/operator/query/size/
 */
 template<typename T>
 struct All
 {
 using CType = std::vector<T>;
 All(CType init)
 : $all(std::move(init))
 {}
 std::vector<T> $all;
 };
 template<typename T>
 struct Elements
 {
 std::optional<T> $eq;
 std::optional<T> $ne;
 std::optional<T> $gt;
 std::optional<T> $gte;
 std::optional<T> $lt;
 std::optional<T> $lte;
 };
 template<typename T>
 struct ElemMatch
 {
 ElemMatch(std::optional<T> eq, std::optional<T> ne, std::optional<T> gt, std::optional<T> gte, std::optional<T> lt, std::optional<T> lte)
 : $elemMatch{std::move(eq), std::move(ne), std::move(gt), std::move(gte), std::move(lt), std::move(lte)}
 {}
 Elements<T> $elemMatch;
 };
 struct Size
 {
 using CType = std::uint32_t;
 Size(std::uint32_t init)
 : $size{init}
 {}
 std::uint32_t $size;
 };
 /*
 * Bitwise Query operator
 * https://www.mongodb.com/docs/manual/reference/operator/query-bitwise/
 * AllClear
 * AllSet
 * AnyClear
 * AnySet
 *
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/
󰍵 */
 struct AllClear
 {
 using CType = std::uint32_t;
 AllClear(CType init)
 : $bitsAllClear{std::move(init)}
 {}
 std::uint32_t $bitsAllClear;
 };
 struct AllSet
 {
 using CType = std::uint32_t;
 AllSet(CType init)
 : $bitsAllSet{std::move(init)}
 {}
 std::uint32_t $bitsAllSet;
 };
 struct AnyClear
 {
 using CType = std::uint32_t;
 AnyClear(CType init)
 : $bitsAnyClear{std::move(init)}
 {}
 std::uint32_t $bitsAnyClear;
 };
 struct AnySet
 {
 using CType = std::uint32_t;
 AnySet(CType init)
 : $bitsAnySet{std::move(init)}
 {}
 std::uint32_t $bitsAnySet;
 }; 

Tests:

 TEST(IntegrationConnectionMongoTest, queryEq)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEQ = Query<FindByName<Eq<std::string>>>;
 using VEQ = std::vector<QNameEQ>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VEQ{{"Tom"}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VEQ{{"Sam"}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNe)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNE = Query<FindByName<Ne<std::string>>>;
 using VNE = std::vector<QNameNE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VNE{{"Sam"}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VNE{{"Tom"}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryGt)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameGT = Query<FindByAge<Gt<int>>>;
 using VGT = std::vector<QNameGT>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VGT{{56}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VGT{{20}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryGte)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameGTE = Query<FindByAge<Gte<int>>>;
 using VGTE = std::vector<QNameGTE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VGTE{{57}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VGTE{{22}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryLt)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameLT = Query<FindByAge<Lt<int>>>;
 using VLT = std::vector<QNameLT>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VLT{22});
 RemoveResult r2Result = mongo["test"]["People"].remove(VLT{58});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryLte)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameLTE = Query<FindByAge<Lte<int>>>;
 using VLTE = std::vector<QNameLTE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VLTE{21});
 RemoveResult r2Result = mongo["test"]["People"].remove(VLTE{56});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryIn)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameIN = Query<FindByAge<In<int>>>;
 using VIN = std::vector<QNameIN>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VIN{std::vector<int>{25,26,48}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VIN{std::vector<int>{23,22,45}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNin)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNIN = Query<FindByAge<Nin<int>>>;
 using VNIN = std::vector<QNameNIN>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VNIN{std::vector<int>{22,23,45}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VNIN{std::vector<int>{25,26,48}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnd)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameAND = Query<And<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameAND{{"Sam", 23}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameAND{{"Sam", 58}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(1, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(2, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryOr)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameOR = Query<Or<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameOR{{"Tom", 23}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameOR{{"Sam", 18}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(1, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(2, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNor)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNOR = Query<Nor<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Sam", 22}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Tom", 30}}));
 RemoveResult r3Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Tom", 12}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(1, r2Result.n);
 EXPECT_EQ(1, r3Result.ok);
 EXPECT_EQ(2, r3Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNot)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNOT = Query<FindByName<Not<Eq<std::string>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOT{"Sam"}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOT{"Tom"}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryExists)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEXISTS = Query<FindByName<Exists>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{false}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{true}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryType)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEXISTS = Query<FindByName<Type>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{BsonType::Double}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{BsonType::String}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryMod)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 25, {"Court", "NY", 12}}, {"Sam", 37, {"Jester", "FW", 23}}, {"Sam", 49, {"Limbo", "FG", 56}}};
 using QNameMod = Query<FindByAge<Mod>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameMod{{12, 0}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameMod{{12, 1}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryRegEx)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Samual", 25, {"Court", "NY", 12}}, {"Samantha", 37, {"Jester", "FW", 23}}, {"Samtra", 49, {"Limbo", "FG", 56}}};
 using QNameRegEx = Query<FindByName<RegEx>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameRegEx{{"Samp.*", "i"}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameRegEx{{"Sam.*", "i"}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryText)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 25, {"Cour terror", "NY", 12}}, {"Sam", 37, {"Jes terror", "FW", 23}}, {"Sam", 49, {"Limbo terror", "FG", 56}}};
 using QNameText = Query<FindByName<Text>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameText{{"Anikin"}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameText{{"Sam"}}));
 RemoveResult r3Result = mongo["test"]["People"].remove(std::make_tuple(Query<FindByName<std::string>>{"Sam"}));
 std::cerr << ThorsAnvil::Serialize::jsonExporter(QNameText{{" Sam "}}) << "\n";
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 std::cerr << "Cleanup: " << r3Result.ok << " Count: " << r3Result.n << "\n";
 }
 TEST(IntegrationConnectionMongoTest, queryAll)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14, 15}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataAll = Query<FindByData<All<int>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataAll{{10, 12, 13, 14}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataAll{{14, 15}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryElemMatch)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14, 15}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataElemMatch = Query<FindByData<ElemMatch<int>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataElemMatch{{25, 14, {}, {}, {}, {}}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataElemMatch{{14, 15, {}, {}, {}, {}}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryElemSize)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataSize = Query<FindByData<Size>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataSize{2}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataSize{3}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAllClear)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAllClear = Query<FindByAge<AllClear>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllClear{0b11000111}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllClear{0b11000110}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAllSet)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAllSet = Query<FindByAge<AllSet>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllSet{0b00111001}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllSet{0b00000001}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnyClear)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAnyClear = Query<FindByAge<AnyClear>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnyClear{0b00000001}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnyClear{0b11111110}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnySet)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAnySet = Query<FindByAge<AnySet>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnySet{0b10000110}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnySet{0b11111111}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 /*
 * query-comparison
 * https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
 * Equal => Eq
 * Not Equal => Ne
 * Greater => Gt
 * Greater or Equal => Gte
 * Less => Lt
 * Less or Equal => Lte
 * Matches a value in array => In
 * Matches no values in array => Nin
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/eq/
 * https://www.mongodb.com/docs/manual/reference/operator/query/ne/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gte/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nin/
 */ template<typename T>
 struct Eq {
 using CType = ConstructorType<T>;
 Eq(CType init): $eq{std::move(init)} {}
 T $eq;
 };
 // Same pattern for Ne, Gt, Gte, Lt, Lte (are basically the same as Eq)
 template<typename T>
 struct In
 {
 using CType = std::vector<T>;
 In(CType init): $in{std::move(init)} {}
 std::vector<T> $in;
 }
 // Same pattern for Nin (is basically the same as In)
 /*
 * query-logical
 * https://www.mongodb.com/docs/manual/reference/operator/query-logical/
 * Logical AND of two Values => And
 * Logical OR of two Values => Or
 * Logical NOR of two Values => Nor => NOR(A, B) => !(OR(A,B))
 * Logical NOT => Not
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/and/
 * https://www.mongodb.com/docs/manual/reference/operator/query/or/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nor/
 * https://www.mongodb.com/docs/manual/reference/operator/query/not/
 */
 template<typename LHS, typename RHS>
 struct And
 {
 using CType = And<LHS, RHS>;
 using LP = ConstructorType<LHS>;
 using RP = ConstructorType<RHS>;
 And(LP lhs, RP rhs)
 : $and(std::move(lhs), std::move(rhs))
 {}
 std::tuple<LHS, RHS> $and;
 };
 // Same pattern for Or and Nor
 template<typename T>
 struct Not
 {
 using CType = ConstructorType<T>;
 Not(CType init)
 : $not(std::move(init))
 {}
 T $not;
 };
 /*
 * query-element
 * https://www.mongodb.com/docs/manual/reference/operator/query-element/
 * An element exists in the object => Exists
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 * An element has a specific Type => Type
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 */
 struct Exists
 {
 using CType = bool;
 Exists(bool init)
 : $exists(init)
 {}
 bool $exists;
 };
 struct Type
 {
 using CType = BsonType;
 Type(BsonType type)
 : $type(static_cast<std::int32_t>(type))
 {}
 std::int32_t $type;
 };
 /*
 * query-evaluation
 * https://www.mongodb.com/docs/manual/reference/operator/query-evaluation/
 * Mod field and test remainder => Mod
 * https://www.mongodb.com/docs/manual/reference/operator/query/mod/
 * Regular Expression => RegEx
 * https://www.mongodb.com/docs/manual/reference/operator/query/regex/
 * Text search of whole object => Text
 * https://www.mongodb.com/docs/manual/reference/operator/query/text/
 * Can't seem to get his working.
 *
 */
 struct Mod
 {
 using CType = std::pair<std::uint32_t, std::uint32_t>;
 Mod(CType init)
 : $mod({init.first, init.second})
 {}
 std::array<std::uint32_t, 2> $mod;
 };
 struct RegEx
 {
 using CType = std::pair<std::string, std::string>;
 RegEx(CType init)
 : $regex(std::move(init.first))
 , $options(std::move(init.second))
 {}
 std::string $regex;
 std::string $options;
 };
 struct TextSearch
 {
 std::string $search;
 std::optional<std::string> $language;
 std::optional<bool> $caseSensitive;
 std::optional<bool> $diacriticSensitive;
 };
 struct Text
 {
 Text(std::string search, std::optional<std::string> language = {}, std::optional<bool> cs = {}, std::optional<bool> ds = {})
 : $text{std::move(search), std::move(language), std::move(cs), std::move(ds)}
 {}
 TextSearch $text;
 };
 /*
 * query-array
 * https://www.mongodb.com/docs/manual/reference/operator/query-array/
 * Has all the following elements: => All
 * An array has an element that matches multiple considitions: => ElemMatch
 * An array has a specific size: => Size
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/all/
 * https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/
 * https://www.mongodb.com/docs/manual/reference/operator/query/size/
 */
 template<typename T>
 struct All
 {
 using CType = std::vector<T>;
 All(CType init)
 : $all(std::move(init))
 {}
 std::vector<T> $all;
 };
 template<typename T>
 struct Elements
 {
 std::optional<T> $eq;
 std::optional<T> $ne;
 std::optional<T> $gt;
 std::optional<T> $gte;
 std::optional<T> $lt;
 std::optional<T> $lte;
 };
 template<typename T>
 struct ElemMatch
 {
 ElemMatch(std::optional<T> eq, std::optional<T> ne, std::optional<T> gt, std::optional<T> gte, std::optional<T> lt, std::optional<T> lte)
 : $elemMatch{std::move(eq), std::move(ne), std::move(gt), std::move(gte), std::move(lt), std::move(lte)}
 {}
 Elements<T> $elemMatch;
 };
 struct Size
 {
 using CType = std::uint32_t;
 Size(std::uint32_t init)
 : $size{init}
 {}
 std::uint32_t $size;
 };
 /*
 * Bitwise Query operator
 * https://www.mongodb.com/docs/manual/reference/operator/query-bitwise/
 * AllClear
 * AllSet
 * AnyClear
 * AnySet
 *
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/
󰍵 */
 struct AllClear
 {
 using CType = std::uint32_t;
 AllClear(CType init)
 : $bitsAllClear{std::move(init)}
 {}
 std::uint32_t $bitsAllClear;
 };
 struct AllSet
 {
 using CType = std::uint32_t;
 AllSet(CType init)
 : $bitsAllSet{std::move(init)}
 {}
 std::uint32_t $bitsAllSet;
 };
 struct AnyClear
 {
 using CType = std::uint32_t;
 AnyClear(CType init)
 : $bitsAnyClear{std::move(init)}
 {}
 std::uint32_t $bitsAnyClear;
 };
 struct AnySet
 {
 using CType = std::uint32_t;
 AnySet(CType init)
 : $bitsAnySet{std::move(init)}
 {}
 std::uint32_t $bitsAnySet;
 }; 
 /*
 * query-comparison
 * https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
 * Equal => Eq
 * Not Equal => Ne
 * Greater => Gt
 * Greater or Equal => Gte
 * Less => Lt
 * Less or Equal => Lte
 * Matches a value in array => In
 * Matches no values in array => Nin
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/eq/
 * https://www.mongodb.com/docs/manual/reference/operator/query/ne/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gte/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nin/
 */ template<typename T>
 struct Eq {
 using CType = ConstructorType<T>;
 Eq(CType init): $eq{std::move(init)} {}
 T $eq;
 };
 // Same pattern for Ne, Gt, Gte, Lt, Lte (are basically the same as Eq)
 template<typename T>
 struct In
 {
 using CType = std::vector<T>;
 In(CType init): $in{std::move(init)} {}
 std::vector<T> $in;
 }
 // Same pattern for Nin (is basically the same as In)
 /*
 * query-logical
 * https://www.mongodb.com/docs/manual/reference/operator/query-logical/
 * Logical AND of two Values => And
 * Logical OR of two Values => Or
 * Logical NOR of two Values => Nor => NOR(A, B) => !(OR(A,B))
 * Logical NOT => Not
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/and/
 * https://www.mongodb.com/docs/manual/reference/operator/query/or/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nor/
 * https://www.mongodb.com/docs/manual/reference/operator/query/not/
 */
 template<typename LHS, typename RHS>
 struct And
 {
 using CType = And<LHS, RHS>;
 using LP = ConstructorType<LHS>;
 using RP = ConstructorType<RHS>;
 And(LP lhs, RP rhs)
 : $and(std::move(lhs), std::move(rhs))
 {}
 std::tuple<LHS, RHS> $and;
 };
 // Same pattern for Or and Nor
 template<typename T>
 struct Not
 {
 using CType = ConstructorType<T>;
 Not(CType init)
 : $not(std::move(init))
 {}
 T $not;
 };
 /*
 * query-element
 * https://www.mongodb.com/docs/manual/reference/operator/query-element/
 * An element exists in the object => Exists
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 * An element has a specific Type => Type
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 */
 struct Exists
 {
 using CType = bool;
 Exists(bool init)
 : $exists(init)
 {}
 bool $exists;
 };

 enum class BsonType { Double = 1, String = 2, Object = 3, Array = 4,
 BinaryData = 5, ObjectId = 7, Boolean = 8, Date = 9,
 Null = 10, RegularExpression = 11, JavaScript = 13, Int32 = 16,
 Timestamp = 17, Int64 = 18, Decimal128 = 19, MinKey = -1,
 MaxKey = 127
 };
 struct Type
 {
 using CType = BsonType;
 Type(BsonType type)
 : $type(static_cast<std::int32_t>(type))
 {}
 std::int32_t $type;
 };
 /*
 * query-evaluation
 * https://www.mongodb.com/docs/manual/reference/operator/query-evaluation/
 * Mod field and test remainder => Mod
 * https://www.mongodb.com/docs/manual/reference/operator/query/mod/
 * Regular Expression => RegEx
 * https://www.mongodb.com/docs/manual/reference/operator/query/regex/
 * Text search of whole object => Text
 * https://www.mongodb.com/docs/manual/reference/operator/query/text/
 * Can't seem to get his working.
 *
 */
 struct Mod
 {
 using CType = std::pair<std::uint32_t, std::uint32_t>;
 Mod(CType init)
 : $mod({init.first, init.second})
 {}
 std::array<std::uint32_t, 2> $mod;
 };
 struct RegEx
 {
 using CType = std::pair<std::string, std::string>;
 RegEx(CType init)
 : $regex(std::move(init.first))
 , $options(std::move(init.second))
 {}
 std::string $regex;
 std::string $options;
 };
 struct TextSearch
 {
 std::string $search;
 std::optional<std::string> $language;
 std::optional<bool> $caseSensitive;
 std::optional<bool> $diacriticSensitive;
 };
 struct Text
 {
 Text(std::string search, std::optional<std::string> language = {}, std::optional<bool> cs = {}, std::optional<bool> ds = {})
 : $text{std::move(search), std::move(language), std::move(cs), std::move(ds)}
 {}
 TextSearch $text;
 };
 /*
 * query-array
 * https://www.mongodb.com/docs/manual/reference/operator/query-array/
 * Has all the following elements: => All
 * An array has an element that matches multiple considitions: => ElemMatch
 * An array has a specific size: => Size
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/all/
 * https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/
 * https://www.mongodb.com/docs/manual/reference/operator/query/size/
 */
 template<typename T>
 struct All
 {
 using CType = std::vector<T>;
 All(CType init)
 : $all(std::move(init))
 {}
 std::vector<T> $all;
 };
 template<typename T>
 struct Elements
 {
 std::optional<T> $eq;
 std::optional<T> $ne;
 std::optional<T> $gt;
 std::optional<T> $gte;
 std::optional<T> $lt;
 std::optional<T> $lte;
 };
 template<typename T>
 struct ElemMatch
 {
 ElemMatch(std::optional<T> eq, std::optional<T> ne, std::optional<T> gt, std::optional<T> gte, std::optional<T> lt, std::optional<T> lte)
 : $elemMatch{std::move(eq), std::move(ne), std::move(gt), std::move(gte), std::move(lt), std::move(lte)}
 {}
 Elements<T> $elemMatch;
 };
 struct Size
 {
 using CType = std::uint32_t;
 Size(std::uint32_t init)
 : $size{init}
 {}
 std::uint32_t $size;
 };
 /*
 * Bitwise Query operator
 * https://www.mongodb.com/docs/manual/reference/operator/query-bitwise/
 * AllClear
 * AllSet
 * AnyClear
 * AnySet
 *
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/
󰍵 */
 struct AllClear
 {
 using CType = std::uint32_t;
 AllClear(CType init)
 : $bitsAllClear{std::move(init)}
 {}
 std::uint32_t $bitsAllClear;
 };
 struct AllSet
 {
 using CType = std::uint32_t;
 AllSet(CType init)
 : $bitsAllSet{std::move(init)}
 {}
 std::uint32_t $bitsAllSet;
 };
 struct AnyClear
 {
 using CType = std::uint32_t;
 AnyClear(CType init)
 : $bitsAnyClear{std::move(init)}
 {}
 std::uint32_t $bitsAnyClear;
 };
 struct AnySet
 {
 using CType = std::uint32_t;
 AnySet(CType init)
 : $bitsAnySet{std::move(init)}
 {}
 std::uint32_t $bitsAnySet;
 }; 

Tests:

 TEST(IntegrationConnectionMongoTest, queryEq)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEQ = Query<FindByName<Eq<std::string>>>;
 using VEQ = std::vector<QNameEQ>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VEQ{{"Tom"}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VEQ{{"Sam"}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNe)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNE = Query<FindByName<Ne<std::string>>>;
 using VNE = std::vector<QNameNE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VNE{{"Sam"}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VNE{{"Tom"}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryGt)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameGT = Query<FindByAge<Gt<int>>>;
 using VGT = std::vector<QNameGT>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VGT{{56}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VGT{{20}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryGte)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameGTE = Query<FindByAge<Gte<int>>>;
 using VGTE = std::vector<QNameGTE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VGTE{{57}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VGTE{{22}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryLt)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameLT = Query<FindByAge<Lt<int>>>;
 using VLT = std::vector<QNameLT>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VLT{22});
 RemoveResult r2Result = mongo["test"]["People"].remove(VLT{58});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryLte)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameLTE = Query<FindByAge<Lte<int>>>;
 using VLTE = std::vector<QNameLTE>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VLTE{21});
 RemoveResult r2Result = mongo["test"]["People"].remove(VLTE{56});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryIn)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameIN = Query<FindByAge<In<int>>>;
 using VIN = std::vector<QNameIN>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VIN{std::vector<int>{25,26,48}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VIN{std::vector<int>{23,22,45}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNin)
 {
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNIN = Query<FindByAge<Nin<int>>>;
 using VNIN = std::vector<QNameNIN>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(VNIN{std::vector<int>{22,23,45}});
 RemoveResult r2Result = mongo["test"]["People"].remove(VNIN{std::vector<int>{25,26,48}});
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnd)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameAND = Query<And<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameAND{{"Sam", 23}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameAND{{"Sam", 58}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(1, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(2, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryOr)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameOR = Query<Or<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameOR{{"Tom", 23}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameOR{{"Sam", 18}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(1, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(2, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNor)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNOR = Query<Nor<FindByName<std::string>, FindByAge<Lt<int>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Sam", 22}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Tom", 30}}));
 RemoveResult r3Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOR{{"Tom", 12}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(1, r2Result.n);
 EXPECT_EQ(1, r3Result.ok);
 EXPECT_EQ(2, r3Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryNot)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameNOT = Query<FindByName<Not<Eq<std::string>>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOT{"Sam"}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameNOT{"Tom"}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryExists)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEXISTS = Query<FindByName<Exists>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{false}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{true}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryType)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 22, {"Court", "NY", 12}}, {"Sam", 23, {"Jester", "FW", 23}}, {"Sam", 45, {"Limbo", "FG", 56}}};
 using QNameEXISTS = Query<FindByName<Type>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{BsonType::Double}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameEXISTS{BsonType::String}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryMod)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 25, {"Court", "NY", 12}}, {"Sam", 37, {"Jester", "FW", 23}}, {"Sam", 49, {"Limbo", "FG", 56}}};
 using QNameMod = Query<FindByAge<Mod>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameMod{{12, 0}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameMod{{12, 1}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryRegEx)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Samual", 25, {"Court", "NY", 12}}, {"Samantha", 37, {"Jester", "FW", 23}}, {"Samtra", 49, {"Limbo", "FG", 56}}};
 using QNameRegEx = Query<FindByName<RegEx>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameRegEx{{"Samp.*", "i"}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameRegEx{{"Sam.*", "i"}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryText)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 25, {"Cour terror", "NY", 12}}, {"Sam", 37, {"Jes terror", "FW", 23}}, {"Sam", 49, {"Limbo terror", "FG", 56}}};
 using QNameText = Query<FindByName<Text>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QNameText{{"Anikin"}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QNameText{{"Sam"}}));
 RemoveResult r3Result = mongo["test"]["People"].remove(std::make_tuple(Query<FindByName<std::string>>{"Sam"}));
 std::cerr << ThorsAnvil::Serialize::jsonExporter(QNameText{{" Sam "}}) << "\n";
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 std::cerr << "Cleanup: " << r3Result.ok << " Count: " << r3Result.n << "\n";
 }
 TEST(IntegrationConnectionMongoTest, queryAll)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14, 15}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataAll = Query<FindByData<All<int>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataAll{{10, 12, 13, 14}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataAll{{14, 15}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryElemMatch)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14, 15}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataElemMatch = Query<FindByData<ElemMatch<int>>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataElemMatch{{25, 14, {}, {}, {}, {}}}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataElemMatch{{14, 15, {}, {}, {}, {}}}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryElemSize)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{
 {"Sam", 25, {"Cour terror", "NY", 12}, {{10, 13, 14}}},
 {"Sam", 37, {"Jes terror", "FW", 23}, {{13, 14, 15}}},
 {"Sam", 49, {"Limbo terror", "FG", 56}, {{10, 14, 15}}}
 };
 using QDataSize = Query<FindByData<Size>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QDataSize{2}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QDataSize{3}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAllClear)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAllClear = Query<FindByAge<AllClear>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllClear{0b11000111}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllClear{0b11000110}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAllSet)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAllSet = Query<FindByAge<AllSet>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllSet{0b00111001}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAllSet{0b00000001}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnyClear)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAnyClear = Query<FindByAge<AnyClear>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnyClear{0b00000001}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnyClear{0b11111110}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
 TEST(IntegrationConnectionMongoTest, queryAnySet)
 {
 using namespace std::string_literals;
 ThorsMongo mongo({"localhost", 27017}, {"test", "testPassword", "test"});
 std::vector<People> people{{"Sam", 17, {"Cour terror", "NY", 12}}, {"Sam", 25, {"Jes terror", "FW", 23}}, {"Sam", 33, {"Limbo terror", "FG", 56}}};
 using QAgeAnySet = Query<FindByAge<AnySet>>;
 InsertResult iResult = mongo["test"]["People"].insert(people);
 RemoveResult r1Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnySet{0b10000110}));
 RemoveResult r2Result = mongo["test"]["People"].remove(std::make_tuple(QAgeAnySet{0b11111111}));
 EXPECT_EQ(1, iResult.ok);
 EXPECT_EQ(3, iResult.n);
 EXPECT_EQ(3, iResult.inserted.size());
 EXPECT_EQ(1, r1Result.ok);
 EXPECT_EQ(0, r1Result.n);
 EXPECT_EQ(1, r2Result.ok);
 EXPECT_EQ(3, r2Result.n);
 }
Source Link
Loki Astari
  • 97.7k
  • 5
  • 126
  • 341

Mongo Queries In C++

The mongo API uses documents (encoded in BSON) as the way to perform queryies (for find / remove etc).

For example a query to find all record where the name field is "John" would look like this (here I will encode the document in JSON for readability but in real life it would be encoded in BSON (which is basically a binary version of JSON).

{
 "name": {
 "$eq": "John"
 }
}

You will notice: The first part is the name of the field "name" then you have the operator "$eq" followed by the value. Note that my serialization library is based on C++ types and will create the appropriate documents automatically if the types are set up correctly. So create the above document I would create following classes:

struct Eq {
 std::string $eq; // I know using $ is an extension.
}
struct FindByName {
 Eq name;
};

Then creating an object would be:

FindByName{{"John"}};

The above example is very trivial version and already requires a doubel braces{{ and }} to initialize. So I wanted to add constructor to make sure we can keep it to one open braces (and deeper nesting don't add up). Side: Note initially I did not use constructors and it became hard to keep track of opening and closing braces without being very careful.

So this would become:

struct Eq {
 std::string $eq; // I know using $ is an extension.
}
struct FindByName {
 FindByName(std::string init)
 : name(std::move(init))
 {}
 Eq name;
};

Since this is a library I don't know the fields names (or the underlying type) as that is part of the application. But the remaining I have created appropriate classes for.

See Member detection within a class for the the class HasCType.

Meta Interface:

// Check downstream type has a type CType.
template<typename T, bool = HasCType<T>::val>
struct ConstructorType_T;
template<typename T>
struct ConstructorType_T<T, true>
{
 using CType = typename T::CType;
};
template<typename T>
struct ConstructorType_T<T, false>
{
 using CType = T;
};
template<typename T>
using ConstructorType = typename ConstructorType_T<T>::CType;

Use this to get CType from child type. If it is not a query type then it will not have a CType and thus it is a value (as in "John") thus we use the T value. Hopefully it will become clear.

template<typename T>
struct Eq {
 using CType = ConstructorType<T>;
 Eq(CType init): $eq{std::move(init)} {}
 T $eq;
};

Then the user of the library would be expected to create their own type following a similar pattern:

template<typename T>
struct FindByName {
 using CType = ConstructorType<T>;
 FindByName(CType init): $eq{std::move(init)} {}
 T name;
};
using FindByNameEqual = FindByName<Eq<std::string>>;
using FindByNameLess = FindByName<Lt<std::string>>;
// etc.
// Construct a query:
auto john = FindByNameEqual{"John"};
auto Middle = FindByNameLess{"Middle"};
template<typename T>
struct FindByAge {
 using CType = ConstructorType<T>;
 FindByName(CType init): $eq{std::move(init)} {}
 T age;
};
using FindByAgeEqual = FindByAge<Eq<std::uint32_t>>;
using FindByAgeLessEqual = FindByAge<Lt<std::uint32_t>>;

So the code review

 /*
 * query-comparison
 * https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
 * Equal => Eq
 * Not Equal => Ne
 * Greater => Gt
 * Greater or Equal => Gte
 * Less => Lt
 * Less or Equal => Lte
 * Matches a value in array => In
 * Matches no values in array => Nin
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/eq/
 * https://www.mongodb.com/docs/manual/reference/operator/query/ne/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/gte/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/lt/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nin/
 */ template<typename T>
 struct Eq {
 using CType = ConstructorType<T>;
 Eq(CType init): $eq{std::move(init)} {}
 T $eq;
 };
 // Same pattern for Ne, Gt, Gte, Lt, Lte (are basically the same as Eq)
 template<typename T>
 struct In
 {
 using CType = std::vector<T>;
 In(CType init): $in{std::move(init)} {}
 std::vector<T> $in;
 }
 // Same pattern for Nin (is basically the same as In)
 /*
 * query-logical
 * https://www.mongodb.com/docs/manual/reference/operator/query-logical/
 * Logical AND of two Values => And
 * Logical OR of two Values => Or
 * Logical NOR of two Values => Nor => NOR(A, B) => !(OR(A,B))
 * Logical NOT => Not
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/and/
 * https://www.mongodb.com/docs/manual/reference/operator/query/or/
 * https://www.mongodb.com/docs/manual/reference/operator/query/nor/
 * https://www.mongodb.com/docs/manual/reference/operator/query/not/
 */
 template<typename LHS, typename RHS>
 struct And
 {
 using CType = And<LHS, RHS>;
 using LP = ConstructorType<LHS>;
 using RP = ConstructorType<RHS>;
 And(LP lhs, RP rhs)
 : $and(std::move(lhs), std::move(rhs))
 {}
 std::tuple<LHS, RHS> $and;
 };
 // Same pattern for Or and Nor
 template<typename T>
 struct Not
 {
 using CType = ConstructorType<T>;
 Not(CType init)
 : $not(std::move(init))
 {}
 T $not;
 };
 /*
 * query-element
 * https://www.mongodb.com/docs/manual/reference/operator/query-element/
 * An element exists in the object => Exists
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 * An element has a specific Type => Type
 * https://www.mongodb.com/docs/manual/reference/operator/query/exists/
 */
 struct Exists
 {
 using CType = bool;
 Exists(bool init)
 : $exists(init)
 {}
 bool $exists;
 };
 struct Type
 {
 using CType = BsonType;
 Type(BsonType type)
 : $type(static_cast<std::int32_t>(type))
 {}
 std::int32_t $type;
 };
 /*
 * query-evaluation
 * https://www.mongodb.com/docs/manual/reference/operator/query-evaluation/
 * Mod field and test remainder => Mod
 * https://www.mongodb.com/docs/manual/reference/operator/query/mod/
 * Regular Expression => RegEx
 * https://www.mongodb.com/docs/manual/reference/operator/query/regex/
 * Text search of whole object => Text
 * https://www.mongodb.com/docs/manual/reference/operator/query/text/
 * Can't seem to get his working.
 *
 */
 struct Mod
 {
 using CType = std::pair<std::uint32_t, std::uint32_t>;
 Mod(CType init)
 : $mod({init.first, init.second})
 {}
 std::array<std::uint32_t, 2> $mod;
 };
 struct RegEx
 {
 using CType = std::pair<std::string, std::string>;
 RegEx(CType init)
 : $regex(std::move(init.first))
 , $options(std::move(init.second))
 {}
 std::string $regex;
 std::string $options;
 };
 struct TextSearch
 {
 std::string $search;
 std::optional<std::string> $language;
 std::optional<bool> $caseSensitive;
 std::optional<bool> $diacriticSensitive;
 };
 struct Text
 {
 Text(std::string search, std::optional<std::string> language = {}, std::optional<bool> cs = {}, std::optional<bool> ds = {})
 : $text{std::move(search), std::move(language), std::move(cs), std::move(ds)}
 {}
 TextSearch $text;
 };
 /*
 * query-array
 * https://www.mongodb.com/docs/manual/reference/operator/query-array/
 * Has all the following elements: => All
 * An array has an element that matches multiple considitions: => ElemMatch
 * An array has a specific size: => Size
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/all/
 * https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/
 * https://www.mongodb.com/docs/manual/reference/operator/query/size/
 */
 template<typename T>
 struct All
 {
 using CType = std::vector<T>;
 All(CType init)
 : $all(std::move(init))
 {}
 std::vector<T> $all;
 };
 template<typename T>
 struct Elements
 {
 std::optional<T> $eq;
 std::optional<T> $ne;
 std::optional<T> $gt;
 std::optional<T> $gte;
 std::optional<T> $lt;
 std::optional<T> $lte;
 };
 template<typename T>
 struct ElemMatch
 {
 ElemMatch(std::optional<T> eq, std::optional<T> ne, std::optional<T> gt, std::optional<T> gte, std::optional<T> lt, std::optional<T> lte)
 : $elemMatch{std::move(eq), std::move(ne), std::move(gt), std::move(gte), std::move(lt), std::move(lte)}
 {}
 Elements<T> $elemMatch;
 };
 struct Size
 {
 using CType = std::uint32_t;
 Size(std::uint32_t init)
 : $size{init}
 {}
 std::uint32_t $size;
 };
 /*
 * Bitwise Query operator
 * https://www.mongodb.com/docs/manual/reference/operator/query-bitwise/
 * AllClear
 * AllSet
 * AnyClear
 * AnySet
 *
 *
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/
 * https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/
󰍵 */
 struct AllClear
 {
 using CType = std::uint32_t;
 AllClear(CType init)
 : $bitsAllClear{std::move(init)}
 {}
 std::uint32_t $bitsAllClear;
 };
 struct AllSet
 {
 using CType = std::uint32_t;
 AllSet(CType init)
 : $bitsAllSet{std::move(init)}
 {}
 std::uint32_t $bitsAllSet;
 };
 struct AnyClear
 {
 using CType = std::uint32_t;
 AnyClear(CType init)
 : $bitsAnyClear{std::move(init)}
 {}
 std::uint32_t $bitsAnyClear;
 };
 struct AnySet
 {
 using CType = std::uint32_t;
 AnySet(CType init)
 : $bitsAnySet{std::move(init)}
 {}
 std::uint32_t $bitsAnySet;
 }; 
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /