@casekit/orm 0.0.1-alpha.14 → 0.0.1-alpha.16

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.
Files changed (215) hide show
  1. package/lib/orm.query.test.d.ts +2 -0
  2. package/lib/orm.query.test.d.ts.map +1 -0
  3. package/lib/orm.query.test.js +22 -0
  4. package/lib/orm.query.test.js.map +1 -0
  5. package/package.json +3 -6
  6. package/src/Connection.ts +65 -0
  7. package/src/errors.ts +18 -0
  8. package/src/index.ts +16 -0
  9. package/src/logger.ts +3 -0
  10. package/src/migrate/commands/implode.ts +46 -0
  11. package/src/migrate/index.ts +1 -0
  12. package/src/migrate/migrator.ts +24 -0
  13. package/src/migrate/sql/createExtensionsSql.test.ts +26 -0
  14. package/src/migrate/sql/createExtensionsSql.ts +16 -0
  15. package/src/migrate/sql/createForeignKeyConstraintSql.test.ts +50 -0
  16. package/src/migrate/sql/createForeignKeyConstraintSql.ts +44 -0
  17. package/src/migrate/sql/createSchemasSql.test.ts +81 -0
  18. package/src/migrate/sql/createSchemasSql.ts +15 -0
  19. package/src/migrate/sql/createTableSql.properties.ts +38 -0
  20. package/src/migrate/sql/createTableSql.test.ts +74 -0
  21. package/src/migrate/sql/createTableSql.ts +53 -0
  22. package/src/migrate/sql/createUniqueConstraintSql.ts +27 -0
  23. package/src/migrate/sql/dropSchemasSql.ts +15 -0
  24. package/src/migrate/sql/dropTableSql.ts +13 -0
  25. package/src/orm.query.test.ts +28 -0
  26. package/src/orm.ts +370 -0
  27. package/src/pull/index.ts +1 -0
  28. package/src/pull/introspect/getForeignKeys.ts +64 -0
  29. package/src/pull/introspect/getPrimaryKeys.ts +26 -0
  30. package/src/pull/introspect/getTables.ts +51 -0
  31. package/src/pull/introspect/getUniqueConstraints.ts +39 -0
  32. package/src/pull/parse/parseCreateUniqueIndexStatement.test.ts +14 -0
  33. package/src/pull/parse/parseCreateUniqueIndexStatement.ts +19 -0
  34. package/src/pull/pull.ts +78 -0
  35. package/src/pull/render/renderModel.test.ts +144 -0
  36. package/src/pull/render/renderModel.ts +141 -0
  37. package/src/pull/render/renderModelsIndex.ts +24 -0
  38. package/src/pull/render/renderRelations.ts +77 -0
  39. package/src/pull/render/renderRelationsIndex.ts +24 -0
  40. package/src/pull/types/ColumnMeta.ts +10 -0
  41. package/src/pull/types/ForeignKey.ts +6 -0
  42. package/src/pull/types/PrimaryKey.ts +6 -0
  43. package/src/pull/types/UniqueConstraint.ts +8 -0
  44. package/src/pull/util/format.ts +17 -0
  45. package/src/pull/util/quote.ts +7 -0
  46. package/src/pull/util/unquote.ts +9 -0
  47. package/src/queries/clauses/IncludeClause.ts +39 -0
  48. package/src/queries/clauses/LateralByClause.ts +4 -0
  49. package/src/queries/clauses/ReturningClause.ts +7 -0
  50. package/src/queries/clauses/SelectClause.ts +7 -0
  51. package/src/queries/clauses/WhereClause.ts +16 -0
  52. package/src/queries/clauses/helpers/OptionalColumn.ts +18 -0
  53. package/src/queries/clauses/helpers/OptionalParams.ts +14 -0
  54. package/src/queries/clauses/helpers/RequiredColumn.ts +8 -0
  55. package/src/queries/clauses/helpers/RequiredParams.ts +14 -0
  56. package/src/queries/clauses/include/IncludedRelationModel.ts +13 -0
  57. package/src/queries/clauses/include/IncludedRelationName.ts +11 -0
  58. package/src/queries/clauses/include/IncludedRelationQuery.ts +20 -0
  59. package/src/queries/clauses/where/buildWhereClause.ts +121 -0
  60. package/src/queries/clauses/where/buildWhereClauses.test.ts +145 -0
  61. package/src/queries/clauses/where/buildWhereClauses.ts +45 -0
  62. package/src/queries/clauses/where/operators.ts +13 -0
  63. package/src/queries/clauses/where/types/WhereClauseValue.ts +39 -0
  64. package/src/queries/count/buildCount.ts +77 -0
  65. package/src/queries/count/countToSql.ts +66 -0
  66. package/src/queries/count/tests/count.test.ts +35 -0
  67. package/src/queries/count/types/BaseCountParams.ts +11 -0
  68. package/src/queries/count/types/CountBuilder.ts +45 -0
  69. package/src/queries/count/types/CountParams.ts +27 -0
  70. package/src/queries/count.ts +33 -0
  71. package/src/queries/create/buildCreate.ts +67 -0
  72. package/src/queries/create/createResultSchema.ts +24 -0
  73. package/src/queries/create/createToSql.ts +44 -0
  74. package/src/queries/create/tests/createMany.varied-keys.test.ts +28 -0
  75. package/src/queries/create/tests/createOne.test-d.ts +116 -0
  76. package/src/queries/create/tests/createOne.test.ts +197 -0
  77. package/src/queries/create/types/BaseCreateManyParams.ts +7 -0
  78. package/src/queries/create/types/BaseCreateOneParams.ts +7 -0
  79. package/src/queries/create/types/CreateManyParams.ts +15 -0
  80. package/src/queries/create/types/CreateManyResult.ts +17 -0
  81. package/src/queries/create/types/CreateOneParams.ts +22 -0
  82. package/src/queries/create/types/CreateOneResult.ts +17 -0
  83. package/src/queries/createMany.ts +38 -0
  84. package/src/queries/createOne.ts +27 -0
  85. package/src/queries/delete/buildDelete.ts +56 -0
  86. package/src/queries/delete/deleteResultSchema.ts +23 -0
  87. package/src/queries/delete/deleteToSql.ts +48 -0
  88. package/src/queries/delete/tests/deleteOne.test.ts +108 -0
  89. package/src/queries/delete/types/BaseDeleteParams.ts +9 -0
  90. package/src/queries/delete/types/DeleteManyResult.ts +16 -0
  91. package/src/queries/delete/types/DeleteOneResult.ts +16 -0
  92. package/src/queries/delete/types/DeleteParams.ts +12 -0
  93. package/src/queries/deleteMany.ts +33 -0
  94. package/src/queries/deleteOne.ts +32 -0
  95. package/src/queries/find/buildFind.ts +138 -0
  96. package/src/queries/find/findResultSchema.ts +32 -0
  97. package/src/queries/find/findToSql.test.ts +123 -0
  98. package/src/queries/find/findToSql.ts +141 -0
  99. package/src/queries/find/getIncludedManyToManyRelations.ts +49 -0
  100. package/src/queries/find/getIncludedOneToManyRelations.ts +44 -0
  101. package/src/queries/find/tests/findMany.include.test.ts +107 -0
  102. package/src/queries/find/tests/findMany.limit.test-d.ts +75 -0
  103. package/src/queries/find/tests/findMany.limit.test.ts +176 -0
  104. package/src/queries/find/tests/findMany.nullable-relations.test.ts +127 -0
  105. package/src/queries/find/tests/findMany.orderBy.test-d.ts +84 -0
  106. package/src/queries/find/tests/findMany.orderBy.test.ts +184 -0
  107. package/src/queries/find/tests/findMany.select.test-d.ts +117 -0
  108. package/src/queries/find/tests/findMany.select.test.ts +188 -0
  109. package/src/queries/find/tests/findMany.too-deep.test-d.ts +154 -0
  110. package/src/queries/find/tests/findMany.where.test-d.ts +85 -0
  111. package/src/queries/find/tests/findMany.where.test.ts +76 -0
  112. package/src/queries/find/tests/middleware.find.where.test.ts +467 -0
  113. package/src/queries/find/types/BaseFindParams.ts +18 -0
  114. package/src/queries/find/types/FindBuilder.ts +73 -0
  115. package/src/queries/find/types/FindManyParams.ts +24 -0
  116. package/src/queries/find/types/FindManyResult.ts +13 -0
  117. package/src/queries/find/types/FindOneParams.ts +17 -0
  118. package/src/queries/find/types/FindOneResult.ts +50 -0
  119. package/src/queries/findMany.ts +134 -0
  120. package/src/queries/findOne.ts +30 -0
  121. package/src/queries/middleware/Middleware.ts +53 -0
  122. package/src/queries/middleware/ValuesMiddleware.ts +9 -0
  123. package/src/queries/middleware/WhereMiddleware.ts +16 -0
  124. package/src/queries/update/buildUpdate.ts +76 -0
  125. package/src/queries/update/tests/updateOne.test.ts +118 -0
  126. package/src/queries/update/types/BaseUpdateParams.ts +10 -0
  127. package/src/queries/update/types/UpdateManyResult.ts +16 -0
  128. package/src/queries/update/types/UpdateOneResult.ts +16 -0
  129. package/src/queries/update/types/UpdateParams.ts +14 -0
  130. package/src/queries/update/types/UpdateValues.ts +12 -0
  131. package/src/queries/update/updateResultSchema.ts +23 -0
  132. package/src/queries/update/updateToSql.ts +43 -0
  133. package/src/queries/updateMany.ts +33 -0
  134. package/src/queries/updateOne.ts +33 -0
  135. package/src/queries/util/hasConditions.test.ts +36 -0
  136. package/src/queries/util/hasConditions.ts +14 -0
  137. package/src/queries/util/rowToObject.test.ts +76 -0
  138. package/src/queries/util/rowToObject.ts +13 -0
  139. package/src/queries/util/tableAlias.test.ts +20 -0
  140. package/src/queries/util/tableAlias.ts +10 -0
  141. package/src/schema/populate/composeMiddleware.ts +51 -0
  142. package/src/schema/populate/populateConfiguration.ts +48 -0
  143. package/src/schema/populate/populateModel.ts +98 -0
  144. package/src/schema/populate/suggestedColumnSchema.ts +62 -0
  145. package/src/schema/types/base/BaseColumn.ts +10 -0
  146. package/src/schema/types/base/BaseConfiguration.ts +14 -0
  147. package/src/schema/types/base/BaseModel.ts +12 -0
  148. package/src/schema/types/base/BaseModels.ts +3 -0
  149. package/src/schema/types/base/BaseOrm.ts +8 -0
  150. package/src/schema/types/base/BaseRelation.ts +19 -0
  151. package/src/schema/types/base/BaseRelations.ts +3 -0
  152. package/src/schema/types/constraints/ForeignKey.ts +13 -0
  153. package/src/schema/types/constraints/UniqueConstraint.ts +9 -0
  154. package/src/schema/types/helpers/ColumnName.ts +6 -0
  155. package/src/schema/types/helpers/ColumnType.ts +23 -0
  156. package/src/schema/types/helpers/Columns.ts +3 -0
  157. package/src/schema/types/helpers/HasDefault.ts +11 -0
  158. package/src/schema/types/helpers/IsNullable.ts +7 -0
  159. package/src/schema/types/helpers/IsProvided.ts +8 -0
  160. package/src/schema/types/helpers/IsSerial.ts +10 -0
  161. package/src/schema/types/helpers/ModelName.ts +6 -0
  162. package/src/schema/types/loose/LooseColumnDefinition.ts +80 -0
  163. package/src/schema/types/loose/LooseModelDefinition.ts +47 -0
  164. package/src/schema/types/loose/LooseModelDefinitions.ts +3 -0
  165. package/src/schema/types/loose/LooseRelationDefinition.ts +10 -0
  166. package/src/schema/types/loose/LooseRelationsDefinition.ts +8 -0
  167. package/src/schema/types/loose/LooseRelationsDefinitions.ts +7 -0
  168. package/src/schema/types/postgres/DataType.ts +65 -0
  169. package/src/schema/types/relations/ManyToManyRelation.ts +15 -0
  170. package/src/schema/types/relations/ManyToOneRelation.ts +14 -0
  171. package/src/schema/types/relations/OneToManyRelation.ts +12 -0
  172. package/src/schema/types/strict/ColumnDefinition.ts +81 -0
  173. package/src/schema/types/strict/ModelDefinition.ts +47 -0
  174. package/src/schema/types/strict/ModelDefinitions.ts +3 -0
  175. package/src/schema/types/strict/RelationDefinition.ts +13 -0
  176. package/src/schema/types/strict/RelationsDefinition.ts +8 -0
  177. package/src/schema/types/strict/RelationsDefinitions.ts +7 -0
  178. package/src/schema/validate/validateConfiguration.ts +9 -0
  179. package/src/schema/validate/validateModel.ts +32 -0
  180. package/src/sql/SQLStatement.test.ts +112 -0
  181. package/src/sql/SQLStatement.ts +86 -0
  182. package/src/sql/index.ts +2 -0
  183. package/src/sql/sql.ts +47 -0
  184. package/src/test/db/index.ts +27 -0
  185. package/src/test/db/models/foo.model.ts +24 -0
  186. package/src/test/db/models/foo.relations.ts +4 -0
  187. package/src/test/db/models/post.model.ts +34 -0
  188. package/src/test/db/models/post.relations.ts +21 -0
  189. package/src/test/db/models/tenant.model.ts +14 -0
  190. package/src/test/db/models/tenant.relations.ts +22 -0
  191. package/src/test/db/models/tenantUser.model.ts +23 -0
  192. package/src/test/db/models/tenantUser.relations.ts +15 -0
  193. package/src/test/db/models/user.model.ts +29 -0
  194. package/src/test/db/models/user.relations.ts +38 -0
  195. package/src/test/db/models.ts +15 -0
  196. package/src/test/db/relations.ts +13 -0
  197. package/src/test/gen/column.ts +39 -0
  198. package/src/test/gen/index.ts +2 -0
  199. package/src/test/gen/model.ts +58 -0
  200. package/src/test/gen/sqldate.ts +8 -0
  201. package/src/test/globalSetup.ts +6 -0
  202. package/src/test/seed/index.ts +75 -0
  203. package/src/test/util/withRollback.ts +18 -0
  204. package/src/test/util/withTransaction.ts +19 -0
  205. package/src/types/ColumnName.ts +6 -0
  206. package/src/types/ColumnType.ts +20 -0
  207. package/src/types/Configuration.ts +21 -0
  208. package/src/types/ModelType.ts +20 -0
  209. package/src/types/util/DeepRequired.ts +7 -0
  210. package/src/types/util/DisallowExtraKeys.ts +5 -0
  211. package/src/types/util/NonEmptyArray.ts +1 -0
  212. package/src/types/util/Simplify.ts +8 -0
  213. package/src/util/ensureArray.ts +4 -0
  214. package/src/util/interleave.test.ts +35 -0
  215. package/src/util/interleave.ts +8 -0
