proto.io 0.0.211 → 0.0.213

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 (48) hide show
  1. package/dist/adapters/file/aliyun-oss.d.ts +3 -3
  2. package/dist/adapters/file/database.d.ts +2 -2
  3. package/dist/adapters/file/database.js +1 -1
  4. package/dist/adapters/file/database.js.map +1 -1
  5. package/dist/adapters/file/database.mjs +2 -2
  6. package/dist/adapters/file/database.mjs.map +1 -1
  7. package/dist/adapters/file/filesystem.d.ts +3 -3
  8. package/dist/adapters/file/google-cloud-storage.d.ts +3 -3
  9. package/dist/adapters/storage/progres.d.ts +8 -6
  10. package/dist/adapters/storage/progres.js +148 -80
  11. package/dist/adapters/storage/progres.js.map +1 -1
  12. package/dist/adapters/storage/progres.mjs +149 -81
  13. package/dist/adapters/storage/progres.mjs.map +1 -1
  14. package/dist/client.d.ts +3 -3
  15. package/dist/client.js +1 -1
  16. package/dist/client.mjs +2 -2
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.js +8 -8
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.mjs +10 -10
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/internals/{base-BCWOHUaQ.d.ts → base-CZGalGrd.d.ts} +2 -2
  23. package/dist/internals/base-CZGalGrd.d.ts.map +1 -0
  24. package/dist/internals/{chunk-2pKgkO5-.d.ts → chunk-BsT9SYny.d.ts} +3 -3
  25. package/dist/internals/chunk-BsT9SYny.d.ts.map +1 -0
  26. package/dist/internals/{index-lTzbCO8S.d.ts → index-Boxwkqe0.d.ts} +32 -15
  27. package/dist/internals/index-Boxwkqe0.d.ts.map +1 -0
  28. package/dist/internals/{index-DPPLcZx8.mjs → index-DG9HHO_U.mjs} +2 -2
  29. package/dist/internals/{index-DPPLcZx8.mjs.map → index-DG9HHO_U.mjs.map} +1 -1
  30. package/dist/internals/{index-Btxxs0KS.js → index-DfnPpl1I.js} +16 -10
  31. package/dist/internals/index-DfnPpl1I.js.map +1 -0
  32. package/dist/internals/{index-8AdKlZUU.d.ts → index-NF-U_3zG.d.ts} +2 -2
  33. package/dist/internals/index-NF-U_3zG.d.ts.map +1 -0
  34. package/dist/internals/{index-CoeDMG5V.mjs → index-ZPbBr9Db.mjs} +16 -10
  35. package/dist/internals/index-ZPbBr9Db.mjs.map +1 -0
  36. package/dist/internals/{random-ycCeBd0S.mjs → random-CufRbivU.mjs} +121 -19
  37. package/dist/internals/random-CufRbivU.mjs.map +1 -0
  38. package/dist/internals/{random-CyU_Y2Ay.js → random-DzvxbWAc.js} +161 -58
  39. package/dist/internals/random-DzvxbWAc.js.map +1 -0
  40. package/package.json +1 -1
  41. package/dist/internals/base-BCWOHUaQ.d.ts.map +0 -1
  42. package/dist/internals/chunk-2pKgkO5-.d.ts.map +0 -1
  43. package/dist/internals/index-8AdKlZUU.d.ts.map +0 -1
  44. package/dist/internals/index-Btxxs0KS.js.map +0 -1
  45. package/dist/internals/index-CoeDMG5V.mjs.map +0 -1
  46. package/dist/internals/index-lTzbCO8S.d.ts.map +0 -1
  47. package/dist/internals/random-CyU_Y2Ay.js.map +0 -1
  48. package/dist/internals/random-ycCeBd0S.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-D8O7SinR.js');
13
13
  require('@o2ter/crypto-js');
14
- var random$1 = require('../../internals/random-CyU_Y2Ay.js');
14
+ var random$1 = require('../../internals/random-DzvxbWAc.js');
15
15
  var _const = require('../../internals/const-C3I6cfav.js');
16
16
  var _private = require('../../internals/private-Ciddhure.js');
17
17
 
