orchid-orm 1.17.13 → 1.17.15

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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { columnTypes, QueryHooks, getColumnTypes, getQueryAs, setQueryObjectValue, pushQueryOn, VirtualColumn, pushQueryValue, isQueryReturnsAll, toSQLCacheKey, OrchidOrmInternalError, NotFoundError, Adapter, Db, anyShape, getClonedQueryData } from 'pqb';
1
+ import { columnTypes, QueryHooks, getColumnTypes, getQueryAs, setQueryObjectValue, pushQueryOn, VirtualColumn, pushQueryValue, isQueryReturnsAll, toSQLCacheKey, OrchidOrmInternalError, NotFoundError, Adapter, Db, anyShape, addComputedColumns, getClonedQueryData } from 'pqb';
2
2
  export { OrchidOrmError, OrchidOrmInternalError, columnTypes, raw, testTransaction } from 'pqb';
3
3
  import { getStackTrace, applyMixins, getCallerFilePath, snakeCaseKey, toSnakeCase, emptyArray, toArray } from 'orchid-core';
4
4
  import { AsyncLocalStorage } from 'node:async_hooks';
@@ -24,7 +24,7 @@ const createBaseTable = ({
24
24
  const base = (_a = class {
25
25
  constructor() {
26
26
  this.snakeCase = snakeCase;
27
- this.columnTypes = columnTypes$1;
27
+ this.types = columnTypes$1;
28
28
  this.q = {};
29
29
  this.language = language;
30
30
  }
@@ -72,6 +72,99 @@ const createBaseTable = ({
72
72
  }
73
73
  return this.constructor.prototype.columns = shape;
74
74
  }
75
+ /**
76
+ * You can add a generated column in the migration (see [generated](/guide/migration-column-methods.html#generated-column)),
77
+ * such column will persist in the database, it can be indexed.
78
+ *
79
+ * Or you can add a computed column on the ORM level, without adding it to the database, in such a way:
80
+ *
81
+ * ```ts
82
+ * import { BaseTable } from './baseTable';
83
+ *
84
+ * export class UserTable extends BaseTable {
85
+ * readonly table = 'user';
86
+ * columns = this.setColumns((t) => ({
87
+ * id: t.identity().primaryKey(),
88
+ * firstName: t.string(),
89
+ * lastName: t.string(),
90
+ * }));
91
+ *
92
+ * computed = this.setComputed({
93
+ * fullName: (q) =>
94
+ * q.sql`${q.column('firstName')} || ' ' || ${q.column('lastName')}`.type(
95
+ * (t) => t.string(),
96
+ * ),
97
+ * });
98
+ * }
99
+ * ```
100
+ *
101
+ * `setComputed` takes an object where keys are computed column names, and values are functions returning raw SQL.
102
+ *
103
+ * Use `q.column` as shown above to reference a table column, it will be prefixed with a correct table name even if the table is joined under a different name.
104
+ *
105
+ * Computed columns are not selected by default, only on demand:
106
+ *
107
+ * ```ts
108
+ * const a = await db.user.take();
109
+ * a.fullName; // not selected
110
+ *
111
+ * const b = await db.user.select('*', 'fullName');
112
+ * b.fullName; // selected
113
+ *
114
+ * // Table post belongs to user as an author.
115
+ * // it's possible to select joined computed column:
116
+ * const posts = await db.post
117
+ * .join('author')
118
+ * .select('post.title', 'author.fullName');
119
+ * ```
120
+ *
121
+ * SQL query can be generated dynamically based on the current request context.
122
+ *
123
+ * Imagine we are using [AsyncLocalStorage](https://nodejs.org/api/async_context.html#asynchronous-context-tracking)
124
+ * to keep track of current user's language.
125
+ *
126
+ * And we have articles translated to different languages, each article has `title_en`, `title_uk`, `title_be` and so on.
127
+ *
128
+ * We can define a computed `title` by passing a function into `sql` method:
129
+ *
130
+ * ```ts
131
+ * type Locale = 'en' | 'uk' | 'be';
132
+ * const asyncLanguageStorage = new AsyncLocalStorage<Locale>();
133
+ * const defaultLocale: Locale = 'en';
134
+ *
135
+ * export class ArticleTable extends BaseTable {
136
+ * readonly table = 'article';
137
+ * columns = this.setColumns((t) => ({
138
+ * id: t.identity().primaryKey(),
139
+ * title_en: t.text(),
140
+ * title_uk: t.text().nullable(),
141
+ * title_be: t.text().nullable(),
142
+ * }));
143
+ *
144
+ * computed = this.setComputed({
145
+ * title: (q) =>
146
+ * q
147
+ * // .sql can take a function that accepts `sql` argument and must return SQL
148
+ * .sql((sql) => {
149
+ * // get locale dynamically based on current storage value
150
+ * const locale = asyncLanguageStorage.getStore() || defaultLocale;
151
+ *
152
+ * // use COALESCE in case when localized title is NULL, use title_en
153
+ * return sql`COALESCE(
154
+ * ${q.column(`title_${locale}`)},
155
+ * ${q.column(`title_${defaultLocale}`)}
156
+ * )`;
157
+ * })
158
+ * .type((t) => t.text()),
159
+ * });
160
+ * }
161
+ * ```
162
+ *
163
+ * @param computed - object where keys are column names and values are functions returning raw SQL
164
+ */
165
+ setComputed(computed) {
166
+ return computed;
167
+ }
75
168
  belongsTo(fn, options) {
76
169
  return {
77
170
  type: "belongsTo",
@@ -102,7 +195,7 @@ const createBaseTable = ({
102
195
  }
103
196
  }, _a.nowSQL = nowSQL, _a.exportAs = exportAs, _a.schema = schemaProvider, _a);
104
197
  applyMixins(base, [QueryHooks]);
105
- base.prototype.columnTypes = columnTypes$1;
198
+ base.prototype.types = columnTypes$1;
106
199
  return base;
107
200
  };
108
201
 
@@ -247,9 +340,7 @@ class BelongsToVirtualColumn extends VirtualColumn {
247
340
  update(q, ctx, set) {
248
341
  q.q.wrapInTransaction = true;
249
342
  const data = set[this.key];
250
- if (this.nestedUpdate(q, set, data, ctx)) {
251
- ctx.willSetKeys = true;
252
- }
343
+ this.nestedUpdate(q, set, data, ctx);
253
344
  }
254
345
  }
255
346
  const makeBelongsToMethod = (relation, relationName, query) => {
@@ -458,7 +549,6 @@ const nestedUpdate$3 = ({ query, primaryKeys, foreignKeys, len }) => {
458
549
  }
459
550
  });
460
551
  }
461
- return !params.update && !params.upsert;
462
552
  };
463
553
  };
464
554
 
@@ -1707,7 +1797,7 @@ const orchidORM = (_a, tables) => {
1707
1797
  qb,
1708
1798
  table.table,
1709
1799
  table.columns,
1710
- table.columnTypes,
1800
+ table.types,
1711
1801
  transactionStorage,
1712
1802
  options2
1713
1803
  );
@@ -1715,6 +1805,12 @@ const orchidORM = (_a, tables) => {
1715
1805
  dbTable.db = result;
1716
1806
  dbTable.filePath = table.filePath;
1717
1807
  dbTable.name = table.constructor.name;
1808
+ if (table.computed) {
1809
+ addComputedColumns(
1810
+ dbTable,
1811
+ table.computed
1812
+ );
1813
+ }
1718
1814
  result[key] = dbTable;
1719
1815
  }
1720
1816
  applyRelations(qb, tableInstances, result);