@@ -0,0 +1,123 @@
1
+ import { unindent } from "@casekit/unindent";
2
+
3
+ import * as uuid from "uuid";
4
+ import { describe, expect, test } from "vitest";
5
+
6
+ import { FindMany, db } from "../../test/db";
7
+ import { $and, $ilike, $lte } from "../clauses/where/operators";
8
+ import { buildFind } from "./buildFind";
9
+ import { findToSql } from "./findToSql";
10
+
11
+ describe("findToSql", () => {
12
+ test("it builds a valid query for simple selects", async () => {
13
+ const builder = buildFind(db.config, "post", {
14
+ select: ["id", "title"],
15
+ });
16
+ const statement = findToSql(db.config, builder);
17
+ expect(statement.text).toEqual(unindent`
18
+ SELECT
19
+ a.id AS a_0,
20
+ a.title AS a_1
21
+ FROM casekit.post a
22
+ WHERE 1 = 1
23
+ `);
24
+ });
25
+
26
+ test("it builds a valid query for included N:1 relations", async () => {
27
+ const builder = buildFind(db.config, "post", {
28
+ select: ["id", "title"],
29
+ include: {
30
+ author: { select: ["username"] },
31
+ },
32
+ } as FindMany<"post">);
33
+
34
+ const statement = findToSql(db.config, builder);
35
+ expect(statement.text).toEqual(unindent`
36
+ SELECT
37
+ a.id AS a_0,
38
+ a.title AS a_1,
39
+ b.username AS b_0,
40
+ b.id AS b_1
41
+ FROM casekit.post a
42
+ JOIN casekit."user" b
43
+ ON a.created_by_id = b.id
44
+ WHERE 1 = 1
45
+ `);
46
+ });
47
+
48
+ test("it builds a valid lateral query for 1:N relations", async () => {
49
+ const builder = buildFind(db.config, "post", {
50
+ select: ["id", "title"],
51
+ include: {
52
+ author: { select: ["username"] },
53
+ },
54
+ lateralBy: [{ column: "id", values: [uuid.v4()] }],
55
+ } as FindMany<"post">);
56
+
57
+ const statement = findToSql(db.config, builder);
58
+ expect(statement.text).toEqual(unindent`
59
+ SELECT d.* FROM (
60
+ SELECT UNNEST(ARRAY[$1]::uuid[]) AS id) c
61
+ JOIN LATERAL (
62
+ SELECT
63
+ a.id AS a_0,
64
+ a.title AS a_1,
65
+ b.username AS b_0,
66
+ b.id AS b_1
67
+ FROM casekit.post a
68
+ JOIN casekit."user" b
69
+ ON a.created_by_id = b.id
70
+ WHERE 1 = 1
71
+ AND a.id = c.id
72
+ ) d ON TRUE
73
+ `);
74
+ });
75
+
76
+ test("it allows specifying where clauses on both the top level table and N:1 relations", async () => {
77
+ const id1 = uuid.v4();
78
+ const id2 = uuid.v4();
79
+ const builder = buildFind(db.config, "post", {
80
+ select: ["id", "title"],
81
+ where: { title: { [$ilike]: "%cats%" } },
82
+ include: {
83
+ author: {
84
+ select: ["username"],
85
+ where: {
86
+ [$and]: [
87
+ { username: "Russell" },
88
+ { joinedAt: { [$lte]: new Date(2024, 6, 4) } },
89
+ ],
90
+ },
91
+ },
92
+ },
93
+ lateralBy: [{ column: "id", values: [id1, id2] }],
94
+ } as FindMany<"post">);
95
+
96
+ const statement = findToSql(db.config, builder);
97
+ expect(statement.text).toEqual(unindent`
98
+ SELECT d.* FROM (
99
+ SELECT UNNEST(ARRAY[$1, $2]::uuid[]) AS id) c
100
+ JOIN LATERAL (
101
+ SELECT
102
+ a.id AS a_0,
103
+ a.title AS a_1,
104
+ b.username AS b_0,
105
+ b.id AS b_1
106
+ FROM casekit.post a
107
+ JOIN casekit."user" b
108
+ ON a.created_by_id = b.id
109
+ AND (((b.username = $3) AND (b.created_at <= $4)))
110
+ WHERE 1 = 1
111
+ AND (a.title ILIKE $5)
112
+ AND a.id = c.id
113
+ ) d ON TRUE
114
+ `);
115
+ expect(statement.values).toEqual([
116
+ id1,
117
+ id2,
118
+ "Russell",
119
+ new Date(2024, 6, 4),
120
+ "%cats%",
121
+ ]);
122
+ });
123
+ });
@@ -0,0 +1,141 @@
1
+ import pgfmt from "pg-format";
2
+
3
+ import { OrmError } from "../../errors";
4
+ import { BaseConfiguration } from "../../schema/types/base/BaseConfiguration";
5
+ import { SQLStatement, sql } from "../../sql";
6
+ import { interleave } from "../../util/interleave";
7
+ import { buildWhereClauses } from "../clauses/where/buildWhereClauses";
8
+ import { hasConditions } from "../util/hasConditions";
9
+ import { FindBuilder } from "./types/FindBuilder";
10
+
11
+ export const findToSql = (
12
+ config: BaseConfiguration,
13
+ builder: FindBuilder,
14
+ ): SQLStatement => {
15
+ const frag = new SQLStatement();
16
+ const table = builder.table;
17
+
18
+ if (builder.lateralBy) {
19
+ const { columns, groupTable, itemTable } = builder.lateralBy;
20
+ frag.push(pgfmt(`SELECT %I.* FROM (\nSELECT `, itemTable));
21
+ columns.forEach((c, index) => {
22
+ frag.push(pgfmt(`UNNEST(ARRAY[`));
23
+
24
+ frag.push(
25
+ ...interleave(
26
+ c.values.map((v) => sql`${v}`),
27
+ sql`, `,
28
+ ),
29
+ );
30
+
31
+ frag.push(pgfmt(`]::%I[]) AS %I`, c.type, c.column));
32
+
33
+ if (index !== columns.length - 1) {
34
+ frag.push(pgfmt(", "));
35
+ }
36
+ });
37
+ frag.push(pgfmt(`) %I\nJOIN LATERAL (\n`, groupTable));
38
+ }
39
+
40
+ frag.push(
41
+ pgfmt(
42
+ `SELECT\n${builder.columns.map((c) => pgfmt(` %I.%I AS %I`, c.table, c.name, c.alias)).join(",\n")}`,
43
+ ),
44
+ );
45
+
46
+ frag.push(pgfmt(`\nFROM %I.%I %I`, table.schema, table.table, table.alias));
47
+
48
+ for (const join of table.joins ?? []) {
49
+ if (join.from.columns.length !== join.to.columns.length) {
50
+ throw new OrmError(
51
+ "Number of foreign keys doesn't match number of primary keys in join",
52
+ { data: builder },
53
+ );
54
+ }
55
+
56
+ frag.push(join.type === "left" ? "\nLEFT JOIN" : "\nJOIN");
57
+ frag.push(
58
+ pgfmt(
59
+ " %I.%I %I\n ON ",
60
+ join.to.schema,
61
+ join.to.table,
62
+ join.to.alias,
63
+ ),
64
+ );
65
+ frag.push(
66
+ join.from.columns
67
+ .map((from, i) =>
68
+ pgfmt(
69
+ "%I.%I = %I.%I",
70
+ join.from.alias,
71
+ from,
72
+ join.to.alias,
73
+ join.to.columns[i],
74
+ ),
75
+ )
76
+ .join(" AND "),
77
+ );
78
+ if (hasConditions(join.where)) {
79
+ frag.push(
80
+ sql`\n AND ${buildWhereClauses(config, join.to, join.where)}`,
81
+ );
82
+ }
83
+ }
84
+
85
+ frag.push(sql`\nWHERE 1 = 1`);
86
+
87
+ if (hasConditions(table.where)) {
88
+ frag.push(
89
+ sql`\n AND ${buildWhereClauses(config, table, table.where)}`,
90
+ );
91
+ }
92
+
93
+ if (builder.lateralBy) {
94
+ const { groupTable, columns } = builder.lateralBy;
95
+ columns.forEach((c) => {
96
+ frag.push(
97
+ pgfmt(
98
+ `\n AND %I.%I = %I.%I`,
99
+ table.alias,
100
+ c.column,
101
+ groupTable,
102
+ c.column,
103
+ ),
104
+ );
105
+ });
106
+ }
107
+
108
+ if (builder.orderBy.length > 0) {
109
+ frag.push(pgfmt(`\nORDER BY `));
110
+ frag.push(
111
+ builder.orderBy
112
+ .map(({ table, column, direction }) =>
113
+ pgfmt(
114
+ `%I.%I ${direction === "asc" ? "ASC" : "DESC"}`,
115
+ table,
116
+ column,
117
+ ),
118
+ )
119
+ .join(", "),
120
+ );
121
+ }
122
+
123
+ if (builder.limit !== undefined) {
124
+ frag.push(sql`\nLIMIT ${builder.limit}`);
125
+ }
126
+
127
+ if (builder.offset !== undefined) {
128
+ frag.push(sql`\nOFFSET ${builder.offset}`);
129
+ }
130
+
131
+ if (builder.for) {
132
+ frag.push(pgfmt(`\nFOR ${builder.for.toUpperCase()}`));
133
+ }
134
+
135
+ if (builder.lateralBy) {
136
+ const { itemTable } = builder.lateralBy;
137
+ frag.push(pgfmt(`\n) %I ON TRUE`, itemTable));
138
+ }
139
+
140
+ return frag;
141
+ };
@@ -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
+ });
@@ -0,0 +1,75 @@
1
+ import { assertType, describe, test } from "vitest";
2
+
3
+ import { db } from "../../../test/db";
4
+
5
+ describe("findMany", () => {
6
+ test("queries can have limits and offsets applied", () => {
7
+ assertType(
8
+ db.findMany("post", {
9
+ select: ["id", "content"],
10
+ limit: 2,
11
+ offset: 5,
12
+ }),
13
+ );
14
+ });
15
+
16
+ test("included N:1 relations cannot have limits applied", () => {
17
+ assertType(
18
+ db.findMany("post", {
19
+ select: ["id", "content"],
20
+ include: {
21
+ author: {
22
+ select: ["username"],
23
+ // @ts-expect-error N:1 relations cannot have limits applied
24
+ limit: 1,
25
+ },
26
+ },
27
+ }),
28
+ );
29
+ });
30
+
31
+ test("included N:1 relations cannot have offsets applied", () => {
32
+ assertType(
33
+ db.findMany("post", {
34
+ select: ["id", "content"],
35
+ include: {
36
+ author: {
37
+ select: ["username"],
38
+ // @ts-expect-error N:1 relations cannot have offsets applied
39
+ offset: 1,
40
+ },
41
+ },
42
+ }),
43
+ );
44
+ });
45
+
46
+ test("included 1:N relations can have limits and offsets applied", () => {
47
+ assertType(
48
+ db.findMany("user", {
49
+ select: ["id", "username"],
50
+ include: {
51
+ posts: {
52
+ select: ["title"],
53
+ offset: 1,
54
+ limit: 2,
55
+ },
56
+ },
57
+ }),
58
+ );
59
+ });
60
+
61
+ test("included N:N relations can have offsets and limits applied", () => {
62
+ assertType(
63
+ db.findMany("user", {
64
+ select: ["id", "username"],
65
+ include: {
66
+ tenants: {
67
+ select: ["name"],
68
+ offset: 1,
69
+ limit: 2,
70
+ },
71
+ },
72
+ }),
73
+ );
74
+ });
75
+ });