SQLite
 sql >> Base de données >  >> RDS >> SQLite

Comment gérer les valeurs booléennes dans SQLite à l'aide de proxys JavaScript

Le problème avec les booléens dans SQLite

Si vous avez déjà travaillé avec SQLite, vous devez connaître les types de données pris en charge et Boolean n'en est pas un. Plus précisément, comme indiqué ici :

2.1. Type de données booléen

SQLite n'a pas de classe de stockage booléenne distincte. Au lieu de cela, les valeurs booléennes sont stockées sous forme d'entiers 0 (faux) et 1 (vrai).

SQLite reconnaît les mots-clés "TRUE" et "FALSE", à partir de la version 3.23.0 (2018-04-02) mais ces mots-clés ne sont en fait que des orthographes alternatives pour les entiers littéraux 1 et 0 respectivement.

La plupart des bibliothèques JavaScript pour SQLite3 ne prennent pas en charge TRUE et FALSE mots-clés et ils vous obligent à préparer les instructions dans votre code en utilisant des entiers. Par exemple, dans better-sqlite3, vous devriez faire ceci :

const payload = {
  isActive: 1, // <======
  username: 'Brad',
  password: '1234',
  email: '[email protected]',
};

const result = database
  .prepare(
    `INSERT INTO accounts(isActive, username, password, email) VALUES(@isActive, @username, @password, @email) `
  )
  .run({ bucketID, taskSiteID, name, username, password, email }).changes;

Utilisation de number au lieu de boolean sur l'ensemble de votre application créerait une expérience de développeur terrible (et utiliserait probablement plus de mémoire).

Vous pouvez utiliser une fonction d'assistance pour transformer le booléen de vos objets de charge utile propriétés en nombres (Je l'avais fait une fois, dans le passé), mais vous deviez ensuite l'exécuter manuellement avant chaque requête. Ouais. Ne serait-il pas formidable que cette logique soit exécutée en arrière-plan, chaque fois que nous préparons et exécutons une instruction ?

Bienvenue aux mandataires ES6 👋 

L'une des nouvelles fonctionnalités JavaScript est le Proxy objet. Proxy sont essentiellement des "traps" qui interceptent les opérations d'objet comme les getters, les setters et les appels de fonction. Utilisation de proxy nous pouvons modifier la bibliothèque wrapper SQLite JS pour exécuter notre propre logique, un peu comme un middleware.

Écrire la fonction d'assistance

Pour faciliter le développement, nous allons utiliser mapValues &isPlainObject fonctions utilitaires de lodash , mais vous pouvez bien sûr coder les vôtres. La fonction ci-dessous mappera à travers un objet (profondeur d'un niveau) et convertira les valeurs de type boolean pour taper number .

import { mapValues } from 'lodash';

const booleanEntriesToNumbers = (object) =>
  mapValues(object, (value) =>
    typeof value === 'boolean' ? Number(value) : value
  );

Utilisation de proxys pour intercepter les appels de requête

Ci-dessous, nous importons better-sqlite3 bibliothèque et créez une nouvelle instance de base de données. Ensuite, nous remplaçons la valeur par défaut prepare méthode par la nôtre, qui à son tour remplace les méthodes run , get et all , en créant un nouveau proxy pour chacun. Vous pouvez bien sûr créer un proxy pour toute autre méthode de votre choix.

import Database from 'better-sqlite3';

// Create new database instance
const db = new Database(dbFilePath);

// We will use this function to override the default "prepare" method
const proxiedPrepare = new Proxy(db.prepare, {
    apply: (prepare, prepareThisArg, [stringStatement]) => {
      const statement = prepare.call(prepareThisArg, stringStatement);

      // Override the default "run" method
      statement.run = new Proxy(statement.run, {
        apply: (run, runThisArg, args) => {
          const mappedArgs = args.map((arg) =>
            isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
          );

          return run.call(runThisArg, ...mappedArgs);
        },
      });

      // Override the default "get" method
      statement.get = new Proxy(statement.get, {
        apply: (get, getThisArg, args) => {
          const mappedArgs = args.map((arg) =>
            isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
          );

          return get.call(getThisArg, ...mappedArgs);
        },
      });

      // Override the default "all" method
      statement.all = new Proxy(statement.all, {
        apply: (all, allThisArg, args) => {
          const mappedArgs = args.map((arg) =>
            isPlainObject(arg) ? booleanEntriesToNumbers(arg) : arg
          );

          return all.call(allThisArg, ...mappedArgs);
        },
      });

      return statement;
    },
  });

// Override the default "prepare" method
db.prepare = proxiedPrepare;

Essentiellement, une fois qu'un appel à la prepare est déclenchée, nous disons à JavaScript :Attendez ! Nous voulons modifier cet appel de fonction. Au lieu d'exécuter la logique prévue par le développeur d'origine, nous voulons d'abord exécuter notre propre logique (qui est le mappage de la charge utile de l'objet). Après avoir exécuté notre propre logique, nous renvoyons le résultat de l'appel de la méthode d'origine en utilisant call pour lier le this argument. Si vous voulez en savoir plus sur le fonctionnement des proxys, lisez ici. Pour notre implémentation, nous avons utilisé le apply méthode ici.

Merci d'avoir lu ce post, j'espère que cela a aidé quelqu'un travaillant avec SQLite en JavaScript 👊