@remix-run/data-table 0.4.0 → 0.5.1

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/README.md CHANGED
@@ -11,7 +11,7 @@ Typed relational query toolkit for JavaScript runtimes.
11
11
  - **Relation-First Queries**: `hasMany`, `hasOne`, `belongsTo`, `hasManyThrough`, and nested eager loading
12
12
  - **Safe Scoped Writes**: `update`/`delete` with `orderBy`/`limit` run safely in a transaction
13
13
  - **First-Class Migrations**: Plain SQL `up.sql`/`down.sql` files with a journaling runner and dry-run planning
14
- - **Database Lifecycle Commands**: Wipe, migrate, inspect, seed, and reset through `remix db`
14
+ - **Database Lifecycle Commands**: Wipe, migrate, rollback, inspect, seed, and reset through `remix db`
15
15
  - **Raw SQL Escape Hatch**: Execute SQL directly with `db.exec(sql\`...\`)`
16
16
 
17
17
  `data-table` gives you two complementary APIs:
@@ -409,7 +409,7 @@ environment variable at command runtime, and paths are resolved relative to `rem
409
409
 
410
410
  ```jsonc
411
411
  {
412
- "$schema": "https://remix.run/schemas/remix.json",
412
+ "$schema": "./node_modules/remix/schema/remix.json",
413
413
  "db": {
414
414
  "adapter": {
415
415
  "type": "postgres",
@@ -433,12 +433,16 @@ Run lifecycle commands through the Remix CLI:
433
433
  remix db status
434
434
  remix db migrate
435
435
  remix db migrate --to 20260301113000_add_user_status
436
+ remix db rollback
437
+ remix db rollback --step 2
438
+ remix db rollback --to 20260301113000_add_user_status
439
+ remix db rollback --dry-run
436
440
  remix db seed
437
- remix db reset
438
- remix db wipe
441
+ remix db reset --force
442
+ remix db wipe --force
439
443
  ```
440
444
 
441
- `--to` accepts a bare migration id (`20260301113000`) or the full directory name (`20260301113000_add_user_status`).
445
+ `rollback` reverts the most recently applied migration by default. Bound it with either `--step <count>` or `--to <migration>`, which reverts through the target migration inclusively. Migration targets accept a bare migration id (`20260301113000`) or the full directory name (`20260301113000_add_user_status`). Use `--dry-run` to report what would be reverted without changing the database.
442
446
 
443
447
  `remix db status` reports applied migrations whose files are no longer present as `missing`. If the journal table does not exist, it reports every migration as pending without creating the table. Forward migration runs stop before executing SQL when an applied journal entry is missing from the current migration set. Rollbacks skip those orphaned journal entries so migrations that are still present can be reverted.
444
448
 
@@ -473,6 +477,15 @@ for (let script of plan.sql) console.log(script)
473
477
 
474
478
  `to` and `step` are mutually exclusive. Omit `journalTable` to use `data_table_migrations`.
475
479
 
480
+ Hosts that embed the database CLI can invoke the same rollback behavior through `runRemixDb()`:
481
+
482
+ ```ts
483
+ import { runRemixDb } from 'remix/data-table/cli'
484
+
485
+ await runRemixDb({ command: 'rollback', db, migrations })
486
+ await runRemixDb({ command: 'rollback', db, migrations, step: 2, dryRun: true })
487
+ ```
488
+
476
489
  Database drivers with migration locking run the complete migration and journal lifecycle through the
477
490
  connection that owns the lock. This keeps advisory locks correctly paired when the driver uses a
478
491
  connection pool, including pools configured with a single connection.
package/dist/cli.d.ts CHANGED
@@ -4,6 +4,28 @@ interface DatabaseCommandOptions {
4
4
  /** Database instance used by the command. */
5
5
  db: Database;
6
6
  }
7
+ type RollbackBoundOptions = {
8
+ /**
9
+ * Reverts back through this migration, inclusive. Accepts a bare
10
+ * migration id or the full `id_name` directory form.
11
+ */
12
+ to: string;
13
+ step?: never;
14
+ } | {
15
+ to?: never;
16
+ /** Reverts this many migrations. Defaults to `1`. */
17
+ step?: number;
18
+ };
19
+ type RollbackCommandOptions = DatabaseCommandOptions & RollbackBoundOptions & {
20
+ /** Reverts applied migrations, newest first. */
21
+ command: 'rollback';
22
+ /** Migrations to revert. */
23
+ migrations: Migrations;
24
+ /** Reports what would be reverted without running it. */
25
+ dryRun?: boolean;
26
+ /** Migration journal table. */
27
+ journalTable?: string;
28
+ };
7
29
  /** Structured invocation options accepted by {@link runRemixDb}. */
8
30
  export type RunRemixDbOptions = (DatabaseCommandOptions & {
9
31
  /** Applies pending migrations. */
@@ -17,7 +39,7 @@ export type RunRemixDbOptions = (DatabaseCommandOptions & {
17
39
  to?: string;
18
40
  /** Migration journal table. */
19
41
  journalTable?: string;
20
- }) | (DatabaseCommandOptions & {
42
+ }) | RollbackCommandOptions | (DatabaseCommandOptions & {
21
43
  /** Wipes, migrates, and optionally seeds the database. */
22
44
  command: 'reset';
23
45
  /** Migrations to apply after wiping the database. */
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAE3D,UAAU,sBAAsB;IAC9B,6CAA6C;IAC7C,EAAE,EAAE,QAAQ,CAAA;CACb;AAED,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GACzB,CAAC,sBAAsB,GAAG;IACxB,kCAAkC;IAClC,OAAO,EAAE,SAAS,CAAA;IAClB,2BAA2B;IAC3B,UAAU,EAAE,UAAU,CAAA;IACtB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAA;IAChB,qDAAqD;IACrD,UAAU,EAAE,UAAU,CAAA;IACtB,yDAAyD;IACzD,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,6CAA6C;IAC7C,IAAI,EAAE,IAAI,CAAA;CACX,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,8CAA8C;IAC9C,OAAO,EAAE,QAAQ,CAAA;IACjB,6BAA6B;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC,CAAA;AAEN;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4D5E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAE3D,UAAU,sBAAsB;IAC9B,6CAA6C;IAC7C,EAAE,EAAE,QAAQ,CAAA;CACb;AAED,KAAK,oBAAoB,GACrB;IACE;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,KAAK,CAAA;CACb,GACD;IACE,EAAE,CAAC,EAAE,KAAK,CAAA;IACV,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAEL,KAAK,sBAAsB,GAAG,sBAAsB,GAClD,oBAAoB,GAAG;IACrB,gDAAgD;IAChD,OAAO,EAAE,UAAU,CAAA;IACnB,4BAA4B;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,yDAAyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAEH,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GACzB,CAAC,sBAAsB,GAAG;IACxB,kCAAkC;IAClC,OAAO,EAAE,SAAS,CAAA;IAClB,2BAA2B;IAC3B,UAAU,EAAE,UAAU,CAAA;IACtB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,sBAAsB,GACtB,CAAC,sBAAsB,GAAG;IACxB,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAA;IAChB,qDAAqD;IACrD,UAAU,EAAE,UAAU,CAAA;IACtB,yDAAyD;IACzD,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,6CAA6C;IAC7C,IAAI,EAAE,IAAI,CAAA;CACX,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,8CAA8C;IAC9C,OAAO,EAAE,QAAQ,CAAA;IACjB,6BAA6B;IAC7B,UAAU,EAAE,UAAU,CAAA;IACtB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,GACF,CAAC,sBAAsB,GAAG;IACxB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAA;CAChB,CAAC,CAAA;AAEN;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2F5E"}
package/dist/cli.js CHANGED
@@ -19,6 +19,31 @@ export async function runRemixDb(options) {
19
19
  }
20
20
  return 0;
21
21
  }
22
+ if (options.command === 'rollback') {
23
+ if (options.to !== undefined && options.step !== undefined) {
24
+ throw new Error('Cannot combine "to" and "step" migration options in the same run');
25
+ }
26
+ let result = options.to === undefined
27
+ ? await options.db.migrate(options.migrations, {
28
+ direction: 'down',
29
+ step: options.step ?? 1,
30
+ dryRun: options.dryRun,
31
+ journalTable: options.journalTable,
32
+ })
33
+ : await options.db.migrate(options.migrations, {
34
+ direction: 'down',
35
+ to: options.to,
36
+ dryRun: options.dryRun,
37
+ journalTable: options.journalTable,
38
+ });
39
+ if (result.reverted.length === 0) {
40
+ console.log('no migrations to revert');
41
+ }
42
+ for (let entry of result.reverted) {
43
+ console.log((options.dryRun ? 'would revert ' : 'reverted ') + entry.id + '_' + entry.name);
44
+ }
45
+ return 0;
46
+ }
22
47
  if (options.command === 'reset') {
23
48
  await options.db.reset({
24
49
  migrations: options.migrations,
@@ -62,10 +62,11 @@ async function loadHasManyThroughValues(database, sourceRows, relation) {
62
62
  if (!relation.through) {
63
63
  throw new DataTableQueryError('hasManyThrough relation is missing through metadata');
64
64
  }
65
+ let through = relation.through;
65
66
  if (sourceRows.length === 0) {
66
67
  return [];
67
68
  }
68
- let throughRelation = relation.through.relation;
69
+ let throughRelation = through.relation;
69
70
  let sourceTuples = uniqueTuples(sourceRows, throughRelation.sourceKey);
70
71
  if (sourceTuples.length === 0) {
71
72
  return sourceRows.map(() => []);
@@ -92,12 +93,12 @@ async function loadHasManyThroughValues(database, sourceRows, relation) {
92
93
  pagedThroughRowsBySource.set(sourceKey, pagedMatchedRows);
93
94
  pagedThroughRows.push(...pagedMatchedRows);
94
95
  }
95
- let throughTuples = uniqueTuples(pagedThroughRows, relation.through.throughSourceKey);
96
+ let throughTuples = uniqueTuples(pagedThroughRows, through.throughSourceKey);
96
97
  if (throughTuples.length === 0) {
97
98
  return sourceRows.map(() => []);
98
99
  }
99
100
  let targetQuery = createQuery(relation.targetTable);
100
- let targetPredicate = buildLinkPredicate(relation.through.throughTargetKey, throughTuples);
101
+ let targetPredicate = buildLinkPredicate(through.throughTargetKey, throughTuples);
101
102
  if (targetPredicate) {
102
103
  targetQuery = targetQuery.where(targetPredicate);
103
104
  }
@@ -105,23 +106,41 @@ async function loadHasManyThroughValues(database, sourceRows, relation) {
105
106
  includePagination: false,
106
107
  });
107
108
  let relatedRows = await loadRowsWithRelationsForQuery(database, targetQuery);
108
- let targetRowsByThrough = groupRowsByTuple(relatedRows, relation.through.throughTargetKey);
109
- return sourceRows.map((sourceRow) => {
109
+ let sourceKeysByThroughKey = new Map();
110
+ let outputRowsBySourceKey = new Map();
111
+ for (let sourceRow of sourceRows) {
110
112
  let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey);
111
113
  let matchedThroughRows = pagedThroughRowsBySource.get(sourceKey) ?? [];
112
- let outputRows = [];
113
- let seen = new Set();
114
+ outputRowsBySourceKey.set(sourceKey, new Map());
114
115
  for (let throughRow of matchedThroughRows) {
115
- let throughKey = getCompositeKey(throughRow, relation.through.throughSourceKey);
116
- let rowsForThrough = targetRowsByThrough.get(throughKey) ?? [];
117
- for (let row of rowsForThrough) {
118
- let rowIdentity = getCompositeKey(row, getTablePrimaryKey(relation.targetTable));
119
- if (!seen.has(rowIdentity)) {
120
- seen.add(rowIdentity);
121
- outputRows.push(row);
122
- }
116
+ let throughKey = getCompositeKey(throughRow, through.throughSourceKey);
117
+ let sourceKeys = sourceKeysByThroughKey.get(throughKey);
118
+ if (!sourceKeys) {
119
+ sourceKeys = new Set();
120
+ sourceKeysByThroughKey.set(throughKey, sourceKeys);
121
+ }
122
+ sourceKeys.add(sourceKey);
123
+ }
124
+ }
125
+ let targetPrimaryKey = getTablePrimaryKey(relation.targetTable);
126
+ for (let row of relatedRows) {
127
+ let throughKey = getCompositeKey(row, through.throughTargetKey);
128
+ let sourceKeys = sourceKeysByThroughKey.get(throughKey);
129
+ if (!sourceKeys) {
130
+ continue;
131
+ }
132
+ let rowIdentity = getCompositeKey(row, targetPrimaryKey);
133
+ for (let sourceKey of sourceKeys) {
134
+ let outputRows = outputRowsBySourceKey.get(sourceKey);
135
+ if (!outputRows || outputRows.has(rowIdentity)) {
136
+ continue;
123
137
  }
138
+ outputRows.set(rowIdentity, row);
124
139
  }
140
+ }
141
+ return sourceRows.map((sourceRow) => {
142
+ let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey);
143
+ let outputRows = Array.from(outputRowsBySourceKey.get(sourceKey)?.values() ?? []);
125
144
  return applyPagination(outputRows, relation.modifiers.limit, relation.modifiers.offset);
126
145
  });
127
146
  }
@@ -15,7 +15,7 @@ function assertMigrationOperationOptions(options) {
15
15
  }
16
16
  }
17
17
  function resolveTargetOption(migrations, to) {
18
- if (!to) {
18
+ if (to === undefined) {
19
19
  return undefined;
20
20
  }
21
21
  // Accept either a bare migration id or the full `id_name` directory form and
@@ -42,6 +42,7 @@ export type WhereObject<column extends string = string> = Partial<Record<column,
42
42
  * User-facing where input accepted by `query.where()` and relation modifiers.
43
43
  */
44
44
  export type WhereInput<column extends string = string> = Predicate<column> | WhereObject<column>;
45
+ type WhereInputColumn<input extends WhereInput> = input extends Predicate<infer column> ? column : keyof input & string;
45
46
  /**
46
47
  * Builds an equality predicate.
47
48
  */
@@ -121,17 +122,17 @@ export declare function isNull<column extends string | ColumnReferenceLike>(colu
121
122
  */
122
123
  export declare function notNull<column extends string | ColumnReferenceLike>(column: column): Predicate<PredicateColumn<column>>;
123
124
  /**
124
- * Combines predicates with logical `AND`.
125
- * @param predicates Child predicates.
125
+ * Combines where inputs with logical `AND`.
126
+ * @param inputs Child predicates or object shorthand filters.
126
127
  * @returns A logical `and` predicate.
127
128
  */
128
- export declare function and<column extends string>(...predicates: Predicate<column>[]): Predicate<column>;
129
+ export declare function and<inputs extends WhereInput[]>(...inputs: inputs): Predicate<WhereInputColumn<inputs[number]>>;
129
130
  /**
130
- * Combines predicates with logical `OR`.
131
- * @param predicates Child predicates.
131
+ * Combines where inputs with logical `OR`.
132
+ * @param inputs Child predicates or object shorthand filters.
132
133
  * @returns A logical `or` predicate.
133
134
  */
134
- export declare function or<column extends string>(...predicates: Predicate<column>[]): Predicate<column>;
135
+ export declare function or<inputs extends WhereInput[]>(...inputs: inputs): Predicate<WhereInputColumn<inputs[number]>>;
135
136
  /**
136
137
  * Returns `true` when a value is a normalized predicate object.
137
138
  * @param value Value to inspect.
@@ -1 +1 @@
1
- {"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/lib/operators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/lib/operators.ts"],"names":[],"mappings":"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"}
@@ -109,22 +109,26 @@ export function notNull(column) {
109
109
  return { type: 'null', operator: 'notNull', column: resolvePredicateColumn(column) };
110
110
  }
111
111
  /**
112
- * Combines predicates with logical `AND`.
113
- * @param predicates Child predicates.
112
+ * Combines where inputs with logical `AND`.
113
+ * @param inputs Child predicates or object shorthand filters.
114
114
  * @returns A logical `and` predicate.
115
115
  */
116
- export function and(...predicates) {
117
- let filtered = predicates.filter(Boolean);
118
- return { type: 'logical', operator: 'and', predicates: filtered };
116
+ export function and(...inputs) {
117
+ let predicates = inputs
118
+ .filter(Boolean)
119
+ .map((input) => normalizeWhereInput(input));
120
+ return { type: 'logical', operator: 'and', predicates };
119
121
  }
120
122
  /**
121
- * Combines predicates with logical `OR`.
122
- * @param predicates Child predicates.
123
+ * Combines where inputs with logical `OR`.
124
+ * @param inputs Child predicates or object shorthand filters.
123
125
  * @returns A logical `or` predicate.
124
126
  */
125
- export function or(...predicates) {
126
- let filtered = predicates.filter(Boolean);
127
- return { type: 'logical', operator: 'or', predicates: filtered };
127
+ export function or(...inputs) {
128
+ let predicates = inputs
129
+ .filter(Boolean)
130
+ .map((input) => normalizeWhereInput(input));
131
+ return { type: 'logical', operator: 'or', predicates };
128
132
  }
129
133
  /**
130
134
  * Returns `true` when a value is a normalized predicate object.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/data-table",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A typed, relational query toolkit for JavaScript",
5
5
  "author": "Michael Jackson <mjijackson@gmail.com>",
6
6
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -6,6 +6,33 @@ interface DatabaseCommandOptions {
6
6
  db: Database
7
7
  }
8
8
 
9
+ type RollbackBoundOptions =
10
+ | {
11
+ /**
12
+ * Reverts back through this migration, inclusive. Accepts a bare
13
+ * migration id or the full `id_name` directory form.
14
+ */
15
+ to: string
16
+ step?: never
17
+ }
18
+ | {
19
+ to?: never
20
+ /** Reverts this many migrations. Defaults to `1`. */
21
+ step?: number
22
+ }
23
+
24
+ type RollbackCommandOptions = DatabaseCommandOptions &
25
+ RollbackBoundOptions & {
26
+ /** Reverts applied migrations, newest first. */
27
+ command: 'rollback'
28
+ /** Migrations to revert. */
29
+ migrations: Migrations
30
+ /** Reports what would be reverted without running it. */
31
+ dryRun?: boolean
32
+ /** Migration journal table. */
33
+ journalTable?: string
34
+ }
35
+
9
36
  /** Structured invocation options accepted by {@link runRemixDb}. */
10
37
  export type RunRemixDbOptions =
11
38
  | (DatabaseCommandOptions & {
@@ -21,6 +48,7 @@ export type RunRemixDbOptions =
21
48
  /** Migration journal table. */
22
49
  journalTable?: string
23
50
  })
51
+ | RollbackCommandOptions
24
52
  | (DatabaseCommandOptions & {
25
53
  /** Wipes, migrates, and optionally seeds the database. */
26
54
  command: 'reset'
@@ -76,6 +104,37 @@ export async function runRemixDb(options: RunRemixDbOptions): Promise<number> {
76
104
  return 0
77
105
  }
78
106
 
107
+ if (options.command === 'rollback') {
108
+ if (options.to !== undefined && options.step !== undefined) {
109
+ throw new Error('Cannot combine "to" and "step" migration options in the same run')
110
+ }
111
+
112
+ let result =
113
+ options.to === undefined
114
+ ? await options.db.migrate(options.migrations, {
115
+ direction: 'down',
116
+ step: options.step ?? 1,
117
+ dryRun: options.dryRun,
118
+ journalTable: options.journalTable,
119
+ })
120
+ : await options.db.migrate(options.migrations, {
121
+ direction: 'down',
122
+ to: options.to,
123
+ dryRun: options.dryRun,
124
+ journalTable: options.journalTable,
125
+ })
126
+
127
+ if (result.reverted.length === 0) {
128
+ console.log('no migrations to revert')
129
+ }
130
+
131
+ for (let entry of result.reverted) {
132
+ console.log((options.dryRun ? 'would revert ' : 'reverted ') + entry.id + '_' + entry.name)
133
+ }
134
+
135
+ return 0
136
+ }
137
+
79
138
  if (options.command === 'reset') {
80
139
  await options.db.reset({
81
140
  migrations: options.migrations,
@@ -116,11 +116,13 @@ async function loadHasManyThroughValues(
116
116
  throw new DataTableQueryError('hasManyThrough relation is missing through metadata')
117
117
  }
118
118
 
119
+ let through = relation.through
120
+
119
121
  if (sourceRows.length === 0) {
120
122
  return []
121
123
  }
122
124
 
123
- let throughRelation = relation.through.relation
125
+ let throughRelation = through.relation
124
126
  let sourceTuples = uniqueTuples(sourceRows, throughRelation.sourceKey)
125
127
 
126
128
  if (sourceTuples.length === 0) {
@@ -163,14 +165,14 @@ async function loadHasManyThroughValues(
163
165
  pagedThroughRows.push(...pagedMatchedRows)
164
166
  }
165
167
 
166
- let throughTuples = uniqueTuples(pagedThroughRows, relation.through.throughSourceKey)
168
+ let throughTuples = uniqueTuples(pagedThroughRows, through.throughSourceKey)
167
169
 
168
170
  if (throughTuples.length === 0) {
169
171
  return sourceRows.map(() => [])
170
172
  }
171
173
 
172
174
  let targetQuery = createQuery(relation.targetTable)
173
- let targetPredicate = buildLinkPredicate(relation.through.throughTargetKey, throughTuples)
175
+ let targetPredicate = buildLinkPredicate(through.throughTargetKey, throughTuples)
174
176
 
175
177
  if (targetPredicate) {
176
178
  targetQuery = targetQuery.where(
@@ -183,27 +185,54 @@ async function loadHasManyThroughValues(
183
185
  })
184
186
 
185
187
  let relatedRows = await loadRowsWithRelationsForQuery(database, targetQuery)
186
- let targetRowsByThrough = groupRowsByTuple(relatedRows, relation.through.throughTargetKey)
188
+ let sourceKeysByThroughKey = new Map<string, Set<string>>()
189
+ let outputRowsBySourceKey = new Map<string, Map<string, Record<string, unknown>>>()
187
190
 
188
- return sourceRows.map((sourceRow) => {
191
+ for (let sourceRow of sourceRows) {
189
192
  let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey)
190
193
  let matchedThroughRows = pagedThroughRowsBySource.get(sourceKey) ?? []
191
- let outputRows: Record<string, unknown>[] = []
192
- let seen = new Set<string>()
194
+
195
+ outputRowsBySourceKey.set(sourceKey, new Map())
193
196
 
194
197
  for (let throughRow of matchedThroughRows) {
195
- let throughKey = getCompositeKey(throughRow, relation.through!.throughSourceKey)
196
- let rowsForThrough = targetRowsByThrough.get(throughKey) ?? []
198
+ let throughKey = getCompositeKey(throughRow, through.throughSourceKey)
199
+ let sourceKeys = sourceKeysByThroughKey.get(throughKey)
200
+
201
+ if (!sourceKeys) {
202
+ sourceKeys = new Set()
203
+ sourceKeysByThroughKey.set(throughKey, sourceKeys)
204
+ }
205
+
206
+ sourceKeys.add(sourceKey)
207
+ }
208
+ }
209
+
210
+ let targetPrimaryKey = getTablePrimaryKey(relation.targetTable)
197
211
 
198
- for (let row of rowsForThrough) {
199
- let rowIdentity = getCompositeKey(row, getTablePrimaryKey(relation.targetTable))
212
+ for (let row of relatedRows) {
213
+ let throughKey = getCompositeKey(row, through.throughTargetKey)
214
+ let sourceKeys = sourceKeysByThroughKey.get(throughKey)
200
215
 
201
- if (!seen.has(rowIdentity)) {
202
- seen.add(rowIdentity)
203
- outputRows.push(row)
204
- }
216
+ if (!sourceKeys) {
217
+ continue
218
+ }
219
+
220
+ let rowIdentity = getCompositeKey(row, targetPrimaryKey)
221
+
222
+ for (let sourceKey of sourceKeys) {
223
+ let outputRows = outputRowsBySourceKey.get(sourceKey)
224
+
225
+ if (!outputRows || outputRows.has(rowIdentity)) {
226
+ continue
205
227
  }
228
+
229
+ outputRows.set(rowIdentity, row)
206
230
  }
231
+ }
232
+
233
+ return sourceRows.map((sourceRow) => {
234
+ let sourceKey = getCompositeKey(sourceRow, throughRelation.sourceKey)
235
+ let outputRows = Array.from(outputRowsBySourceKey.get(sourceKey)?.values() ?? [])
207
236
 
208
237
  return applyPagination(outputRows, relation.modifiers.limit, relation.modifiers.offset)
209
238
  })
@@ -63,7 +63,7 @@ function resolveTargetOption(
63
63
  migrations: MigrationDescriptor[],
64
64
  to: string | undefined,
65
65
  ): string | undefined {
66
- if (!to) {
66
+ if (to === undefined) {
67
67
  return undefined
68
68
  }
69
69
 
@@ -66,6 +66,9 @@ export type WhereObject<column extends string = string> = Partial<Record<column,
66
66
  */
67
67
  export type WhereInput<column extends string = string> = Predicate<column> | WhereObject<column>
68
68
 
69
+ type WhereInputColumn<input extends WhereInput> =
70
+ input extends Predicate<infer column> ? column : keyof input & string
71
+
69
72
  /**
70
73
  * Builds an equality predicate.
71
74
  */
@@ -293,23 +296,33 @@ export function notNull<column extends string | ColumnReferenceLike>(
293
296
  }
294
297
 
295
298
  /**
296
- * Combines predicates with logical `AND`.
297
- * @param predicates Child predicates.
299
+ * Combines where inputs with logical `AND`.
300
+ * @param inputs Child predicates or object shorthand filters.
298
301
  * @returns A logical `and` predicate.
299
302
  */
300
- export function and<column extends string>(...predicates: Predicate<column>[]): Predicate<column> {
301
- let filtered = predicates.filter(Boolean)
302
- return { type: 'logical', operator: 'and', predicates: filtered }
303
+ export function and<inputs extends WhereInput[]>(
304
+ ...inputs: inputs
305
+ ): Predicate<WhereInputColumn<inputs[number]>> {
306
+ type column = WhereInputColumn<inputs[number]>
307
+ let predicates = inputs
308
+ .filter(Boolean)
309
+ .map((input) => normalizeWhereInput(input) as Predicate<column>)
310
+ return { type: 'logical', operator: 'and', predicates }
303
311
  }
304
312
 
305
313
  /**
306
- * Combines predicates with logical `OR`.
307
- * @param predicates Child predicates.
314
+ * Combines where inputs with logical `OR`.
315
+ * @param inputs Child predicates or object shorthand filters.
308
316
  * @returns A logical `or` predicate.
309
317
  */
310
- export function or<column extends string>(...predicates: Predicate<column>[]): Predicate<column> {
311
- let filtered = predicates.filter(Boolean)
312
- return { type: 'logical', operator: 'or', predicates: filtered }
318
+ export function or<inputs extends WhereInput[]>(
319
+ ...inputs: inputs
320
+ ): Predicate<WhereInputColumn<inputs[number]>> {
321
+ type column = WhereInputColumn<inputs[number]>
322
+ let predicates = inputs
323
+ .filter(Boolean)
324
+ .map((input) => normalizeWhereInput(input) as Predicate<column>)
325
+ return { type: 'logical', operator: 'or', predicates }
313
326
  }
314
327
 
315
328
  /**