proto.io 0.0.211 → 0.0.212

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 +142 -78
  11. package/dist/adapters/storage/progres.js.map +1 -1
  12. package/dist/adapters/storage/progres.mjs +143 -79
  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
@@ -5,9 +5,9 @@ import QueryStream from 'pg-query-stream';
5
5
  import { asyncStream, IteratorPool } 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-DPPLcZx8.mjs';
8
+ import { a as QueryCoditionalSelector, b as QueryFieldSelector, c as QueryExpressionSelector, d as QueryDistanceExpression, e as QueryKeyExpression, f as QueryValueExpression, g as QueryCoditionalExpression, h as QueryComparisonExpression, i as QueryNotExpression, j as QueryArrayExpression, Q as QuerySelector, F as FieldSelectorExpression } from '../../internals/index-DG9HHO_U.mjs';
9
9
  import '@o2ter/crypto-js';
10
- import { r as resolveColumn, a as resolveDataType$1, g as generateId, Q as QueryValidator } from '../../internals/random-ycCeBd0S.mjs';
10
+ import { r as resolveColumn, a as resolveDataType$1, g as generateId, b as accumulatorKeyTypes, Q as QueryValidator } from '../../internals/random-CufRbivU.mjs';
11
11
  import { c as PROTO_EVENT } from '../../internals/const-Dkp7Nsv5.mjs';
12
12
  import { P as PVK } from '../../internals/private-CNw40LZ7.mjs';
13
13
 
