proto.io 0.0.167 → 0.0.169

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 (42) hide show
  1. package/dist/adapters/file/database.d.ts +2 -2
  2. package/dist/adapters/file/database.js +1 -1
  3. package/dist/adapters/file/database.mjs +1 -1
  4. package/dist/adapters/file/filesystem.d.ts +2 -2
  5. package/dist/adapters/file/google-cloud-storage.d.ts +2 -2
  6. package/dist/adapters/storage/progres.d.ts +7 -2
  7. package/dist/adapters/storage/progres.js +324 -353
  8. package/dist/adapters/storage/progres.js.map +1 -1
  9. package/dist/adapters/storage/progres.mjs +324 -353
  10. package/dist/adapters/storage/progres.mjs.map +1 -1
  11. package/dist/client.d.ts +3 -3
  12. package/dist/client.js +1 -1
  13. package/dist/client.mjs +2 -2
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.js +3 -3
  16. package/dist/index.mjs +4 -4
  17. package/dist/internals/{index-BejQNqvC.mjs → index-B1wqSio6.mjs} +1 -2
  18. package/dist/internals/index-B1wqSio6.mjs.map +1 -0
  19. package/dist/internals/{index-rkqvel7o.d.ts → index-B5u7VXjz.d.ts} +2 -2
  20. package/dist/internals/index-B5u7VXjz.d.ts.map +1 -0
  21. package/dist/internals/{index-y8EePsDY.d.ts → index-BB2vDnq0.d.ts} +2 -2
  22. package/dist/internals/index-BB2vDnq0.d.ts.map +1 -0
  23. package/dist/internals/{index-CyzpkgJB.js → index-BJP46VGq.js} +2 -2
  24. package/dist/internals/index-BJP46VGq.js.map +1 -0
  25. package/dist/internals/{index-CSNRyhjB.js → index-CzfsyXvb.js} +1 -2
  26. package/dist/internals/index-CzfsyXvb.js.map +1 -0
  27. package/dist/internals/{index-Dyfia5Om.mjs → index-D0hHgn2P.mjs} +2 -2
  28. package/dist/internals/index-D0hHgn2P.mjs.map +1 -0
  29. package/dist/internals/{index-BQggoDNX.d.ts → index-D_GYwO8X.d.ts} +2 -2
  30. package/dist/internals/index-D_GYwO8X.d.ts.map +1 -0
  31. package/dist/internals/{random-DrURPPxr.mjs → random-BCpwYpyw.mjs} +2 -2
  32. package/dist/internals/{random-DrURPPxr.mjs.map → random-BCpwYpyw.mjs.map} +1 -1
  33. package/dist/internals/{random-q0PeamQE.js → random-Dytum6Nh.js} +2 -2
  34. package/dist/internals/{random-q0PeamQE.js.map → random-Dytum6Nh.js.map} +1 -1
  35. package/package.json +1 -1
  36. package/dist/internals/index-BQggoDNX.d.ts.map +0 -1
  37. package/dist/internals/index-BejQNqvC.mjs.map +0 -1
  38. package/dist/internals/index-CSNRyhjB.js.map +0 -1
  39. package/dist/internals/index-CyzpkgJB.js.map +0 -1
  40. package/dist/internals/index-Dyfia5Om.mjs.map +0 -1
  41. package/dist/internals/index-rkqvel7o.d.ts.map +0 -1
  42. package/dist/internals/index-y8EePsDY.d.ts.map +0 -1
@@ -9,9 +9,9 @@ var QueryStream = require('pg-query-stream');
9
9
  var utilsJs = require('@o2ter/utils-js');
10
10
  var Decimal = require('decimal.js');
11
11
  var utils = require('pg/lib/utils');
12
- var index$1 = require('../../internals/index-CSNRyhjB.js');
12
+ var index$1 = require('../../internals/index-CzfsyXvb.js');
13
13
  require('@o2ter/crypto-js');
14
- var random$1 = require('../../internals/random-q0PeamQE.js');
14
+ var random$1 = require('../../internals/random-Dytum6Nh.js');
15
15
  var _private = require('../../internals/private-CSB1Ep4g.js');
16
16
 
17
17
  //
