proto.io 0.0.164 → 0.0.166

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 (53) hide show
  1. package/dist/adapters/file/database.d.ts +2 -2
  2. package/dist/adapters/file/database.js.map +1 -1
  3. package/dist/adapters/file/database.mjs +2 -2
  4. package/dist/adapters/file/database.mjs.map +1 -1
  5. package/dist/adapters/file/filesystem.d.ts +2 -2
  6. package/dist/adapters/file/filesystem.js.map +1 -1
  7. package/dist/adapters/file/filesystem.mjs.map +1 -1
  8. package/dist/adapters/file/google-cloud-storage.d.ts +2 -2
  9. package/dist/adapters/file/google-cloud-storage.js.map +1 -1
  10. package/dist/adapters/file/google-cloud-storage.mjs.map +1 -1
  11. package/dist/adapters/storage/progres.d.ts +16 -13
  12. package/dist/adapters/storage/progres.js +136 -81
  13. package/dist/adapters/storage/progres.js.map +1 -1
  14. package/dist/adapters/storage/progres.mjs +138 -83
  15. package/dist/adapters/storage/progres.mjs.map +1 -1
  16. package/dist/client.d.ts +3 -3
  17. package/dist/client.js +1 -1
  18. package/dist/client.mjs +3 -3
  19. package/dist/index.d.ts +7 -12
  20. package/dist/index.js +84 -47
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +87 -50
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/internals/{index-Dz3jvqxZ.js → index-B-pq8xkP.js} +5 -21
  25. package/dist/internals/index-B-pq8xkP.js.map +1 -0
  26. package/dist/internals/{index-BsuUdR0W.d.ts → index-BQggoDNX.d.ts} +2 -2
  27. package/dist/internals/index-BQggoDNX.d.ts.map +1 -0
  28. package/dist/internals/{index-ByfpVHca.mjs → index-BYbMU-Ao.mjs} +2 -2
  29. package/dist/internals/{index-ByfpVHca.mjs.map → index-BYbMU-Ao.mjs.map} +1 -1
  30. package/dist/internals/{index-S5Bq-KsU.mjs → index-BejQNqvC.mjs} +2 -2
  31. package/dist/internals/{index-S5Bq-KsU.mjs.map → index-BejQNqvC.mjs.map} +1 -1
  32. package/dist/internals/index-BqFdBhFc.js.map +1 -1
  33. package/dist/internals/index-CSNRyhjB.js.map +1 -1
  34. package/dist/internals/{index-BoP4kl2R.mjs → index-CUJMde7V.mjs} +6 -22
  35. package/dist/internals/index-CUJMde7V.mjs.map +1 -0
  36. package/dist/internals/index-CVutVPmd.js.map +1 -1
  37. package/dist/internals/index-be1VYBY2.mjs.map +1 -1
  38. package/dist/internals/{index-BeV63sFw.d.ts → index-rkqvel7o.d.ts} +2 -2
  39. package/dist/internals/index-rkqvel7o.d.ts.map +1 -0
  40. package/dist/internals/{index-YdOGTHp1.d.ts → index-y8EePsDY.d.ts} +10 -6
  41. package/dist/internals/index-y8EePsDY.d.ts.map +1 -0
  42. package/dist/internals/{random-w8WDYQEe.mjs → random-DrURPPxr.mjs} +5 -10
  43. package/dist/internals/random-DrURPPxr.mjs.map +1 -0
  44. package/dist/internals/{random-uT4D0y_q.js → random-q0PeamQE.js} +3 -8
  45. package/dist/internals/random-q0PeamQE.js.map +1 -0
  46. package/package.json +20 -20
  47. package/dist/internals/index-BeV63sFw.d.ts.map +0 -1
  48. package/dist/internals/index-BoP4kl2R.mjs.map +0 -1
  49. package/dist/internals/index-BsuUdR0W.d.ts.map +0 -1
  50. package/dist/internals/index-Dz3jvqxZ.js.map +0 -1
  51. package/dist/internals/index-YdOGTHp1.d.ts.map +0 -1
  52. package/dist/internals/random-uT4D0y_q.js.map +0 -1
  53. package/dist/internals/random-w8WDYQEe.mjs.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import _ from 'lodash';
