@easy-cms/drizzle 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +107 -9
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -214,4 +214,5 @@ declare class Migrator {
|
|
|
214
214
|
private files;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
export {
|
|
217
|
+
export { DEFAULT_MIGRATION_DIR, DEFAULT_TABLE_PREFIX, Migrator, buildSchema, connectDatabase, snake, unionSnapshot };
|
|
218
|
+
export type { AnyColumn, AnyTable, ColumnBuilder, Connection, Dialect, DrizzleAdapterOptions, DrizzleDb, IndexBuilder, JsonElement, JsonValueType, SchemaModel, Snapshot, SqlRunner, Statement };
|
package/dist/index.js
CHANGED
|
@@ -1031,6 +1031,49 @@ var WhereBuilder = class {
|
|
|
1031
1031
|
order.push(desc(col2(model, "id")));
|
|
1032
1032
|
return order;
|
|
1033
1033
|
}
|
|
1034
|
+
/**
|
|
1035
|
+
* The columns a cursor goes by, in `orderBy`'s order: the sort's, then `id`. Only plain
|
|
1036
|
+
* columns: a sort on a value inside blocks has nothing to compare a cursor with.
|
|
1037
|
+
*/
|
|
1038
|
+
keyset(model, sort) {
|
|
1039
|
+
const keys = sort.map((entry) => {
|
|
1040
|
+
const descending = entry.startsWith("-");
|
|
1041
|
+
const path = descending ? entry.slice(1) : entry;
|
|
1042
|
+
const target = resolvePath(model, path.split("."), this.drafts, this.localization);
|
|
1043
|
+
if (target.kind !== "column")
|
|
1044
|
+
throw new QueryError(
|
|
1045
|
+
`A cursor can't follow a sort by "${path}"; sort by a field of the document, or use page`
|
|
1046
|
+
);
|
|
1047
|
+
return { column: target.column, descending };
|
|
1048
|
+
});
|
|
1049
|
+
keys.push({ column: col2(model, "id"), descending: true });
|
|
1050
|
+
return keys;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Rows after `values` (the keys of the last row of the page before) in the keyset's order.
|
|
1054
|
+
* Where nulls go follows the database's default order, as `orderBy` leaves it: last in
|
|
1055
|
+
* ascending order on Postgres, first on SQLite.
|
|
1056
|
+
*/
|
|
1057
|
+
after(keys, values) {
|
|
1058
|
+
const nullsHigh = this.dialect.name === "postgres";
|
|
1059
|
+
const equal = (key, value) => value === null ? isNull(key.column) : eq2(key.column, value);
|
|
1060
|
+
const later = (key, value) => {
|
|
1061
|
+
const nullsLater = nullsHigh !== key.descending;
|
|
1062
|
+
if (value === null) return nullsLater ? sql`1 = 0` : isNotNull(key.column);
|
|
1063
|
+
const beyond = key.descending ? lt(key.column, value) : gt(key.column, value);
|
|
1064
|
+
return nullsLater ? or(beyond, isNull(key.column)) : beyond;
|
|
1065
|
+
};
|
|
1066
|
+
const branches = keys.map(
|
|
1067
|
+
(key, i) => and2(...keys.slice(0, i).map((k, j) => equal(k, values[j])), later(key, values[i]))
|
|
1068
|
+
);
|
|
1069
|
+
const rest = or(...branches);
|
|
1070
|
+
const [first] = keys;
|
|
1071
|
+
const start = values[0];
|
|
1072
|
+
if (!first || start === null || start === void 0) return rest;
|
|
1073
|
+
const from = first.descending ? lte(first.column, start) : gte(first.column, start);
|
|
1074
|
+
const bound = nullsHigh !== first.descending ? or(from, isNull(first.column)) : from;
|
|
1075
|
+
return and2(bound, rest);
|
|
1076
|
+
}
|
|
1034
1077
|
};
|
|
1035
1078
|
|
|
1036
1079
|
// src/database.ts
|
|
@@ -1052,7 +1095,8 @@ async function connectDatabase(dialect, connection, args, options = {}) {
|
|
|
1052
1095
|
});
|
|
1053
1096
|
if (args.schema === "push") await migrator.push();
|
|
1054
1097
|
else if (args.schema === "verify") await migrator.verify();
|
|
1055
|
-
|
|
1098
|
+
const countCacheMs = (args.config.countCacheSeconds ?? 5) * 1e3;
|
|
1099
|
+
return new DrizzleDatabase(dialect, connection, schema, migrator, prefix, countCacheMs);
|
|
1056
1100
|
} catch (error) {
|
|
1057
1101
|
await connection.close().catch(() => {
|
|
1058
1102
|
});
|
|
@@ -1060,12 +1104,13 @@ async function connectDatabase(dialect, connection, args, options = {}) {
|
|
|
1060
1104
|
}
|
|
1061
1105
|
}
|
|
1062
1106
|
var DrizzleDatabase = class {
|
|
1063
|
-
constructor(dialect, connection, schema, migrator, tablePrefix) {
|
|
1107
|
+
constructor(dialect, connection, schema, migrator, tablePrefix, countCacheMs = 0) {
|
|
1064
1108
|
this.dialect = dialect;
|
|
1065
1109
|
this.connection = connection;
|
|
1066
1110
|
this.schema = schema;
|
|
1067
1111
|
this.migrator = migrator;
|
|
1068
1112
|
this.tablePrefix = tablePrefix;
|
|
1113
|
+
this.countCacheMs = countCacheMs;
|
|
1069
1114
|
this.db = connection.db;
|
|
1070
1115
|
for (const [slug, model] of schema.collections) {
|
|
1071
1116
|
this.builders.set(
|
|
@@ -1079,7 +1124,14 @@ var DrizzleDatabase = class {
|
|
|
1079
1124
|
schema;
|
|
1080
1125
|
migrator;
|
|
1081
1126
|
tablePrefix;
|
|
1127
|
+
countCacheMs;
|
|
1082
1128
|
builders = /* @__PURE__ */ new Map();
|
|
1129
|
+
/**
|
|
1130
|
+
* Totals of recent paginated finds, by collection and query, for `countCacheSeconds`: counting
|
|
1131
|
+
* can't use an index, and many people open the same lists. Writes don't clear them (on a busy
|
|
1132
|
+
* site they would all the time), so a total is up to that old, on every instance alike.
|
|
1133
|
+
*/
|
|
1134
|
+
counts = /* @__PURE__ */ new Map();
|
|
1083
1135
|
db;
|
|
1084
1136
|
model(slug) {
|
|
1085
1137
|
const model = this.schema.collections.get(slug);
|
|
@@ -1097,13 +1149,8 @@ var DrizzleDatabase = class {
|
|
|
1097
1149
|
return rows.map((row, i) => toDocument(row, docs[i] ?? {}, model.config.drafts === true));
|
|
1098
1150
|
}
|
|
1099
1151
|
async find(args) {
|
|
1100
|
-
const model = this.
|
|
1101
|
-
const
|
|
1102
|
-
const orderBy = this.builder(args.collection).orderBy(model.root, args.sort);
|
|
1103
|
-
let query = this.db.select().from(model.root.table).where(condition).orderBy(...orderBy).$dynamic();
|
|
1104
|
-
if (args.limit > 0) query = query.limit(args.limit).offset((args.page - 1) * args.limit);
|
|
1105
|
-
const rows = await query;
|
|
1106
|
-
const totalDocs = args.limit > 0 && (args.page > 1 || rows.length === args.limit) ? await this.count({ collection: args.collection, where: args.where }) : (args.page - 1) * args.limit + rows.length;
|
|
1152
|
+
const { model, rows } = await this.findRows(args, args.limit);
|
|
1153
|
+
const totalDocs = args.limit > 0 && (args.page > 1 || rows.length === args.limit) ? await this.cachedCount(args.collection, args.where) : (args.page - 1) * args.limit + rows.length;
|
|
1107
1154
|
const totalPages = args.limit > 0 ? Math.max(1, Math.ceil(totalDocs / args.limit)) : 1;
|
|
1108
1155
|
return {
|
|
1109
1156
|
docs: await this.load(model, rows),
|
|
@@ -1115,6 +1162,45 @@ var DrizzleDatabase = class {
|
|
|
1115
1162
|
hasPrevPage: args.page > 1
|
|
1116
1163
|
};
|
|
1117
1164
|
}
|
|
1165
|
+
async findPage(args) {
|
|
1166
|
+
const { model, rows } = await this.findRows(args, args.limit + 1);
|
|
1167
|
+
const more = rows.length > args.limit;
|
|
1168
|
+
return {
|
|
1169
|
+
docs: await this.load(model, more ? rows.slice(0, args.limit) : rows),
|
|
1170
|
+
totalDocs: null,
|
|
1171
|
+
limit: args.limit,
|
|
1172
|
+
page: args.page,
|
|
1173
|
+
totalPages: null,
|
|
1174
|
+
hasNextPage: more,
|
|
1175
|
+
hasPrevPage: args.page > 1
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
async findAfter(args) {
|
|
1179
|
+
const model = this.model(args.collection);
|
|
1180
|
+
const builder = this.builder(args.collection);
|
|
1181
|
+
const keys = builder.keyset(model.root, args.sort);
|
|
1182
|
+
const condition = this.where(args.collection, args.where);
|
|
1183
|
+
const after = args.after ? builder.after(keys, args.after) : void 0;
|
|
1184
|
+
const columns = Object.entries(getTableColumns(model.root.table));
|
|
1185
|
+
const names = keys.map(({ column }) => columns.find(([, c]) => c === column)?.[0]);
|
|
1186
|
+
const rows = await this.db.select().from(model.root.table).where(and3(condition, after)).orderBy(...builder.orderBy(model.root, args.sort)).limit(args.limit + 1);
|
|
1187
|
+
const more = rows.length > args.limit;
|
|
1188
|
+
const page = more ? rows.slice(0, args.limit) : rows;
|
|
1189
|
+
const last = page.at(-1);
|
|
1190
|
+
return {
|
|
1191
|
+
docs: await this.load(model, page),
|
|
1192
|
+
hasNextPage: more,
|
|
1193
|
+
next: last ? names.map((name) => last[name] ?? null) : null
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
async findRows(args, take) {
|
|
1197
|
+
const model = this.model(args.collection);
|
|
1198
|
+
const condition = this.where(args.collection, args.where);
|
|
1199
|
+
const orderBy = this.builder(args.collection).orderBy(model.root, args.sort);
|
|
1200
|
+
let query = this.db.select().from(model.root.table).where(condition).orderBy(...orderBy).$dynamic();
|
|
1201
|
+
if (args.limit > 0) query = query.limit(take).offset((args.page - 1) * args.limit);
|
|
1202
|
+
return { model, rows: await query };
|
|
1203
|
+
}
|
|
1118
1204
|
async findById(args) {
|
|
1119
1205
|
const [doc] = await this.findByIds({ collection: args.collection, ids: [args.id] });
|
|
1120
1206
|
return doc ?? null;
|
|
@@ -1125,6 +1211,18 @@ var DrizzleDatabase = class {
|
|
|
1125
1211
|
const rows = await this.db.select().from(model.root.table).where(inArray3(model.root.table.id, [...args.ids]));
|
|
1126
1212
|
return this.load(model, rows);
|
|
1127
1213
|
}
|
|
1214
|
+
/** A paginated find's total, from `counts` while it is fresh. */
|
|
1215
|
+
cachedCount(collection, where) {
|
|
1216
|
+
if (this.countCacheMs <= 0) return this.count({ collection, where });
|
|
1217
|
+
const key = `${collection}\0${JSON.stringify(where ?? null)}`;
|
|
1218
|
+
const now = Date.now();
|
|
1219
|
+
const cached = this.counts.get(key);
|
|
1220
|
+
if (cached && cached.until > now) return cached.total;
|
|
1221
|
+
const total = this.count({ collection, where });
|
|
1222
|
+
this.counts.set(key, { total, until: now + this.countCacheMs });
|
|
1223
|
+
total.catch(() => this.counts.delete(key));
|
|
1224
|
+
return total;
|
|
1225
|
+
}
|
|
1128
1226
|
async count(args) {
|
|
1129
1227
|
const model = this.model(args.collection);
|
|
1130
1228
|
const [row] = await this.db.select({ total: count() }).from(model.root.table).where(this.where(args.collection, args.where));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easy-cms/drizzle",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Shared Drizzle ORM layer for Easy CMS database adapters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"drizzle-orm": "0.45.4"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@easy-cms/core": "^1.
|
|
26
|
+
"@easy-cms/core": "^1.9.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@easy-cms/core": "^1.
|
|
29
|
+
"@easy-cms/core": "^1.9.0"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|