Partage
  • Partager sur Facebook
  • Partager sur Twitter

[ndb] Interface générique pour SGBD en C++17

Sqlite, MySQL, MongoDB

    9 avril 2018 à 1:46:09

    Salut !

    ndb est une interface générique qui permet de se connecter à n'importe quel moteur de données (SGBD ou autre) Les requêtes sont écrites en C++ et peuvent être exécutées sur différents moteurs de données en changeant un seul paramètre. Les modèles de données sont définis et accessibles compile-time . MySQL et MongoDB sont encore en cours de dev/test.

    Tester : https://github.com/ads00/ndb

    Features

    • Header only
    • Requêtes full C++ et génériques
    • Support de tout type de moteur de données
    • Expressions SQL générées compile-time
    • Gestion des types personnalisable
    • Accès aux champs via des structures

    Exemple

    Database (avec macros)

    ndb_table(movie,
              ndb_field(id, int, 8),
              ndb_field(name, std::string, 255)
    )
    ndb_table(music,
              ndb_field(id, int, 8),
              ndb_field(image, std::string, 255)
    )
    

    Requêtes

    ndb::query<zeta>() << ( movie.id, movie.image ); // get
    ndb::query<zeta>() << ( movie.id == a && movie.name == b ); // get all par condition
    ndb::query<zeta>() + ( movie.id = 3, movie.name = "test" ); // add
    ndb::query<zeta>() - ( movie.id == 3 ); // del
    

    Résultats

    for (auto& line : ndb::query<:zeta>() << (movie.id, movie.name) )
    {
        std::cout << "\nID : " <<  line[movie.id];
        std::cout << "\nName : " <<  line[movie.name];
    }
    

    Minimal

    Un exemple avec une base de donnée libray en utilisant le modèle collection et une table movie

    #include "my_database.h"
    
    static constexpr const models::collection collection;
    
    int main()
    {
        const auto& movie = collection.movie;
    
        ndb::initializer<sqlite> init;
        ndb::connect<library>();
    
        ndb::query<library>() + (movie.name = "Interstellar", movie.duration = 2.49_h) );
    
        return 0;
    }
    

    Edit: Ajout de balise html pour afficher le code correctement

    -
    Edité par ads00 9 avril 2018 à 18:18:40

    • Partager sur Facebook
    • Partager sur Twitter
      12 avril 2018 à 5:00:08

      Salut,

      Je viens d'ajouter la gestion des fonctions paramétrées ou d'expressions dans les requêtes. Ce qui permet de faire :

      ndb::query<dbs::zeta>() <<  ( (ndb::now(), ndb::count(movie.id), movie.name) << ndb::limit(2) );
      

      Mais je me demande comment récupérer les résultats sans utiliser les index. Soit je crée un système d'alias pour les champs accessibles via une structure pour le scope actuel. Soit on accède au champs via son expression ce qui donnerait

      ndb_alias(count, ndb::count(movie.id), now, ndb::now()); // création de la structure
      ndb::query<zeta>() << (alias.now(), alias.count(), movie.name);
      line[alias.now]; // accès
      
      ou
      ndb::query<zeta>() << (ndb::now(), ndb::count(movie.id), movie.name);
      line[now()]; // accès
      line[count(movie.id)];
      

      -
      Edité par ads00 12 avril 2018 à 5:03:20

      • Partager sur Facebook
      • Partager sur Twitter
        4 mai 2018 à 1:28:50

        Salut,

        Quelques mise à jour pour créer un modèle avec les macros :

        ndb_table(movie,
                  ndb_field(id, int, 8),
                  ndb_field(name, std::string, 255)
        )
        ndb_table(music,
                  ndb_field(id, int, 8),
                  ndb_field(image, std::string, 255)
        )
        // ndb_model([model_name], [tables]...)
        ndb_model(library, movie, music)
        
        // ndb_project([project_name], [databases]...)
        // ndb_database([database_name], [model_name], [engine])
        ndb_project(my_project,
                    ndb_database(alpha, library, ndb::sqlite),
                    ndb_database(zeta, library, ndb::sqlite)
        )



        • Partager sur Facebook
        • Partager sur Twitter
          17 mai 2018 à 3:10:56

          Salut,

          Quelques modifications côté macro et améliorations de la gestion des types.

          Voici un exemple complet fonctionnel :

          #include <ndb/initializer.hpp>
          #include <ndb/engine/sqlite/sqlite.hpp> // engine
          #include <ndb/preprocessor.hpp> // database macros
          #include <ndb/function.hpp> // ndb::clear
          #include <ndb/query.hpp> // query and expression
          
          // database
          ndb_table(movie,
                     ndb_field_id,
                    ndb_field(name, std::string, ndb::size<255>),
                    ndb_field(duration, std::chrono::duration<double, std::ratio<3600>>)
          )
          ndb_table(music,
                    ndb_field(id, int),
                    ndb_field(image, std::string)
          )
          ndb_model(library, movie, music)
          
          ndb_project(my_project,
                      ndb_database(library, library, ndb::sqlite)
          )
          
          /*! Generate constexpr instances usable for query expressions
           * Names are generated in namespaces ndb::models, ndb::tables, ndb::databases::my_project
           *
           * Add _ to the name tu use the type (example : ndb::tables::movie_)
           */
          
          // alias
          namespace dbs
          {
              using library = ndb::databases::my_project::library_;
          }
          
          int main()
          {
              using namespace std::chrono_literals;
              const auto& movie = ndb::models::library.movie;
          
              ndb::initializer<ndb::sqlite> init;
              //! connect to database library
              ndb::connect<dbs::library>();
              //! clear movie table
              ndb::clear<dbs::library>(movie);
          
              //! add records
              ndb::query<dbs::library>() + (movie.name = "Interstellar", movie.duration = 2.49h);
              ndb::query<dbs::library>() + (movie.name = "Watchmen", movie.duration = 3.30h);
          
              //! get movie with specified duration
              //! missing informations of the query are deduced compile time
              //! for SQL-like engines, the string is generated compile time
              for (auto& line : ndb::query<dbs::library>() << (movie.duration == 3.30h))
              {
                  //! access fields from result using field name
                  std::cout << "movie.id : " << line[movie.id] << std::endl;
                  std::cout << "movie.name : " << line[movie.name] << std::endl;
                  std::cout << "movie.duration : " << line[movie.duration].count() << " Hours" << std::endl;
              }
              return 0;
          }



          • Partager sur Facebook
          • Partager sur Twitter
            19 mai 2018 à 21:29:58

            Salut,

            Le modèle génère maintenant des objets qui sont directement utilisable lors des requêtes :

                auto [interstellar] = ndb::oquery<dbs::library>() << (movie.name == "Watchmen");
                std::cout << interstellar.id << " | " << interstellar.name << std::endl;
            
                for (auto [id, name, duration] : ndb::oquery<dbs::library>() << movie)
                {
                    std::cout << "id : " << id << std::endl;
                    std::cout << "name : " << name << std::endl;
                    std::cout << "name : " << duration.count() << std::endl;
                }



            • Partager sur Facebook
            • Partager sur Twitter
              21 juin 2018 à 4:53:39

              Salut,

              - La gestion des types a été améliorées via l'utilisation de proxy types.

              On peut donc redéfinir les types dans différents scopes.

              // proxy_type -> cpp_type
              template<> struct type_map<ndb::string_, ndb::scope::database<my_db>> { using type = my_string; };
              
              // cpp_type -> proxy_type
              template<> struct type_map<my_string, ndb::scope::group<my_project>> { using type = ndb::string_; };
              
              template<> struct type_map<QString, ndb::scope::group<my_project>> { using type = ndb::string_; };
              
              

              - Les customs types peuvent utiliser les types utilisateurs :

                  template<class Database>
                  struct custom_type<const char*, Database> : basic_type<const char*, string_, Database>
                  {
                      static auto encode(const char* v)
                      {
                          return cpp_type_t<string_, Database>{ v };
                      }
                  };

              - On peut désormais utiliser des requêtes natives au SGBD

              - Ajout de paramètres lors de la connexion

              - Gestion de PostgreSQL en cours

              • Partager sur Facebook
              • Partager sur Twitter
                25 juin 2018 à 13:32:01

                Super projet, déjà intégré dans Sielo ! Comme quoi on peut déjà faire beaucoup de chose avec ndb ;)

                • Partager sur Facebook
                • Partager sur Twitter
                Au plaisir de vous aider =D, venez voir ce que je fais
                  8 juillet 2018 à 2:46:44

                  Salut,

                  Je suis en train de refaire la partie expressions dans le but d'ajouter les alias, les requêtes préparées, les formes d'expressions et les requêtes dynamiques.

                  Je cherche quelqu'un qui serait intéressé par l'implémentation  de l'interface pour un moteur de stockage utilisant des expressions différentes de SQL comme bson ou même un moteur qui effectue les opérations de l'expression directement sur le moteur (un tuple engine par exemple)

                  Si ça vous intéresse, contactez moi !

                  • Partager sur Facebook
                  • Partager sur Twitter
                    17 novembre 2018 à 9:59:36

                    Salut,

                    Quelques news concernant la prochaine version.

                    La syntaxe va légèrement changer pour devenir plus explicite (tout en restant générique) et permettre la construction de requêtes dynamiques et préparées.

                    La déduction sera mise de côté, c'est une feature qui demande plus d'opérations à la compilation (ce qui peut gêner les gens qui n'ont pas de supercalculateur) et qui demande plus de temps de maintenance (principalement à cause de msvc).

                    La feature sera peut-être rajouter plus tard mais de manière optionnelle.

                    J'ajouterai à la place plus de fonctions utilitaires permettant d'effectuer des requêtes standards.


                    constexpr auto expr = ndb::statement << ndb::del << ndb::source(movie) << ndb::filter(movie.id == 3);
                    
                    ndb::dynamic_statement<db> stmt;
                    stmt << ndb::get(movie.id, movie.name) << ndb::source(movie) << ndb::filter;
                    for (int i = 0; i < 3; ++i)
                    {
                        stmt << (movie.id == 3);
                    
                        if (i < 3 - 1) stmt << ndb::logical_and;
                    }
                    std::cout << "\ndyn output : " << stmt.native();
                    


                    -
                    Edité par ads00 17 novembre 2018 à 10:00:14

                    • Partager sur Facebook
                    • Partager sur Twitter
                      22 février 2019 à 18:49:00

                      Salut,

                      Il est désormais possible d'ajouter des options à la création des tables.

                      ndb_table(movie
                          , ndb_field_id
                          , ndb_field(name, std::string, ndb::size<255>, ndb::option<ndb::field_option::unique>)
                          , ndb_field(duration, std::chrono::duration<double, std::ratio<3600>>)
                          , ndb_option(ndb::table_option::unique<name_, duration_>)

                      La gestion des relations se fera par une interface orientée graph mais permettra de gérer n'importe quel type de SGBD.

                      Ca devrait ressembler à ça, je ne sais pas encore si le style Cypher sera implémenté.

                      // cypher
                      ndb::query<dbs::library>() << (ndb::get() << ndb::relation(user - user.arcs.possess -> music || user - user.arcs.like - music) << ndb::filter(user.name == "ads"s))
                      
                      // fonctionnel
                      ndb::query<dbs::library>() << (ndb::get() << ndb::relation(ndb::arc(user, music, user.arcs.possess) || ndb::edge(user, music, user.arcs.like)) << ndb::filter(user.name == "ads"s))




                      • Partager sur Facebook
                      • Partager sur Twitter
                        2 mai 2019 à 20:37:11

                        Salut,

                        • Les requêtes préparées sont prêtes !

                            ndb_prepare(get_movie) &lt;&lt; (ndb::get()
                               &lt;&lt; ndb::source(movie)
                                &lt;&lt; ndb::filter(movie.duration == _ || movie.name == _));
                          
                        • La limite du nombre de champs a été levées.

                        • On peut définir un type de retour pour les expressions
                        • Les alias sont prêts !
                          ndb_alias(count_id, ndb::count(movie.id));
                          ndb_alias(count_name, ndb::count(movie.name));
                          auto q = (
                          ndb::get(
                            movie.id, movie.name, movie.duration
                            , count_id
                            , count_name)
                            &lt;&lt; ndb::source(movie)
                          );
                          ...
                          

                        std::cout << "count_id : " << line[count_id] << std::endl;

                        -
                        Edité par ads00 2 mai 2019 à 20:43:10

                        • Partager sur Facebook
                        • Partager sur Twitter
                          31 mai 2019 à 22:56:54

                          Salut,

                          La gestion des objets a été ajoutée, ça ressemble à ça

                          ::movie interstellar{ "params" };
                          interstellar.name = "Interstellar";
                          interstellar.duration = 2.49h;
                          ndb::store(interstellar);
                          
                          ::movie movie{"params"};
                          ndb::load(movie, interstellar.oid);
                          movie.display();
                          movie.edit();
                          ndb::store(movie);
                          
                          auto edited_movie = ndb::make&lt;::movie&gt;(movie.oid, "params");
                          edited_movie.display();
                          
                          ndb::unload(interstellar);
                          interstellar.display();
                          

                          L'exemple complet se trouve ici : https://github.com/ads00/ndb/blob/dev/example/object.cpp

                          • Partager sur Facebook
                          • Partager sur Twitter
                            2 septembre 2019 à 15:59:35

                            Salut,
                            J'ai ajouté un système de variables persistantes qui permettent par exemple de créer des sauvegardes de configuration.

                            struct browser : ndb::persistent_group
                            {
                                using persistent_group::persistent_group;
                            
                                ndb::persistent<std::string> home{ this, "home", "www.google.fr" };
                            
                                struct : ndb::persistent_group
                                {
                                    using persistent_group::persistent_group;
                            
                                    ndb::persistent<bool> auto_load{ this, "auto_load", false };
                                    ndb::persistent<int> history_size{ this, "history_size", 10 };
                                } tabs{ this, "tabs" };
                            };
                            
                            int main()
                            {
                                ndb::initializer<ndb::sqlite> init;
                            
                                ::browser browser{ "browser" };
                            
                                std::cout << "\n" << browser.home.path() << " : " << browser.home.get();
                                std::cout << "\n" << browser.tabs.auto_load.path() << " : " << browser.tabs.auto_load.get();
                                std::cout << "\n" << browser.tabs.history_size.path() << " : " << browser.tabs.history_size.get();
                            
                                browser.home = "https://openclassrooms.com";
                                browser.tabs.auto_load = true;
                                browser.tabs.history_size = 200;
                            
                                return 0;
                            }




                            1ère exécution :
                            browser.home : www.google.fr
                            browser.tabs.auto_load : 0
                            browser.tabs.history_size : 10

                            Exécutions suivantes:
                            browser.home : https://zestedesavoir.com
                            browser.tabs.auto_load : 1
                            browser.tabs.history_size : 200



                            Exemple complet : https://github.com/ads00/ndb/blob/dev/example/persistent.cpp

                            -
                            Edité par ads00 2 septembre 2019 à 16:10:23

                            • Partager sur Facebook
                            • Partager sur Twitter

                            [ndb] Interface générique pour SGBD en C++17

                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                            • Editeur
                            • Markdown