proto.io 0.0.163 → 0.0.165

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