@casekit/orm 0.0.1-alpha.3 → 0.0.1-alpha.4

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.
@@ -0,0 +1,17 @@
1
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
2
+ import { BaseFindParams } from "./types/BaseFindParams";
3
+ export type ManyToManySubQuery = {
4
+ model: string;
5
+ name: string;
6
+ relation: {
7
+ model: string;
8
+ type: "N:N";
9
+ foreignKey: string | string[];
10
+ otherKey: string | string[];
11
+ through: string;
12
+ };
13
+ query: BaseFindParams;
14
+ path: string[];
15
+ };
16
+ export declare const getIncludedManyToManyRelations: (config: BaseConfiguration, m: string, query: BaseFindParams, path?: string[]) => ManyToManySubQuery[];
17
+ //# sourceMappingURL=getIncludedManyToManyRelations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getIncludedManyToManyRelations.d.ts","sourceRoot":"","sources":["../../../src/queries/find/getIncludedManyToManyRelations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,KAAK,CAAC;QACZ,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC9B,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,8BAA8B,WAC/B,iBAAiB,KACtB,MAAM,SACF,cAAc,SACf,MAAM,EAAE,yBA2BjB,CAAC"}
@@ -0,0 +1,20 @@
1
+ export const getIncludedManyToManyRelations = (config, m, query, path = []) => {
2
+ const rels = config.relations[m];
3
+ const includedManyToManyRelations = Object.entries(query.include ?? {})
4
+ .filter(([r, _]) => rels[r].type === "N:N")
5
+ .map(([r, q]) => ({
6
+ model: m,
7
+ name: r,
8
+ relation: rels[r],
9
+ query: q,
10
+ path: [...path, r],
11
+ }));
12
+ const joinedManyToManyRelations = Object.entries(query.include ?? {})
13
+ .filter(([r, _]) => rels[r].type === "N:1")
14
+ .flatMap(([r, q]) => getIncludedManyToManyRelations(config, rels[r].model, q, [
15
+ ...path,
16
+ r,
17
+ ]));
18
+ return [...includedManyToManyRelations, ...joinedManyToManyRelations];
19
+ };
20
+ //# sourceMappingURL=getIncludedManyToManyRelations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getIncludedManyToManyRelations.js","sourceRoot":"","sources":["../../../src/queries/find/getIncludedManyToManyRelations.ts"],"names":[],"mappings":"AAiBA,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC1C,MAAyB,EACzB,CAAS,EACT,KAAqB,EACrB,OAAiB,EAAE,EACrB,EAAE;IACA,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,2BAA2B,GAAyB,MAAM,CAAC,OAAO,CACpE,KAAK,CAAC,OAAO,IAAI,EAAE,CACtB;SACI,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,CAAC;QACR,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAmC;QACnD,KAAK,EAAE,CAAE;QACT,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;KACrB,CAAC,CAAC,CAAC;IAER,MAAM,yBAAyB,GAAyB,MAAM,CAAC,OAAO,CAClE,KAAK,CAAC,OAAO,IAAI,EAAE,CACtB;SACI,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC;SAC1C,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAChB,8BAA8B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAE,EAAE;QACtD,GAAG,IAAI;QACP,CAAC;KACJ,CAAC,CACL,CAAC;IAEN,OAAO,CAAC,GAAG,2BAA2B,EAAE,GAAG,yBAAyB,CAAC,CAAC;AAC1E,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
2
+ import { BaseRelation } from "../../schema/types/base/BaseRelation";
3
+ import { BaseFindParams } from "./types/BaseFindParams";
4
+ export type OneToManySubQuery = {
5
+ model: string;
6
+ name: string;
7
+ relation: BaseRelation;
8
+ query: BaseFindParams;
9
+ path: string[];
10
+ };
11
+ export declare const getIncludedOneToManyRelations: (config: BaseConfiguration, m: string, query: BaseFindParams, path?: string[]) => OneToManySubQuery[];
12
+ //# sourceMappingURL=getIncludedOneToManyRelations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getIncludedOneToManyRelations.d.ts","sourceRoot":"","sources":["../../../src/queries/find/getIncludedOneToManyRelations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,6BAA6B,WAC9B,iBAAiB,KACtB,MAAM,SACF,cAAc,SACf,MAAM,EAAE,wBA2BjB,CAAC"}
@@ -0,0 +1,20 @@
1
+ export const getIncludedOneToManyRelations = (config, m, query, path = []) => {
2
+ const rels = config.relations[m];
3
+ const includedOneToManyRelations = Object.entries(query.include ?? {})
4
+ .filter(([r, _]) => rels[r].type === "1:N")
5
+ .map(([r, q]) => ({
6
+ model: m,
7
+ name: r,
8
+ relation: rels[r],
9
+ query: q,
10
+ path: [...path, r],
11
+ }));
12
+ const joinedOneToManyRelations = Object.entries(query.include ?? {})
13
+ .filter(([r, _]) => rels[r].type === "N:1")
14
+ .flatMap(([r, q]) => getIncludedOneToManyRelations(config, rels[r].model, q, [
15
+ ...path,
16
+ r,
17
+ ]));
18
+ return [...includedOneToManyRelations, ...joinedOneToManyRelations];
19
+ };
20
+ //# sourceMappingURL=getIncludedOneToManyRelations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getIncludedOneToManyRelations.js","sourceRoot":"","sources":["../../../src/queries/find/getIncludedOneToManyRelations.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,MAAM,6BAA6B,GAAG,CACzC,MAAyB,EACzB,CAAS,EACT,KAAqB,EACrB,OAAiB,EAAE,EACrB,EAAE;IACA,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,0BAA0B,GAAwB,MAAM,CAAC,OAAO,CAClE,KAAK,CAAC,OAAO,IAAI,EAAE,CACtB;SACI,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACd,KAAK,EAAE,CAAC;QACR,IAAI,EAAE,CAAC;QACP,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QACjB,KAAK,EAAE,CAAE;QACT,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;KACrB,CAAC,CAAC,CAAC;IAER,MAAM,wBAAwB,GAAwB,MAAM,CAAC,OAAO,CAChE,KAAK,CAAC,OAAO,IAAI,EAAE,CACtB;SACI,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC;SAC1C,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAChB,6BAA6B,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAE,EAAE;QACrD,GAAG,IAAI;QACP,CAAC;KACJ,CAAC,CACL,CAAC;IAEN,OAAO,CAAC,GAAG,0BAA0B,EAAE,GAAG,wBAAwB,CAAC,CAAC;AACxE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=findMany.include.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findMany.include.test.d.ts","sourceRoot":"","sources":["../../../../src/queries/find/tests/findMany.include.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { db } from "../../../test/db";
3
+ import { seed } from "../../../test/seed";
4
+ describe("findMany", () => {
5
+ test("it can include N:1 -> 1:N relations", async () => {
6
+ await db.transact(async (db) => {
7
+ await seed(db, {
8
+ users: [
9
+ {
10
+ username: "Russell",
11
+ tenants: [
12
+ { name: "WFMA", posts: 1 },
13
+ { name: "Popova Park", posts: 0 },
14
+ ],
15
+ },
16
+ {
17
+ username: "Dan",
18
+ tenants: [{ name: "WFMA", posts: 2 }],
19
+ },
20
+ {
21
+ username: "Fairooz",
22
+ tenants: [{ name: "WFMA", posts: 0 }],
23
+ },
24
+ ],
25
+ });
26
+ const results = await db.findMany("post", {
27
+ select: ["id", "title"],
28
+ include: {
29
+ author: {
30
+ select: ["id", "username"],
31
+ include: {
32
+ posts: { select: ["id", "title"] },
33
+ },
34
+ },
35
+ },
36
+ limit: 2,
37
+ });
38
+ expect(results.map((p) => [
39
+ p.author.username,
40
+ p.author.posts.map((p) => p.title),
41
+ ])).toEqual([
42
+ ["Russell", ["Post a"]],
43
+ ["Dan", ["Post b", "Post c"]],
44
+ ]);
45
+ }, { rollback: true });
46
+ });
47
+ test("it can include N:1 -> N:N relations", async () => {
48
+ await db.transact(async (db) => {
49
+ await seed(db, {
50
+ users: [
51
+ {
52
+ username: "Russell",
53
+ tenants: [
54
+ { name: "WFMA", posts: 1 },
55
+ { name: "Popova Park", posts: 0 },
56
+ ],
57
+ },
58
+ {
59
+ username: "Dan",
60
+ tenants: [{ name: "WFMA", posts: 2 }],
61
+ },
62
+ {
63
+ username: "Fairooz",
64
+ tenants: [{ name: "WFMA", posts: 0 }],
65
+ },
66
+ ],
67
+ });
68
+ const results = await db.findMany("post", {
69
+ select: ["id", "title"],
70
+ include: {
71
+ author: {
72
+ select: ["id", "username"],
73
+ include: {
74
+ tenants: {
75
+ select: ["id", "name"],
76
+ orderBy: ["name"],
77
+ },
78
+ },
79
+ },
80
+ },
81
+ limit: 2,
82
+ });
83
+ expect(results.map((p) => [
84
+ p.author.username,
85
+ p.author.tenants.map((t) => t.name),
86
+ ])).toEqual([
87
+ ["Russell", ["Popova Park", "WFMA"]],
88
+ ["Dan", ["WFMA"]],
89
+ ]);
90
+ }, { rollback: true });
91
+ });
92
+ });
93
+ //# sourceMappingURL=findMany.include.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findMany.include.test.js","sourceRoot":"","sources":["../../../../src/queries/find/tests/findMany.include.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"findMany.d.ts","sourceRoot":"","sources":["../../src/queries/findMany.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAM3C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAG7D,eAAO,MAAM,QAAQ,SACX,UAAU,UACR,iBAAiB,KACtB,MAAM,SACF,cAAc,uCA+FxB,CAAC"}
1
+ {"version":3,"file":"findMany.d.ts","sourceRoot":"","sources":["../../src/queries/findMany.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yCAAyC,CAAC;AAE5E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQ3C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAG7D,eAAO,MAAM,QAAQ,SACX,UAAU,UACR,iBAAiB,KACtB,MAAM,SACF,cAAc,uCAyGxB,CAAC"}
@@ -1,10 +1,12 @@
1
- import { groupBy } from "lodash-es";
1
+ import { dropRight, get, groupBy, set } from "lodash-es";
2
2
  import hash from "object-hash";
3
3
  import { OrmError } from "../errors";
4
4
  import { logger } from "../logger";
5
5
  import { ensureArray } from "../util/ensureArray";
6
6
  import { buildFind } from "./find/buildFind";
7
7
  import { findToSql } from "./find/findToSql";
8
+ import { getIncludedManyToManyRelations } from "./find/getIncludedManyToManyRelations";
9
+ import { getIncludedOneToManyRelations } from "./find/getIncludedOneToManyRelations";
8
10
  import { rowToObject } from "./util/rowToObject";
9
11
  export const findMany = async (conn, config, m, query) => {
10
12
  const builder = buildFind(config, m, query);
@@ -19,51 +21,62 @@ export const findMany = async (conn, config, m, query) => {
19
21
  const results = await conn
20
22
  .query(statement)
21
23
  .then((result) => result.rows.map(rowToObject(builder.columns)));
22
- for (const [r, subquery] of Object.entries(query.include ?? {})) {
23
- const relation = config.relations[m][r];
24
- if (relation.type === "1:N") {
25
- const relation = config.relations[m][r];
26
- const pk = config.models[m].primaryKey;
27
- const fk = ensureArray(relation.foreignKey);
28
- const lateralBy = fk.map((c, index) => ({
29
- column: c,
30
- values: results.map((result) => result[pk[index]]),
31
- }));
32
- const subqueryResults = await findMany(conn, config, relation.model, { ...subquery, lateralBy });
24
+ const fetchIncludedOneToManyRelations = getIncludedOneToManyRelations(config, m, query).map(({ model, relation, query, path }) => {
25
+ console.log("Fetching 1:N relation");
26
+ const pk = config.models[model].primaryKey;
27
+ const fk = ensureArray(relation.foreignKey);
28
+ const lateralBy = fk.map((c, index) => ({
29
+ column: c,
30
+ values: results.map((result) => get(result, [...dropRight(path, 1), pk[index]])),
31
+ }));
32
+ return findMany(conn, config, relation.model, {
33
+ ...query,
34
+ lateralBy,
35
+ }).then((subqueryResults) => {
33
36
  const lookup = groupBy(subqueryResults, (result) => {
34
37
  return hash(fk.map((c) => result[c]));
35
38
  });
36
39
  for (const result of results) {
37
- const key = hash(pk.map((c) => result[c]));
38
- result[r] = lookup[key] ?? [];
40
+ const key = hash(pk.map((c) => get(result, [...dropRight(path, 1), c])));
41
+ console.log([...path]);
42
+ set(result, [...path], lookup[key] ?? []);
39
43
  }
40
- }
41
- else if (relation.type === "N:N") {
42
- const joinFrom = Object.entries(config.relations[m]).find(([, rel]) => rel.type === "1:N" && rel.model === relation.through)?.[0];
43
- const joinTo = Object.entries(config.relations[relation.through]).find(([, rel]) => rel.type === "N:1" && rel.model === relation.model)?.[0];
44
- if (joinFrom === undefined || joinTo === undefined) {
45
- throw new OrmError("Both sides of a N:N relation must be defined", { data: { joinFrom, joinTo, query } });
46
- }
47
- const pk = config.models[m].primaryKey;
48
- const fk = ensureArray(relation.foreignKey);
49
- const lateralBy = fk.map((c, index) => ({
50
- column: c,
51
- values: results.map((result) => result[pk[index]]),
52
- }));
53
- const subqueryResults = await findMany(conn, config, relation.through, {
54
- select: ensureArray(relation.foreignKey),
55
- include: { [joinTo]: subquery },
56
- lateralBy,
44
+ });
45
+ });
46
+ const fetchIncludedManyToManyRelations = getIncludedManyToManyRelations(config, m, query).map(({ model, relation, query, path }) => {
47
+ console.log("Fetching N:N relation");
48
+ const joinFrom = Object.entries(config.relations[model]).find(([, rel]) => rel.type === "1:N" && rel.model === relation.through)?.[0];
49
+ const joinTo = Object.entries(config.relations[relation.through]).find(([, rel]) => rel.type === "N:1" && rel.model === relation.model)?.[0];
50
+ if (joinFrom === undefined || joinTo === undefined) {
51
+ throw new OrmError("Both sides of a N:N relation must be defined", {
52
+ data: { joinFrom, joinTo, query },
57
53
  });
54
+ }
55
+ const pk = config.models[model].primaryKey;
56
+ const fk = ensureArray(relation.foreignKey);
57
+ const lateralBy = fk.map((c, index) => ({
58
+ column: c,
59
+ values: results.map((result) => get(result, [...dropRight(path, 1), pk[index]])),
60
+ }));
61
+ return findMany(conn, config, relation.through, {
62
+ select: ensureArray(relation.foreignKey),
63
+ include: { [joinTo]: query },
64
+ lateralBy,
65
+ }).then((subqueryResults) => {
58
66
  const lookup = groupBy(subqueryResults, (result) => {
59
67
  return hash(fk.map((c) => result[c]));
60
68
  });
61
69
  for (const result of results) {
62
- const key = hash(pk.map((c) => result[c]));
63
- result[r] = (lookup[key] ?? []).map((relation) => relation[joinTo] ?? []);
70
+ const key = hash(pk.map((c) => get(result, [...dropRight(path, 1), c])));
71
+ console.log([...path]);
72
+ set(result, [...path], (lookup[key] ?? []).map((r) => r[joinTo] ?? []));
64
73
  }
65
- }
66
- }
74
+ });
75
+ });
76
+ await Promise.all([
77
+ ...fetchIncludedOneToManyRelations,
78
+ ...fetchIncludedManyToManyRelations,
79
+ ]);
67
80
  return results;
68
81
  };
69
82
  //# sourceMappingURL=findMany.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"findMany.js","sourceRoot":"","sources":["../../src/queries/findMany.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"findMany.js","sourceRoot":"","sources":["../../src/queries/findMany.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@casekit/orm",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.0.1-alpha.4",
4
4
  "description": "A simple ORM",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -20,7 +20,7 @@
20
20
  "@types/pg-format": "^1.0.5",
21
21
  "@types/pluralize": "^0.0.33",
22
22
  "@types/uuid": "^9.0.8",
23
- "@typescript-eslint/eslint-plugin": "^7.6.0",
23
+ "@typescript-eslint/eslint-plugin": "^7.7.0",
24
24
  "@vitest/coverage-istanbul": "^1.4.0",
25
25
  "@vitest/coverage-v8": "^1.4.0",
26
26
  "@vitest/ui": "^1.4.0",
@@ -0,0 +1,49 @@
1
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
2
+ import { BaseFindParams } from "./types/BaseFindParams";
3
+
4
+ export type ManyToManySubQuery = {
5
+ model: string;
6
+ name: string;
7
+ relation: {
8
+ model: string;
9
+ type: "N:N";
10
+ foreignKey: string | string[];
11
+ otherKey: string | string[];
12
+ through: string;
13
+ };
14
+ query: BaseFindParams;
15
+ path: string[];
16
+ };
17
+
18
+ export const getIncludedManyToManyRelations = (
19
+ config: BaseConfiguration,
20
+ m: string,
21
+ query: BaseFindParams,
22
+ path: string[] = [],
23
+ ) => {
24
+ const rels = config.relations[m];
25
+ const includedManyToManyRelations: ManyToManySubQuery[] = Object.entries(
26
+ query.include ?? {},
27
+ )
28
+ .filter(([r, _]) => rels[r].type === "N:N")
29
+ .map(([r, q]) => ({
30
+ model: m,
31
+ name: r,
32
+ relation: rels[r] as ManyToManySubQuery["relation"],
33
+ query: q!,
34
+ path: [...path, r],
35
+ }));
36
+
37
+ const joinedManyToManyRelations: ManyToManySubQuery[] = Object.entries(
38
+ query.include ?? {},
39
+ )
40
+ .filter(([r, _]) => rels[r].type === "N:1")
41
+ .flatMap(([r, q]) =>
42
+ getIncludedManyToManyRelations(config, rels[r].model, q!, [
43
+ ...path,
44
+ r,
45
+ ]),
46
+ );
47
+
48
+ return [...includedManyToManyRelations, ...joinedManyToManyRelations];
49
+ };
@@ -0,0 +1,44 @@
1
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
2
+ import { BaseRelation } from "../../schema/types/base/BaseRelation";
3
+ import { BaseFindParams } from "./types/BaseFindParams";
4
+
5
+ export type OneToManySubQuery = {
6
+ model: string;
7
+ name: string;
8
+ relation: BaseRelation;
9
+ query: BaseFindParams;
10
+ path: string[];
11
+ };
12
+
13
+ export const getIncludedOneToManyRelations = (
14
+ config: BaseConfiguration,
15
+ m: string,
16
+ query: BaseFindParams,
17
+ path: string[] = [],
18
+ ) => {
19
+ const rels = config.relations[m];
20
+ const includedOneToManyRelations: OneToManySubQuery[] = Object.entries(
21
+ query.include ?? {},
22
+ )
23
+ .filter(([r, _]) => rels[r].type === "1:N")
24
+ .map(([r, q]) => ({
25
+ model: m,
26
+ name: r,
27
+ relation: rels[r],
28
+ query: q!,
29
+ path: [...path, r],
30
+ }));
31
+
32
+ const joinedOneToManyRelations: OneToManySubQuery[] = Object.entries(
33
+ query.include ?? {},
34
+ )
35
+ .filter(([r, _]) => rels[r].type === "N:1")
36
+ .flatMap(([r, q]) =>
37
+ getIncludedOneToManyRelations(config, rels[r].model, q!, [
38
+ ...path,
39
+ r,
40
+ ]),
41
+ );
42
+
43
+ return [...includedOneToManyRelations, ...joinedOneToManyRelations];
44
+ };
@@ -0,0 +1,107 @@
1
+ import { describe, expect, test } from "vitest";
2
+
3
+ import { db } from "../../../test/db";
4
+ import { seed } from "../../../test/seed";
5
+
6
+ describe("findMany", () => {
7
+ test("it can include N:1 -> 1:N relations", async () => {
8
+ await db.transact(
9
+ async (db) => {
10
+ await seed(db, {
11
+ users: [
12
+ {
13
+ username: "Russell",
14
+ tenants: [
15
+ { name: "WFMA", posts: 1 },
16
+ { name: "Popova Park", posts: 0 },
17
+ ],
18
+ },
19
+ {
20
+ username: "Dan",
21
+ tenants: [{ name: "WFMA", posts: 2 }],
22
+ },
23
+ {
24
+ username: "Fairooz",
25
+ tenants: [{ name: "WFMA", posts: 0 }],
26
+ },
27
+ ],
28
+ });
29
+ const results = await db.findMany("post", {
30
+ select: ["id", "title"],
31
+ include: {
32
+ author: {
33
+ select: ["id", "username"],
34
+ include: {
35
+ posts: { select: ["id", "title"] },
36
+ },
37
+ },
38
+ },
39
+ limit: 2,
40
+ });
41
+
42
+ expect(
43
+ results.map((p) => [
44
+ p.author.username,
45
+ p.author.posts.map((p) => p.title),
46
+ ]),
47
+ ).toEqual([
48
+ ["Russell", ["Post a"]],
49
+ ["Dan", ["Post b", "Post c"]],
50
+ ]);
51
+ },
52
+ { rollback: true },
53
+ );
54
+ });
55
+
56
+ test("it can include N:1 -> N:N relations", async () => {
57
+ await db.transact(
58
+ async (db) => {
59
+ await seed(db, {
60
+ users: [
61
+ {
62
+ username: "Russell",
63
+ tenants: [
64
+ { name: "WFMA", posts: 1 },
65
+ { name: "Popova Park", posts: 0 },
66
+ ],
67
+ },
68
+ {
69
+ username: "Dan",
70
+ tenants: [{ name: "WFMA", posts: 2 }],
71
+ },
72
+ {
73
+ username: "Fairooz",
74
+ tenants: [{ name: "WFMA", posts: 0 }],
75
+ },
76
+ ],
77
+ });
78
+ const results = await db.findMany("post", {
79
+ select: ["id", "title"],
80
+ include: {
81
+ author: {
82
+ select: ["id", "username"],
83
+ include: {
84
+ tenants: {
85
+ select: ["id", "name"],
86
+ orderBy: ["name"],
87
+ },
88
+ },
89
+ },
90
+ },
91
+ limit: 2,
92
+ });
93
+
94
+ expect(
95
+ results.map((p) => [
96
+ p.author.username,
97
+ p.author.tenants.map((t) => t.name),
98
+ ]),
99
+ ).toEqual([
100
+ ["Russell", ["Popova Park", "WFMA"]],
101
+ ["Dan", ["WFMA"]],
102
+ ]);
103
+ },
104
+ { rollback: true },
105
+ );
106
+ });
107
+ });
@@ -1,4 +1,4 @@
1
- import { groupBy } from "lodash-es";
1
+ import { dropRight, get, groupBy, set } from "lodash-es";
2
2
  import hash from "object-hash";
3
3
  import { BaseConfiguration } from "src/schema/types/base/BaseConfiguration";
4
4
 
@@ -8,6 +8,8 @@ import { logger } from "../logger";
8
8
  import { ensureArray } from "../util/ensureArray";
9
9
  import { buildFind } from "./find/buildFind";
10
10
  import { findToSql } from "./find/findToSql";
11
+ import { getIncludedManyToManyRelations } from "./find/getIncludedManyToManyRelations";
12
+ import { getIncludedOneToManyRelations } from "./find/getIncludedOneToManyRelations";
11
13
  import { BaseFindParams } from "./find/types/BaseFindParams";
12
14
  import { rowToObject } from "./util/rowToObject";
13
15
 
@@ -32,82 +34,92 @@ export const findMany = async (
32
34
  .query(statement)
33
35
  .then((result) => result.rows.map(rowToObject(builder.columns)));
34
36
 
35
- for (const [r, subquery] of Object.entries(query.include ?? {})) {
36
- const relation = config.relations[m]![r];
37
- if (relation.type === "1:N") {
38
- const relation = config.relations[m][r];
39
-
40
- const pk = config.models[m].primaryKey;
41
- const fk = ensureArray(relation.foreignKey);
42
-
43
- const lateralBy = fk.map((c, index) => ({
44
- column: c,
45
- values: results.map((result) => result[pk[index]]),
46
- }));
47
-
48
- const subqueryResults = await findMany(
49
- conn,
50
- config,
51
- relation.model,
52
- { ...subquery!, lateralBy },
53
- );
54
-
37
+ const fetchIncludedOneToManyRelations = getIncludedOneToManyRelations(
38
+ config,
39
+ m,
40
+ query,
41
+ ).map(({ model, relation, query, path }) => {
42
+ console.log("Fetching 1:N relation");
43
+ const pk = config.models[model].primaryKey;
44
+ const fk = ensureArray(relation.foreignKey);
45
+ const lateralBy = fk.map((c, index) => ({
46
+ column: c,
47
+ values: results.map((result) =>
48
+ get(result, [...dropRight(path, 1), pk[index]]),
49
+ ),
50
+ }));
51
+ return findMany(conn, config, relation.model, {
52
+ ...query,
53
+ lateralBy,
54
+ }).then((subqueryResults) => {
55
55
  const lookup = groupBy(subqueryResults, (result) => {
56
56
  return hash(fk.map((c) => result[c]));
57
57
  });
58
-
59
58
  for (const result of results) {
60
- const key = hash(pk.map((c) => result[c]));
61
- result[r] = lookup[key] ?? [];
62
- }
63
- } else if (relation.type === "N:N") {
64
- const joinFrom = Object.entries(config.relations[m]).find(
65
- ([, rel]) =>
66
- rel.type === "1:N" && rel.model === relation.through,
67
- )?.[0];
68
-
69
- const joinTo = Object.entries(
70
- config.relations[relation.through],
71
- ).find(
72
- ([, rel]) => rel.type === "N:1" && rel.model === relation.model,
73
- )?.[0];
74
-
75
- if (joinFrom === undefined || joinTo === undefined) {
76
- throw new OrmError(
77
- "Both sides of a N:N relation must be defined",
78
- { data: { joinFrom, joinTo, query } },
59
+ const key = hash(
60
+ pk.map((c) => get(result, [...dropRight(path, 1), c])),
79
61
  );
62
+ console.log([...path]);
63
+ set(result, [...path], lookup[key] ?? []);
80
64
  }
65
+ });
66
+ });
81
67
 
82
- const pk = config.models[m].primaryKey;
83
- const fk = ensureArray(relation.foreignKey);
84
- const lateralBy = fk.map((c, index) => ({
85
- column: c,
86
- values: results.map((result) => result[pk[index]]),
87
- }));
68
+ const fetchIncludedManyToManyRelations = getIncludedManyToManyRelations(
69
+ config,
70
+ m,
71
+ query,
72
+ ).map(({ model, relation, query, path }) => {
73
+ console.log("Fetching N:N relation");
74
+ const joinFrom = Object.entries(config.relations[model]).find(
75
+ ([, rel]) => rel.type === "1:N" && rel.model === relation.through,
76
+ )?.[0];
77
+
78
+ const joinTo = Object.entries(config.relations[relation.through]).find(
79
+ ([, rel]) => rel.type === "N:1" && rel.model === relation.model,
80
+ )?.[0];
81
+
82
+ if (joinFrom === undefined || joinTo === undefined) {
83
+ throw new OrmError("Both sides of a N:N relation must be defined", {
84
+ data: { joinFrom, joinTo, query },
85
+ });
86
+ }
88
87
 
89
- const subqueryResults = await findMany(
90
- conn,
91
- config,
92
- relation.through,
93
- {
94
- select: ensureArray(relation.foreignKey),
95
- include: { [joinTo]: subquery! },
96
- lateralBy,
97
- },
98
- );
88
+ const pk = config.models[model].primaryKey;
89
+ const fk = ensureArray(relation.foreignKey);
90
+ const lateralBy = fk.map((c, index) => ({
91
+ column: c,
92
+ values: results.map((result) =>
93
+ get(result, [...dropRight(path, 1), pk[index]]),
94
+ ),
95
+ }));
96
+
97
+ return findMany(conn, config, relation.through, {
98
+ select: ensureArray(relation.foreignKey),
99
+ include: { [joinTo]: query },
100
+ lateralBy,
101
+ }).then((subqueryResults) => {
99
102
  const lookup = groupBy(subqueryResults, (result) => {
100
103
  return hash(fk.map((c) => result[c]));
101
104
  });
102
-
103
105
  for (const result of results) {
104
- const key = hash(pk.map((c) => result[c]));
105
- result[r] = (lookup[key] ?? []).map(
106
- (relation) => relation[joinTo] ?? [],
106
+ const key = hash(
107
+ pk.map((c) => get(result, [...dropRight(path, 1), c])),
108
+ );
109
+ console.log([...path]);
110
+ set(
111
+ result,
112
+ [...path],
113
+ (lookup[key] ?? []).map((r) => r[joinTo] ?? []),
107
114
  );
108
115
  }
109
- }
110
- }
116
+ });
117
+ });
118
+
119
+ await Promise.all([
120
+ ...fetchIncludedOneToManyRelations,
121
+ ...fetchIncludedManyToManyRelations,
122
+ ]);
111
123
 
112
124
  return results;
113
125
  };