proto.io 0.0.166 → 0.0.168
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/storage/progres.d.ts +6 -1
- package/dist/adapters/storage/progres.js +305 -294
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +305 -294
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.js +1 -1
- package/dist/client.mjs +2 -2
- package/dist/index.js +3 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -12
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{index-B-pq8xkP.js → index-CyzpkgJB.js} +5 -2
- package/dist/internals/index-CyzpkgJB.js.map +1 -0
- package/dist/internals/{index-CUJMde7V.mjs → index-Dyfia5Om.mjs} +6 -3
- package/dist/internals/index-Dyfia5Om.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/internals/index-B-pq8xkP.js.map +0 -1
- package/dist/internals/index-CUJMde7V.mjs.map +0 -1
|
@@ -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,37 @@ 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
|
-
|
|
887
|
+
populate,
|
|
888
|
+
mapElem: (callback) => sql `SELECT
|
|
916
889
|
${callback(sql `${json ? sql `VALUE` : sql `UNNEST`}`)}
|
|
917
890
|
FROM ${json ? sql `jsonb_array_elements(${element})` : sql `UNNEST(${element})`}`,
|
|
918
|
-
}
|
|
891
|
+
},
|
|
919
892
|
};
|
|
920
893
|
}
|
|
921
894
|
const [colname, ...subpath] = _.toPath(field);
|
|
@@ -1874,6 +1847,231 @@ const encodeQueryExpression = (compiler, parent, expr) => {
|
|
|
1874
1847
|
return encodeBooleanExpression(compiler, parent, expr);
|
|
1875
1848
|
};
|
|
1876
1849
|
|
|
1850
|
+
//
|
|
1851
|
+
// populate.ts
|
|
1852
|
+
//
|
|
1853
|
+
// The MIT License
|
|
1854
|
+
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
1855
|
+
//
|
|
1856
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1857
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
1858
|
+
// in the Software without restriction, including without limitation the rights
|
|
1859
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1860
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
1861
|
+
// furnished to do so, subject to the following conditions:
|
|
1862
|
+
//
|
|
1863
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1864
|
+
// all copies or substantial portions of the Software.
|
|
1865
|
+
//
|
|
1866
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1867
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1868
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1869
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1870
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1871
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1872
|
+
// THE SOFTWARE.
|
|
1873
|
+
//
|
|
1874
|
+
const resolveSubpaths = (compiler, populate) => {
|
|
1875
|
+
const subpaths = [];
|
|
1876
|
+
for (const [name, type] of _.toPairs(populate.includes)) {
|
|
1877
|
+
if (isPointer(type)) {
|
|
1878
|
+
subpaths.push(..._.map(resolveSubpaths(compiler, populate.populates[name]), ({ path, type }) => ({
|
|
1879
|
+
path: `${name}.${path}`,
|
|
1880
|
+
type,
|
|
1881
|
+
})));
|
|
1882
|
+
}
|
|
1883
|
+
else {
|
|
1884
|
+
subpaths.push({
|
|
1885
|
+
path: name,
|
|
1886
|
+
type,
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
return subpaths;
|
|
1891
|
+
};
|
|
1892
|
+
const _isPointer = (schema, className, path) => {
|
|
1893
|
+
let fields = schema[className].fields;
|
|
1894
|
+
let last;
|
|
1895
|
+
for (const key of _.toPath(path)) {
|
|
1896
|
+
const dataType = fields[key];
|
|
1897
|
+
if (_.isNil(dataType))
|
|
1898
|
+
throw Error(`Invalid path: ${path}`);
|
|
1899
|
+
if (isPrimitive(dataType) || isVector(dataType))
|
|
1900
|
+
throw Error(`Invalid path: ${path}`);
|
|
1901
|
+
if (isShape(dataType)) {
|
|
1902
|
+
fields = dataType.shape;
|
|
1903
|
+
continue;
|
|
1904
|
+
}
|
|
1905
|
+
if (dataType.type !== 'pointer')
|
|
1906
|
+
return false;
|
|
1907
|
+
if (_.isNil(schema[dataType.target]))
|
|
1908
|
+
throw Error(`Invalid path: ${path}`);
|
|
1909
|
+
fields = schema[dataType.target].fields;
|
|
1910
|
+
last = dataType;
|
|
1911
|
+
}
|
|
1912
|
+
return last?.type === 'pointer';
|
|
1913
|
+
};
|
|
1914
|
+
const _selectRelationPopulate = (compiler, parent, populate, field, encode) => {
|
|
1915
|
+
const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
|
|
1916
|
+
const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
|
|
1917
|
+
const subpaths = resolveSubpaths(compiler, populate);
|
|
1918
|
+
let cond;
|
|
1919
|
+
if (_.isNil(populate.foreignField)) {
|
|
1920
|
+
cond = sql `${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ANY(${_local(field)})`;
|
|
1921
|
+
}
|
|
1922
|
+
else if (_isPointer(compiler.schema, populate.className, populate.foreignField)) {
|
|
1923
|
+
cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(populate.colname)}`;
|
|
1924
|
+
}
|
|
1925
|
+
else {
|
|
1926
|
+
cond = sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(populate.colname)})`;
|
|
1927
|
+
}
|
|
1928
|
+
return sql `
|
|
1929
|
+
SELECT ${_.compact(_.flatMap(subpaths, ({ path, type }) => [
|
|
1930
|
+
encode && _encodePopulateInclude(populate.name, path, type),
|
|
1931
|
+
!encode && sql `${{ identifier: populate.name }}.${{ identifier: path }}`,
|
|
1932
|
+
!encode && isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }}`,
|
|
1933
|
+
]))}
|
|
1934
|
+
FROM ${{ identifier: populate.name }} WHERE ${cond}
|
|
1935
|
+
${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
|
|
1936
|
+
${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
|
|
1937
|
+
${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
|
|
1938
|
+
${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
|
|
1939
|
+
`;
|
|
1940
|
+
};
|
|
1941
|
+
const selectPopulate = (compiler, parent, populate, field) => {
|
|
1942
|
+
if (populate.type === 'relation') {
|
|
1943
|
+
return {
|
|
1944
|
+
columns: [
|
|
1945
|
+
sql `
|
|
1946
|
+
ARRAY(
|
|
1947
|
+
SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
|
|
1948
|
+
${_selectRelationPopulate(compiler, parent, populate, field, true)}
|
|
1949
|
+
) ${{ identifier: populate.name }}
|
|
1950
|
+
) AS ${{ identifier: field }}
|
|
1951
|
+
`,
|
|
1952
|
+
sql `${{ identifier: parent.name }}.${{ identifier: field }} AS ${{ identifier: `$${field}` }}`
|
|
1953
|
+
],
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
|
|
1957
|
+
const _foreign = (field) => sql `${{ identifier: populate.name }}.${{ identifier: field }}`;
|
|
1958
|
+
const subpaths = resolveSubpaths(compiler, populate);
|
|
1959
|
+
return {
|
|
1960
|
+
columns: _.compact(_.flatMap(subpaths, ({ path, type }) => [
|
|
1961
|
+
sql `${{ identifier: populate.name }}.${{ identifier: path }} AS ${{ identifier: `${field}.${path}` }}`,
|
|
1962
|
+
isRelation(type) && sql `${{ identifier: populate.name }}.${{ identifier: `$${path}` }} AS ${{ identifier: `$${field}.${path}` }}`,
|
|
1963
|
+
])),
|
|
1964
|
+
join: sql `
|
|
1965
|
+
LEFT JOIN ${{ identifier: populate.name }}
|
|
1966
|
+
ON ${sql `(${{ quote: populate.className + '$' }} || ${_foreign('_id')})`} = ${_local(field)}
|
|
1967
|
+
`,
|
|
1968
|
+
};
|
|
1969
|
+
};
|
|
1970
|
+
const encodeRemix = (parent, remix) => sql `${remix?.className === parent.className ? sql `
|
|
1971
|
+
(SELECT * FROM ${{ identifier: remix.name }} UNION SELECT * FROM ${{ identifier: parent.className }})
|
|
1972
|
+
` : { identifier: parent.className }}`;
|
|
1973
|
+
const encodeForeignField = (compiler, parent, foreignField, remix) => {
|
|
1974
|
+
const { paths: [colname, ...subpath], dataType } = resolveColumn(compiler.schema, parent.className, foreignField);
|
|
1975
|
+
const tempName = `_populate_$${compiler.nextIdx()}`;
|
|
1976
|
+
const _local = (field) => sql `${{ identifier: parent.name }}.${{ identifier: field }}`;
|
|
1977
|
+
const _foreign = (field) => sql `${{ identifier: tempName }}.${{ identifier: field }}`;
|
|
1978
|
+
if (_.isEmpty(subpath) && isRelation(dataType) && dataType.foreignField) {
|
|
1979
|
+
const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, dataType.foreignField, remix);
|
|
1980
|
+
return {
|
|
1981
|
+
joins: [],
|
|
1982
|
+
field: sql `(
|
|
1983
|
+
SELECT ${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`}
|
|
1984
|
+
FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
|
|
1985
|
+
${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
|
|
1986
|
+
WHERE ${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${array || rows ? sql `ANY(${field})` : field}
|
|
1987
|
+
)`,
|
|
1988
|
+
array: false,
|
|
1989
|
+
rows: true,
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
if (_.isEmpty(subpath)) {
|
|
1993
|
+
return {
|
|
1994
|
+
joins: [],
|
|
1995
|
+
field: sql `${{ identifier: parent.name }}.${{ identifier: foreignField }}`,
|
|
1996
|
+
array: isRelation(dataType),
|
|
1997
|
+
rows: false,
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
if (!isPointer(dataType) && !isRelation(dataType))
|
|
2001
|
+
throw Error(`Invalid path: ${foreignField}`);
|
|
2002
|
+
const { joins, field, rows, array } = encodeForeignField(compiler, { className: dataType.target, name: tempName }, subpath.join('.'), remix);
|
|
2003
|
+
const cond = [];
|
|
2004
|
+
if (compiler.extraFilter) {
|
|
2005
|
+
const filter = compiler.extraFilter(dataType.target);
|
|
2006
|
+
cond.push(compiler._encodeFilter({ className: dataType.target, name: tempName }, filter));
|
|
2007
|
+
}
|
|
2008
|
+
if (isPointer(dataType)) {
|
|
2009
|
+
cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ${_local(colname)}`);
|
|
2010
|
+
return {
|
|
2011
|
+
joins: [sql `
|
|
2012
|
+
LEFT JOIN ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
|
|
2013
|
+
ON ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
|
|
2014
|
+
`, ...joins],
|
|
2015
|
+
field,
|
|
2016
|
+
array,
|
|
2017
|
+
rows,
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
if (_.isNil(dataType.foreignField)) {
|
|
2021
|
+
cond.push(sql `${sql `(${{ quote: dataType.target + '$' }} || ${_foreign('_id')})`} = ANY(${_local(colname)})`);
|
|
2022
|
+
}
|
|
2023
|
+
else if (_isPointer(compiler.schema, dataType.target, dataType.foreignField)) {
|
|
2024
|
+
cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ${_foreign(dataType.foreignField)}`);
|
|
2025
|
+
}
|
|
2026
|
+
else {
|
|
2027
|
+
cond.push(sql `${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${_foreign(dataType.foreignField)})`);
|
|
2028
|
+
}
|
|
2029
|
+
return {
|
|
2030
|
+
joins: [],
|
|
2031
|
+
field: sql `(
|
|
2032
|
+
SELECT ${array ? sql `UNNEST(${field})` : field}
|
|
2033
|
+
FROM ${encodeRemix({ className: dataType.target }, remix)} AS ${{ identifier: tempName }}
|
|
2034
|
+
${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
|
|
2035
|
+
WHERE ${{ literal: _.map(_.compact(cond), x => sql `(${x})`), separator: ' AND ' }}
|
|
2036
|
+
)`,
|
|
2037
|
+
array: false,
|
|
2038
|
+
rows: true,
|
|
2039
|
+
};
|
|
2040
|
+
};
|
|
2041
|
+
const encodePopulate = (compiler, parent, remix) => {
|
|
2042
|
+
const _filter = _.compact([
|
|
2043
|
+
parent.filter && compiler._encodeFilter(parent, parent.filter),
|
|
2044
|
+
compiler.extraFilter && compiler._encodeFilter(parent, compiler.extraFilter(parent.className)),
|
|
2045
|
+
]);
|
|
2046
|
+
const _populates = _.map(parent.populates, (populate, field) => selectPopulate(compiler, parent, populate, field));
|
|
2047
|
+
const _joins = _.compact(_.map(_populates, ({ join }) => join));
|
|
2048
|
+
const _includes = _.pickBy(parent.includes, v => isPrimitive(v));
|
|
2049
|
+
const { joins: _joins2 = [], field: _foreignField = undefined, rows = false, } = parent.foreignField ? encodeForeignField(compiler, {
|
|
2050
|
+
className: parent.className,
|
|
2051
|
+
name: parent.name,
|
|
2052
|
+
}, parent.foreignField, remix) : {};
|
|
2053
|
+
return _.reduce(parent.populates, (acc, populate) => ({
|
|
2054
|
+
...encodePopulate(compiler, populate, remix),
|
|
2055
|
+
...acc,
|
|
2056
|
+
}), {
|
|
2057
|
+
[parent.name]: sql `
|
|
2058
|
+
SELECT * FROM (
|
|
2059
|
+
SELECT
|
|
2060
|
+
${{
|
|
2061
|
+
literal: [
|
|
2062
|
+
..._.map(_.keys(_includes), colname => sql `${{ identifier: parent.name }}.${{ identifier: colname }}`),
|
|
2063
|
+
..._.flatMap(_populates, ({ columns: column }) => column),
|
|
2064
|
+
..._foreignField ? [sql `${rows ? sql `ARRAY(${_foreignField})` : _foreignField} AS ${{ identifier: parent.colname }}`] : [],
|
|
2065
|
+
], separator: ',\n'
|
|
2066
|
+
}}
|
|
2067
|
+
FROM ${encodeRemix(parent, remix)} AS ${{ identifier: parent.name }}
|
|
2068
|
+
${!_.isEmpty(_joins) || !_.isEmpty(_joins2) ? { literal: [..._joins, ..._joins2], separator: '\n' } : sql ``}
|
|
2069
|
+
) AS ${{ identifier: parent.name }}
|
|
2070
|
+
${!_.isEmpty(_filter) ? sql `WHERE ${{ literal: _.map(_.compact(_filter), x => sql `(${x})`), separator: ' AND ' }}` : sql ``}
|
|
2071
|
+
`,
|
|
2072
|
+
});
|
|
2073
|
+
};
|
|
2074
|
+
|
|
1877
2075
|
//
|
|
1878
2076
|
// selectors.ts
|
|
1879
2077
|
//
|
|
@@ -2060,12 +2258,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2060
2258
|
if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
|
|
2061
2259
|
return sql `${element} <@ ${{ value: _encodeValue(expr.value) }}`;
|
|
2062
2260
|
}
|
|
2063
|
-
|
|
2261
|
+
if (relation) {
|
|
2064
2262
|
if (!_.every(expr.value, x => x instanceof TObject && x.objectId))
|
|
2065
2263
|
break;
|
|
2066
|
-
return sql `ARRAY(${relation.
|
|
2264
|
+
return sql `ARRAY(${relation.mapElem((v) => sql `${v} ->> '_id'`)}) <@ ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
|
|
2067
2265
|
}
|
|
2068
|
-
|
|
2266
|
+
if (!dataType) {
|
|
2069
2267
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} <@ ${_encodeJsonValue(_encodeValue(expr.value))}`;
|
|
2070
2268
|
}
|
|
2071
2269
|
}
|
|
@@ -2078,12 +2276,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2078
2276
|
if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
|
|
2079
2277
|
return sql `${element} @> ${{ value: _encodeValue(expr.value) }}`;
|
|
2080
2278
|
}
|
|
2081
|
-
|
|
2279
|
+
if (relation) {
|
|
2082
2280
|
if (!_.every(expr.value, x => x instanceof TObject && x.objectId))
|
|
2083
2281
|
break;
|
|
2084
|
-
return sql `ARRAY(${relation.
|
|
2282
|
+
return sql `ARRAY(${relation.mapElem((v) => sql `${v} ->> '_id'`)}) @> ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
|
|
2085
2283
|
}
|
|
2086
|
-
|
|
2284
|
+
if (!dataType) {
|
|
2087
2285
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} @> ${_encodeJsonValue(_encodeValue(expr.value))}`;
|
|
2088
2286
|
}
|
|
2089
2287
|
}
|
|
@@ -2096,12 +2294,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2096
2294
|
if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
|
|
2097
2295
|
return sql `NOT ${element} && ${{ value: _encodeValue(expr.value) }}`;
|
|
2098
2296
|
}
|
|
2099
|
-
|
|
2297
|
+
if (relation) {
|
|
2100
2298
|
if (!_.every(expr.value, x => x instanceof TObject && x.objectId))
|
|
2101
2299
|
break;
|
|
2102
|
-
return sql `NOT ARRAY(${relation.
|
|
2300
|
+
return sql `NOT ARRAY(${relation.mapElem((v) => sql `${v} ->> '_id'`)}) && ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
|
|
2103
2301
|
}
|
|
2104
|
-
|
|
2302
|
+
if (!dataType) {
|
|
2105
2303
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND NOT ${element} && ${_encodeJsonValue(_encodeValue(expr.value))}`;
|
|
2106
2304
|
}
|
|
2107
2305
|
}
|
|
@@ -2114,12 +2312,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2114
2312
|
if (dataType === 'array' || (!_.isString(dataType) && dataType?.type === 'array')) {
|
|
2115
2313
|
return sql `${element} && ${{ value: _encodeValue(expr.value) }}`;
|
|
2116
2314
|
}
|
|
2117
|
-
|
|
2315
|
+
if (relation) {
|
|
2118
2316
|
if (!_.every(expr.value, x => x instanceof TObject && x.objectId))
|
|
2119
2317
|
break;
|
|
2120
|
-
return sql `ARRAY(${relation.
|
|
2318
|
+
return sql `ARRAY(${relation.mapElem((v) => sql `${v} ->> '_id'`)}) && ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.objectId }}`)}]`;
|
|
2121
2319
|
}
|
|
2122
|
-
|
|
2320
|
+
if (!dataType) {
|
|
2123
2321
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} && ${_encodeJsonValue(_encodeValue(expr.value))}`;
|
|
2124
2322
|
}
|
|
2125
2323
|
}
|
|
@@ -2135,7 +2333,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2135
2333
|
if (_.isString(expr.value)) {
|
|
2136
2334
|
return sql `${element} LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
|
|
2137
2335
|
}
|
|
2138
|
-
|
|
2336
|
+
if (_.isRegExp(expr.value)) {
|
|
2139
2337
|
if (expr.value.ignoreCase)
|
|
2140
2338
|
return sql `${element} ~* ${{ value: expr.value.source }}`;
|
|
2141
2339
|
return sql `${element} ~ ${{ value: expr.value.source }}`;
|
|
@@ -2145,7 +2343,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2145
2343
|
if (_.isString(expr.value)) {
|
|
2146
2344
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
|
|
2147
2345
|
}
|
|
2148
|
-
|
|
2346
|
+
if (_.isRegExp(expr.value)) {
|
|
2149
2347
|
if (expr.value.ignoreCase)
|
|
2150
2348
|
return sql `${element} ~* ${{ value: expr.value.source }}`;
|
|
2151
2349
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') ~ ${{ value: expr.value.source }}`;
|
|
@@ -2159,7 +2357,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2159
2357
|
if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
|
|
2160
2358
|
return sql `${element} LIKE ${{ value: `${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
|
|
2161
2359
|
}
|
|
2162
|
-
|
|
2360
|
+
if (!dataType) {
|
|
2163
2361
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `${expr.value.replace(/([\\_%])/g, '\\$1')}%` }}`;
|
|
2164
2362
|
}
|
|
2165
2363
|
}
|
|
@@ -2170,7 +2368,7 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2170
2368
|
if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
|
|
2171
2369
|
return sql `${element} LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}` }}`;
|
|
2172
2370
|
}
|
|
2173
|
-
|
|
2371
|
+
if (!dataType) {
|
|
2174
2372
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'string' AND (${element} #>> '{}') LIKE ${{ value: `%${expr.value.replace(/([\\_%])/g, '\\$1')}` }}`;
|
|
2175
2373
|
}
|
|
2176
2374
|
}
|
|
@@ -2181,10 +2379,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2181
2379
|
if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
|
|
2182
2380
|
return sql `COALESCE(length(${element}), 0) = ${{ value: expr.value }}`;
|
|
2183
2381
|
}
|
|
2184
|
-
|
|
2382
|
+
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
|
|
2185
2383
|
return sql `COALESCE(array_length(${element}, 1), 0) = ${{ value: expr.value }}`;
|
|
2186
2384
|
}
|
|
2187
|
-
|
|
2385
|
+
if (!dataType) {
|
|
2188
2386
|
return sql `(
|
|
2189
2387
|
CASE jsonb_typeof(${element})
|
|
2190
2388
|
WHEN 'array' THEN jsonb_array_length(${element}) = ${{ value: expr.value }}
|
|
@@ -2201,10 +2399,10 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2201
2399
|
if (dataType === 'string' || (!_.isString(dataType) && dataType?.type === 'string')) {
|
|
2202
2400
|
return sql `COALESCE(length(${element}), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
|
|
2203
2401
|
}
|
|
2204
|
-
|
|
2402
|
+
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector' || dataType?.type === 'relation'))) {
|
|
2205
2403
|
return sql `COALESCE(array_length(${element}, 1), 0) ${{ literal: expr.value ? '=' : '<>' }} 0`;
|
|
2206
2404
|
}
|
|
2207
|
-
|
|
2405
|
+
if (!dataType) {
|
|
2208
2406
|
return sql `(
|
|
2209
2407
|
CASE jsonb_typeof(${element})
|
|
2210
2408
|
WHEN 'array' THEN jsonb_array_length(${element}) ${{ literal: expr.value ? '=' : '<>' }} 0
|
|
@@ -2218,23 +2416,34 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2218
2416
|
{
|
|
2219
2417
|
if (!(expr.value instanceof QuerySelector))
|
|
2220
2418
|
break;
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2419
|
+
if (relation?.populate && parent.className) {
|
|
2420
|
+
const tempName = `_populate_expr_$${compiler.nextIdx()}`;
|
|
2421
|
+
const filter = compiler._encodeFilter({
|
|
2422
|
+
name: tempName,
|
|
2423
|
+
className: relation.target,
|
|
2424
|
+
populates: relation.populate.populates,
|
|
2425
|
+
}, expr.value);
|
|
2426
|
+
if (!filter)
|
|
2427
|
+
break;
|
|
2226
2428
|
return sql `NOT EXISTS(
|
|
2227
|
-
SELECT * FROM (${
|
|
2429
|
+
SELECT * FROM (${_selectRelationPopulate(compiler, {
|
|
2430
|
+
className: parent.className,
|
|
2431
|
+
name: parent.name,
|
|
2432
|
+
}, relation.populate, `$${field}`, false)}) AS ${{ identifier: tempName }}
|
|
2228
2433
|
WHERE NOT (${filter})
|
|
2229
2434
|
)`;
|
|
2230
2435
|
}
|
|
2231
|
-
|
|
2436
|
+
const tempName = `_doller_expr_$${compiler.nextIdx()}`;
|
|
2437
|
+
const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
|
|
2438
|
+
if (!filter)
|
|
2439
|
+
break;
|
|
2440
|
+
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
|
|
2232
2441
|
return sql `NOT EXISTS(
|
|
2233
2442
|
SELECT * FROM (SELECT UNNEST AS "$" FROM UNNEST(${element})) AS ${{ identifier: tempName }}
|
|
2234
2443
|
WHERE NOT (${filter})
|
|
2235
2444
|
)`;
|
|
2236
2445
|
}
|
|
2237
|
-
|
|
2446
|
+
if (!dataType) {
|
|
2238
2447
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND NOT EXISTS(
|
|
2239
2448
|
SELECT * FROM (SELECT value AS "$" FROM jsonb_array_elements(${element})) AS ${{ identifier: tempName }}
|
|
2240
2449
|
WHERE NOT (${filter})
|
|
@@ -2245,23 +2454,34 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2245
2454
|
{
|
|
2246
2455
|
if (!(expr.value instanceof QuerySelector))
|
|
2247
2456
|
break;
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2457
|
+
if (relation?.populate && parent.className) {
|
|
2458
|
+
const tempName = `_populate_expr_$${compiler.nextIdx()}`;
|
|
2459
|
+
const filter = compiler._encodeFilter({
|
|
2460
|
+
name: tempName,
|
|
2461
|
+
className: relation.target,
|
|
2462
|
+
populates: relation.populate.populates,
|
|
2463
|
+
}, expr.value);
|
|
2464
|
+
if (!filter)
|
|
2465
|
+
break;
|
|
2253
2466
|
return sql `EXISTS(
|
|
2254
|
-
SELECT * FROM (${
|
|
2467
|
+
SELECT * FROM (${_selectRelationPopulate(compiler, {
|
|
2468
|
+
className: parent.className,
|
|
2469
|
+
name: parent.name,
|
|
2470
|
+
}, relation.populate, `$${field}`, false)}) AS ${{ identifier: tempName }}
|
|
2255
2471
|
WHERE ${filter}
|
|
2256
2472
|
)`;
|
|
2257
2473
|
}
|
|
2258
|
-
|
|
2474
|
+
const tempName = `_doller_expr_$${compiler.nextIdx()}`;
|
|
2475
|
+
const filter = compiler._encodeFilter({ name: tempName, className: relation?.target }, expr.value);
|
|
2476
|
+
if (!filter)
|
|
2477
|
+
break;
|
|
2478
|
+
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'vector'))) {
|
|
2259
2479
|
return sql `EXISTS(
|
|
2260
2480
|
SELECT * FROM (SELECT UNNEST AS "$" FROM UNNEST(${element})) AS ${{ identifier: tempName }}
|
|
2261
2481
|
WHERE ${filter}
|
|
2262
2482
|
)`;
|
|
2263
2483
|
}
|
|
2264
|
-
|
|
2484
|
+
if (!dataType) {
|
|
2265
2485
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND EXISTS(
|
|
2266
2486
|
SELECT * FROM (SELECT value AS "$" FROM jsonb_array_elements(${element})) AS ${{ identifier: tempName }}
|
|
2267
2487
|
WHERE ${filter}
|
|
@@ -2272,215 +2492,6 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2272
2492
|
throw Error('Invalid expression');
|
|
2273
2493
|
};
|
|
2274
2494
|
|
|
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
2495
|
//
|
|
2485
2496
|
// relation.ts
|
|
2486
2497
|
//
|