@@ -218,12 +218,12 @@ class QueryCompiler {
218
218
  _encodeIncludes(query) {
219
219
  const names = {};
220
220
  const populates = {};
221
- const countMatches = [];
221
+ const groupMatches = {};
222
222
  for (const include of query.includes) {
223
223
  const { paths: [colname, ...subpath], dataType } = random$1.resolveColumn(this.schema, query.className, include);
224
224
  names[colname] = dataType;
225
- if (index.isRelation(dataType) && _.includes(query.countMatches, colname))
226
- countMatches.push(colname);
225
+ if (index.isRelation(dataType) && !_.isNil(query.groupMatches[colname]))
226
+ groupMatches[colname] = query.groupMatches[colname];
227
227
  if (index.isPointer(dataType) || index.isRelation(dataType)) {
228
228
  if (_.isEmpty(subpath))
229
229
  throw Error(`Invalid path: ${include}`);
@@ -254,21 +254,26 @@ class QueryCompiler {
254
254
  }
255
255
  for (const [colname, populate] of _.toPairs(populates)) {
256
256
  const _matches = query.matches[colname];
257
- const { includes, populates, countMatches } = this._encodeIncludes({
257
+ const { includes, populates, groupMatches } = this._encodeIncludes({
258
258
  className: populate.className,
259
259
  includes: populate.subpaths,
260
260
  matches: _matches.matches,
261
- countMatches: [
262
- ..._.filter(query.countMatches, x => _.startsWith(x, `${colname}.`)).map(x => x.slice(colname.length + 1)),
263
- ..._matches.countMatches ?? [],
264
- ],
261
+ groupMatches: {
262
+ ..._.mapKeys(_.pickBy(query.groupMatches, (x, k) => _.startsWith(k, `${colname}.`)), (x, k) => k.slice(colname.length + 1)),
263
+ ..._matches.groupMatches ?? {},
264
+ },
265
265
  });
266
266
  populate.sort = _encodeSorting(includes, populates, _matches.sort);
267
267
  populate.includes = includes;
268
268
  populate.populates = populates;
269
- populate.countMatches = countMatches;
269
+ populate.groupMatches = groupMatches;
270
270
  }
271
- return { className: query.className, includes: names, populates, countMatches: _.uniq(countMatches) };
271
+ return {
272
+ className: query.className,
273
+ includes: names,
274
+ populates,
275
+ groupMatches,
276
+ };
272
277
  }
273
278
  _baseSelectQuery(query, options) {
274
279
  const fetchName = `_fetch_$${query.className.toLowerCase()}`;
@@ -304,7 +309,11 @@ class QueryCompiler {
304
309
  ) AS ${{ identifier: fetchName }}
305
310
  ${!_.isEmpty(filter) ? sql `WHERE ${{ literal: _.map(filter, x => sql `(${x})`), separator: ' AND ' }}` : sql ``}
306
311
  ${_options?.sort ? _options?.sort : sql ``}
307
- ${!_.isEmpty(query.sort) ? sql `ORDER BY ${this._encodeSort(query.sort, { className: query.className, name: fetchName })}` : sql ``}
312
+ ${!_.isEmpty(query.sort) ? sql `ORDER BY ${this._encodeSort(query.sort, {
313
+ name: fetchName,
314
+ className: query.className,
315
+ groupMatches: query.groupMatches,
316
+ })}` : sql ``}
308
317
  ${query.limit ? sql `LIMIT ${{ literal: `${query.limit}` }}` : sql ``}
309
318
  ${query.skip ? sql `OFFSET ${{ literal: `${query.skip}` }}` : sql ``}
310
319
  `,
@@ -409,7 +418,7 @@ class QueryCompiler {
409
418
  }
410
419
  _selectIncludes(className, includes) {
411
420
  const _includes = _.pickBy(includes, v => _.isString(v) || (v.type !== 'pointer' && v.type !== 'relation'));
412
- return _.map(_includes, (dataType, colname) => {
421
+ return _.flatMap(_includes, (dataType, colname) => {
413
422
  if (!_.isString(dataType) && index.isPrimitive(dataType) && !_.isNil(dataType.default)) {
414
423
  return sql `COALESCE(${{ identifier: className }}.${{ identifier: colname }}, ${{ value: dataType.default }}) AS ${{ identifier: colname }}`;
415
424
  }
@@ -430,7 +439,7 @@ class QueryCompiler {
430
439
  `)}`;
431
440
  }
432
441
  _selectPopulateMap(context) {
433
- return _.map(context.populates, (populate, field) => this.dialect.selectPopulate(this, context, populate, field, _.includes(context.countMatches, field)));
442
+ return _.map(context.populates, (populate, field) => this.dialect.selectPopulate(this, context, populate, field));
434
443
  }
435
444
  insert(options, values) {
436
445
  const _values = _.map(values, attr => ({
@@ -568,9 +577,15 @@ class SqlStorage {
568
577
  const { query, values } = sql.compile(this.dialect);
569
578
  return this._query(query, values);
570
579
  }
571
- _decodeShapedObject(dataType, value) {
580
+ _decodeMatchTypes(value, matchType) {
581
+ if (!_.isPlainObject(value))
582
+ return;
583
+ return _.mapValues(value, (v, k) => this.dialect.decodeType(matchType[k], v));
584
+ }
585
+ _decodeShapedObject(dataType, value, matchesType) {
572
586
  const result = {};
573
587
  for (const { path, type } of index.shapePaths(dataType)) {
588
+ const matchType = _.get(matchesType, path) ?? {};
574
589
  if (_.isString(type)) {
575
590
  const _value = this.dialect.decodeType(type, _.get(value, path));
576
591
  if (!_.isNil(_value))
@@ -579,17 +594,17 @@ class SqlStorage {
579
594
  else if (index.isPointer(type)) {
580
595
  const _value = _.get(value, path);
581
596
  if (_.isPlainObject(_value)) {
582
- const decoded = this._decodeObject(type.target, _value);
597
+ const decoded = this._decodeObject(type.target, _value, matchType);
583
598
  if (decoded.objectId)
584
599
  _.set(result, path, decoded);
585
600
  }
586
601
  }
587
602
  else if (index.isRelation(type)) {
588
603
  const _value = _.get(value, path);
589
- if (_.isString(_value) && _value.match(/^\d+$/g))
590
- _.set(result, path, parseInt(_value));
591
- else if (_.isArray(_value))
592
- _.set(result, path, _value.map(x => this._decodeObject(type.target, x)));
604
+ if (_.isArray(_value))
605
+ _.set(result, path, _value.map(x => this._decodeObject(type.target, x, matchType)));
606
+ else if (_.isPlainObject(_value))
607
+ _.set(result, path, this._decodeMatchTypes(_value, matchType));
593
608
  }
594
609
  else {
595
610
  const _value = this.dialect.decodeType(type.type, _.get(value, path)) ?? type.default;
@@ -599,7 +614,7 @@ class SqlStorage {
599
614
  }
600
615
  return result;
601
616
  }
602
- _decodeObject(className, attrs) {
617
+ _decodeObject(className, attrs, matchesType) {
603
618
  const fields = this.schema[className].fields;
604
619
  const obj = new index.TObject(className);
605
620
  const _attrs = {};
@@ -607,6 +622,7 @@ class SqlStorage {
607
622
  _.set(_attrs, key, value);
608
623
  }
609
624
  for (const [key, value] of _.toPairs(_attrs)) {
625
+ const matchType = matchesType[key] ?? {};
610
626
  const dataType = fields[key];
611
627
  if (!dataType)
612
628
  continue;
@@ -614,20 +630,20 @@ class SqlStorage {
614
630
  obj[_private.PVK].attributes[key] = this.dialect.decodeType(dataType, value);
615
631
  }
616
632
  else if (index.isShape(dataType)) {
617
- obj[_private.PVK].attributes[key] = this._decodeShapedObject(dataType, value);
633
+ obj[_private.PVK].attributes[key] = this._decodeShapedObject(dataType, value, matchType);
618
634
  }
619
635
  else if (index.isPointer(dataType)) {
620
636
  if (_.isPlainObject(value)) {
621
- const decoded = this._decodeObject(dataType.target, value);
637
+ const decoded = this._decodeObject(dataType.target, value, matchType);
622
638
  if (decoded.objectId)
623
639
  obj[_private.PVK].attributes[key] = decoded;
624
640
  }
625
641
  }
626
642
  else if (index.isRelation(dataType)) {
627
- if (_.isString(value) && value.match(/^\d+$/g))
628
- obj[_private.PVK].attributes[key] = parseInt(value);
629
- else if (_.isArray(value))
630
- obj[_private.PVK].attributes[key] = value.map(x => this._decodeObject(dataType.target, x));
643
+ if (_.isArray(value))
644
+ obj[_private.PVK].attributes[key] = value.map(x => this._decodeObject(dataType.target, x, matchType));
645
+ else if (_.isPlainObject(value))
646
+ obj[_private.PVK].attributes[key] = this._decodeMatchTypes(value, matchType);
631
647
  }
632
648
  else {
633
649
  obj[_private.PVK].attributes[key] = this.dialect.decodeType(dataType.type, value) ?? dataType.default;
@@ -656,6 +672,18 @@ class SqlStorage {
656
672
  const count = parseInt(_count);
657
673
  return _.isFinite(count) ? count : 0;
658
674
  }
675
+ _matchesType(options) {
676
+ const types = {};
677
+ for (const [key, match] of _.entries(options.matches)) {
678
+ types[key] = this._matchesType(match);
679
+ }
680
+ for (const [key, group] of _.entries(options.groupMatches)) {
681
+ for (const [field, expr] of _.entries(group)) {
682
+ _.set(types, `${key}.${field}`, random$1.accumulatorKeyTypes[expr.type]);
683
+ }
684
+ }
685
+ return types;
686
+ }
659
687
  find(query) {
660
688
  const self = this;
661
689
  const compiler = self._makeCompiler(false, query.extraFilter);
@@ -663,7 +691,7 @@ class SqlStorage {
663
691
  return (async function* () {
664
692
  const objects = self.query(_query);
665
693
  for await (const object of objects) {
666
- yield self._decodeObject(query.className, object);
694
+ yield self._decodeObject(query.className, object, self._matchesType(query));
667
695
  }
668
696
  })();
669
697
  }
@@ -676,7 +704,7 @@ class SqlStorage {
676
704
  return (async function* () {
677
705
  const objects = self.query(_query);
678
706
  for await (const object of objects) {
679
- yield self._decodeObject(query.className, object);
707
+ yield self._decodeObject(query.className, object, self._matchesType(query));
680
708
  }
681
709
  })();
682
710
  }
@@ -690,7 +718,7 @@ class SqlStorage {
690
718
  return (async function* () {
691
719
  const objects = self.query(query);
692
720
  for await (const { _class, ...object } of objects) {
693
- yield self._decodeObject(_class, object);
721
+ yield self._decodeObject(_class, object, {});
694
722
  }
695
723
  })();
696
724
  }
@@ -705,29 +733,29 @@ class SqlStorage {
705
733
  return (async function* () {
706
734
  const objects = self.query(_query);
707
735
  for await (const object of objects) {
708
- yield self._decodeObject(query.className, object);
736
+ yield self._decodeObject(query.className, object, self._matchesType(query));
709
737
  }
710
738
  })();
711
739
  }
712
740
  async insert(options, values) {
713
741
  const compiler = this._makeCompiler(true);
714
742
  const result = await this.query(compiler.insert(options, values));
715
- return _.map(result, x => this._decodeObject(options.className, x));
743
+ return _.map(result, x => this._decodeObject(options.className, x, this._matchesType(options)));
716
744
  }
717
745
  async update(query, update) {
718
746
  const compiler = this._makeCompiler(true, query.extraFilter);
719
747
  const updated = await this.query(compiler.update(query, update));
720
- return _.map(updated, x => this._decodeObject(query.className, x));
748
+ return _.map(updated, x => this._decodeObject(query.className, x, this._matchesType(query)));
721
749
  }
722
750
  async upsert(query, update, setOnInsert) {
723
751
  const compiler = this._makeCompiler(true, query.extraFilter);
724
752
  const upserted = await this.query(compiler.upsert(query, update, setOnInsert));
725
- return _.map(upserted, x => this._decodeObject(query.className, x));
753
+ return _.map(upserted, x => this._decodeObject(query.className, x, this._matchesType(query)));
726
754
  }
727
755
  async delete(query) {
728
756
  const compiler = this._makeCompiler(true, query.extraFilter);
729
757
  const deleted = await this.query(compiler.delete(query));
730
- return _.map(deleted, x => this._decodeObject(query.className, x));
758
+ return _.map(deleted, x => this._decodeObject(query.className, x, this._matchesType(query)));
731
759
  }
732
760
  }
733
761
 
@@ -773,7 +801,15 @@ const _fetchElement = (parent, colname, subpath, dataType) => {
773
801
  }
774
802
  else if (!_.isEmpty(subpath)) {
775
803
  const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
776
- if (dataType && index._isTypeof(dataType, ['array', 'string[]', 'relation'])) {
804
+ const match = parent.groupMatches?.[colname]?.[subpath[0]];
805
+ if (dataType && index.isRelation(dataType) && subpath.length === 1 && match) {
806
+ return {
807
+ element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}.${subpath[0]}` }}`,
808
+ json: false,
809
+ dataType: random$1.accumulatorKeyTypes[match.type],
810
+ };
811
+ }
812
+ else if (dataType && index._isTypeof(dataType, ['array', 'string[]', 'relation'])) {
777
813
  return {
778
814
  element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
779
815
  json: true,
@@ -833,7 +869,7 @@ const _resolvePopulate = (path, populates) => {
833
869
  const fetchElement = (compiler, parent, field) => {
834
870
  if (parent.className) {
835
871
  const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
836
- const { element, json } = _fetchElement(parent, colname, subpath, dataType);
872
+ const { element, json, dataType: _dataType } = _fetchElement(parent, colname, subpath, dataType);
837
873
  if (index.isPointer(dataType))
838
874
  return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
839
875
  const populate = index.isRelation(dataType) && _resolvePopulate(_.toPath(colname), parent.populates);
@@ -841,7 +877,7 @@ const fetchElement = (compiler, parent, field) => {
841
877
  return { element, dataType: json ? null : dataType };
842
878
  return {
843
879
  element,
844
- dataType: json ? null : dataType,
880
+ dataType: json ? null : _dataType ?? dataType,
845
881
  relation: {
846
882
  colname,
847
883
  target: dataType.target,
@@ -1181,7 +1217,7 @@ const _encodeJsonValue = (value) => {
1181
1217
  return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
1182
1218
  return sql `to_jsonb(${{ value }})`;
1183
1219
  };
1184
- const _encodePopulateInclude = (className, colname, dataType) => {
1220
+ const _jsonPopulateInclude = (className, colname, dataType) => {
1185
1221
  switch (index._typeof(dataType)) {
1186
1222
  case 'decimal':
1187
1223
  return sql `jsonb_build_object(
@@ -1896,45 +1932,83 @@ const _selectRelationPopulate = (compiler, parent, populate, field, encode) => {
1896
1932
  cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
1897
1933
  }
1898
1934
  return sql `
1899
- SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => [
1900
- encode && _encodePopulateInclude(populate.name, path, type),
1901
- !encode && sql `${{ identifier: populate.name }}.${{ identifier: path }}`,
1902
- !encode && index.isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
1935
+ SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => encode ? [
1936
+ _jsonPopulateInclude(populate.name, path, type)
1937
+ ] : [
1938
+ ...(populate.groupMatches[path] ? _.map(_.keys(populate.groupMatches[path]), k => sql `${{ identifier: populate.name }}.${{ identifier: `${path}.${k}` }}`) : [
1939
+ sql `${{ identifier: populate.name }}.${{ identifier: path }}`
1940
+ ]),
1941
+ index.isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
1903
1942
  ]))}
1904
1943
  FROM ${{ identifier: populate.name }} WHERE ${cond}
1905
- ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
1944
+ ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, populate)}` : sql ``}
1906
1945
  ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
1907
1946
  ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
1908
1947
  ${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
1909
1948
  `;
1910
1949
  };
1911
- const selectPopulate = (compiler, parent, populate, field, countMatches) => {
1950
+ const selectPopulate = (compiler, parent, populate, field) => {
1912
1951
  if (populate.type === 'relation') {
1913
- return {
1914
- columns: [
1915
- countMatches ? sql `
1916
- (
1917
- SELECT COUNT(*) FROM (
1918
- ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1919
- ) ${{ identifier: populate.name }}
1920
- ) AS ${{ identifier: field }}
1921
- ` : sql `
1922
- ARRAY(
1923
- SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
1924
- ${_selectRelationPopulate(compiler, parent, populate, field, true)}
1925
- ) ${{ identifier: populate.name }}
1926
- ) AS ${{ identifier: field }}
1927
- `,
1928
- sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`
1929
- ],
1930
- };
1952
+ const { groupMatches } = parent;
1953
+ const columns = [
1954
+ sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`,
1955
+ ];
1956
+ if (!_.isEmpty(groupMatches?.[field])) {
1957
+ for (const [key, { type, expr }] of _.entries(groupMatches[field])) {
1958
+ switch (type) {
1959
+ case '$count':
1960
+ columns.push(sql `
1961
+ (
1962
+ SELECT COUNT(*) FROM (
1963
+ ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1964
+ ) ${{ identifier: populate.name }}
1965
+ ) AS ${{ identifier: `${field}.${key}` }}
1966
+ `);
1967
+ break;
1968
+ case '$avg':
1969
+ case '$sum':
1970
+ {
1971
+ const op = {
1972
+ '$avg': 'AVG',
1973
+ '$sum': 'SUM',
1974
+ }[type];
1975
+ if (!expr)
1976
+ throw Error('Invalid expression');
1977
+ const exprs = encodeTypedQueryExpression(compiler, populate, expr);
1978
+ const { sql: value } = (_.includes(['$avg'], type) ? _.find(exprs, e => e.type === 'number') : _.first(exprs)) ?? {};
1979
+ if (!value)
1980
+ throw Error('Invalid expression');
1981
+ columns.push(sql `
1982
+ (
1983
+ SELECT ${{ literal: op }}(${value}) FROM (
1984
+ ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1985
+ ) ${{ identifier: populate.name }}
1986
+ ) AS ${{ identifier: `${field}.${key}` }}
1987
+ `);
1988
+ }
1989
+ break;
1990
+ }
1991
+ }
1992
+ }
1993
+ else {
1994
+ columns.push(sql `
1995
+ ARRAY(
1996
+ SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
1997
+ ${_selectRelationPopulate(compiler, parent, populate, field, true)}
1998
+ ) ${{ identifier: populate.name }}
1999
+ ) AS ${{ identifier: field }}
2000
+ `);
2001
+ }
2002
+ return { columns };
1931
2003
  }
1932
2004
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
1933
2005
  const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
1934
2006
  const subpaths = resolveSubpaths(compiler, populate);
1935
2007
  return {
1936
2008
  columns: _.compact(_.flatMap(subpaths, ({ path, type }) => [
1937
- sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`,
2009
+ ...populate.groupMatches[path] ? _.map(_.keys(populate.groupMatches[path]), k => sql `${{ identifier: populate.name }}.${{ identifier: `${path}.${k}` }} AS ${{ identifier: `${field}.${path}.${k}` }}`) : [
2010
+ sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`
2011
+ ],
1938
2012
  index.isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }} AS ${{ identifier: `$${field}.${path}` }}`,
1939
2013
  ])),
