Add order/limit support to find queries
This commit is contained in:
parent
b4eeb2c296
commit
153725f785
4 changed files with 91 additions and 18 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@endeavorance/prequel",
|
"name": "@endeavorance/prequel",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -27,7 +27,10 @@ export class PrequelKVStore {
|
||||||
primary: true,
|
primary: true,
|
||||||
autoincrement: true,
|
autoincrement: true,
|
||||||
},
|
},
|
||||||
key: "TEXT",
|
key: {
|
||||||
|
type: "TEXT",
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
value: "TEXT",
|
value: "TEXT",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
63
src/table.ts
63
src/table.ts
|
@ -136,27 +136,36 @@ export class Table<RowShape> {
|
||||||
.map((colName) => `$${colName}`)
|
.map((colName) => `$${colName}`)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
this._createTableSQL = `CREATE TABLE IF NOT EXISTS "${name}" (${columnSQL});`;
|
|
||||||
const insertSql = `INSERT INTO "${name}" (${allCols}) VALUES (${allColVars}) RETURNING *;`;
|
|
||||||
const updateSql = `UPDATE "${name}" SET ${updateColumnSQL} WHERE "${this._primaryColumnName}" = $${this._primaryColumnName} RETURNING *;`;
|
|
||||||
const getByIdSql = `SELECT * FROM "${name}" WHERE "${this._primaryColumnName}" = ? LIMIT 1;`;
|
|
||||||
const delByIdSql = `DELETE FROM "${name}" WHERE "${this._primaryColumnName}" = ?;`;
|
|
||||||
const deleteRowSql = `DELETE FROM "${name}" WHERE "${this._primaryColumnName}" = $${this._primaryColumnName};`;
|
|
||||||
const truncateQuery = `DELETE FROM "${name}";`;
|
|
||||||
|
|
||||||
// Ensure the table exists in the database
|
// Ensure the table exists in the database
|
||||||
|
this._createTableSQL = `CREATE TABLE IF NOT EXISTS "${name}" (${columnSQL});`;
|
||||||
this.db.exec(this._createTableSQL);
|
this.db.exec(this._createTableSQL);
|
||||||
|
|
||||||
// Prepare common queries
|
// Prepare common queries
|
||||||
this._insertQuery = this.db.query<RowShape, ArbitraryRow>(insertSql);
|
this._insertQuery = this.db.query<RowShape, ArbitraryRow>(
|
||||||
this._updateQuery = this.db.query<RowShape, ArbitraryRow>(updateSql);
|
`INSERT INTO "${name}" (${allCols}) VALUES (${allColVars}) RETURNING *;`,
|
||||||
this._findQuery = this.db.query<RowShape, string>(getByIdSql);
|
);
|
||||||
this._deleteQuery = this.db.query<void, string>(delByIdSql);
|
|
||||||
|
this._updateQuery = this.db.query<RowShape, ArbitraryRow>(
|
||||||
|
`UPDATE "${name}" SET ${updateColumnSQL} WHERE "${this._primaryColumnName}" = $${this._primaryColumnName} RETURNING *;`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this._findQuery = this.db.query<RowShape, string>(
|
||||||
|
`SELECT * FROM "${name}" WHERE "${this._primaryColumnName}" = ? LIMIT 1;`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this._deleteQuery = this.db.query<void, string>(
|
||||||
|
`DELETE FROM "${name}" WHERE "${this._primaryColumnName}" = ?;`,
|
||||||
|
);
|
||||||
|
|
||||||
this._sizeQuery = this.db.query<{ count: number }, []>(
|
this._sizeQuery = this.db.query<{ count: number }, []>(
|
||||||
`SELECT count(*) as count FROM ${name}`,
|
`SELECT count(*) as count FROM ${name}`,
|
||||||
);
|
);
|
||||||
this._deleteRowQuery = this.db.query<void, ArbitraryRow>(deleteRowSql);
|
|
||||||
this._truncateQuery = this.db.query<void, []>(truncateQuery);
|
this._deleteRowQuery = this.db.query<void, ArbitraryRow>(
|
||||||
|
`DELETE FROM "${name}" WHERE "${this._primaryColumnName}" = $${this._primaryColumnName};`,
|
||||||
|
);
|
||||||
|
|
||||||
|
this._truncateQuery = this.db.query<void, []>(`DELETE FROM "${name}"`);
|
||||||
|
|
||||||
// If using with a Prequel instance, ensure the table is attached
|
// If using with a Prequel instance, ensure the table is attached
|
||||||
// This is only for when creating tables directly and passing in a Prequel instance
|
// This is only for when creating tables directly and passing in a Prequel instance
|
||||||
|
@ -424,12 +433,34 @@ export class Table<RowShape> {
|
||||||
* The keys are column names and the values are the values to match.
|
* The keys are column names and the values are the values to match.
|
||||||
* @returns An array of rows that match the specified conditions.
|
* @returns An array of rows that match the specified conditions.
|
||||||
*/
|
*/
|
||||||
public findAllWhere(conditions: Partial<RowShape>): RowShape[] {
|
public findAllWhere(
|
||||||
|
conditions: Partial<RowShape>,
|
||||||
|
options?: {
|
||||||
|
order?: string;
|
||||||
|
limit?: number;
|
||||||
|
skip?: number;
|
||||||
|
},
|
||||||
|
): RowShape[] {
|
||||||
const keys = Object.keys(conditions);
|
const keys = Object.keys(conditions);
|
||||||
const whereParts = keys.map((key) => `"${key}" = $${key}`);
|
const whereParts = keys.map((key) => `"${key}" = $${key}`);
|
||||||
const whereClause = whereParts.join(" AND ");
|
const whereClause = whereParts.join(" AND ");
|
||||||
|
|
||||||
|
const optionParts: string[] = [];
|
||||||
|
|
||||||
|
if (options?.order) {
|
||||||
|
optionParts.push(`ORDER BY ${options.order}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.limit) {
|
||||||
|
optionParts.push(`LIMIT ${options.limit}`);
|
||||||
|
|
||||||
|
if (options?.skip) {
|
||||||
|
optionParts.push(`OFFSET ${options.skip}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const query = this._db.prepare<RowShape, ArbitraryRow>(
|
const query = this._db.prepare<RowShape, ArbitraryRow>(
|
||||||
`SELECT * FROM ${this} WHERE ${whereClause};`,
|
`SELECT * FROM ${this} WHERE ${whereClause} ${optionParts.join(" ")};`,
|
||||||
);
|
);
|
||||||
|
|
||||||
return query.all(shapeForQuery(conditions));
|
return query.all(shapeForQuery(conditions));
|
||||||
|
|
|
@ -222,6 +222,45 @@ test(".findAllWhere() returns a row based on basic criteria", () => {
|
||||||
expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" });
|
expect(manyJacks[2]).toEqual({ user_id: 3, name: "Jack" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(".findAllWhere() can set a limit on returned rows", () => {
|
||||||
|
const table = makeTestTable();
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
|
||||||
|
const manyJacks = table.findAllWhere({ name: "Jack" }, { limit: 1 });
|
||||||
|
|
||||||
|
expect(manyJacks).toHaveLength(1);
|
||||||
|
expect(manyJacks[0]).toEqual({ user_id: 1, name: "Jack" });
|
||||||
|
});
|
||||||
|
|
||||||
|
test(".findAllWhere() can set an offset on a limit", () => {
|
||||||
|
const table = makeTestTable();
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
|
||||||
|
const manyJacks = table.findAllWhere({ name: "Jack" }, { limit: 1, skip: 1 });
|
||||||
|
|
||||||
|
expect(manyJacks).toHaveLength(1);
|
||||||
|
expect(manyJacks[0]).toEqual({ user_id: 2, name: "Jack" });
|
||||||
|
});
|
||||||
|
|
||||||
|
test(".findAllWhere() can set an order on the results", () => {
|
||||||
|
const table = makeTestTable();
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
table.insertPartial({ name: "Jack" });
|
||||||
|
|
||||||
|
const manyJacks = table.findAllWhere(
|
||||||
|
{ name: "Jack" },
|
||||||
|
{ order: "user_id DESC" },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(manyJacks).toHaveLength(3);
|
||||||
|
expect(manyJacks[0]).toEqual({ user_id: 3, name: "Jack" });
|
||||||
|
});
|
||||||
|
|
||||||
test(".findOneById() returns a single element by its primary column", () => {
|
test(".findOneById() returns a single element by its primary column", () => {
|
||||||
const table = makeTestTable();
|
const table = makeTestTable();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue