Skip to main content
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;
  };