1940
2014
  join: sql `
@@ -2019,9 +2093,8 @@ const encodePopulate = (compiler, parent, remix) => {
2019
2093
  parent.filter && compiler._encodeFilter(parent, parent.filter),
2020
2094
  compiler.extraFilter && compiler._encodeFilter(parent, compiler.extraFilter(parent.className)),
2021
2095
  ]);
2022
- const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field, _.includes(parent.countMatches, field)));
2096
+ const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2023
2097
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
2024
- const _includes = _.pickBy(parent.includes, v => index.isPrimitive(v));
2025
2098
  const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2026
2099
  className: parent.className,
2027
2100
  name: parent.name,
@@ -2035,7 +2108,7 @@ const encodePopulate = (compiler, parent, remix) => {
2035
2108
  SELECT
2036
2109
  ${{
2037
2110
  literal: [
2038
- ..._.map(_.keys(_includes), colname => sql `${{ identifier: parent.name }}.${{ identifier: colname }}`),
2111
+ ...compiler._selectIncludes(parent.name, parent.includes),
2039
2112
  ..._.flatMap(_populates, ({ columns: column }) => column),
2040
2113
  ..._foreignField ? [sql `${rows ? sql `ARRAY(${_foreignField})` : _foreignField} AS ${{ identifier: parent.colname }}`] : [],
2041
2114
  ], separator: ',\n'
@@ -2088,9 +2161,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2088
2161
  break;
2089
2162
  return sql `${element} ${nullSafeEqual()} ${{ value: expr.value.objectId }}`;
2090
2163
  }
2091
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2092
- return sql `${element} ${nullSafeEqual()} ${encodeType(colname, 'number', expr.value)}`;
2093
- }
2094
2164
  return sql `${element} ${nullSafeEqual()} ${encodeValue(expr.value)}`;
2095
2165
  }
2096
2166
  case '$ne':
@@ -2104,9 +2174,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2104
2174
  break;
2105
2175
  return sql `${element} ${nullSafeNotEqual()} ${{ value: expr.value.objectId }}`;
2106
2176
  }
2107
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2108
- return sql `${element} ${nullSafeNotEqual()} ${encodeType(colname, 'number', expr.value)}`;
2109
- }
2110
2177
  return sql `${element} ${nullSafeNotEqual()} ${encodeValue(expr.value)}`;
2111
2178
  }
2112
2179
  case '$gt':
@@ -2150,9 +2217,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2150
2217
  else if (!_.isString(dataType) && dataType?.type === 'pointer' && expr.value instanceof index.TObject && expr.value.objectId) {
2151
2218
  return sql `${element} ${{ literal: op }} ${{ value: expr.value.objectId }}`;
2152
2219
  }
2153
- else if (relation && _.includes(parent.countMatches, relation.colname)) {
2154
- return sql `${element} ${{ literal: op }} ${encodeType(colname, 'number', expr.value)}`;
2155
- }
2156
2220
  else if (!dataType) {
2157
2221
  if (expr.value instanceof Decimal || _.isNumber(expr.value)) {
2158
2222
  return sql `(
@@ -2343,8 +2407,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2343
2407
  if (dataType && index._isTypeof(dataType, 'string')) {
2344
2408
  return sql `COALESCE(length(${element}), 0) = ${{ value: expr.value }}`;
2345
2409
  }
2346
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2347
- return sql `${element} = ${{ value: expr.value }}`;
2410
+ if (relation && parent.className && parent.groupMatches?.[colname]) {
2411
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2412
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2413
+ return sql `(SELECT COUNT(*) FROM (${populate}) AS ${{ identifier: tempName }}) = ${{ value: expr.value }}`;
2348
2414
  }
2349
2415
  if (dataType && index._isTypeof(dataType, ['array', 'string[]', 'vector', 'relation'])) {
2350
2416
  return sql `COALESCE(array_length(${element}, 1), 0) = ${{ value: expr.value }}`;
@@ -2367,8 +2433,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2367
2433
  if (dataType && index._isTypeof(dataType, 'string')) {
2368
2434
  return sql `COALESCE(length(${element}), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
2369
2435
  }
2370
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2371
- return sql `${element} ${{ literal: expr.value ? '=' : '<>' }} 0`;
2436
+ if (relation && parent.className && parent.groupMatches?.[colname]) {
2437
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2438
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2439
+ return sql `${{ literal: expr.value ? 'NOT EXISTS' : 'EXISTS' }}(SELECT * FROM (${populate}) AS ${{ identifier: tempName }})`;
2372
2440
  }
2373
2441
  if (dataType && index._isTypeof(dataType, ['array', 'string[]', 'vector', 'relation'])) {
2374
2442
  return sql `COALESCE(array_length(${element}, 1), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;