@@ -214,12 +214,12 @@ class QueryCompiler {
214
214
  _encodeIncludes(query) {
215
215
  const names = {};
216
216
  const populates = {};
217
- const countMatches = [];
217
+ const groupMatches = {};
218
218
  for (const include of query.includes) {
219
219
  const { paths: [colname, ...subpath], dataType } = resolveColumn(this.schema, query.className, include);
220
220
  names[colname] = dataType;
221
- if (isRelation(dataType) && _.includes(query.countMatches, colname))
222
- countMatches.push(colname);
221
+ if (isRelation(dataType) && !_.isNil(query.groupMatches[colname]))
222
+ groupMatches[colname] = query.groupMatches[colname];
223
223
  if (isPointer(dataType) || isRelation(dataType)) {
224
224
  if (_.isEmpty(subpath))
225
225
  throw Error(`Invalid path: ${include}`);
@@ -250,21 +250,26 @@ class QueryCompiler {
250
250
  }
251
251
  for (const [colname, populate] of _.toPairs(populates)) {
252
252
  const _matches = query.matches[colname];
253
- const { includes, populates, countMatches } = this._encodeIncludes({
253
+ const { includes, populates, groupMatches } = this._encodeIncludes({
254
254
  className: populate.className,
255
255
  includes: populate.subpaths,
256
256
  matches: _matches.matches,
257
- countMatches: [
258
- ..._.filter(query.countMatches, x => _.startsWith(x, `${colname}.`)).map(x => x.slice(colname.length + 1)),
259
- ..._matches.countMatches ?? [],
260
- ],
257
+ groupMatches: {
258
+ ..._.mapKeys(_.pickBy(query.groupMatches, (x, k) => _.startsWith(k, `${colname}.`)), (x, k) => k.slice(colname.length + 1)),
259
+ ..._matches.groupMatches ?? {},
260
+ },
261
261
  });
262
262
  populate.sort = _encodeSorting(includes, populates, _matches.sort);
263
263
  populate.includes = includes;
264
264
  populate.populates = populates;
265
- populate.countMatches = countMatches;
265
+ populate.groupMatches = groupMatches;
266
266
  }
267
- return { className: query.className, includes: names, populates, countMatches: _.uniq(countMatches) };
267
+ return {
268
+ className: query.className,
269
+ includes: names,
270
+ populates,
271
+ groupMatches,
272
+ };
268
273
  }
269
274
  _baseSelectQuery(query, options) {
270
275
  const fetchName = `_fetch_$${query.className.toLowerCase()}`;
@@ -405,7 +410,7 @@ class QueryCompiler {
405
410
  }
406
411
  _selectIncludes(className, includes) {
407
412
  const _includes = _.pickBy(includes, v => _.isString(v) || (v.type !== 'pointer' && v.type !== 'relation'));
408
- return _.map(_includes, (dataType, colname) => {
413
+ return _.flatMap(_includes, (dataType, colname) => {
409
414
  if (!_.isString(dataType) && isPrimitive(dataType) && !_.isNil(dataType.default)) {
410
415
  return sql `COALESCE(${{ identifier: className }}.${{ identifier: colname }}, ${{ value: dataType.default }}) AS ${{ identifier: colname }}`;
411
416
  }
@@ -426,7 +431,7 @@ class QueryCompiler {
426
431
  `)}`;
427
432
  }
428
433
  _selectPopulateMap(context) {
429
- return _.map(context.populates, (populate, field) => this.dialect.selectPopulate(this, context, populate, field, _.includes(context.countMatches, field)));
434
+ return _.map(context.populates, (populate, field) => this.dialect.selectPopulate(this, context, populate, field));
430
435
  }
431
436
  insert(options, values) {
432
437
  const _values = _.map(values, attr => ({
@@ -564,9 +569,15 @@ class SqlStorage {
564
569
  const { query, values } = sql.compile(this.dialect);
565
570
  return this._query(query, values);
566
571
  }
567
- _decodeShapedObject(dataType, value) {
572
+ _decodeMatchTypes(value, matchType) {
573
+ if (!_.isPlainObject(value))
574
+ return;
575
+ return _.mapValues(value, (v, k) => this.dialect.decodeType(matchType[k], v));
576
+ }
577
+ _decodeShapedObject(dataType, value, matchesType) {
568
578
  const result = {};
569
579
  for (const { path, type } of shapePaths(dataType)) {
580
+ const matchType = _.get(matchesType, path) ?? {};
570
581
  if (_.isString(type)) {
571
582
  const _value = this.dialect.decodeType(type, _.get(value, path));
572
583
  if (!_.isNil(_value))
@@ -575,17 +586,17 @@ class SqlStorage {
575
586
  else if (isPointer(type)) {
576
587
  const _value = _.get(value, path);
577
588
  if (_.isPlainObject(_value)) {
578
- const decoded = this._decodeObject(type.target, _value);
589
+ const decoded = this._decodeObject(type.target, _value, matchType);
579
590
  if (decoded.objectId)
580
591
  _.set(result, path, decoded);
581
592
  }
582
593
  }
583
594
  else if (isRelation(type)) {
584
595
  const _value = _.get(value, path);
585
- if (_.isString(_value) && _value.match(/^\d+$/g))
586
- _.set(result, path, parseInt(_value));
587
- else if (_.isArray(_value))
588
- _.set(result, path, _value.map(x => this._decodeObject(type.target, x)));
596
+ if (_.isArray(_value))
597
+ _.set(result, path, _value.map(x => this._decodeObject(type.target, x, matchType)));
598
+ else if (_.isPlainObject(_value))
599
+ _.set(result, path, this._decodeMatchTypes(_value, matchType));
589
600
  }
590
601
  else {
591
602
  const _value = this.dialect.decodeType(type.type, _.get(value, path)) ?? type.default;
@@ -595,7 +606,7 @@ class SqlStorage {
595
606
  }
596
607
  return result;
597
608
  }
598
- _decodeObject(className, attrs) {
609
+ _decodeObject(className, attrs, matchesType) {
599
610
  const fields = this.schema[className].fields;
600
611
  const obj = new TObject(className);
601
612
  const _attrs = {};
@@ -603,6 +614,7 @@ class SqlStorage {
603
614
  _.set(_attrs, key, value);
604
615
  }
605
616
  for (const [key, value] of _.toPairs(_attrs)) {
617
+ const matchType = matchesType[key] ?? {};
606
618
  const dataType = fields[key];
607
619
  if (!dataType)
608
620
  continue;
@@ -610,20 +622,20 @@ class SqlStorage {
610
622
  obj[PVK].attributes[key] = this.dialect.decodeType(dataType, value);
611
623
  }
612
624
  else if (isShape(dataType)) {
613
- obj[PVK].attributes[key] = this._decodeShapedObject(dataType, value);
625
+ obj[PVK].attributes[key] = this._decodeShapedObject(dataType, value, matchType);
614
626
  }
615
627
  else if (isPointer(dataType)) {
616
628
  if (_.isPlainObject(value)) {
617
- const decoded = this._decodeObject(dataType.target, value);
629
+ const decoded = this._decodeObject(dataType.target, value, matchType);
618
630
  if (decoded.objectId)
619
631
  obj[PVK].attributes[key] = decoded;
620
632
  }
621
633
  }
622
634
  else if (isRelation(dataType)) {
623
- if (_.isString(value) && value.match(/^\d+$/g))
624
- obj[PVK].attributes[key] = parseInt(value);
625
- else if (_.isArray(value))
626
- obj[PVK].attributes[key] = value.map(x => this._decodeObject(dataType.target, x));
635
+ if (_.isArray(value))
636
+ obj[PVK].attributes[key] = value.map(x => this._decodeObject(dataType.target, x, matchType));
637
+ else if (_.isPlainObject(value))
638
+ obj[PVK].attributes[key] = this._decodeMatchTypes(value, matchType);
627
639
  }
628
640
  else {
629
641
  obj[PVK].attributes[key] = this.dialect.decodeType(dataType.type, value) ?? dataType.default;
@@ -652,6 +664,18 @@ class SqlStorage {
652
664
  const count = parseInt(_count);
653
665
  return _.isFinite(count) ? count : 0;
654
666
  }
667
+ _matchesType(options) {
668
+ const types = {};
669
+ for (const [key, match] of _.entries(options.matches)) {
670
+ types[key] = this._matchesType(match);
671
+ }
672
+ for (const [key, group] of _.entries(options.groupMatches)) {
673
+ for (const [field, expr] of _.entries(group)) {
674
+ _.set(types, `${key}.${field}`, accumulatorKeyTypes[expr.type]);
675
+ }
676
+ }
677
+ return types;
678
+ }
655
679
  find(query) {
656
680
  const self = this;
657
681
  const compiler = self._makeCompiler(false, query.extraFilter);
@@ -659,7 +683,7 @@ class SqlStorage {
659
683
  return (async function* () {
660
684
  const objects = self.query(_query);
661
685
  for await (const object of objects) {
662
- yield self._decodeObject(query.className, object);
686
+ yield self._decodeObject(query.className, object, self._matchesType(query));
663
687
  }
664
688
  })();
665
689
  }
@@ -672,7 +696,7 @@ class SqlStorage {
672
696
  return (async function* () {
673
697
  const objects = self.query(_query);
674
698
  for await (const object of objects) {
675
- yield self._decodeObject(query.className, object);
699
+ yield self._decodeObject(query.className, object, self._matchesType(query));
676
700
  }
677
701
  })();
678
702
  }
@@ -686,7 +710,7 @@ class SqlStorage {
686
710
  return (async function* () {
687
711
  const objects = self.query(query);
688
712
  for await (const { _class, ...object } of objects) {
689
- yield self._decodeObject(_class, object);
713
+ yield self._decodeObject(_class, object, {});
690
714
  }
691
715
  })();
692
716
  }
@@ -701,29 +725,29 @@ class SqlStorage {
701
725
  return (async function* () {
702
726
  const objects = self.query(_query);
703
727
  for await (const object of objects) {
704
- yield self._decodeObject(query.className, object);
728
+ yield self._decodeObject(query.className, object, self._matchesType(query));
705
729
  }
706
730
  })();
707
731
  }
708
732
  async insert(options, values) {
709
733
  const compiler = this._makeCompiler(true);
710
734
  const result = await this.query(compiler.insert(options, values));
711
- return _.map(result, x => this._decodeObject(options.className, x));
735
+ return _.map(result, x => this._decodeObject(options.className, x, this._matchesType(options)));
712
736
  }
713
737
  async update(query, update) {
714
738
  const compiler = this._makeCompiler(true, query.extraFilter);
715
739
  const updated = await this.query(compiler.update(query, update));
716
- return _.map(updated, x => this._decodeObject(query.className, x));
740
+ return _.map(updated, x => this._decodeObject(query.className, x, this._matchesType(query)));
717
741
  }
718
742
  async upsert(query, update, setOnInsert) {
719
743
  const compiler = this._makeCompiler(true, query.extraFilter);
720
744
  const upserted = await this.query(compiler.upsert(query, update, setOnInsert));
721
- return _.map(upserted, x => this._decodeObject(query.className, x));
745
+ return _.map(upserted, x => this._decodeObject(query.className, x, this._matchesType(query)));
722
746
  }
723
747
  async delete(query) {
724
748
  const compiler = this._makeCompiler(true, query.extraFilter);
725
749
  const deleted = await this.query(compiler.delete(query));
726
- return _.map(deleted, x => this._decodeObject(query.className, x));
750
+ return _.map(deleted, x => this._decodeObject(query.className, x, this._matchesType(query)));
727
751
  }
728
752
  }
729
753
 
@@ -769,7 +793,15 @@ const _fetchElement = (parent, colname, subpath, dataType) => {
769
793
  }
770
794
  else if (!_.isEmpty(subpath)) {
771
795
  const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
772
- if (dataType && _isTypeof(dataType, ['array', 'string[]', 'relation'])) {
796
+ const match = parent.groupMatches?.[colname]?.[subpath[0]];
797
+ if (dataType && isRelation(dataType) && subpath.length === 1 && match) {
798
+ return {
799
+ element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}.${subpath[0]}` }}`,
800
+ json: false,
801
+ dataType: accumulatorKeyTypes[match.type],
802
+ };
803
+ }
804
+ else if (dataType && _isTypeof(dataType, ['array', 'string[]', 'relation'])) {
773
805
  return {
774
806
  element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
775
807
  json: true,
@@ -829,7 +861,7 @@ const _resolvePopulate = (path, populates) => {
829
861
  const fetchElement = (compiler, parent, field) => {
830
862
  if (parent.className) {
831
863
  const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
832
- const { element, json } = _fetchElement(parent, colname, subpath, dataType);
864
+ const { element, json, dataType: _dataType } = _fetchElement(parent, colname, subpath, dataType);
833
865
  if (isPointer(dataType))
834
866
  return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
835
867
  const populate = isRelation(dataType) && _resolvePopulate(_.toPath(colname), parent.populates);
@@ -837,7 +869,7 @@ const fetchElement = (compiler, parent, field) => {
837
869
  return { element, dataType: json ? null : dataType };
838
870
  return {
839
871
  element,
840
- dataType: json ? null : dataType,
872
+ dataType: json ? null : _dataType ?? dataType,
841
873
  relation: {
842
874
  colname,
843
875
  target: dataType.target,
@@ -1177,7 +1209,7 @@ const _encodeJsonValue = (value) => {
1177
1209
  return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
1178
1210
  return sql `to_jsonb(${{ value }})`;
1179
1211
  };
1180
- const _encodePopulateInclude = (className, colname, dataType) => {
1212
+ const _jsonPopulateInclude = (className, colname, dataType) => {
1181
1213
  switch (_typeof(dataType)) {
1182
1214
  case 'decimal':
1183
1215
  return sql `jsonb_build_object(
@@ -1892,10 +1924,13 @@ const _selectRelationPopulate = (compiler, parent, populate, field, encode) => {
1892
1924
  cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
1893
1925
  }
1894
1926
  return sql `
1895
- SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => [
1896
- encode && _encodePopulateInclude(populate.name, path, type),
1897
- !encode && sql `${{ identifier: populate.name }}.${{ identifier: path }}`,
1898
- !encode && isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
1927
+ SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => encode ? [
1928
+ _jsonPopulateInclude(populate.name, path, type)
1929
+ ] : [
1930
+ ...(populate.groupMatches[path] ? _.map(_.keys(populate.groupMatches[path]), k => sql `${{ identifier: populate.name }}.${{ identifier: `${path}.${k}` }}`) : [
1931
+ sql `${{ identifier: populate.name }}.${{ identifier: path }}`
1932
+ ]),
1933
+ isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
1899
1934
  ]))}
1900
1935
  FROM ${{ identifier: populate.name }} WHERE ${cond}
1901
1936
  ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
@@ -1904,33 +1939,68 @@ const _selectRelationPopulate = (compiler, parent, populate, field, encode) => {
1904
1939
  ${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
1905
1940
  `;
1906
1941
  };
1907
- const selectPopulate = (compiler, parent, populate, field, countMatches) => {
1942
+ const selectPopulate = (compiler, parent, populate, field) => {
1908
1943
  if (populate.type === 'relation') {
1909
- return {
1910
- columns: [
1911
- countMatches ? sql `
1912
- (
1913
- SELECT COUNT(*) FROM (
1914
- ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1915
- ) ${{ identifier: populate.name }}
1916
- ) AS ${{ identifier: field }}
1917
- ` : sql `
1918
- ARRAY(
1919
- SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
1920
- ${_selectRelationPopulate(compiler, parent, populate, field, true)}
1921
- ) ${{ identifier: populate.name }}
1922
- ) AS ${{ identifier: field }}
1923
- `,
1924
- sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`
1925
- ],
1926
- };
1944
+ const { groupMatches } = parent;
1945
+ const columns = [
1946
+ sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`,
1947
+ ];
1948
+ if (!_.isEmpty(groupMatches?.[field])) {
1949
+ for (const [key, { type, expr }] of _.entries(groupMatches[field])) {
1950
+ switch (type) {
1951
+ case '$count':
1952
+ columns.push(sql `
1953
+ (
1954
+ SELECT COUNT(*) FROM (
1955
+ ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1956
+ ) ${{ identifier: populate.name }}
1957
+ ) AS ${{ identifier: `${field}.${key}` }}
1958
+ `);
1959
+ break;
1960
+ case '$avg':
1961
+ case '$sum':
1962
+ {
1963
+ const op = {
1964
+ '$avg': 'AVG',
1965
+ '$sum': 'SUM',
1966
+ }[type];
1967
+ if (!expr)
1968
+ throw Error('Invalid expression');
1969
+ const exprs = encodeTypedQueryExpression(compiler, populate, expr);
1970
+ const { sql: value } = (_.includes(['$avg'], type) ? _.find(exprs, e => e.type === 'number') : _.first(exprs)) ?? {};
1971
+ if (!value)
1972
+ throw Error('Invalid expression');
1973
+ columns.push(sql `
1974
+ (
1975
+ SELECT ${{ literal: op }}(${value}) FROM (
1976
+ ${_selectRelationPopulate(compiler, parent, populate, field, false)}
1977
+ ) ${{ identifier: populate.name }}
1978
+ ) AS ${{ identifier: `${field}.${key}` }}
1979
+ `);
1980
+ }
1981
+ break;
1982
+ }
1983
+ }
1984
+ }
1985
+ else {
1986
+ columns.push(sql `
1987
+ ARRAY(
1988
+ SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
1989
+ ${_selectRelationPopulate(compiler, parent, populate, field, true)}
1990
+ ) ${{ identifier: populate.name }}
1991
+ ) AS ${{ identifier: field }}
1992
+ `);
1993
+ }
1994
+ return { columns };
1927
1995
  }
1928
1996
  const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
1929
1997
  const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
1930
1998
  const subpaths = resolveSubpaths(compiler, populate);
1931
1999
  return {
1932
2000
  columns: _.compact(_.flatMap(subpaths, ({ path, type }) => [
1933
- sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`,
2001
+ ...populate.groupMatches[path] ? _.map(_.keys(populate.groupMatches[path]), k => sql `${{ identifier: populate.name }}.${{ identifier: `${path}.${k}` }} AS ${{ identifier: `${field}.${path}.${k}` }}`) : [
2002
+ sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`
2003
+ ],
1934
2004
  isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }} AS ${{ identifier: `$${field}.${path}` }}`,
1935
2005
  ])),
1936
2006
  join: sql `
@@ -2015,9 +2085,8 @@ const encodePopulate = (compiler, parent, remix) => {
2015
2085
  parent.filter && compiler._encodeFilter(parent, parent.filter),
2016
2086
  compiler.extraFilter && compiler._encodeFilter(parent, compiler.extraFilter(parent.className)),
2017
2087
  ]);
2018
- const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field, _.includes(parent.countMatches, field)));
2088
+ const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2019
2089
  const _joins = _.compact(_.map(_populates, ({ join }) => join));
2020
- const _includes = _.pickBy(parent.includes, v => isPrimitive(v));
2021
2090
  const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2022
2091
  className: parent.className,
2023
2092
  name: parent.name,
@@ -2031,7 +2100,7 @@ const encodePopulate = (compiler, parent, remix) => {
2031
2100
  SELECT
2032
2101
  ${{
2033
2102
  literal: [
2034
- ..._.map(_.keys(_includes), colname => sql `${{ identifier: parent.name }}.${{ identifier: colname }}`),
2103
+ ...compiler._selectIncludes(parent.name, parent.includes),
2035
2104
  ..._.flatMap(_populates, ({ columns: column }) => column),
2036
2105
  ..._foreignField ? [sql `${rows ? sql `ARRAY(${_foreignField})` : _foreignField} AS ${{ identifier: parent.colname }}`] : [],
2037
2106
  ], separator: ',\n'
@@ -2084,9 +2153,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2084
2153
  break;
2085
2154
  return sql `${element} ${nullSafeEqual()} ${{ value: expr.value.objectId }}`;
2086
2155
  }
2087
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2088
- return sql `${element} ${nullSafeEqual()} ${encodeType(colname, 'number', expr.value)}`;
2089
- }
2090
2156
  return sql `${element} ${nullSafeEqual()} ${encodeValue(expr.value)}`;
2091
2157
  }
2092
2158
  case '$ne':
@@ -2100,9 +2166,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2100
2166
  break;
2101
2167
  return sql `${element} ${nullSafeNotEqual()} ${{ value: expr.value.objectId }}`;
2102
2168
  }
2103
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2104
- return sql `${element} ${nullSafeNotEqual()} ${encodeType(colname, 'number', expr.value)}`;
2105
- }
2106
2169
  return sql `${element} ${nullSafeNotEqual()} ${encodeValue(expr.value)}`;
2107
2170
  }
2108
2171
  case '$gt':
@@ -2146,9 +2209,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2146
2209
  else if (!_.isString(dataType) && dataType?.type === 'pointer' && expr.value instanceof TObject && expr.value.objectId) {
2147
2210
  return sql `${element} ${{ literal: op }} ${{ value: expr.value.objectId }}`;
2148
2211
  }
2149
- else if (relation && _.includes(parent.countMatches, relation.colname)) {
2150
- return sql `${element} ${{ literal: op }} ${encodeType(colname, 'number', expr.value)}`;
2151
- }
2152
2212
  else if (!dataType) {
2153
2213
  if (expr.value instanceof Decimal || _.isNumber(expr.value)) {
2154
2214
  return sql `(
@@ -2339,8 +2399,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2339
2399
  if (dataType && _isTypeof(dataType, 'string')) {
2340
2400
  return sql `COALESCE(length(${element}), 0) = ${{ value: expr.value }}`;
2341
2401
  }
2342
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2343
- return sql `${element} = ${{ value: expr.value }}`;
2402
+ if (relation && parent.className && parent.groupMatches?.[colname]) {
2403
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2404
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2405
+ return sql `(SELECT COUNT(*) FROM (${populate}) AS ${{ identifier: tempName }}) = ${{ value: expr.value }}`;
2344
2406
  }
2345
2407
  if (dataType && _isTypeof(dataType, ['array', 'string[]', 'vector', 'relation'])) {
2346
2408
  return sql `COALESCE(array_length(${element}, 1), 0) = ${{ value: expr.value }}`;
@@ -2363,8 +2425,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2363
2425
  if (dataType && _isTypeof(dataType, 'string')) {
2364
2426
  return sql `COALESCE(length(${element}), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
2365
2427
  }
2366
- if (relation && _.includes(parent.countMatches, relation.colname)) {
2367
- return sql `${element} ${{ literal: expr.value ? '=' : '<>' }} 0`;
2428
+ if (relation && parent.className && parent.groupMatches?.[colname]) {
2429
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2430
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2431
+ return sql `${{ literal: expr.value ? 'NOT EXISTS' : 'EXISTS' }}(SELECT * FROM (${populate}) AS ${{ identifier: tempName }})`;
2368
2432
  }
2369
2433
  if (dataType && _isTypeof(dataType, ['array', 'string[]', 'vector', 'relation'])) {
2370
2434
  return sql `COALESCE(array_length(${element}, 1), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;