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