SQLite
 sql >> Datenbank >  >> RDS >> SQLite

Umgang mit booleschen Werten in SQLite mit JavaScript-Proxies

Das Problem mit Booleans in SQLite

Wenn Sie jemals mit SQLite gearbeitet haben, sollten Sie die unterstützten Datentypen und Boolean kennen ist keiner von ihnen. Genauer gesagt wie hier angegeben:

2.1. Boolescher Datentyp

SQLite hat keine separate boolesche Speicherklasse. Stattdessen werden boolesche Werte als ganze Zahlen 0 (falsch) und 1 (wahr) gespeichert.

SQLite erkennt die Schlüsselwörter „TRUE“ und „FALSE“ ab Version 3.23.0 (2018-04-02), aber diese Schlüsselwörter sind wirklich nur alternative Schreibweisen für die Integer-Literale 1 bzw. 0.

Die meisten JavaScript-Bibliotheken für SQLite3 unterstützen TRUE nicht und FALSE Schlüsselwörter und erfordern, dass Sie die Anweisungen in Ihrem Code mithilfe von Ganzzahlen vorbereiten. In better-sqlite3 müssten Sie beispielsweise Folgendes tun:

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;

Mit number statt boolean in Ihrer gesamten App würde zu einer schrecklichen Entwicklererfahrung führen (und wahrscheinlich mehr Speicher verbrauchen).

Sie könnten eine Hilfsfunktion verwenden, um den booleschen Wert Ihrer Payload-Objekte umzuwandeln Eigenschaften zu Zahlen (Ich hatte das in der Vergangenheit tatsächlich einmal gemacht), aber dann müssten Sie es vor jeder Abfrage manuell ausführen. Huch. Wäre es nicht großartig, wenn diese Logik jedes Mal im Hintergrund ausgeführt würde, wenn wir eine Erklärung vorbereiten und ausführen?

Willkommen ES6-Proxys 👋 

Eines der neueren JavaScript-Features ist der Proxy Objekt. Proxys sind im Wesentlichen "Fallen", die Objektoperationen wie Getter, Setter und Funktionsaufrufe abfangen. Verwendung von Proxys Wir können die SQLite JS-Wrapper-Bibliothek modifizieren, um unsere eigene Logik auszuführen, ähnlich wie eine Middleware.

Schreiben der Hilfsfunktion

Zur Vereinfachung der Entwicklung werden wir mapValues verwenden &isPlainObject Hilfsfunktionen von lodash , aber Sie können natürlich auch Ihre eigenen codieren. Die folgende Funktion wird durch ein Objekt abgebildet (eine Ebene tief) und Werte vom Typ boolean konvertieren um number einzugeben .

import { mapValues } from 'lodash';

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

Verwenden von Proxys zum Abfangen von Abfrageaufrufen

Unten importieren wir better-sqlite3 Bibliothek und erstellen Sie eine neue Datenbankinstanz. Danach überschreiben wir die Voreinstellung prepare Methode mit unserer eigenen, die wiederum die Methoden run überschreibt , get und all , indem Sie für jeden einen neuen Proxy erstellen. Sie können natürlich einen Proxy für jede andere Methode erstellen, die Sie möchten.

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;

Im Wesentlichen einmal einen Aufruf zum prepare Methode ausgelöst wird, teilen wir JavaScript mit:Warte! Wir wollen diesen Funktionsaufruf modifizieren. Anstatt die vom ursprünglichen Entwickler beabsichtigte Logik auszuführen, möchten wir zuerst unsere eigene Logik ausführen (die die Zuordnung der Objektnutzlast darstellt). Nachdem wir unsere eigene Logik ausgeführt haben, geben wir das Ergebnis des Aufrufs der ursprünglichen Methode zurück, indem wir call verwenden um den this zu binden Streit. Wenn Sie mehr darüber erfahren möchten, wie Proxys funktionieren, lesen Sie hier. Für unsere Implementierung haben wir den apply verwendet Methode hier.

Vielen Dank für das Lesen dieses Beitrags. Ich hoffe, er hat jemandem geholfen, der mit SQLite in JavaScript arbeitet 👊