Il problema con i booleani in SQLite
Se hai mai lavorato con SQLite, dovresti essere a conoscenza dei tipi di dati supportati e di Boolean
non è uno di loro. Più precisamente come indicato qui:
2.1. Tipo di dati booleano
SQLite non ha una classe di archiviazione booleana separata. Al contrario, i valori booleani vengono memorizzati come numeri interi 0 (falso) e 1 (vero).
SQLite riconosce le parole chiave "TRUE" e "FALSE", a partire dalla versione 3.23.0 (2018-04-02) ma quelle parole chiave sono in realtà solo ortografie alternative per i letterali interi 1 e 0 rispettivamente.
La maggior parte delle librerie JavaScript per SQLite3 non supporta TRUE
e FALSE
parole chiave e richiedono di preparare le istruzioni nel codice utilizzando numeri interi. Ad esempio, in better-sqlite3 dovresti fare questo:
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;
Usando number
invece di boolean
nell'intera app rappresenterebbe un'esperienza di sviluppo terribile (oltre a probabilmente utilizzare più memoria).
Potresti usare una funzione di supporto per trasformare i tuoi oggetti payload in booleano proprietà in numeri (In realtà l'avevo fatto una volta, in passato), ma dovresti eseguirlo manualmente prima di ogni query. Yikes. Non sarebbe fantastico se questa logica venisse eseguita in background, ogni volta che preparavamo ed eseguivamo un'istruzione?
Benvenuto ai proxy ES6 👋
Una delle funzionalità JavaScript più recenti è il Proxy
oggetto. Proxy sono essenzialmente "trappole" che intercettano operazioni sugli oggetti come getter, setter e chiamate di funzione. Utilizzo di proxy possiamo modificare la libreria wrapper SQLite JS per eseguire la nostra logica, un po' come un middleware.
Scrittura della funzione di supporto
Per facilitare lo sviluppo, utilizzeremo mapValues
&isPlainObject
funzioni di utilità da lodash , ma puoi ovviamente codificare le tue. La funzione seguente eseguirà il mapping tramite un oggetto (profondo di un livello) e convertirà valori di tipo boolean
per digitare number
.
import { mapValues } from 'lodash';
const booleanEntriesToNumbers = (object) =>
mapValues(object, (value) =>
typeof value === 'boolean' ? Number(value) : value
);
Utilizzo di proxy per intercettare le chiamate di query
Di seguito importiamo better-sqlite3
libreria e creare una nuova istanza di database. Successivamente, sovrascriviamo il predefinito prepare
metodo con il nostro, che a sua volta sovrascrive i metodi run
, get
e all
, creando un nuovo proxy per ciascuno. Ovviamente puoi creare un proxy per qualsiasi altro metodo che desideri.
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;
In sostanza, una volta chiamata al prepare
viene attivato, diciamo a JavaScript:Aspetta! Vogliamo modificare questa chiamata di funzione. Invece di eseguire la logica prevista dallo sviluppatore originale, vogliamo invece eseguire prima la nostra logica (che è la mappatura del payload dell'oggetto). Dopo aver eseguito la nostra logica, restituiamo il risultato della chiamata al metodo originale utilizzando call
per vincolare il this
discussione. Se vuoi saperne di più su come funzionano i proxy, leggi qui. Per la nostra implementazione abbiamo utilizzato apply
metodo qui.
Grazie per aver letto questo post, spero che abbia aiutato qualcuno a lavorare con SQLite in JavaScript 👊