@@ -291,7 +291,7 @@ class QueryCompiler {
291
291
  const _stages = _.mapValues(context.populates, (populate) => this.dialect.encodePopulate(this, populate));
292
292
  const stages = _.fromPairs(_.flatMap(_.values(_stages), (p) => _.toPairs(p)));
293
293
  const fetchName = `_fetch_$${query.className.toLowerCase()}`;
294
- const parent = { className: query.className, name: fetchName };
294
+ const parent = { className: query.className, name: fetchName, populates: context.populates };
295
295
  const baseFilter = this._encodeFilter(parent, query.filter);
296
296
  const populates = this._selectPopulateMap(context, query.className, fetchName);
297
297
  const joins = _.compact(_.map(populates, ({ join }) => join));
@@ -799,36 +799,8 @@ class SqlStorage {
799
799
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
800
800
  // THE SOFTWARE.
801
801
  //
802
- const foreignFieldType = (schema, className, path) => {
803
- let fields = schema[className].fields;
804
- let last;
805
- let result = false;
806
- for (const key of _.toPath(path)) {
807
- const dataType = fields[key];
808
- if (_.isNil(dataType))
809
- return;
810
- if (index.isPrimitive(dataType) || index.isVector(dataType))
811
- return;
812
- if (index.isShape(dataType)) {
813
- fields = dataType.shape;
814
- continue;
815
- }
816
- if (_.isNil(schema[dataType.target]))
817
- return;
818
- if (dataType.type === 'relation')
819
- result = true;
820
- fields = schema[dataType.target].fields;
821
- last = dataType;
822
- }
823
- if (!last)
824
- return;
825
- return {
826
- target: last.target,
827
- type: result ? 'relation' : last.type,
828
- };
829
- };
830
802
  const _fetchElement = (parent, colname, subpath, dataType) => {
831
- const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_expr_$') ? '$' : colname }}`;
803
+ const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_doller_expr_$') ? '$' : colname }}`;
832
804
  if (!parent.className) {
833
805
  if (colname !== '$') {
834
806
  return {
@@ -858,7 +830,7 @@ const _fetchElement = (parent, colname, subpath, dataType) => {
858
830
  };
859
831
  }
860
832
  }
861
- if (parent.name.startsWith('_expr_$') && colname !== '$') {
833
+ if (parent.name.startsWith('_doller_expr_$') && colname !== '$') {
862
834
  return {
863
835
  element: sql `jsonb_extract_path(${element}, ${{ quote: colname.startsWith('$') ? `$${colname}` : colname }})`,
864
836
  json: true,
@@ -890,36 +862,34 @@ const resolvePaths = (compiler, className, paths) => {
890
862
  }
891
863
  return { dataType, colname, subpath };
892
864
  };
865
+ const _resolvePopulate = (path, populates) => {
866
+ let [colname, ...subpath] = path;
867
+ while (populates && !_.isEmpty(subpath)) {
868
+ const populate = populates[colname];
869
+ if (populate)
870
+ return _resolvePopulate(subpath, populate.populates);
871
+ const [key, ...remain] = subpath;
872
+ colname = `${colname}.${key}`;
873
+ subpath = remain;
874
+ }
875
+ return populates?.[colname];
876
+ };
893
877
  const fetchElement = (compiler, parent, field) => {
894
878
  if (parent.className) {
895
879
  const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
896
880
  const { element, json } = _fetchElement(parent, colname, subpath, dataType);
897
- if (!_.isEmpty(subpath)) {
898
- const foreignField = foreignFieldType(compiler.schema, parent.className, field);
899
- if (foreignField) {
900
- return {
901
- element,
902
- dataType: foreignField,
903
- relation: {
904
- target: foreignField.target,
905
- sql: (callback) => sql `SELECT
906
- ${callback(sql `UNNEST`)}
907
- FROM UNNEST(${{ identifier: parent.name }}.${{ identifier: colname }})`,
908
- },
909
- };
910
- }
911
- }
912
881
  if (index.isPointer(dataType))
913
882
  return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
883
+ const populate = index.isRelation(dataType) && _resolvePopulate(_.toPath(colname), parent.populates);
884
+ if (!populate)
885
+ return { element, dataType: json ? null : dataType };
914
886
  return {
915
887
  element,
916
888
  dataType: json ? null : dataType,
917
- relation: index.isRelation(dataType) ? {
889
+ relation: {
918
890
  target: dataType.target,
919
- sql: (callback) => sql `SELECT
920
- ${callback(sql `${json ? sql `VALUE` : sql `UNNEST`}`)}
921
- FROM ${json ? sql `jsonb_array_elements(${element})` : sql `UNNEST(${element})`}`,
922
- } : null,
891
+ populate,
892
+ },
923
893
  };
924
894
  }
925
895
  const [colname, ...subpath] = _.toPath(field);
@@ -1878,6 +1848,231 @@ const encodeQueryExpression = (compiler, parent, expr) => {
1878
1848
  return encodeBooleanExpression(compiler, parent, expr);
1879
1849
  };
1880
1850
 
1851
+ //
1852
+ // populate.ts
1853
+ //
1854
+ // The MIT License
1855
+ // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
1856
+ //
1857
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
1858
+ // of this software and associated documentation files (the "Software"), to deal
1859
+ // in the Software without restriction, including without limitation the rights
1860
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1861
+ // copies of the Software, and to permit persons to whom the Software is
1862
+ // furnished to do so, subject to the following conditions:
1863
+ //
1864
+ // The above copyright notice and this permission notice shall be included in
1865
+ // all copies or substantial portions of the Software.
1866
+ //
1867
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1868
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1869
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1870
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1871
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1872
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1873
+ // THE SOFTWARE.
1874
+ //
1875
+ const resolveSubpaths = (compiler, populate) => {
1876
+ const subpaths = [];
1877
+ for (const [name, type] of _.toPairs(populate.includes)) {
1878
+ if (index.isPointer(type)) {
1879
+ subpaths.push(..._.map(resolveSubpaths(compiler, populate.populates[name]), ({ path, type }) => ({
1880
+ path: `${name}.${path}`,
1881
+ type,
1882
+ })));
1883
+ }
1884
+ else {
1885
+ subpaths.push({
1886
+ path: name,
1887
+ type,
1888
+ });
1889
+ }
1890
+ }
1891
+ return subpaths;
1892
+ };
1893
+ const _isPointer = (schema, className, path) => {
1894
+ let fields = schema[className].fields;
1895
+ let last;
1896
+ for (const key of _.toPath(path)) {
1897
+ const dataType = fields[key];
1898
+ if (_.isNil(dataType))
1899
+ throw Error(`Invalid path: ${path}`);
1900
+ if (index.isPrimitive(dataType) || index.isVector(dataType))
1901
+ throw Error(`Invalid path: ${path}`);
1902
+ if (index.isShape(dataType)) {
1903
+ fields = dataType.shape;
1904
+ continue;
1905
+ }
1906
+ if (dataType.type !== 'pointer')
1907
+ return false;
1908
+ if (_.isNil(schema[dataType.target]))
1909
+ throw Error(`Invalid path: ${path}`);
1910
+ fields = schema[dataType.target].fields;
1911
+ last = dataType;
1912
+ }
1913
+ return last?.type === 'pointer';
1914
+ };
1915
+ const _selectRelationPopulate = (compiler, parent, populate, field, encode) => {
1916
+ const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
1917
+ const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
1918
+ const subpaths = resolveSubpaths(compiler, populate);
1919
+ let cond;
1920
+ if (_.isNil(populate.foreignField)) {
1921
+ cond = sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`;
1922
+ }
1923
+ else if (_isPointer(compiler.schema, populate.className, populate.foreignField)) {
1924
+ cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`;
1925
+ }
1926
+ else {
1927
+ cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
1928
+ }
1929
+ return sql `
1930
+ SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => [
1931
+ encode && _encodePopulateInclude(populate.name, path, type),
1932
+ !encode && sql `${{ identifier: populate.name }}.${{ identifier: path }}`,
1933
+ !encode && index.isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
1934
+ ]))}
1935
+ FROM ${{ identifier: populate.name }} WHERE ${cond}
1936
+ ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
1937
+ ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
1938
+ ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
1939
+ ${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
1940
+ `;
1941
+ };
1942
+ const selectPopulate = (compiler, parent, populate, field) => {
1943
+ if (populate.type === 'relation') {
1944
+ return {
1945
+ columns: [
1946
+ sql `
1947
+ ARRAY(
1948
+ SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
1949
+ ${_selectRelationPopulate(compiler, parent, populate, field, true)}
1950
+ ) ${{ identifier: populate.name }}
1951
+ ) AS ${{ identifier: field }}
1952
+ `,
1953
+ sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`
1954
+ ],
1955
+ };
1956
+ }
1957
+ const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
1958
+ const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
1959
+ const subpaths = resolveSubpaths(compiler, populate);
1960
+ return {
1961
+ columns: _.compact(_.flatMap(subpaths, ({ path, type }) => [
1962
+ sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`,
1963
+ index.isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }} AS ${{ identifier: `$${field}.${path}` }}`,
1964
+ ])),
1965
+ join: sql `
1966
+ LEFT JOIN ${{ identifier: populate.name }}
1967
+ ON ${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}
1968
+ `,
1969
+ };
1970
+ };
1971
+ const encodeRemix = (parent, remix) => sql `${remix?.className === parent.className ? sql `
1972
+ (SELECT * FROM ${{ identifier: remix.name }} UNION SELECT * FROM ${{ identifier: parent.className }})
1973
+ ` : { identifier: parent.className }}`;
1974
+ const encodeForeignField = (compiler, parent, foreignField, remix) => {
1975
+ const { paths: [colname, ...subpath], dataType } = random$1.resolveColumn(compiler.schema, parent.className, foreignField);
1976
+ const tempName = `_populate_$${compiler.nextIdx()}`;
1977
+ const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
1978
+ const _foreign = (field) => sql `${{ identifier: tempName }}.${{ identifier: field }}`;
1979
+ if (_.isEmpty(subpath) && index.isRelation(dataType) && dataType.foreignField) {
1980
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
1981
+ return {
1982
+ joins: [],
1983
+ field: sql `(
1984
+ SELECT ${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`}
1985
+ FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
1986
+ ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
1987
+ WHERE ${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${array || rows ? sql `ANY(${field})` : field}
1988
+ )`,
1989
+ array: false,
1990
+ rows: true,
1991
+ };
1992
+ }
1993
+ if (_.isEmpty(subpath)) {
1994
+ return {
1995
+ joins: [],
1996
+ field: sql `${{ identifier: parent.name }}.${{ identifier: foreignField }}`,
1997
+ array: index.isRelation(dataType),
1998
+ rows: false,
1999
+ };
2000
+ }
2001
+ if (!index.isPointer(dataType) && !index.isRelation(dataType))
2002
+ throw Error(`Invalid path: ${foreignField}`);
2003
+ const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
2004
+ const cond = [];
2005
+ if (compiler.extraFilter) {
2006
+ const filter = compiler.extraFilter(dataType.target);
2007
+ cond.push(compiler._encodeFilter({ className: dataType.target, name: tempName }, filter));
2008
+ }
2009
+ if (index.isPointer(dataType)) {
2010
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}`);
2011
+ return {
2012
+ joins: [sql `
2013
+ LEFT JOIN ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2014
+ ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2015
+ `, ...joins],
2016
+ field,
2017
+ array,
2018
+ rows,
2019
+ };
2020
+ }
2021
+ if (_.isNil(dataType.foreignField)) {
2022
+ cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`);
2023
+ }
2024
+ else if (_isPointer(compiler.schema, dataType.target, dataType.foreignField)) {
2025
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`);
2026
+ }
2027
+ else {
2028
+ cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`);
2029
+ }
2030
+ return {
2031
+ joins: [],
2032
+ field: sql `(
2033
+ SELECT ${array ? sql `UNNEST(${field})` : field}
2034
+ FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2035
+ ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
2036
+ WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2037
+ )`,
2038
+ array: false,
2039
+ rows: true,
2040
+ };
2041
+ };
2042
+ const encodePopulate = (compiler, parent, remix) => {
2043
+ const _filter = _.compact([
2044
+ parent.filter && compiler._encodeFilter(parent, parent.filter),
2045
+ compiler.extraFilter && compiler._encodeFilter(parent, compiler.extraFilter(parent.className)),
2046
+ ]);
2047
+ const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2048
+ const _joins = _.compact(_.map(_populates, ({ join }) => join));
2049
+ const _includes = _.pickBy(parent.includes, v => index.isPrimitive(v));
2050
+ const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2051
+ className: parent.className,
2052
+ name: parent.name,
2053
+ }, parent.foreignField, remix) : {};
2054
+ return _.reduce(parent.populates, (acc, populate) => ({
2055
+ ...encodePopulate(compiler, populate, remix),
2056
+ ...acc,
2057
+ }), {
2058
+ [parent.name]: sql `
2059
+ SELECT * FROM (
2060
+ SELECT
2061
+ ${{
2062
+ literal: [
2063
+ ..._.map(_.keys(_includes), colname => sql `${{ identifier: parent.name }}.${{ identifier: colname }}`),
2064
+ ..._.flatMap(_populates, ({ columns: column }) => column),
2065
+ ..._foreignField ? [sql `${rows ? sql `ARRAY(${_foreignField})` : _foreignField} AS ${{ identifier: parent.colname }}`] : [],
2066
+ ], separator: ',\n'
2067
+ }}
2068
+ FROM ${encodeRemix(parent, remix)} AS ${{ identifier: parent.name }}
2069
+ ${!_.isEmpty(_joins) || !_.isEmpty(_joins2) ? { literal: [..._joins, ..._joins2], separator: '\n' } : sql ``}
2070
+ ) AS ${{ identifier: parent.name }}
2071
+ ${!_.isEmpty(_filter) ? sql `WHERE ${{ literal: _.map(_.compact(_filter), x => sql `(${x})`), separator: ' AND ' }}` : sql ``}
2072
+ `,
2073
+ });
2074
+ };
2075
+
1881
2076
  //
1882
2077
  // selectors.ts
1883
2078
  //
@@ -1934,12 +2129,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
1934
2129
  case '$lt':
1935
2130
  case '$lte':
1936
2131
  {
1937
- const operatorMap = {
2132
+ const op = {
1938
2133
  '$gt': '>',
1939
2134
  '$gte': '>=',
1940
2135
  '$lt': '<',
1941
2136
  '$lte': '<=',
1942
- };
2137
+ }[expr.type];
1943
2138
  if (_.isRegExp(expr.value) || expr.value instanceof index$1.QuerySelector || expr.value instanceof index$1.FieldSelectorExpression)
1944
2139
  break;
1945
2140
  if (dataType && index.isPrimitive(dataType)) {
@@ -1947,46 +2142,47 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
1947
2142
  case 'boolean':
1948
2143
  if (!_.isBoolean(expr.value))
1949
2144
  break;
1950
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}`;
2145
+ return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
1951
2146
  case 'number':
1952
2147
  case 'decimal':
1953
2148
  if (!(expr.value instanceof Decimal) && !_.isNumber(expr.value))
1954
2149
  break;
1955
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}`;
2150
+ return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
1956
2151
  case 'string':
1957
2152
  if (!_.isString(expr.value))
1958
2153
  break;
1959
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}`;
2154
+ return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
1960
2155
  case 'date':
1961
2156
  if (!_.isDate(expr.value))
1962
2157
  break;
1963
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}`;
2158
+ return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
1964
2159
  }
1965
2160
  }
1966
2161
  else if (!_.isString(dataType) && dataType?.type === 'pointer' && expr.value instanceof index.TObject && expr.value.objectId) {
1967
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${{ value: expr.value.objectId }}`;
2162
+ return sql `${element} ${{ literal: op }} ${{ value: expr.value.objectId }}`;
1968
2163
  }
1969
2164
  else if (!dataType) {
1970
2165
  if (expr.value instanceof Decimal || _.isNumber(expr.value)) {
1971
2166
  return sql `(
1972
2167
  jsonb_typeof(${element}) ${nullSafeEqual()} 'number'
1973
- AND ${element}::NUMERIC ${{ literal: operatorMap[expr.type] }} ${{ value: expr.value instanceof Decimal ? expr.value.toNumber() : expr.value }}
2168
+ AND ${element}::NUMERIC ${{ literal: op }} ${{ value: expr.value instanceof Decimal ? expr.value.toNumber() : expr.value }}
1974
2169
  ) OR (
1975
2170
  jsonb_typeof(${element} -> '$decimal') ${nullSafeEqual()} 'string'
1976
- AND (${element} ->> '$decimal')::DECIMAL ${{ literal: operatorMap[expr.type] }} ${{ value: expr.value instanceof Decimal ? expr.value.toString() : expr.value }}::DECIMAL
2171
+ AND (${element} ->> '$decimal')::DECIMAL ${{ literal: op }} ${{ value: expr.value instanceof Decimal ? expr.value.toString() : expr.value }}::DECIMAL
1977
2172
  )`;
1978
2173
  }
1979
2174
  else if (_.isDate(expr.value)) {
1980
2175
  return sql `(
1981
2176
  jsonb_typeof(${element} -> '$date') ${nullSafeEqual()} 'string'
1982
- AND ${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}
2177
+ AND ${element} ${{ literal: op }} ${encodeValue(expr.value)}
1983
2178
  )`;
1984
2179
  }
1985
2180
  else {
1986
- return sql `${element} ${{ literal: operatorMap[expr.type] }} ${encodeValue(expr.value)}`;
2181
+ return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
1987
2182
  }
1988
2183
  }
1989
2184
  }
2185
+ break;
1990
2186
  case '$in':
1991
2187
  {
1992
2188
  if (!_.isArray(expr.value))
@@ -2022,6 +2218,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2022
2218
  return sql `${element} IN (${_.map(values, x => encodeValue(x))})`;
2023
2219
  }
2024
2220
  }
2221
+ break;
2025
2222
  case '$nin':
2026
2223
  {
2027
2224
  if (!_.isArray(expr.value))
@@ -2057,76 +2254,34 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2057
2254
  return sql `${element} NOT IN (${_.map(values, x => encodeValue(x))})`;
2058
2255
  }
2059
2256
  }
2257
+ break;
2060
2258
  case '$subset':
2061
- {
2062
- if (!_.isArray(expr.value))
2063
- break;
2064
- if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
2065
- return sql `${element} <@ ${{ value: index._encodeValue(expr.value) }}`;
2066
- }
2067
- else if (relation) {
2068
- if (!_.every(expr.value, x => x instanceof index.TObject && x.objectId))
2069
- break;
2070
- return sql `ARRAY(${relation.sql((v) => sql `${v} ->> '_id'`)}) <@ ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
2071
- }
2072
- else if (!dataType) {
2073
- return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} <@ ${_encodeJsonValue(index._encodeValue(expr.value))}`;
2074
- }
2075
- }
2076
2259
  case '$superset':
2077
- {
2078
- if (!_.isArray(expr.value))
2079
- break;
2080
- if (_.isEmpty(expr.value))
2081
- return sql `true`;
2082
- if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
2083
- return sql `${element} @> ${{ value: index._encodeValue(expr.value) }}`;
2084
- }
2085
- else if (relation) {
2086
- if (!_.every(expr.value, x => x instanceof index.TObject && x.objectId))
2087
- break;
2088
- return sql `ARRAY(${relation.sql((v) => sql `${v} ->> '_id'`)}) @> ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
2089
- }
2090
- else if (!dataType) {
2091
- return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} @> ${_encodeJsonValue(index._encodeValue(expr.value))}`;
2092
- }
2093
- }
2094
- case '$disjoint':
2095
- {
2096
- if (!_.isArray(expr.value))
2097
- break;
2098
- if (_.isEmpty(expr.value))
2099
- return sql `true`;
2100
- if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
2101
- return sql `NOT ${element} && ${{ value: index._encodeValue(expr.value) }}`;
2102
- }
2103
- else if (relation) {
2104
- if (!_.every(expr.value, x => x instanceof index.TObject && x.objectId))
2105
- break;
2106
- return sql `NOT ARRAY(${relation.sql((v) => sql `${v} ->> '_id'`)}) && ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
2107
- }
2108
- else if (!dataType) {
2109
- return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND NOT ${element} && ${_encodeJsonValue(index._encodeValue(expr.value))}`;
2110
- }
2111
- }
2112
2260
  case '$intersect':
2113
2261
  {
2262
+ const op = {
2263
+ '$subset': '<@',
2264
+ '$superset': '@>',
2265
+ '$intersect': '&&',
2266
+ }[expr.type];
2114
2267
  if (!_.isArray(expr.value))
2115
2268
  break;
2116
2269
  if (_.isEmpty(expr.value))
2117
- return sql `false`;
2270
+ return sql `true`;
2118
2271
  if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
2119
- return sql `${element} && ${{ value: index._encodeValue(expr.value) }}`;
2272
+ return sql `${element} ${{ literal: op }} ${{ value: index._encodeValue(expr.value) }}`;
2120
2273
  }
2121
- else if (relation) {
2274
+ if (relation && parent.className) {
2122
2275
  if (!_.every(expr.value, x => x instanceof index.TObject && x.objectId))
2123
2276
  break;
2124
- return sql `ARRAY(${relation.sql((v) => sql `${v} ->> '_id'`)}) && ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
2277
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2278
+ return sql `ARRAY(SELECT ${{ identifier: '_id' }} FROM (${populate})) ${{ literal: op }} ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
2125
2279
  }
2126
- else if (!dataType) {
2127
- return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} && ${_encodeJsonValue(index._encodeValue(expr.value))}`;
2280
+ if (!dataType) {
2281
+ return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} ${{ literal: op }} ${_encodeJsonValue(index._encodeValue(expr.value))}`;
2128
2282
  }
2129
2283
  }
2284
+ break;
2130
2285
  case '$not':
2131
2286
  {
2132
2287
  if (!(expr.value instanceof index$1.FieldSelectorExpression))
@@ -2139,7 +2294,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2139
2294
  if (_.isString(expr.value)) {
2140
2295
  return sql `${element} LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
2141
2296
  }
