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.
- package/dist/adapters/file/database.d.ts +2 -2
- package/dist/adapters/file/database.js +1 -1
- package/dist/adapters/file/database.mjs +1 -1
- package/dist/adapters/file/filesystem.d.ts +2 -2
- package/dist/adapters/file/google-cloud-storage.d.ts +2 -2
- package/dist/adapters/storage/progres.d.ts +7 -2
- package/dist/adapters/storage/progres.js +324 -353
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +324 -353
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/client.mjs +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/index.mjs +4 -4
- package/dist/internals/{index-BejQNqvC.mjs → index-B1wqSio6.mjs} +1 -2
- package/dist/internals/index-B1wqSio6.mjs.map +1 -0
- package/dist/internals/{index-rkqvel7o.d.ts → index-B5u7VXjz.d.ts} +2 -2
- package/dist/internals/index-B5u7VXjz.d.ts.map +1 -0
- package/dist/internals/{index-y8EePsDY.d.ts → index-BB2vDnq0.d.ts} +2 -2
- package/dist/internals/index-BB2vDnq0.d.ts.map +1 -0
- package/dist/internals/{index-CyzpkgJB.js → index-BJP46VGq.js} +2 -2
- package/dist/internals/index-BJP46VGq.js.map +1 -0
- package/dist/internals/{index-CSNRyhjB.js → index-CzfsyXvb.js} +1 -2
- package/dist/internals/index-CzfsyXvb.js.map +1 -0
- package/dist/internals/{index-Dyfia5Om.mjs → index-D0hHgn2P.mjs} +2 -2
- package/dist/internals/index-D0hHgn2P.mjs.map +1 -0
- package/dist/internals/{index-BQggoDNX.d.ts → index-D_GYwO8X.d.ts} +2 -2
- package/dist/internals/index-D_GYwO8X.d.ts.map +1 -0
- package/dist/internals/{random-DrURPPxr.mjs → random-BCpwYpyw.mjs} +2 -2
- package/dist/internals/{random-DrURPPxr.mjs.map → random-BCpwYpyw.mjs.map} +1 -1
- package/dist/internals/{random-q0PeamQE.js → random-Dytum6Nh.js} +2 -2
- package/dist/internals/{random-q0PeamQE.js.map → random-Dytum6Nh.js.map} +1 -1
- package/package.json +1 -1
- package/dist/internals/index-BQggoDNX.d.ts.map +0 -1
- package/dist/internals/index-BejQNqvC.mjs.map +0 -1
- package/dist/internals/index-CSNRyhjB.js.map +0 -1
- package/dist/internals/index-CyzpkgJB.js.map +0 -1
- package/dist/internals/index-Dyfia5Om.mjs.map +0 -1
- package/dist/internals/index-rkqvel7o.d.ts.map +0 -1
- 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-
|
|
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-
|
|
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('
|
|
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('
|
|
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:
|
|
885
|
+
relation: {
|
|
914
886
|
target: dataType.target,
|
|
915
|
-
|
|
916
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
2173
|
+
AND ${element} ${{ literal: op }} ${encodeValue(expr.value)}
|
|
1979
2174
|
)`;
|
|
1980
2175
|
}
|
|
1981
2176
|
else {
|
|
1982
|
-
return sql `${element} ${{ literal:
|
|
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 `
|
|
2266
|
+
return sql `true`;
|
|
2114
2267
|
if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
|
|
2115
|
-
return sql `${element}
|
|
2268
|
+
return sql `${element} ${{ literal: op }} ${{ value: _encodeValue(expr.value) }}`;
|
|
2116
2269
|
}
|
|
2117
|
-
|
|
2270
|
+
if (relation && parent.className) {
|
|
2118
2271
|
if (!_.every(expr.value, x => x instanceof TObject && x.objectId))
|
|
2119
2272
|
break;
|
|
2120
|
-
|
|
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
|
-
|
|
2123
|
-
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
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 (${
|
|
2392
|
+
SELECT * FROM (${populate}) AS ${{ identifier: tempName }}
|
|
2228
2393
|
WHERE NOT (${filter})
|
|
2229
2394
|
)`;
|
|
2230
2395
|
}
|
|
2231
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
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 (${
|
|
2429
|
+
SELECT * FROM (${populate}) AS ${{ identifier: tempName }}
|
|
2255
2430
|
WHERE ${filter}
|
|
2256
2431
|
)`;
|
|
2257
2432
|
}
|
|
2258
|
-
|
|
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
|
-
|
|
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
|
//
|