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
@@ -11,7 +11,7 @@ var Decimal = require('decimal.js');
11
11
  var utils = require('pg/lib/utils');
12
12
  var index$1 = require('../../internals/index-CSNRyhjB.js');
13
13
  require('@o2ter/crypto-js');
14
- var random$1 = require('../../internals/random-uT4D0y_q.js');
14
+ var random$1 = require('../../internals/random-q0PeamQE.js');
15
15
  var _private = require('../../internals/private-CSB1Ep4g.js');
16
16
 
17
17
  //
@@ -116,7 +116,8 @@ class SQL {
116
116
  query += dialect.boolean(value.value);
117
117
  }
118
118
  else if (_.isString(value.value)) {
119
- query += `${dialect.quote(value.value)}::TEXT`;
119
+ query += `${dialect.placeholder(nextIdx())}::TEXT`;
120
+ values.push(value.value);
120
121
  }
121
122
  else if (_.isSafeInteger(value.value)) {
122
123
  query += `${value.value}`;
@@ -223,12 +224,14 @@ class QueryCompiler {
223
224
  dialect;
224
225
  selectLock;
225
226
  isUpdate;
227
+ extraFilter;
226
228
  idx = 0;
227
- constructor(schema, dialect, selectLock, isUpdate) {
228
- this.schema = schema;
229
- this.dialect = dialect;
230
- this.selectLock = selectLock;
231
- this.isUpdate = isUpdate;
229
+ constructor(options) {
230
+ this.schema = options.schema;
231
+ this.dialect = options.dialect;
232
+ this.selectLock = options.selectLock;
233
+ this.isUpdate = options.isUpdate;
234
+ this.extraFilter = options.extraFilter;
232
235
  }
233
236
  nextIdx() {
234
237
  return this.idx++;
@@ -285,11 +288,11 @@ class QueryCompiler {
285
288
  }
286
289
  _baseSelectQuery(query, options) {
287
290
  const context = this._makeContext(query);
288
- const _stages = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, context, populate));
291
+ const _stages = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, populate));
289
292
  const stages = _.fromPairs(_.flatMap(_.values(_stages), (p) => _.toPairs(p)));
290
293
  const fetchName = `_fetch_$${query.className.toLowerCase()}`;
291
294
  const parent = { className: query.className, name: fetchName };
292
- const baseFilter = this._encodeFilter(context, parent, query.filter);
295
+ const baseFilter = this._encodeFilter(parent, query.filter);
293
296
  const populates = this._selectPopulateMap(context, query.className, fetchName);
294
297
  const joins = _.compact(_.map(populates, ({ join }) => join));
295
298
  const includes = {
@@ -303,7 +306,7 @@ class QueryCompiler {
303
306
  const filter = _.compact([
304
307
  baseFilter,
305
308
  _options?.extraFilter,
306
- query.relatedBy && this.dialect.encodeRelation(this, context, parent, query.relatedBy),
309
+ query.relatedBy && this.dialect.encodeRelation(this, parent, query.relatedBy),
307
310
  ]);
308
311
  return {
309
312
  stages,
@@ -324,9 +327,9 @@ class QueryCompiler {
324
327
  `,
325
328
  };
326
329
  }
327
- _refetch(name, query, context) {
330
+ _refetch(name, query) {
328
331
  const _context = this._encodeIncludes(query.className, query.includes, query.matches);
329
- const populates = _.mapValues(_context.populates, (populate) => this.dialect.encodePopulate(this, context, populate, { className: query.className, name }));
332
+ const populates = _.mapValues(_context.populates, (populate) => this.dialect.encodePopulate(this, populate, { className: query.className, name }));
330
333
  const stages = _.fromPairs(_.flatMap(_.values(populates), (p) => _.toPairs(p)));
331
334
  const _populates = this._selectPopulateMap(_context, query.className, name);
332
335
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
@@ -397,8 +400,8 @@ class QueryCompiler {
397
400
  }
398
401
  return result;
399
402
  }
400
- _encodeCoditionalSelector(parent, filter, context) {
401
- const queries = _.compact(_.map(filter.exprs, x => this._encodeFilter(context, parent, x)));
403
+ _encodeCoditionalSelector(parent, filter) {
404
+ const queries = _.compact(_.map(filter.exprs, x => this._encodeFilter(parent, x)));
402
405
  if (_.isEmpty(queries))
403
406
  return;
404
407
  switch (filter.type) {
@@ -407,12 +410,12 @@ class QueryCompiler {
407
410
  case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
408
411
  }
409
412
  }
410
- _encodeFilter(context, parent, filter) {
413
+ _encodeFilter(parent, filter) {
411
414
  if (filter instanceof index$1.QueryCoditionalSelector) {
412
- return this._encodeCoditionalSelector(parent, filter, context);
415
+ return this._encodeCoditionalSelector(parent, filter);
413
416
  }
414
417
  if (filter instanceof index$1.QueryFieldSelector) {
415
- return this.dialect.encodeFieldExpression(this, context, parent, filter.field, filter.expr);
418
+ return this.dialect.encodeFieldExpression(this, parent, filter.field, filter.expr);
416
419
  }
417
420
  if (filter instanceof index$1.QueryExpressionSelector) {
418
421
  return this.dialect.encodeQueryExpression(this, parent, filter.expr);
@@ -453,7 +456,7 @@ class QueryCompiler {
453
456
  });
454
457
  const name = `_insert_$${options.className.toLowerCase()}`;
455
458
  const context = this._makeContext(options);
456
- const populates = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, context, populate));
459
+ const populates = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, populate));
457
460
  const stages = _.fromPairs(_.flatMap(_.values(populates), (p) => _.toPairs(p)));
458
461
  const _populates = this._selectPopulateMap(context, options.className, name);
459
462
  const joins = _.compact(_.map(_populates, ({ join }) => join));
@@ -488,7 +491,7 @@ class QueryCompiler {
488
491
  `;
489
492
  }
490
493
  updateOne(query, update) {
491
- return this._modifyQuery({ ...query, limit: 1 }, (fetchName, context) => {
494
+ return this._modifyQuery({ ...query, limit: 1 }, (fetchName) => {
492
495
  const name = `_update_$${query.className.toLowerCase()}`;
493
496
  return sql `
494
497
  , ${{ identifier: name }} AS (
@@ -498,7 +501,7 @@ class QueryCompiler {
498
501
  WHERE ${{ identifier: query.className }}._id IN (SELECT ${{ identifier: fetchName }}._id FROM ${{ identifier: fetchName }})
499
502
  RETURNING *
500
503
  )
501
- ${this._refetch(name, query, context)}
504
+ ${this._refetch(name, query)}
502
505
  `;
503
506
  });
504
507
  }
@@ -507,7 +510,7 @@ class QueryCompiler {
507
510
  ..._defaultInsertOpts(query),
508
511
  ...this._encodeObjectAttrs(query.className, setOnInsert),
509
512
  });
510
- return this._modifyQuery({ ...query, limit: 1 }, (fetchName, context) => {
513
+ return this._modifyQuery({ ...query, limit: 1 }, (fetchName) => {
511
514
  const updateName = `_update_$${query.className.toLowerCase()}`;
512
515
  const insertName = `_insert_$${query.className.toLowerCase()}`;
513
516
  const upsertName = `_upsert_$${query.className.toLowerCase()}`;
@@ -531,7 +534,7 @@ class QueryCompiler {
531
534
  UNION
532
535
  SELECT * FROM ${{ identifier: insertName }}
533
536
  )
534
- ${this._refetch(upsertName, query, context)}
537
+ ${this._refetch(upsertName, query)}
535
538
  `;
536
539
  });
537
540
  }
@@ -666,12 +669,21 @@ class SqlStorage {
666
669
  }
667
670
  return obj;
668
671
  }
672
+ _makeCompiler(isUpdate, extraFilter) {
673
+ return new QueryCompiler({
674
+ schema: this.schema,
675
+ dialect: this.dialect,
676
+ selectLock: this.selectLock(),
677
+ isUpdate,
678
+ extraFilter,
679
+ });
680
+ }
669
681
  async explain(query) {
670
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), false);
682
+ const compiler = this._makeCompiler(false, query.extraFilter);
671
683
  return this._explain(compiler, query);
672
684
  }
673
685
  async count(query) {
674
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), false);
686
+ const compiler = this._makeCompiler(false, query.extraFilter);
675
687
  const [{ count: _count }] = await this.query(compiler._selectQuery(query, {
676
688
  select: sql `COUNT(*) AS count`,
677
689
  }));
@@ -680,7 +692,7 @@ class SqlStorage {
680
692
  }
681
693
  find(query) {
682
694
  const self = this;
683
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
695
+ const compiler = self._makeCompiler(false, query.extraFilter);
684
696
  const _query = compiler._selectQuery(query);
685
697
  return (async function* () {
686
698
  const objects = self.query(_query);
@@ -691,7 +703,7 @@ class SqlStorage {
691
703
  }
692
704
  random(query, opts) {
693
705
  const self = this;
694
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
706
+ const compiler = self._makeCompiler(false, query.extraFilter);
695
707
  const _query = compiler._selectQuery({ ...query, sort: {} }, {
696
708
  sort: sql `ORDER BY ${self.dialect.random(opts ?? {})}`,
697
709
  });
@@ -718,7 +730,7 @@ class SqlStorage {
718
730
  }
719
731
  nonrefs(query) {
720
732
  const self = this;
721
- const compiler = new QueryCompiler(self.schema, self.dialect, self.selectLock(), false);
733
+ const compiler = self._makeCompiler(false, query.extraFilter);
722
734
  const _query = compiler._selectQuery(query, ({ fetchName }) => ({
723
735
  extraFilter: sql `
724
736
  NOT EXISTS (${this._refs(this.schema, query.className, ['_id'], sql `(${{ quote: query.className + '$' }} || ${{ identifier: fetchName }}.${{ identifier: '_id' }})`)})
@@ -732,32 +744,32 @@ class SqlStorage {
732
744
  })();
733
745
  }
734
746
  async insert(options, attrs) {
735
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
747
+ const compiler = this._makeCompiler(true);
736
748
  const result = _.first(await this.query(compiler.insert(options, attrs)));
737
749
  return _.isNil(result) ? undefined : this._decodeObject(options.className, result);
738
750
  }
739
751
  async insertMany(options, values) {
740
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
752
+ const compiler = this._makeCompiler(true);
741
753
  const result = await this.query(compiler.insertMany(options, values));
742
754
  return result.length;
743
755
  }
744
756
  async updateOne(query, update) {
745
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
757
+ const compiler = this._makeCompiler(true, query.extraFilter);
746
758
  const updated = _.first(await this.query(compiler.updateOne(query, update)));
747
759
  return _.isNil(updated) ? undefined : this._decodeObject(query.className, updated);
748
760
  }
749
761
  async upsertOne(query, update, setOnInsert) {
750
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
762
+ const compiler = this._makeCompiler(true, query.extraFilter);
751
763
  const upserted = _.first(await this.query(compiler.upsertOne(query, update, setOnInsert)));
752
764
  return _.isNil(upserted) ? undefined : this._decodeObject(query.className, upserted);
753
765
  }
754
766
  async deleteOne(query) {
755
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
767
+ const compiler = this._makeCompiler(true, query.extraFilter);
756
768
  const deleted = _.first(await this.query(compiler.deleteOne(query)));
757
769
  return _.isNil(deleted) ? undefined : this._decodeObject(query.className, deleted);
758
770
  }
759
771
  async deleteMany(query) {
760
- const compiler = new QueryCompiler(this.schema, this.dialect, this.selectLock(), true);
772
+ const compiler = this._makeCompiler(true, query.extraFilter);
761
773
  const deleted = await this.query(compiler.deleteMany(query));
762
774
  return deleted.length;
763
775
  }
@@ -1113,10 +1125,55 @@ class PostgresClientDriver {
1113
1125
  });
1114
1126
  }
1115
1127
  }
1128
+ class PostgresPubSub {
1129
+ client;
1130
+ subscribers = {};
1131
+ channels = [];
1132
+ constructor(client) {
1133
+ this.client = client;
1134
+ (async () => {
1135
+ try {
1136
+ (await client).on('notification', ({ channel, payload }) => {
1137
+ if (!payload)
1138
+ return;
1139
+ try {
1140
+ const _payload = index._decodeValue(JSON.parse(payload));
1141
+ for (const subscriber of this.subscribers[_.toUpper(channel)]) {
1142
+ subscriber(_payload);
1143
+ }
1144
+ }
1145
+ catch (e) {
1146
+ console.error(`Unknown payload: ${e}`);
1147
+ }
1148
+ });
1149
+ }
1150
+ catch (e) {
1151
+ console.error(e);
1152
+ }
1153
+ })();
1154
+ }
1155
+ async shutdown() {
1156
+ (await this.client).release();
1157
+ }
1158
+ async listen(channel) {
1159
+ this.channels.push(channel);
1160
+ await (await this.client).query(`LISTEN ${channel}`);
1161
+ }
1162
+ subscribe(channel, callback) {
1163
+ if (_.isNil(this.subscribers[channel]))
1164
+ this.subscribers[channel] = [];
1165
+ this.subscribers[channel].push(callback);
1166
+ return () => {
1167
+ this.subscribers[channel] = this.subscribers[channel].filter(x => x !== callback);
1168
+ };
1169
+ }
1170
+ isEmpty() {
1171
+ return _.every(_.values(this.subscribers), x => _.isEmpty(x));
1172
+ }
1173
+ }
1116
1174
  class PostgresDriver extends PostgresClientDriver {
1117
1175
  database;
1118
1176
  pubsub;
1119
- subscribers = [];
1120
1177
  constructor(config) {
1121
1178
  if (_.isEmpty(config))
1122
1179
  throw Error('Invalid postgre config.');
@@ -1129,26 +1186,11 @@ class PostgresDriver extends PostgresClientDriver {
1129
1186
  await this._release_pubsub();
1130
1187
  await this.database.end();
1131
1188
  }
1132
- async _init_pubsub() {
1189
+ _init_pubsub() {
1133
1190
  if (this.pubsub)
1134
1191
  return;
1135
1192
  try {
1136
- this.pubsub = this.database.connect();
1137
- const pubsub = await this.pubsub;
1138
- pubsub.on('notification', ({ channel, payload }) => {
1139
- if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
1140
- return;
1141
- try {
1142
- const _payload = index._decodeValue(JSON.parse(payload));
1143
- for (const subscriber of this.subscribers) {
1144
- subscriber(_payload);
1145
- }
1146
- }
1147
- catch (e) {
1148
- console.error(`Unknown payload: ${e}`);
1149
- }
1150
- });
1151
- await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
1193
+ this.pubsub = new PostgresPubSub(this.database.connect());
1152
1194
  }
1153
1195
  catch (e) {
1154
1196
  console.error(e);
@@ -1157,18 +1199,22 @@ class PostgresDriver extends PostgresClientDriver {
1157
1199
  async _release_pubsub() {
1158
1200
  const pubsub = this.pubsub;
1159
1201
  this.pubsub = undefined;
1160
- await (await pubsub)?.release();
1202
+ await pubsub?.shutdown();
1161
1203
  }
1162
- subscribe(callback) {
1204
+ _subscribe(channel, callback) {
1163
1205
  this._init_pubsub();
1164
- this.subscribers.push(callback);
1206
+ if (!_.includes(this.pubsub.channels, channel))
1207
+ this.pubsub.listen(channel);
1208
+ const release = this.pubsub.subscribe(channel, callback);
1165
1209
  return () => {
1166
- this.subscribers = this.subscribers.filter(x => x !== callback);
1167
- if (_.isEmpty(this.subscribers)) {
1210
+ release();
1211
+ if (this.pubsub?.isEmpty())
1168
1212
  this._release_pubsub();
1169
- }
1170
1213
  };
1171
1214
  }
1215
+ subscribe(callback) {
1216
+ return this._subscribe(PROTO_POSTGRES_MSG, callback);
1217
+ }
1172
1218
  }
1173
1219
 
1174
1220
  //
@@ -1856,7 +1902,7 @@ const encodeQueryExpression = (compiler, parent, expr) => {
1856
1902
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1857
1903
  // THE SOFTWARE.
1858
1904
  //
1859
- const encodeFieldExpression = (compiler, context, parent, field, expr) => {
1905
+ const encodeFieldExpression = (compiler, parent, field, expr) => {
1860
1906
  const [colname] = _.toPath(field);
1861
1907
  const { element, dataType, relation } = fetchElement(compiler, parent, field);
1862
1908
  const encodeValue = (value) => dataType ? encodeType(colname, dataType, value) : _encodeJsonValue(index._encodeValue(value));
@@ -2085,7 +2131,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2085
2131
  {
2086
2132
  if (!(expr.value instanceof index$1.FieldSelectorExpression))
2087
2133
  break;
2088
- return sql `NOT (${encodeFieldExpression(compiler, context, parent, field, expr.value)})`;
2134
+ return sql `NOT (${encodeFieldExpression(compiler, parent, field, expr.value)})`;
2089
2135
  }
2090
2136
  case '$pattern':
2091
2137
  {
@@ -2177,7 +2223,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2177
2223
  if (!(expr.value instanceof index$1.QuerySelector))
2178
2224
  break;
2179
2225
  const tempName = `_expr_$${compiler.nextIdx()}`;
2180
- const filter = compiler._encodeFilter(context, { name: tempName, className: relation?.target }, expr.value);
2226
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2181
2227
  if (!filter)
2182
2228
  break;
2183
2229
  if (relation) {
@@ -2204,7 +2250,7 @@ const encodeFieldExpression = (compiler, context, parent, field, expr) => {
2204
2250
  if (!(expr.value instanceof index$1.QuerySelector))
2205
2251
  break;
2206
2252
  const tempName = `_expr_$${compiler.nextIdx()}`;
2207
- const filter = compiler._encodeFilter(context, { name: tempName, className: relation?.target }, expr.value);
2253
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2208
2254
  if (!filter)
2209
2255
  break;
2210
2256
  if (relation) {
@@ -2298,31 +2344,36 @@ const selectPopulate = (compiler, parent, populate, field) => {
2298
2344
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2299
2345
  const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
2300
2346
  const subpaths = resolveSubpaths(compiler, populate);
2347
+ const cond = [];
2348
+ if (compiler.extraFilter) {
2349
+ const filter = compiler.extraFilter(populate.className);
2350
+ cond.push(compiler._encodeFilter(populate, filter));
2351
+ }
2301
2352
  if (populate.type === 'pointer') {
2353
+ cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}`);
2302
2354
  return {
2303
2355
  columns: _.map(subpaths, ({ path }) => sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`),
2304
2356
  join: sql `
2305
2357
  LEFT JOIN ${{ identifier: populate.name }}
2306
- ON ${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}
2358
+ ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2307
2359
  `,
2308
2360
  };
2309
2361
  }
2310
- let cond;
2311
2362
  if (_.isNil(populate.foreignField)) {
2312
- cond = sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`;
2363
+ cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`);
2313
2364
  }
2314
2365
  else if (_isPointer(compiler.schema, populate.className, populate.foreignField)) {
2315
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`;
2366
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`);
2316
2367
  }
2317
2368
  else {
2318
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
2369
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`);
2319
2370
  }
2320
2371
  return {
2321
2372
  columns: [sql `
2322
2373
  ARRAY(
2323
2374
  SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
2324
2375
  SELECT ${_.map(subpaths, ({ path, type }) => _encodePopulateInclude(populate.name, path, type))}
2325
- FROM ${{ identifier: populate.name }} WHERE ${cond}
2376
+ FROM ${{ identifier: populate.name }} WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2326
2377
  ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
2327
2378
  ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
2328
2379
  ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
@@ -2335,13 +2386,13 @@ const selectPopulate = (compiler, parent, populate, field) => {
2335
2386
  const encodeRemix = (parent, remix) => sql `${remix?.className === parent.className ? sql `
2336
2387
  (SELECT * FROM ${{ identifier: remix.name }} UNION SELECT * FROM ${{ identifier: parent.className }})
2337
2388
  ` : { identifier: parent.className }}`;
2338
- const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2389
+ const encodeForeignField = (compiler, parent, foreignField, remix) => {
2339
2390
  const { paths: [colname, ...subpath], dataType } = random$1.resolveColumn(compiler.schema, parent.className, foreignField);
2340
2391
  const tempName = `_populate_$${compiler.nextIdx()}`;
2341
2392
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2342
2393
  const _foreign = (field) => sql `${{ identifier: tempName }}.${{ identifier: field }}`;
2343
2394
  if (_.isEmpty(subpath) && index.isRelation(dataType) && dataType.foreignField) {
2344
- const { joins, field, rows, array } = encodeForeignField(compiler, context, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
2395
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
2345
2396
  return {
2346
2397
  joins: [],
2347
2398
  field: sql `(
@@ -2364,27 +2415,32 @@ const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2364
2415
  }
2365
2416
  if (!index.isPointer(dataType) && !index.isRelation(dataType))
2366
2417
  throw Error(`Invalid path: ${foreignField}`);
2367
- const { joins, field, rows, array } = encodeForeignField(compiler, context, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
2418
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
2419
+ const cond = [];
2420
+ if (compiler.extraFilter) {
2421
+ const filter = compiler.extraFilter(dataType.target);
2422
+ cond.push(compiler._encodeFilter({ className: dataType.target, name: tempName }, filter));
2423
+ }
2368
2424
  if (index.isPointer(dataType)) {
2425
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}`);
2369
2426
  return {
2370
2427
  joins: [sql `
2371
2428
  LEFT JOIN ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2372
- ON ${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}
2429
+ ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2373
2430
  `, ...joins],
2374
2431
  field,
2375
2432
  array,
2376
2433
  rows,
2377
2434
  };
2378
2435
  }
2379
- let cond;
2380
2436
  if (_.isNil(dataType.foreignField)) {
2381
- cond = sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`;
2437
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`);
2382
2438
  }
2383
2439
  else if (_isPointer(compiler.schema, dataType.target, dataType.foreignField)) {
2384
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`;
2440
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`);
2385
2441
  }
2386
2442
  else {
2387
- cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`;
2443
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`);
2388
2444
  }
2389
2445
  return {
2390
2446
  joins: [],
@@ -2392,23 +2448,23 @@ const encodeForeignField = (compiler, context, parent, foreignField, remix) => {
2392
2448
  SELECT ${array ? sql `UNNEST(${field})` : field}
2393
2449
  FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2394
2450
  ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
2395
- WHERE ${cond}
2451
+ WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2396
2452
  )`,
2397
2453
  array: false,
2398
2454
  rows: true,
2399
2455
  };
2400
2456
  };
2401
- const encodePopulate = (compiler, context, parent, remix) => {
2402
- const _filter = compiler._encodeFilter(context, parent, parent.filter);
2457
+ const encodePopulate = (compiler, parent, remix) => {
2458
+ const _filter = parent.filter && compiler._encodeFilter(parent, parent.filter);
2403
2459
  const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2404
2460
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
2405
2461
  const _includes = _.pickBy(parent.includes, v => index.isPrimitive(v));
2406
- const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, context, {
2462
+ const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2407
2463
  className: parent.className,
2408
2464
  name: parent.name,
2409
2465
  }, parent.foreignField, remix) : {};
2410
2466
  return _.reduce(parent.populates, (acc, populate) => ({
2411
- ...encodePopulate(compiler, context, populate, remix),
2467
+ ...encodePopulate(compiler, populate, remix),
2412
2468
  ...acc,
2413
2469
  }), {
2414
2470
  [parent.name]: sql `
@@ -2453,11 +2509,11 @@ const encodePopulate = (compiler, context, parent, remix) => {
2453
2509
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2454
2510
  // THE SOFTWARE.
2455
2511
  //
2456
- const encodeRelation = (compiler, context, parent, relatedBy) => {
2512
+ const encodeRelation = (compiler, parent, relatedBy) => {
2457
2513
  const name = `_relation_$${relatedBy.className.toLowerCase()}`;
2458
2514
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2459
2515
  const _foreign = (field) => sql `${{ identifier: name }}.${{ identifier: field }}`;
2460
- const { joins, field } = encodeForeignField(compiler, context, { className: relatedBy.className, name }, relatedBy.key);
2516
+ const { joins, field } = encodeForeignField(compiler, { className: relatedBy.className, name }, relatedBy.key);
2461
2517
  return sql `EXISTS (
2462
2518
  SELECT 1
2463
2519
  FROM ${{ identifier: relatedBy.className }} AS ${{ identifier: name }}