2142
- else if (_.isRegExp(expr.value)) {
2297
+ if (_.isRegExp(expr.value)) {
2143
2298
  if (expr.value.ignoreCase)
2144
2299
  return sql `${element} ~* ${{ value: expr.value.source }}`;
2145
2300
  return sql `${element} ~ ${{ value: expr.value.source }}`;
@@ -2149,13 +2304,14 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2149
2304
  if (_.isString(expr.value)) {
2150
2305
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
2151
2306
  }
2152
- else if (_.isRegExp(expr.value)) {
2307
+ if (_.isRegExp(expr.value)) {
2153
2308
  if (expr.value.ignoreCase)
2154
2309
  return sql `${element} ~* ${{ value: expr.value.source }}`;
2155
2310
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') ~ ${{ value: expr.value.source }}`;
2156
2311
  }
2157
2312
  }
2158
2313
  }
2314
+ break;
2159
2315
  case '$starts':
2160
2316
  {
2161
2317
  if (!_.isString(expr.value))
@@ -2163,10 +2319,11 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2163
2319
  if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
2164
2320
  return sql `${element} LIKE ${{ value: `${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
2165
2321
  }
2166
- else if (!dataType) {
2322
+ if (!dataType) {
2167
2323
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
2168
2324
  }
2169
2325
  }
2326
+ break;
2170
2327
  case '$ends':
2171
2328
  {
2172
2329
  if (!_.isString(expr.value))
@@ -2174,10 +2331,11 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2174
2331
  if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
2175
2332
  return sql `${element} LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}` }}`;
2176
2333
  }
2177
- else if (!dataType) {
2334
+ if (!dataType) {
2178
2335
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}` }}`;
2179
2336
  }
2180
2337
  }
2338
+ break;
2181
2339
  case '$size':
2182
2340
  {
2183
2341
  if (!_.isNumber(expr.value) || !_.isSafeInteger(expr.value))
@@ -2185,10 +2343,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2185
2343
  if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
2186
2344
  return sql `COALESCE(length(${element}), 0) = ${{ value: expr.value }}`;
2187
2345
  }
2188
- else if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
2346
+ if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
2189
2347
  return sql `COALESCE(array_length(${element}, 1), 0) = ${{ value: expr.value }}`;
2190
2348
  }
2191
- else if (!dataType) {
2349
+ if (!dataType) {
2192
2350
  return sql `(
2193
2351
  CASE jsonb_typeof(${element})
2194
2352
  WHEN 'array' THEN jsonb_array_length(${element}) = ${{ value: expr.value }}
@@ -2198,6 +2356,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2198
2356
  )`;
2199
2357
  }
2200
2358
  }
2359
+ break;
2201
2360
  case '$empty':
2202
2361
  {
2203
2362
  if (!_.isBoolean(expr.value))
@@ -2205,10 +2364,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2205
2364
  if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
2206
2365
  return sql `COALESCE(length(${element}), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
2207
2366
  }