2
- import { i as isPointer, a as isRelation, b as isShape, d as decodeUpdateOp, s as shapePaths, e as isPrimitive, T as TObject, f as isVector, _ as _decodeValue, g as _encodeValue, h as dimensionOf, j as _typeof } from '../../internals/index-ByfpVHca.mjs';
2
+ import { i as isPointer, a as isRelation, b as isShape, d as decodeUpdateOp, s as shapePaths, e as isPrimitive, T as TObject, f as isVector, _ as _encodeValue, g as _decodeValue, h as dimensionOf, j as _typeof } from '../../internals/index-BYbMU-Ao.mjs';
3
3
  import { Pool, types } from 'pg';
4
4
  import QueryStream from 'pg-query-stream';
5
5
  import { asyncStream } from '@o2ter/utils-js';
6
6
  import Decimal from 'decimal.js';
7
7
  import { escapeLiteral, escapeIdentifier } from 'pg/lib/utils';
8
- import { a as QueryCoditionalSelector, b as QueryFieldSelector, c as QueryExpressionSelector, d as QueryDistanceExpression, e as QueryCoditionalExpression, f as QueryComparisonExpression, g as QueryNotExpression, h as QueryArrayExpression, i as QueryValueExpression, j as QueryKeyExpression, Q as QuerySelector, F as FieldSelectorExpression } from '../../internals/index-S5Bq-KsU.mjs';
8
+ import { a as QueryCoditionalSelector, b as QueryFieldSelector, c as QueryExpressionSelector, d as QueryDistanceExpression, e as QueryCoditionalExpression, f as QueryComparisonExpression, g as QueryNotExpression, h as QueryArrayExpression, i as QueryValueExpression, j as QueryKeyExpression, Q as QuerySelector, F as FieldSelectorExpression } from '../../internals/index-BejQNqvC.mjs';
9
9
  import '@o2ter/crypto-js';
10
- import { r as resolveColumn, a as resolveDataType, g as generateId, Q as QueryValidator } from '../../internals/random-w8WDYQEe.mjs';
10
+ import { r as resolveColumn, a as resolveDataType, g as generateId, Q as QueryValidator } from '../../internals/random-DrURPPxr.mjs';
11
11
  import { P as PVK } from '../../internals/private-BUpLAMZi.mjs';
12
12
 
13
13
  //
@@ -220,12 +220,14 @@ class QueryCompiler {
220
220
  dialect;
221
221
  selectLock;
222
222
  isUpdate;
223
+ extraFilter;
223
224
  idx = 0;
224
- constructor(schema, dialect, selectLock, isUpdate) {
225
- this.schema = schema;
226
- this.dialect = dialect;
227
- this.selectLock = selectLock;
228
- this.isUpdate = isUpdate;
225
+ constructor(options) {
226
+ this.schema = options.schema;
227
+ this.dialect = options.dialect;
228
+ this.selectLock = options.selectLock;
229
+ this.isUpdate = options.isUpdate;
230
+ this.extraFilter = options.extraFilter;
229
231
  }
230
232
  nextIdx() {
231
233
  return this.idx++;
@@ -282,11 +284,11 @@ class QueryCompiler {
282
284
  }
283
285
  _baseSelectQuery(query, options) {
284
286
  const context = this._makeContext(query);
285
- const _stages = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, context, populate));
287
+ const _stages = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, populate));
286
288
  const stages = _.fromPairs(_.flatMap(_.values(_stages), (p) => _.toPairs(p)));
287
289
  const fetchName = `_fetch_$${query.className.toLowerCase()}`;
288
290
  const parent = { className: query.className, name: fetchName };
289
- const baseFilter = this._encodeFilter(context, parent, query.filter);
291
+ const baseFilter = this._encodeFilter(parent, query.filter);
290
292
  const populates = this._selectPopulateMap(context, query.className, fetchName);
291
293
  const joins = _.compact(_.map(populates, ({ join }) => join));
