relations.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import { getTableUniqueName, Table } from "./table.js";
  2. import { Column } from "./column.js";
  3. import { entityKind, is } from "./entity.js";
  4. import { PrimaryKeyBuilder } from "./pg-core/primary-keys.js";
  5. import {
  6. and,
  7. asc,
  8. between,
  9. desc,
  10. eq,
  11. exists,
  12. gt,
  13. gte,
  14. ilike,
  15. inArray,
  16. isNotNull,
  17. isNull,
  18. like,
  19. lt,
  20. lte,
  21. ne,
  22. not,
  23. notBetween,
  24. notExists,
  25. notIlike,
  26. notInArray,
  27. notLike,
  28. or
  29. } from "./sql/expressions/index.js";
  30. import { SQL, sql } from "./sql/sql.js";
  31. class Relation {
  32. constructor(sourceTable, referencedTable, relationName) {
  33. this.sourceTable = sourceTable;
  34. this.referencedTable = referencedTable;
  35. this.relationName = relationName;
  36. this.referencedTableName = referencedTable[Table.Symbol.Name];
  37. }
  38. static [entityKind] = "Relation";
  39. referencedTableName;
  40. fieldName;
  41. }
  42. class Relations {
  43. constructor(table, config) {
  44. this.table = table;
  45. this.config = config;
  46. }
  47. static [entityKind] = "Relations";
  48. }
  49. class One extends Relation {
  50. constructor(sourceTable, referencedTable, config, isNullable) {
  51. super(sourceTable, referencedTable, config?.relationName);
  52. this.config = config;
  53. this.isNullable = isNullable;
  54. }
  55. static [entityKind] = "One";
  56. withFieldName(fieldName) {
  57. const relation = new One(
  58. this.sourceTable,
  59. this.referencedTable,
  60. this.config,
  61. this.isNullable
  62. );
  63. relation.fieldName = fieldName;
  64. return relation;
  65. }
  66. }
  67. class Many extends Relation {
  68. constructor(sourceTable, referencedTable, config) {
  69. super(sourceTable, referencedTable, config?.relationName);
  70. this.config = config;
  71. }
  72. static [entityKind] = "Many";
  73. withFieldName(fieldName) {
  74. const relation = new Many(
  75. this.sourceTable,
  76. this.referencedTable,
  77. this.config
  78. );
  79. relation.fieldName = fieldName;
  80. return relation;
  81. }
  82. }
  83. function getOperators() {
  84. return {
  85. and,
  86. between,
  87. eq,
  88. exists,
  89. gt,
  90. gte,
  91. ilike,
  92. inArray,
  93. isNull,
  94. isNotNull,
  95. like,
  96. lt,
  97. lte,
  98. ne,
  99. not,
  100. notBetween,
  101. notExists,
  102. notLike,
  103. notIlike,
  104. notInArray,
  105. or,
  106. sql
  107. };
  108. }
  109. function getOrderByOperators() {
  110. return {
  111. sql,
  112. asc,
  113. desc
  114. };
  115. }
  116. function extractTablesRelationalConfig(schema, configHelpers) {
  117. if (Object.keys(schema).length === 1 && "default" in schema && !is(schema["default"], Table)) {
  118. schema = schema["default"];
  119. }
  120. const tableNamesMap = {};
  121. const relationsBuffer = {};
  122. const tablesConfig = {};
  123. for (const [key, value] of Object.entries(schema)) {
  124. if (is(value, Table)) {
  125. const dbName = getTableUniqueName(value);
  126. const bufferedRelations = relationsBuffer[dbName];
  127. tableNamesMap[dbName] = key;
  128. tablesConfig[key] = {
  129. tsName: key,
  130. dbName: value[Table.Symbol.Name],
  131. schema: value[Table.Symbol.Schema],
  132. columns: value[Table.Symbol.Columns],
  133. relations: bufferedRelations?.relations ?? {},
  134. primaryKey: bufferedRelations?.primaryKey ?? []
  135. };
  136. for (const column of Object.values(
  137. value[Table.Symbol.Columns]
  138. )) {
  139. if (column.primary) {
  140. tablesConfig[key].primaryKey.push(column);
  141. }
  142. }
  143. const extraConfig = value[Table.Symbol.ExtraConfigBuilder]?.(value[Table.Symbol.ExtraConfigColumns]);
  144. if (extraConfig) {
  145. for (const configEntry of Object.values(extraConfig)) {
  146. if (is(configEntry, PrimaryKeyBuilder)) {
  147. tablesConfig[key].primaryKey.push(...configEntry.columns);
  148. }
  149. }
  150. }
  151. } else if (is(value, Relations)) {
  152. const dbName = getTableUniqueName(value.table);
  153. const tableName = tableNamesMap[dbName];
  154. const relations2 = value.config(
  155. configHelpers(value.table)
  156. );
  157. let primaryKey;
  158. for (const [relationName, relation] of Object.entries(relations2)) {
  159. if (tableName) {
  160. const tableConfig = tablesConfig[tableName];
  161. tableConfig.relations[relationName] = relation;
  162. if (primaryKey) {
  163. tableConfig.primaryKey.push(...primaryKey);
  164. }
  165. } else {
  166. if (!(dbName in relationsBuffer)) {
  167. relationsBuffer[dbName] = {
  168. relations: {},
  169. primaryKey
  170. };
  171. }
  172. relationsBuffer[dbName].relations[relationName] = relation;
  173. }
  174. }
  175. }
  176. }
  177. return { tables: tablesConfig, tableNamesMap };
  178. }
  179. function relations(table, relations2) {
  180. return new Relations(
  181. table,
  182. (helpers) => Object.fromEntries(
  183. Object.entries(relations2(helpers)).map(([key, value]) => [
  184. key,
  185. value.withFieldName(key)
  186. ])
  187. )
  188. );
  189. }
  190. function createOne(sourceTable) {
  191. return function one(table, config) {
  192. return new One(
  193. sourceTable,
  194. table,
  195. config,
  196. config?.fields.reduce((res, f) => res && f.notNull, true) ?? false
  197. );
  198. };
  199. }
  200. function createMany(sourceTable) {
  201. return function many(referencedTable, config) {
  202. return new Many(sourceTable, referencedTable, config);
  203. };
  204. }
  205. function normalizeRelation(schema, tableNamesMap, relation) {
  206. if (is(relation, One) && relation.config) {
  207. return {
  208. fields: relation.config.fields,
  209. references: relation.config.references
  210. };
  211. }
  212. const referencedTableTsName = tableNamesMap[getTableUniqueName(relation.referencedTable)];
  213. if (!referencedTableTsName) {
  214. throw new Error(
  215. `Table "${relation.referencedTable[Table.Symbol.Name]}" not found in schema`
  216. );
  217. }
  218. const referencedTableConfig = schema[referencedTableTsName];
  219. if (!referencedTableConfig) {
  220. throw new Error(`Table "${referencedTableTsName}" not found in schema`);
  221. }
  222. const sourceTable = relation.sourceTable;
  223. const sourceTableTsName = tableNamesMap[getTableUniqueName(sourceTable)];
  224. if (!sourceTableTsName) {
  225. throw new Error(
  226. `Table "${sourceTable[Table.Symbol.Name]}" not found in schema`
  227. );
  228. }
  229. const reverseRelations = [];
  230. for (const referencedTableRelation of Object.values(
  231. referencedTableConfig.relations
  232. )) {
  233. if (relation.relationName && relation !== referencedTableRelation && referencedTableRelation.relationName === relation.relationName || !relation.relationName && referencedTableRelation.referencedTable === relation.sourceTable) {
  234. reverseRelations.push(referencedTableRelation);
  235. }
  236. }
  237. if (reverseRelations.length > 1) {
  238. throw relation.relationName ? new Error(
  239. `There are multiple relations with name "${relation.relationName}" in table "${referencedTableTsName}"`
  240. ) : new Error(
  241. `There are multiple relations between "${referencedTableTsName}" and "${relation.sourceTable[Table.Symbol.Name]}". Please specify relation name`
  242. );
  243. }
  244. if (reverseRelations[0] && is(reverseRelations[0], One) && reverseRelations[0].config) {
  245. return {
  246. fields: reverseRelations[0].config.references,
  247. references: reverseRelations[0].config.fields
  248. };
  249. }
  250. throw new Error(
  251. `There is not enough information to infer relation "${sourceTableTsName}.${relation.fieldName}"`
  252. );
  253. }
  254. function createTableRelationsHelpers(sourceTable) {
  255. return {
  256. one: createOne(sourceTable),
  257. many: createMany(sourceTable)
  258. };
  259. }
  260. function mapRelationalRow(tablesConfig, tableConfig, row, buildQueryResultSelection, mapColumnValue = (value) => value) {
  261. const result = {};
  262. for (const [
  263. selectionItemIndex,
  264. selectionItem
  265. ] of buildQueryResultSelection.entries()) {
  266. if (selectionItem.isJson) {
  267. const relation = tableConfig.relations[selectionItem.tsKey];
  268. const rawSubRows = row[selectionItemIndex];
  269. const subRows = typeof rawSubRows === "string" ? JSON.parse(rawSubRows) : rawSubRows;
  270. result[selectionItem.tsKey] = is(relation, One) ? subRows && mapRelationalRow(
  271. tablesConfig,
  272. tablesConfig[selectionItem.relationTableTsKey],
  273. subRows,
  274. selectionItem.selection,
  275. mapColumnValue
  276. ) : subRows.map(
  277. (subRow) => mapRelationalRow(
  278. tablesConfig,
  279. tablesConfig[selectionItem.relationTableTsKey],
  280. subRow,
  281. selectionItem.selection,
  282. mapColumnValue
  283. )
  284. );
  285. } else {
  286. const value = mapColumnValue(row[selectionItemIndex]);
  287. const field = selectionItem.field;
  288. let decoder;
  289. if (is(field, Column)) {
  290. decoder = field;
  291. } else if (is(field, SQL)) {
  292. decoder = field.decoder;
  293. } else {
  294. decoder = field.sql.decoder;
  295. }
  296. result[selectionItem.tsKey] = value === null ? null : decoder.mapFromDriverValue(value);
  297. }
  298. }
  299. return result;
  300. }
  301. export {
  302. Many,
  303. One,
  304. Relation,
  305. Relations,
  306. createMany,
  307. createOne,
  308. createTableRelationsHelpers,
  309. extractTablesRelationalConfig,
  310. getOperators,
  311. getOrderByOperators,
  312. mapRelationalRow,
  313. normalizeRelation,
  314. relations
  315. };
  316. //# sourceMappingURL=relations.js.map