2208
- else if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
2367
+ if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
2209
2368
  return sql `COALESCE(array_length(${element}, 1), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
2210
2369
  }
2211
- else if (!dataType) {
2370
+ if (!dataType) {
2212
2371
  return sql `(
2213
2372
  CASE jsonb_typeof(${element})
2214
2373
  WHEN 'array' THEN jsonb_array_length(${element}) ${{ literal: expr.value ? '=' : '<>' }} 0
@@ -2218,273 +2377,85 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
2218
2377
  )`;
2219
2378
  }
2220
2379
  }
2380
+ break;
2221
2381
  case '$every':
2222
2382
  {
2223
2383
  if (!(expr.value instanceof index$1.QuerySelector))
2224
2384
  break;
2225
- const tempName = `_expr_$${compiler.nextIdx()}`;
2226
- const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2227
- if (!filter)
2228
- break;
2229
- if (relation) {
2385
+ if (relation && parent.className) {
2386
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2387
+ const filter = compiler._encodeFilter({
2388
+ name: tempName,
2389
+ className: relation.target,
2390
+ populates: relation.populate.populates,
2391
+ }, expr.value);
2392
+ if (!filter)
2393
+ break;
2394
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2230
2395
  return sql `NOT EXISTS(
2231
- SELECT * FROM (${relation.sql((v) => sql `${v} AS "$"`)}) AS ${{ identifier: tempName }}
2396
+ SELECT * FROM (${populate}) AS ${{ identifier: tempName }}
2232
2397
  WHERE NOT (${filter})
2233
2398
  )`;
2234
2399
  }
2235
- else if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
2400
+ const tempName = `_doller_expr_$${compiler.nextIdx()}`;
2401
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2402
+ if (!filter)
2403
+ break;
2404
+ if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
2236
2405
  return sql `NOT EXISTS(
2237
2406
  SELECT * FROM (SELECT UNNEST AS "$" FROM UNNEST(${element})) AS ${{ identifier: tempName }}
2238
2407
  WHERE NOT (${filter})
2239
2408
  )`;
2240
2409
  }
2241
- else if (!dataType) {
2410
+ if (!dataType) {
2242
2411
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND NOT EXISTS(
2243
2412
  SELECT * FROM (SELECT value AS "$" FROM jsonb_array_elements(${element})) AS ${{ identifier: tempName }}
2244
2413
  WHERE NOT (${filter})
2245
2414
  )`;