292
294
  const includes = {
@@ -300,7 +302,7 @@ class QueryCompiler {
300
302
  const filter = _.compact([
301
303
  baseFilter,
302
304
  _options?.extraFilter,
303
- query.relatedBy && this.dialect.encodeRelation(this, context, parent, query.relatedBy),
305
+ query.relatedBy && this.dialect.encodeRelation(this, parent, query.relatedBy),
304
306
  ]);
305
307
  return {
306
308
  stages,
@@ -321,9 +323,9 @@ class QueryCompiler {
321
323
  `,
322
324
  };
323
325
  }
324
- _refetch(name, query, context) {
326
+ _refetch(name, query) {
325
327
  const _context = this._encodeIncludes(query.className, query.includes, query.matches);
326
- const populates = _.mapValues(_context.populates, (populate) => this.dialect.encodePopulate(this, context, populate, { className: query.className, name }));
328
+ const populates = _.mapValues(_context.populates, (populate) => this.dialect.encodePopulate(this, populate, { className: query.className, name }));
327
329
  const stages = _.fromPairs(_.flatMap(_.values(populates), (p) => _.toPairs(p)));
328
330
  const _populates = this._selectPopulateMap(_context, query.className, name);
329
331
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
@@ -394,8 +396,8 @@ class QueryCompiler {
394
396
  }
395
397
  return result;
396
398
  }
397
- _encodeCoditionalSelector(parent, filter, context) {
398
- const queries = _.compact(_.map(filter.exprs, x => this._encodeFilter(context, parent, x)));
399
+ _encodeCoditionalSelector(parent, filter) {
400
+ const queries = _.compact(_.map(filter.exprs, x => this._encodeFilter(parent, x)));
399
401
  if (_.isEmpty(queries))
400
402
  return;
401
403
  switch (filter.type) {
@@ -404,12 +406,12 @@ class QueryCompiler {
404
406
  case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
405
407
  }
406
408
  }
407
- _encodeFilter(context, parent, filter) {
409
+ _encodeFilter(parent, filter) {
408
410
  if (filter instanceof QueryCoditionalSelector) {
409
- return this._encodeCoditionalSelector(parent, filter, context);
411
+ return this._encodeCoditionalSelector(parent, filter);
410
412
  }
411
413
  if (filter instanceof QueryFieldSelector) {
412
- return this.dialect.encodeFieldExpression(this, context, parent, filter.field, filter.expr);
414
+ return this.dialect.encodeFieldExpression(this, parent, filter.field, filter.expr);
413
415
  }
414
416
  if (filter instanceof QueryExpressionSelector) {
415
417
  return this.dialect.encodeQueryExpression(this, parent, filter.expr);
@@ -450,7 +452,7 @@ class QueryCompiler {
450
452
  });
451
453
  const name = `_insert_$${options.className.toLowerCase()}`;
452
454
  const context = this._makeContext(options);
453
- const populates = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, context, populate));
455
+ const populates = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, populate));
454
456
  const stages = _.fromPairs(_.flatMap(_.values(populates), (p) => _.toPairs(p)));
455
457
  const _populates = this._selectPopulateMap(context, options.className, name);
456
458
  const joins = _.compact(_.map(_populates, ({ join }) => join));
@@ -485,7 +487,7 @@ class QueryCompiler {
485
487
  `;
486
488
  }
487
489
  updateOne(query, update) {
488
- return this._modifyQuery({ ...query, limit: 1 }, (fetchName, context) => {
490
+ return this._modifyQuery({ ...query, limit: 1 }, (fetchName) => {
489
491
  const name = `_update_$${query.className.toLowerCase()}`;
490
492
  return sql `
491
493
  , ${{ identifier: name }} AS (
@@ -495,7 +497,7 @@ class QueryCompiler {
495
497
  WHERE ${{ identifier: query.className }}._id IN (SELECT ${{ identifier: fetchName }}._id FROM ${{ identifier: fetchName }})
496
498
  RETURNING *
497
499
  )
498
- ${this._refetch(name, query, context)}
500
+ ${this._refetch(name, query)}
499
501
  `;
500
502
  });
501
503
  }
@@ -504,7 +506,7 @@ class QueryCompiler {
504
506
  ..._defaultInsertOpts(query),
505
507
  ...this._encodeObjectAttrs(query.className, setOnInsert),
506
508
  });
507
- return this._modifyQuery({ ...query, limit: 1 }, (fetchName, context) => {
509
+ return this._modifyQuery({ ...query, limit: 1 }, (fetchName) => {
508
510
  const updateName = `_update_$${query.className.toLowerCase()}`;
509
511
  const insertName = `_insert_$${query.className.toLowerCase()}`;
510
512
  const upsertName = `_upsert_$${query.className.toLowerCase()}`;
@@ -528,7 +530,7 @@ class QueryCompiler {
528
530
  UNION
529
531
  SELECT * FROM ${{ identifier: insertName }}
530
532
  )
531
- ${this._refetch(upsertName, query, context)}
533
+ ${this._refetch(upsertName, query)}
532
534
  `;
533
535
  });
534
536
  }
@@ -663,12 +665,21 @@ class SqlStorage {
663
665
  }
664
666
  return obj;
665
667
  }
668
+ _makeCompiler(isUpdate, extraFilter) {
669
+ return new QueryCompiler({
670
+ schema: this.schema,
671
+ dialect: this.dialect,
672
+ selectLock: this.selectLock(),
673
+ isUpdate,
674
+ extraFilter,
675
+ });
676
+ }
666
677
  async explain(query) {
667
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), false);
678
+ const compiler = this._makeCompiler(false, query.extraFilter);
668
679
  return this._explain(compiler, query);
669
680
  }
670
681
  async count(query) {
671
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), false);
682
+ const compiler = this._makeCompiler(false, query.extraFilter);
672
683
  const [{ count: _count }] = await this.query(compiler._selectQuery(query, {
673
684
  select: sql `COUNT(*) AS count`,
674
685
  }));
@@ -677,7 +688,7 @@ class SqlStorage {
677
688
  }
678
689
  find(query) {
679
690
  const self = this;
680
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
691
+ const compiler = self._makeCompiler(false, query.extraFilter);
681
692
  const _query = compiler._selectQuery(query);
682
693
  return (async function* () {
683
694
  const objects = self.query(_query);
@@ -688,7 +699,7 @@ class SqlStorage {
688
699
  }
689
700
  random(query, opts) {
690
701
  const self = this;
691
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
702
+ const compiler = self._makeCompiler(false, query.extraFilter);
692
703
  const _query = compiler._selectQuery({ ...query, sort: {} }, {
693
704
  sort: sql `ORDER BY ${self.dialect.random(opts ?? {})}`,
694
705
  });
@@ -715,7 +726,7 @@ class SqlStorage {
715
726
  }
716
727
  nonrefs(query) {
717
728
  const self = this;
718
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
729
+ const compiler = self._makeCompiler(false, query.extraFilter);
719
730
  const _query = compiler._selectQuery(query, ({ fetchName }) => ({
720
731
  extraFilter: sql `
721
732
  NOT EXISTS (${this._refs(this.schema, query.className, ['_id'], sql `(${{ quote: query.className + '$' }} || ${{ identifier: fetchName }}.${{ identifier: '_id' }})`)})
@@ -729,32 +740,32 @@ class SqlStorage {
729
740
  })();
730
741
  }
731
742
  async insert(options, attrs) {
732
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
743
+ const compiler = this._makeCompiler(true);
733
744
  const result = _.first(await this.query(compiler.insert(options, attrs)));
734
745
  return _.isNil(result) ? undefined : this._decodeObject(options.className, result);
735
746
  }
736
747
  async insertMany(options, values) {
737
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
748
+ const compiler = this._makeCompiler(true);
738
749
  const result = await this.query(compiler.insertMany(options, values));
739
750
  return result.length;
740
751
  }
741
752
  async updateOne(query, update) {
742
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
753
+ const compiler = this._makeCompiler(true, query.extraFilter);
743
754
  const updated = _.first(await this.query(compiler.updateOne(query, update)));
744
755
  return _.isNil(updated) ? undefined : this._decodeObject(query.className, updated);
745
756
  }
746
757
  async upsertOne(query, update, setOnInsert) {
747
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
758
+ const compiler = this._makeCompiler(true, query.extraFilter);
748
759
  const upserted = _.first(await this.query(compiler.upsertOne(query, update, setOnInsert)));
749
760
  return _.isNil(upserted) ? undefined : this._decodeObject(query.className, upserted);
750
761
  }
751
762
  async deleteOne(query) {
752
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
763
+ const compiler = this._makeCompiler(true, query.extraFilter);
753
764
  const deleted = _.first(await this.query(compiler.deleteOne(query)));
754
765
  return _.isNil(deleted) ? undefined : this._decodeObject(query.className, deleted);
755
766
  }
756
767
  async deleteMany(query) {
757
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
768
+ const compiler = this._makeCompiler(true, query.extraFilter);
758
769
  const deleted = await this.query(compiler.deleteMany(query));
759
770
  return deleted.length;
760
771
  }
@@ -1110,10 +1121,55 @@ class PostgresClientDriver {
1110
1121
  });
1111
1122
  }
1112
1123
  }
1124
+ class PostgresPubSub {
1125
+ client;
1126
+ subscribers = {};
1127
+ channels = [];
1128
+ constructor(client) {
1129
+ this.client = client;
1130
+ (async () => {
1131
+ try {
1132
+ (await client).on('notification', ({ channel, payload }) => {
1133
+ if (!payload)
1134
+ return;
1135
+ try {
1136
+ const _payload = _decodeValue(JSON.parse(payload));
1137
+ for (const subscriber of this.subscribers[_.toUpper(channel)]) {
1138
+ subscriber(_payload);
1139
+ }
1140
+ }
1141
+ catch (e) {
1142
+ console.error(`Unknown payload: ${e}`);
1143
+ }
1144
+ });
1145
+ }
1146
+ catch (e) {
1147
+ console.error(e);
1148
+ }
1149
+ })();
1150
+ }
1151
+ async shutdown() {
1152
+ (await this.client).release();
1153
+ }
1154
+ async listen(channel) {
1155
+ this.channels.push(channel);
1156
+ await (await this.client).query(`LISTEN ${channel}`);
1157
+ }
1158
+ subscribe(channel, callback) {
1159
+ if (_.isNil(this.subscribers[channel]))
1160
+ this.subscribers[channel] = [];
1161
+ this.subscribers[channel].push(callback);
1162
+ return () => {
1163
+ this.subscribers[channel] = this.subscribers[channel].filter(x => x !== callback);
1164
+ };
1165
+ }
1166
+ isEmpty() {
1167
+ return _.every(_.values(this.subscribers), x => _.isEmpty(x));
1168
+ }
1169
+ }
1113
1170
  class PostgresDriver extends PostgresClientDriver {
1114
1171
  database;
1115
1172
  pubsub;
1116
- subscribers = [];
1117
1173
  constructor(config) {
1118
1174
  if (_.isEmpty(config))
1119
1175
  throw Error('Invalid postgre config.');
@@ -1126,26 +1182,11 @@ class PostgresDriver extends PostgresClientDriver {
1126
1182
  await this._release_pubsub();
1127
1183
  await this.database.end();
1128
1184
  }
1129
- async _init_pubsub() {
1185
+ _init_pubsub() {
1130
1186
  if (this.pubsub)
1131
1187
  return;
1132
1188
  try {
1133
- this.pubsub = this.database.connect();
1134
- const pubsub = await this.pubsub;
1135
- pubsub.on('notification', ({ channel, payload }) => {
1136
- if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
1137
- return;
1138
- try {
1139
- const _payload = _decodeValue(JSON.parse(payload));
1140
- for (const subscriber of this.subscribers) {
1141
- subscriber(_payload);
1142
- }
1143
- }
1144
- catch (e) {
1145
- console.error(`Unknown payload: ${e}`);
1146
- }
1147
- });
1148
- await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
1189
+ this.pubsub = new PostgresPubSub(this.database.connect());
1149
1190
  }
1150
1191
  catch (e) {
1151
1192
  console.error(e);
@@ -1154,18 +1195,22 @@ class PostgresDriver extends PostgresClientDriver {
1154
1195
  async _release_pubsub() {
1155
1196
  const pubsub = this.pubsub;
1156
1197
  this.pubsub = undefined;
1157
- await (await pubsub)?.release();
1198
+ await pubsub?.shutdown();
1158
1199
  }
1159
- subscribe(callback) {
1200
+ _subscribe(channel, callback) {
1160
1201
  this._init_pubsub();
1161
- this.subscribers.push(callback);
1202
+ if (!_.includes(this.pubsub.channels, channel))
1203
+ this.pubsub.listen(channel);
1204
+ const release = this.pubsub.subscribe(channel, callback);
1162
1205
  return () => {
1163
- this.subscribers = this.subscribers.filter(x => x !== callback);
1164
- if (_.isEmpty(this.subscribers)) {
1206
+ release();
1207
+ if (this.pubsub?.isEmpty())
1165
1208
  this._release_pubsub();
1166
- }
1167
1209
  };
1168
1210
  }
1211
+ subscribe(callback) {
1212
+ return this._subscribe(PROTO_POSTGRES_MSG, callback);
1213
+ }
1169
1214
  }
1170
1215
 
1171
1216
  //
@@ -1853,7 +1898,7 @@ const encodeQueryExpression = (compiler, parent, expr) => {
1853
1898
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1854
1899
  // THE SOFTWARE.
1855
1900
  //
1856
- const encodeFieldExpression = (compiler, context, parent, field, expr) => {
1901
+ const encodeFieldExpression = (compiler, parent, field, expr) => {
1857
1902
  const [colname] = _.toPath(field);
1858
1903
  const { element, dataType, relation } = fetchElement(compiler, parent, field);
1859
1904
  const encodeValue = (value) => dataType ? encodeType(colname, dataType, value) : _encodeJsonValue(_encodeValue(value));
@@ -2082,7 +2127,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2082
2127
  {
2083
2128
  if (!(expr.value instanceof FieldSelectorExpression))
2084
2129
  break;
2085
- return sql `NOT (${encodeFieldExpression(compiler, context, parent, field, expr.value)})`;
2130
+ return sql `NOT (${encodeFieldExpression(compiler, parent, field, expr.value)})`;
2086
2131
  }
2087
2132
  case '$pattern':
2088
2133
  {
@@ -2174,7 +2219,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2174
2219
  if (!(expr.value instanceof QuerySelector))
2175
2220
  break;
2176
2221
  const tempName = `_expr_$${compiler.nextIdx()}`;
2177
- const filter = compiler._encodeFilter(context, { name: tempName, className: relation?.target }, expr.value);
2222
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2178
2223
  if (!filter)
2179
2224
  break;
2180
2225
  if (relation) {
@@ -2201,7 +2246,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2201
2246
  if (!(expr.value instanceof QuerySelector))
2202
2247
  break;
2203
2248
  const tempName = `_expr_$${compiler.nextIdx()}`;
2204
- const filter = compiler._encodeFilter(context, { name: tempName, className: relation?.target }, expr.value);
2249
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2205
2250
  if (!filter)
2206
2251
  break;
2207
2252
  if (relation) {
@@ -2295,31 +2340,36 @@ const selectPopulate = (compiler, parent, populate, field) => {
2295
2340
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2296
2341
  const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
2297
2342
  const subpaths = resolveSubpaths(compiler, populate);
2343
+ const cond = [];
2344
+ if (compiler.extraFilter) {
2345
+ const filter = compiler.extraFilter(populate.className);
2346
+ cond.push(compiler._encodeFilter(populate, filter));
2347
+ }
2298
2348
  if (populate.type === 'pointer') {
2349
+ cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}`);
2299
2350
  return {
2300
2351
  columns: _.map(subpaths, ({ path }) => sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`),
2301
2352
  join: sql `
2302
2353
  LEFT JOIN ${{ identifier: populate.name }}
2303
- ON ${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}
2354
+ ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2304
2355
  `,
2305
2356
  };
2306
2357
  }
2307
- let cond;
2308
2358
  if (_.isNil(populate.foreignField)) {
2309
- cond = sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`;
2359
+ cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`);
2310
2360
  }
2311
2361
  else if (_isPointer(compiler.schema, populate.className, populate.foreignField)) {
2312
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`;
2362
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`);
2313
2363
  }
2314
2364
  else {
2315
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
2365
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`);
2316
2366
  }
2317
2367
  return {
2318
2368
  columns: [sql `
2319
2369
  ARRAY(
2320
2370
  SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
2321
2371
  SELECT ${_.map(subpaths, ({ path, type }) => _encodePopulateInclude(populate.name, path, type))}
2322
- FROM ${{ identifier: populate.name }} WHERE ${cond}
2372
+ FROM ${{ identifier: populate.name }} WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2323
2373
  ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
2324
2374
  ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
2325
2375
  ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
@@ -2332,13 +2382,13 @@ const selectPopulate = (compiler, parent, populate, field) => {
2332
2382
  const encodeRemix = (parent, remix) => sql `${remix?.className === parent.className ? sql `
2333
2383
  (SELECT * FROM ${{ identifier: remix.name }} UNION SELECT * FROM ${{ identifier: parent.className }})
2334
2384
  ` : { identifier: parent.className }}`;
2335
- const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2385
+ const encodeForeignField = (compiler, parent, foreignField, remix) => {
2336
2386
  const { paths: [colname, ...subpath], dataType } = resolveColumn(compiler.schema, parent.className, foreignField);
2337
2387
  const tempName = `_populate_$${compiler.nextIdx()}`;
2338
2388
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2339
2389
  const _foreign = (field) => sql `${{ identifier: tempName }}.${{ identifier: field }}`;
2340
2390
  if (_.isEmpty(subpath) && isRelation(dataType) && dataType.foreignField) {
2341
- const { joins, field, rows, array } = encodeForeignField(compiler, context, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
2391
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
2342
2392
  return {
2343
2393
  joins: [],
2344
2394
  field: sql `(
@@ -2361,27 +2411,32 @@ const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2361
2411
  }
2362
2412
  if (!isPointer(dataType) && !isRelation(dataType))
2363
2413
  throw Error(`Invalid path: ${foreignField}`);
2364
- const { joins, field, rows, array } = encodeForeignField(compiler, context, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
2414
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
2415
+ const cond = [];
2416
+ if (compiler.extraFilter) {
2417
+ const filter = compiler.extraFilter(dataType.target);
2418
+ cond.push(compiler._encodeFilter({ className: dataType.target, name: tempName }, filter));
2419
+ }
2365
2420
  if (isPointer(dataType)) {
2421
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}`);
2366
2422
  return {
2367
2423
  joins: [sql `
2368
2424
  LEFT JOIN ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2369
- ON ${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}
2425
+ ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2370
2426
  `, ...joins],
2371
2427
  field,
2372
2428
  array,
2373
2429
  rows,
2374
2430
  };
2375
2431
  }
2376
- let cond;
2377
2432
  if (_.isNil(dataType.foreignField)) {
2378
- cond = sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`;
2433
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`);
2379
2434
  }
2380
2435
  else if (_isPointer(compiler.schema, dataType.target, dataType.foreignField)) {
2381
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`;
2436
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`);
2382
2437
  }
2383
2438
  else {
2384
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`;
2439
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`);
2385
2440
  }
2386
2441
  return {
2387
2442
  joins: [],
@@ -2389,23 +2444,23 @@ const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2389
2444
  SELECT ${array ? sql `UNNEST(${field})` : field}
2390
2445
  FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2391
2446
  ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
2392
- WHERE ${cond}
2447
+ WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2393
2448
  )`,
2394
2449
  array: false,
2395
2450
  rows: true,
2396
2451
  };
2397
2452
  };
2398
- const encodePopulate = (compiler, context, parent, remix) => {
2399
- const _filter = compiler._encodeFilter(context, parent, parent.filter);
2453
+ const encodePopulate = (compiler, parent, remix) => {
2454
+ const _filter = parent.filter && compiler._encodeFilter(parent, parent.filter);
2400
2455
  const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2401
2456
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
2402
2457
  const _includes = _.pickBy(parent.includes, v => isPrimitive(v));
2403
- const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, context, {
2458
+ const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2404
2459
  className: parent.className,
2405
2460
  name: parent.name,
2406
2461
  }, parent.foreignField, remix) : {};
2407
2462
  return _.reduce(parent.populates, (acc, populate) => ({
2408
- ...encodePopulate(compiler, context, populate, remix),
2463
+ ...encodePopulate(compiler, populate, remix),
2409
2464
  ...acc,
2410
2465
  }), {
2411
2466
  [parent.name]: sql `
@@ -2450,11 +2505,11 @@ const encodePopulate = (compiler, context, parent, remix) => {
2450
2505
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2451
2506
  // THE SOFTWARE.
2452
2507
  //
2453
- const encodeRelation = (compiler, context, parent, relatedBy) => {
2508
+ const encodeRelation = (compiler, parent, relatedBy) => {
2454
2509
  const name = `_relation_$${relatedBy.className.toLowerCase()}`;
2455
2510
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2456
2511
  const _foreign = (field) => sql `${{ identifier: name }}.${{ identifier: field }}`;
2457
- const { joins, field } = encodeForeignField(compiler, context, { className: relatedBy.className, name }, relatedBy.key);
2512
+ const { joins, field } = encodeForeignField(compiler, { className: relatedBy.className, name }, relatedBy.key);
2458
2513
  return sql `EXISTS (
2459
2514
  SELECT 1
2460
2515
  FROM ${{ identifier: relatedBy.className }} AS ${{ identifier: name }}