¿Cómo puedo combinar múltiples funciones de puntos knex en una función de puntos grandes?
Por ejemplo tengo esta consulta:
await knex .from(common) .select('x','y') .innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`) .where({ field1: 1, field2: 2 })Las declaraciones de unión interna y whereNull son comunes a algunas otras consultas. ¿Es posible extraer esta parte?
.innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`) y reemplácelo con una función, .joinWhereNull .
Entonces la consulta original podría reescribirse así:
await knex .from(common) .select('x','y') .joinWhereNull() .where({ field1: 1, field2: 2 })He intentado escribir funciones personalizadas como esta
const joinWhereNull = () => { return (query) => { return query .innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`) } } Sin embargo, esto se siente torpe de usar y descubrí que la declaración de select también necesita su propia función, de lo contrario, se llama prematuramente.
¿Hay una manera más fácil de combinar funciones knex?
Editar También intenté usar un QueryBuilder personalizado:
Knex.QueryBuilder.extend('joinWhereNull', function (table1, table2) { return this .innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`) })Sin embargo, aparece un error de tipo que indica que joinwherenull no es una función.
Puede utilizar el enfoque de QueryBuilder como se muestra aquí: Ampliación del Generador de consultas .
Sin embargo, TypeScript dará un error de tipo a menos que amplíe la interfaz de joinWhereNull con la función joinWhereNull. Las instrucciones para hacerlo están en la misma página: Ampliación del generador de consultas .
Citando aquí:
- Cree un archivo knex.d.ts dentro de una carpeta @types (o cualquier otra carpeta).
// knex.d.ts import { Knex as KnexOriginal } from 'knex'; declare module 'knex' { namespace Knex { interface QueryBuilder { customSelect<TRecord, TResult>(value: number): KnexOriginal.QueryBuilder<TRecord, TResult>; } } }
- Agregue la nueva carpeta @types a typeRoots en su tsconfig.json.
// tsconfig.json { "compilerOptions": { "typeRoots": [ "node_modules/@types", "@types" ], } }
Lamentablemente, hay varias instancias de QueryBuilder en juego. Por lo tanto, definir el nuevo método en una instancia no funciona. Podemos establecer el nuevo método en QueryBuilder.prototype para que esté disponible en todas las instancias.
El siguiente código funcionó en mi entorno de prueba:
const knex = require('knex')({ client: 'sqlite3', connection: { filename: './mydb.sqlite' }}); let common = 'common'; let table1 = 'table1'; let table2 = 'table2'; let field1 = 'field1'; let field2 = 'field2'; // using default methods console.log('one: ', knex .from(common) .select('x', 'y') .innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`) .where({ field1: 1, field2: 2, }) .toString() ); // define the new method //knex.queryBuilder().__proto__['joinWhereNull'] = function () { Object.getPrototypeOf(knex.queryBuilder())['joinWhereNull'] = function () { return this.innerJoin(table1, `${table2}.id`, `${table1}.table2_id`) .whereNull(`${table1}.date`) .whereNull(`${table1}.file`); }; // using our method console.log('two: ', knex .from(common) .select('x', 'y') .joinWhereNull() .where({ field1: 3, field2: 4, }) .toString() );Producción:
one: select `x`, `y` from `common` inner join `table1` on `table2`.`id` = `table1`.`table2_id` where `table1`.`date` is null and `table1`.`file` is null and `field1` = 1 and `field2` = 2 two: select `x`, `y` from `common` inner join `table1` on `table2`.`id` = `table1`.`table2_id` where `table1`.`date` is null and `table1`.`file` is null and `field1` = 3 and `field2` = 4 No tengo mucha idea de cómo funciona internamente la biblioteca. Pero encontré el siguiente comentario en el código:
hacer-knex.js
// Allow chaining methods from the root object, before // any other information is specified. // // TODO: `QueryBuilder.extend(..)` allows new QueryBuilder // methods to be introduced via external components. // As a side-effect, it also pushes the new method names // into the `QueryInterface` array. // // The Problem: due to the way the code is currently // structured, these new methods cannot be retroactively // injected into existing `knex` instances! As a result, // some `knex` instances will support the methods, and // others will not. // // We should revisit this once we figure out the desired // behavior / usage. For instance: do we really want to // allow external components to directly manipulate `knex` // data structures? Or, should we come up w/ a different // approach that avoids side-effects / mutation? // // (FYI: I noticed this issue because I attempted to integrate // this logic directly into the `KNEX_PROPERTY_DEFINITIONS` // construction. However, `KNEX_PROPERTY_DEFINITIONS` is // constructed before any `knex` instances are created. // As a result, the method extensions were missing from all // `knex` instances.)