2246
2415
  }
2247
2416
  }
2417
+ break;
2248
2418
  case '$some':
2249
2419
  {
2250
2420
  if (!(expr.value instanceof index$1.QuerySelector))
2251
2421
  break;
2252
- const tempName = `_expr_$${compiler.nextIdx()}`;
2253
- const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2254
- if (!filter)
2255
- break;
2256
- if (relation) {
2422
+ if (relation && parent.className) {
2423
+ const tempName = `_populate_expr_$${compiler.nextIdx()}`;
2424
+ const filter = compiler._encodeFilter({
2425
+ name: tempName,
2426
+ className: relation.target,
2427
+ populates: relation.populate.populates,
2428
+ }, expr.value);
2429
+ if (!filter)
2430
+ break;
2431
+ const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
2257
2432
  return sql `EXISTS(
2258
- SELECT * FROM (${relation.sql((v) => sql `${v} AS "$"`)}) AS ${{ identifier: tempName }}
2433
+ SELECT * FROM (${populate}) AS ${{ identifier: tempName }}
2259
2434
  WHERE ${filter}
2260
2435
  )`;
2261
2436
  }
2262
- else if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
2437
+ const tempName = `_doller_expr_$${compiler.nextIdx()}`;
2438
+ const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
2439
+ if (!filter)
2440
+ break;
2441
+ if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
2263
2442
  return sql `EXISTS(
2264
2443
  SELECT * FROM (SELECT UNNEST AS "$" FROM UNNEST(${element})) AS ${{ identifier: tempName }}
2265
2444
  WHERE ${filter}
2266
2445
  )`;
2267
2446
  }
2268
- else if (!dataType) {
2447
+ if (!dataType) {
2269
2448
  return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND EXISTS(
2270
2449
  SELECT * FROM (SELECT value AS "$" FROM jsonb_array_elements(${element})) AS ${{ identifier: tempName }}
2271
2450
  WHERE ${filter}
2272
2451
  )`;
2273
2452
  }
2274
2453
  }
2454
+ break;
2275
2455
  }
2276
2456
  throw Error('Invalid expression');
2277
2457
  };
2278
2458
 
2279
- //
2280
- // populate.ts
2281
- //
2282
- // The MIT License
2283
- // Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
2284
- //
2285
- // Permission is hereby granted, free of charge, to any person obtaining a copy
2286
- // of this software and associated documentation files (the "Software"), to deal
2287
- // in the Software without restriction, including without limitation the rights
2288
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2289
- // copies of the Software, and to permit persons to whom the Software is
2290
- // furnished to do so, subject to the following conditions:
2291
- //
2292
- // The above copyright notice and this permission notice shall be included in
2293
- // all copies or substantial portions of the Software.
2294
- //
2295
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2296
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2297
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2298
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2299
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2300
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2301
- // THE SOFTWARE.
2302
- //
2303
- const resolveSubpaths = (compiler, populate) => {
2304
- const subpaths = [];
2305
- for (const [name, type] of _.toPairs(populate.includes)) {
2306
- if (index.isPointer(type)) {
2307
- subpaths.push(..._.map(resolveSubpaths(compiler, populate.populates[name]), ({ path, type }) => ({
2308
- path: `${name}.${path}`,
2309
- type,
2310
- })));
2311
- }
2312
- else {
2313
- subpaths.push({
2314
- path: name,
2315
- type,
2316
- });
2317
- }
2318
- }
2319
- return subpaths;
2320
- };
2321
- const _isPointer = (schema, className, path) => {
2322
- let fields = schema[className].fields;
2323
- let last;
2324
- for (const key of _.toPath(path)) {
2325
- const dataType = fields[key];
2326
- if (_.isNil(dataType))
2327
- throw Error(`Invalid path: ${path}`);
2328
- if (index.isPrimitive(dataType) || index.isVector(dataType))
2329
- throw Error(`Invalid path: ${path}`);
2330
- if (index.isShape(dataType)) {
2331
- fields = dataType.shape;
2332
- continue;
2333
- }
2334
- if (dataType.type !== 'pointer')
2335
- return false;
2336
- if (_.isNil(schema[dataType.target]))
2337
- throw Error(`Invalid path: ${path}`);
2338
- fields = schema[dataType.target].fields;
2339
- last = dataType;
2340
- }
2341
- return last?.type === 'pointer';
2342
- };
2343
- const selectPopulate = (compiler, parent, populate, field) => {
2344
- const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2345
- const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
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
- }
2352
- if (populate.type === 'pointer') {
2353
- cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}`);
2354
- return {
2355
- columns: _.map(subpaths, ({ path }) => sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`),
2356
- join: sql `
2357
- LEFT JOIN ${{ identifier: populate.name }}
2358
- ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2359
- `,
2360
- };
2361
- }
2362
- if (_.isNil(populate.foreignField)) {
2363
- cond.push(sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`);
2364
- }
2365
- else if (_isPointer(compiler.schema, populate.className, populate.foreignField)) {
2366
- cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`);
2367
- }
2368
- else {
2369
- cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`);
2370
- }
2371
- return {
2372
- columns: [sql `
2373
- ARRAY(
2374
- SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
2375
- SELECT ${_.map(subpaths, ({ path, type }) => _encodePopulateInclude(populate.name, path, type))}
2376
- FROM ${{ identifier: populate.name }} WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2377
- ${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
2378
- ${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
2379
- ${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
2380
- ${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
2381
- ) ${{ identifier: populate.name }}
2382
- ) AS ${{ identifier: field }}
2383
- `],
2384
- };
2385
- };
2386
- const encodeRemix = (parent, remix) => sql `${remix?.className === parent.className ? sql `
2387
- (SELECT * FROM ${{ identifier: remix.name }} UNION SELECT * FROM ${{ identifier: parent.className }})
2388
- ` : { identifier: parent.className }}`;
2389
- const encodeForeignField = (compiler, parent, foreignField, remix) => {
2390
- const { paths: [colname, ...subpath], dataType } = random$1.resolveColumn(compiler.schema, parent.className, foreignField);
2391
- const tempName = `_populate_$${compiler.nextIdx()}`;
2392
- const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
2393
- const _foreign = (field) => sql `${{ identifier: tempName }}.${{ identifier: field }}`;
2394
- if (_.isEmpty(subpath) && index.isRelation(dataType) && dataType.foreignField) {
2395
- const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
2396
- return {
2397
- joins: [],
2398
- field: sql `(
2399
- SELECT ${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`}
2400
- FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2401
- ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
2402
- WHERE ${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${array || rows ? sql `ANY(${field})` : field}
2403
- )`,
2404
- array: false,
2405
- rows: true,
2406
- };
2407
- }
2408
- if (_.isEmpty(subpath)) {
2409
- return {
2410
- joins: [],
2411
- field: sql `${{ identifier: parent.name }}.${{ identifier: foreignField }}`,
2412
- array: index.isRelation(dataType),
2413
- rows: false,
2414
- };
2415
- }
2416
- if (!index.isPointer(dataType) && !index.isRelation(dataType))
2417
- throw Error(`Invalid path: ${foreignField}`);
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
- }
2424
- if (index.isPointer(dataType)) {
2425
- cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}`);
2426
- return {
2427
- joins: [sql `
2428
- LEFT JOIN ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2429
- ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2430
- `, ...joins],
2431
- field,
2432
- array,
2433
- rows,
2434
- };
2435
- }
2436
- if (_.isNil(dataType.foreignField)) {
2437
- cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`);
2438
- }
2439
- else if (_isPointer(compiler.schema, dataType.target, dataType.foreignField)) {
2440
- cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`);
2441
- }
2442
- else {
2443
- cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`);
2444
- }
2445
- return {
2446
- joins: [],
2447
- field: sql `(
2448
- SELECT ${array ? sql `UNNEST(${field})` : field}
2449
- FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
2450
- ${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
2451
- WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
2452
- )`,
2453
- array: false,
2454
- rows: true,
2455
- };
2456
- };
2457
- const encodePopulate = (compiler, parent, remix) => {
2458
- const _filter = parent.filter && compiler._encodeFilter(parent, parent.filter);
2459
- const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
2460
- const _joins = _.compact(_.map(_populates, ({ join }) => join));
2461
- const _includes = _.pickBy(parent.includes, v => index.isPrimitive(v));
2462
- const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
2463
- className: parent.className,
2464
- name: parent.name,
2465
- }, parent.foreignField, remix) : {};
2466
- return _.reduce(parent.populates, (acc, populate) => ({
2467
- ...encodePopulate(compiler, populate, remix),
2468
- ...acc,
2469
- }), {
2470
- [parent.name]: sql `
2471
- SELECT * FROM (
2472
- SELECT
2473
- ${{
2474
- literal: [
2475
- ..._.map(_.keys(_includes), colname => sql `${{ identifier: parent.name }}.${{ identifier: colname }}`),
2476
- ..._.flatMap(_populates, ({ columns: column }) => column),
2477
- ..._foreignField ? [sql `${rows ? sql `ARRAY(${_foreignField})` : _foreignField} AS ${{ identifier: parent.colname }}`] : [],
2478
- ], separator: ',\n'
2479
- }}
2480
- FROM ${encodeRemix(parent, remix)} AS ${{ identifier: parent.name }}
2481
- ${!_.isEmpty(_joins) || !_.isEmpty(_joins2) ? { literal: [..._joins, ..._joins2], separator: '\n' } : sql ``}
2482
- ) AS ${{ identifier: parent.name }}
2483
- ${_filter ? sql `WHERE ${_filter}` : sql ``}
2484
- `,
2485
- });
2486
- };
2487
-
2488
2459
  //
2489
2460
  // relation.ts
2490
2461
  //