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

Ecrire des requêtes pour n'importe quel SGBD en C++

a marqué ce sujet comme résolu.
Auteur du sujet

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
  • Déduction d’expressions partielles

Exemple

Database (avec macros)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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(library, movie, music)

ndb_project(my_project,
            ndb_database(alpha, library, ndb::sqlite),
            ndb_database(zeta, library, ndb::mongo)
)

Requêtes

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

Résultats

1
2
3
4
5
for (auto& line : ndb::query<dbs::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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include "my_database.h"

int main()
{
    const auto& movie = ndb::models::collection.movie; // alias

    ndb::initializer<ndb::sqlite> init;
    ndb::connect<dbs::library>();

    ndb::query<dbs::library>() + (movie.name = "Interstellar", movie.duration = 2.49_h) );

    return 0;
}

Édité par ads00

+4 -0
Auteur du sujet

Salut,

Voici un exemple complet fonctionnel qui montre l’utilisation de type custom

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#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

/*! Generate database entities (instances, structures etc ...)
 * Entites are generated in namespaces ndb::models, ndb::tables, ndb::databases::my_project
 * Add _ to the name tu use the type (example : ndb::tables::movie_)
 */

// 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)
)

// 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;
}

Édité par ads00

+3 -0
Auteur du sujet

Salut,

Nouvelle feature (bientôt complète), les oquery (object query)

Prenons la table movie, on peut désormais récupérer un objet movie depuis une requête

La génération sera automatique demain, il suffira donc de remplacer ndb::query par ndb::oquery pour avoir un objet à la place d’une ligne.

1
2
3
4
5
6
7
8
auto [interstellar] =  ndb::oquery<dbs::library>() << (movie.id == 1);
std::cout << interstellar.id << " | " << interstellar.name << std::endl;

for (auto [id, name] : ndb::oquery<dbs::library>() << movie)
{
    std::cout << "id : " << id << std::endl;
    std::cout << "name : " << name << std::endl;
}

Ce qui donne :

1
2
3
4
5
1 | Interstellar
id : 1
name : Interstellar
id : 2
name : Watchmen
+3 -0
Auteur du sujet

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 !

+0 -0
Auteur du sujet

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();
+1 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte