proto.io 0.0.216 → 0.0.217
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/aliyun-oss.d.ts +3 -3
- 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 +3 -3
- package/dist/adapters/file/google-cloud-storage.d.ts +3 -3
- package/dist/adapters/storage/progres.d.ts +5 -3
- package/dist/adapters/storage/progres.js +474 -111
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +474 -111
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -7
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +2 -2
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{base-BO3ZP6EF.d.ts → base-CWnOBKD5.d.ts} +2 -2
- package/dist/internals/base-CWnOBKD5.d.ts.map +1 -0
- package/dist/internals/{chunk-DDkLpKXp.d.ts → chunk-CNNSQpRF.d.ts} +3 -3
- package/dist/internals/chunk-CNNSQpRF.d.ts.map +1 -0
- package/dist/internals/{index-CYhA8SU8.d.ts → index-BRIlS3mY.d.ts} +3 -9
- package/dist/internals/index-BRIlS3mY.d.ts.map +1 -0
- package/dist/internals/{index-HdMgLYtD.d.ts → index-Bs8n7Q8f.d.ts} +55 -18
- package/dist/internals/index-Bs8n7Q8f.d.ts.map +1 -0
- package/dist/internals/{index-DF2AfSGK.mjs → index-DfqABzjr.mjs} +134 -141
- package/dist/internals/index-DfqABzjr.mjs.map +1 -0
- package/dist/internals/{index-DfnPpl1I.js → index-DylUjD_1.js} +133 -146
- package/dist/internals/index-DylUjD_1.js.map +1 -0
- package/dist/internals/{validator-LNgZGT_l.mjs → validator-CEcBF4Cn.mjs} +728 -27
- package/dist/internals/validator-CEcBF4Cn.mjs.map +1 -0
- package/dist/internals/{validator-BBjOdLiT.js → validator-mcBCJP4P.js} +734 -27
- package/dist/internals/validator-mcBCJP4P.js.map +1 -0
- package/package.json +1 -1
- package/dist/internals/base-BO3ZP6EF.d.ts.map +0 -1
- package/dist/internals/chunk-DDkLpKXp.d.ts.map +0 -1
- package/dist/internals/index-CYhA8SU8.d.ts.map +0 -1
- package/dist/internals/index-DF2AfSGK.mjs.map +0 -1
- package/dist/internals/index-DfnPpl1I.js.map +0 -1
- package/dist/internals/index-HdMgLYtD.d.ts.map +0 -1
- package/dist/internals/validator-BBjOdLiT.js.map +0 -1
- package/dist/internals/validator-LNgZGT_l.mjs.map +0 -1
|
@@ -9,7 +9,7 @@ var QueryStream = require('pg-query-stream');
|
|
|
9
9
|
var utilsJs = require('@o2ter/utils-js');
|
|
10
10
|
var Decimal = require('decimal.js');
|
|
11
11
|
var utils = require('pg/lib/utils');
|
|
12
|
-
var validator = require('../../internals/validator-
|
|
12
|
+
var validator = require('../../internals/validator-mcBCJP4P.js');
|
|
13
13
|
require('@o2ter/crypto-js');
|
|
14
14
|
var _const = require('../../internals/const-C3I6cfav.js');
|
|
15
15
|
var random$1 = require('../../internals/random-nkOQ9U6S.js');
|
|
@@ -413,7 +413,7 @@ class QueryCompiler {
|
|
|
413
413
|
return this.dialect.encodeFieldExpression(this, parent, filter.field, filter.expr);
|
|
414
414
|
}
|
|
415
415
|
if (filter instanceof validator.QueryExpressionSelector) {
|
|
416
|
-
return this.dialect.
|
|
416
|
+
return this.dialect.encodeBooleanExpression(this, parent, filter.expr);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
_selectIncludes(className, includes) {
|
|
@@ -428,7 +428,7 @@ class QueryCompiler {
|
|
|
428
428
|
_encodeSort(sort, parent) {
|
|
429
429
|
if (_.isArray(sort)) {
|
|
430
430
|
return sql `${_.map(sort, ({ expr, order }) => {
|
|
431
|
-
const _expr = this.dialect.
|
|
431
|
+
const _expr = this.dialect.encodeSortExpression(this, parent, expr);
|
|
432
432
|
if (!_expr)
|
|
433
433
|
throw Error('Invalid expression');
|
|
434
434
|
return sql `${_expr} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}`;
|
|
@@ -708,7 +708,7 @@ const decodeType = (type, value) => {
|
|
|
708
708
|
};
|
|
709
709
|
|
|
710
710
|
//
|
|
711
|
-
//
|
|
711
|
+
// types.ts
|
|
712
712
|
//
|
|
713
713
|
// The MIT License
|
|
714
714
|
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
@@ -731,7 +731,8 @@ const decodeType = (type, value) => {
|
|
|
731
731
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
732
732
|
// THE SOFTWARE.
|
|
733
733
|
//
|
|
734
|
-
const
|
|
734
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
735
|
+
const isArrayExpr = (expr) => {
|
|
735
736
|
if (expr instanceof validator.QueryArrayExpression)
|
|
736
737
|
return true;
|
|
737
738
|
if (expr instanceof validator.QueryValueExpression)
|
|
@@ -745,62 +746,428 @@ const arrayLength = (expr) => {
|
|
|
745
746
|
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
746
747
|
return 0;
|
|
747
748
|
};
|
|
748
|
-
const
|
|
749
|
+
const mapExpr = (expr, callback) => {
|
|
749
750
|
if (expr instanceof validator.QueryArrayExpression)
|
|
750
751
|
return _.map(expr.exprs, x => callback(x));
|
|
751
752
|
if (expr instanceof validator.QueryValueExpression)
|
|
752
753
|
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new validator.QueryValueExpression(x))) : [];
|
|
753
754
|
return [];
|
|
754
755
|
};
|
|
755
|
-
const
|
|
756
|
+
const zipExpr = (lhs, rhs) => {
|
|
757
|
+
const result = [];
|
|
758
|
+
for (const [l, r] of _.zip(lhs, rhs)) {
|
|
759
|
+
if (!l || !r)
|
|
760
|
+
return;
|
|
761
|
+
if (l.type === r.type)
|
|
762
|
+
result.push([l, r]);
|
|
763
|
+
else if (l.type === 'number' && r.type === 'decimal')
|
|
764
|
+
result.push([l, { type: 'decimal', sql: sql `CAST((${r.sql}) AS DECIMAL)` }]);
|
|
765
|
+
else if (l.type === 'decimal' && r.type === 'number')
|
|
766
|
+
result.push([{ type: 'decimal', sql: sql `CAST((${l.sql}) AS DECIMAL)` }, r]);
|
|
767
|
+
else
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
};
|
|
772
|
+
const typeCastExpr = (expr, type) => {
|
|
773
|
+
if (!expr)
|
|
774
|
+
return;
|
|
775
|
+
if (expr.type === type)
|
|
776
|
+
return expr;
|
|
777
|
+
if (expr.type === 'number' && type === 'decimal')
|
|
778
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DECIMAL)` };
|
|
779
|
+
if (expr.type === 'decimal' && type === 'number')
|
|
780
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DOUBLE PRECISION)` };
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
//
|
|
784
|
+
// vector.ts
|
|
785
|
+
//
|
|
786
|
+
// The MIT License
|
|
787
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
788
|
+
//
|
|
789
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
790
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
791
|
+
// in the Software without restriction, including without limitation the rights
|
|
792
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
793
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
794
|
+
// furnished to do so, subject to the following conditions:
|
|
795
|
+
//
|
|
796
|
+
// The above copyright notice and this permission notice shall be included in
|
|
797
|
+
// all copies or substantial portions of the Software.
|
|
798
|
+
//
|
|
799
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
800
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
801
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
802
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
803
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
804
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
805
|
+
// THE SOFTWARE.
|
|
806
|
+
//
|
|
807
|
+
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
808
|
+
if (exprs.length === 1) {
|
|
809
|
+
const [expr] = exprs;
|
|
810
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
811
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
812
|
+
if (!dataType || !index.isVector(dataType))
|
|
813
|
+
throw Error('Invalid expression');
|
|
814
|
+
return { sql: element, dimension: dataType.dimension };
|
|
815
|
+
}
|
|
816
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
817
|
+
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
818
|
+
throw Error('Invalid expression');
|
|
819
|
+
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const result = _.compact(_.map(exprs, x => typeCastExpr(encodeTypedQueryExpression(compiler, parent, x), 'number')?.sql));
|
|
823
|
+
if (result.length !== exprs.length)
|
|
824
|
+
throw Error('Invalid expression');
|
|
825
|
+
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
826
|
+
};
|
|
827
|
+
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
828
|
+
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
829
|
+
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
830
|
+
if (d1 !== d2)
|
|
831
|
+
throw Error('Invalid expression');
|
|
832
|
+
const operatorMap = {
|
|
833
|
+
'$distance': sql `<->`,
|
|
834
|
+
'$innerProduct': sql `<#>`,
|
|
835
|
+
'$negInnerProduct': sql `<#>`,
|
|
836
|
+
'$cosineDistance': sql `<=>`,
|
|
837
|
+
'$rectilinearDistance': sql `<+>`,
|
|
838
|
+
};
|
|
839
|
+
const _expr = sql `
|
|
840
|
+
CAST(
|
|
841
|
+
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
842
|
+
)
|
|
843
|
+
${operatorMap[expr.type]}
|
|
844
|
+
CAST(
|
|
845
|
+
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
846
|
+
)
|
|
847
|
+
`;
|
|
848
|
+
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
//
|
|
852
|
+
// index.ts
|
|
853
|
+
//
|
|
854
|
+
// The MIT License
|
|
855
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
856
|
+
//
|
|
857
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
858
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
859
|
+
// in the Software without restriction, including without limitation the rights
|
|
860
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
861
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
862
|
+
// furnished to do so, subject to the following conditions:
|
|
863
|
+
//
|
|
864
|
+
// The above copyright notice and this permission notice shall be included in
|
|
865
|
+
// all copies or substantial portions of the Software.
|
|
866
|
+
//
|
|
867
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
868
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
869
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
870
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
871
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
872
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
873
|
+
// THE SOFTWARE.
|
|
874
|
+
//
|
|
756
875
|
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
757
876
|
if (expr instanceof validator.QueryKeyExpression) {
|
|
758
877
|
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
759
878
|
const _dataType = dataType ? index._typeof(dataType) : null;
|
|
760
|
-
if (_dataType
|
|
761
|
-
return
|
|
762
|
-
|
|
763
|
-
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
764
|
-
];
|
|
765
|
-
}
|
|
766
|
-
else if (_dataType === 'decimal') {
|
|
767
|
-
return [
|
|
768
|
-
{ type: 'decimal', sql: element },
|
|
769
|
-
{ type: 'number', sql: element },
|
|
770
|
-
];
|
|
771
|
-
}
|
|
772
|
-
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
773
|
-
return [{ type: _dataType, sql: element }];
|
|
774
|
-
}
|
|
879
|
+
if (!_dataType || !_PrimitiveValue.includes(_dataType))
|
|
880
|
+
return;
|
|
881
|
+
return { type: _dataType, sql: element };
|
|
775
882
|
}
|
|
776
883
|
if (expr instanceof validator.QueryValueExpression) {
|
|
777
884
|
if (_.isBoolean(expr.value))
|
|
778
|
-
return
|
|
885
|
+
return { type: 'boolean', sql: sql `${{ value: expr.value }}` };
|
|
779
886
|
if (_.isNumber(expr.value))
|
|
780
|
-
return
|
|
781
|
-
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
782
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
783
|
-
];
|
|
887
|
+
return { type: 'number', sql: sql `${{ value: expr.value }}` };
|
|
784
888
|
if (expr.value instanceof Decimal)
|
|
785
|
-
return
|
|
786
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
787
|
-
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
788
|
-
];
|
|
889
|
+
return { type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` };
|
|
789
890
|
if (_.isString(expr.value))
|
|
790
|
-
return
|
|
891
|
+
return { type: 'string', sql: sql `${{ value: expr.value }}` };
|
|
791
892
|
if (_.isDate(expr.value))
|
|
792
|
-
return
|
|
893
|
+
return { type: 'date', sql: sql `${{ value: expr.value }}` };
|
|
793
894
|
}
|
|
794
895
|
if (expr instanceof validator.QueryCoditionalExpression ||
|
|
795
896
|
expr instanceof validator.QueryComparisonExpression ||
|
|
796
897
|
expr instanceof validator.QueryNotExpression) {
|
|
797
898
|
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
798
899
|
if (value)
|
|
799
|
-
return
|
|
900
|
+
return { type: 'boolean', sql: value };
|
|
800
901
|
}
|
|
801
902
|
if (expr instanceof validator.QueryDistanceExpression) {
|
|
802
903
|
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
803
|
-
return
|
|
904
|
+
return { type: 'number', sql: value };
|
|
905
|
+
}
|
|
906
|
+
if (expr instanceof validator.QueryZeroParamExpression) {
|
|
907
|
+
switch (expr.type) {
|
|
908
|
+
case '$rand': return { type: 'number', sql: sql `RANDOM()` };
|
|
909
|
+
case '$now': return { type: 'date', sql: sql `CAST(NOW() AS TIMESTAMP(3) WITH TIME ZONE)` };
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (expr instanceof validator.QueryUnaryExpression) {
|
|
913
|
+
const value = encodeTypedQueryExpression(compiler, parent, expr.expr);
|
|
914
|
+
if (!value)
|
|
915
|
+
return;
|
|
916
|
+
switch (expr.type) {
|
|
917
|
+
case '$abs':
|
|
918
|
+
case '$neg':
|
|
919
|
+
case '$sqrt':
|
|
920
|
+
case '$cbrt':
|
|
921
|
+
case '$ceil':
|
|
922
|
+
case '$floor':
|
|
923
|
+
case '$round':
|
|
924
|
+
case '$exp':
|
|
925
|
+
case '$ln':
|
|
926
|
+
case '$log10':
|
|
927
|
+
case '$sin':
|
|
928
|
+
case '$cos':
|
|
929
|
+
case '$tan':
|
|
930
|
+
case '$asin':
|
|
931
|
+
case '$acos':
|
|
932
|
+
case '$atan':
|
|
933
|
+
case '$asinh':
|
|
934
|
+
case '$acosh':
|
|
935
|
+
case '$atanh':
|
|
936
|
+
case '$sinh':
|
|
937
|
+
case '$cosh':
|
|
938
|
+
case '$tanh':
|
|
939
|
+
case '$degrees':
|
|
940
|
+
case '$radians':
|
|
941
|
+
case '$sign':
|
|
942
|
+
{
|
|
943
|
+
const op = {
|
|
944
|
+
'$abs': 'ABS',
|
|
945
|
+
'$neg': '-',
|
|
946
|
+
'$sqrt': 'SQRT',
|
|
947
|
+
'$cbrt': 'CBRT',
|
|
948
|
+
'$ceil': 'CEIL',
|
|
949
|
+
'$floor': 'FLOOR',
|
|
950
|
+
'$round': 'ROUND',
|
|
951
|
+
'$exp': 'EXP',
|
|
952
|
+
'$ln': 'LN',
|
|
953
|
+
'$log10': 'LOG10',
|
|
954
|
+
'$sin': 'SIN',
|
|
955
|
+
'$cos': 'COS',
|
|
956
|
+
'$tan': 'TAN',
|
|
957
|
+
'$asin': 'ASIN',
|
|
958
|
+
'$acos': 'ACOS',
|
|
959
|
+
'$atan': 'ATAN',
|
|
960
|
+
'$asinh': 'ASINH',
|
|
961
|
+
'$acosh': 'ACOSH',
|
|
962
|
+
'$atanh': 'ATANH',
|
|
963
|
+
'$sinh': 'SINH',
|
|
964
|
+
'$cosh': 'COSH',
|
|
965
|
+
'$tanh': 'TANH',
|
|
966
|
+
'$degrees': 'DEGREES',
|
|
967
|
+
'$radians': 'RADIANS',
|
|
968
|
+
'$sign': 'SIGN',
|
|
969
|
+
}[expr.type];
|
|
970
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
971
|
+
return;
|
|
972
|
+
return { type: 'number', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
973
|
+
}
|
|
974
|
+
case '$log2':
|
|
975
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
976
|
+
return;
|
|
977
|
+
return { type: value.type, sql: sql `(LOG(${value.sql}) / LOG(2))` };
|
|
978
|
+
case '$size':
|
|
979
|
+
if (!_.includes(['string', 'string[]', 'array'], value.type))
|
|
980
|
+
return;
|
|
981
|
+
return { type: 'number', sql: sql `LENGTH(${value.sql})` };
|
|
982
|
+
case '$lower':
|
|
983
|
+
case '$upper':
|
|
984
|
+
{
|
|
985
|
+
const op = {
|
|
986
|
+
'$lower': 'LOWER',
|
|
987
|
+
'$upper': 'UPPER',
|
|
988
|
+
}[expr.type];
|
|
989
|
+
if (value.type !== 'string')
|
|
990
|
+
return;
|
|
991
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (expr instanceof validator.QueryBinaryExpression) {
|
|
996
|
+
const left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
997
|
+
const right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
998
|
+
if (!left || !right)
|
|
999
|
+
return;
|
|
1000
|
+
switch (expr.type) {
|
|
1001
|
+
case '$divide':
|
|
1002
|
+
case '$subtract':
|
|
1003
|
+
{
|
|
1004
|
+
const op = {
|
|
1005
|
+
'$divide': '/',
|
|
1006
|
+
'$subtract': '-',
|
|
1007
|
+
}[expr.type];
|
|
1008
|
+
if (left.type === right.type) {
|
|
1009
|
+
return { type: left.type, sql: sql `(${left.sql}) ${{ literal: op }} (${right.sql})` };
|
|
1010
|
+
}
|
|
1011
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1012
|
+
return { type: 'decimal', sql: sql `CAST((${left.sql}) AS DECIMAL) ${{ literal: op }} (${right.sql})` };
|
|
1013
|
+
}
|
|
1014
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1015
|
+
return { type: 'decimal', sql: sql `(${left.sql}) ${{ literal: op }} CAST((${right.sql}) AS DECIMAL)` };
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
case '$log':
|
|
1020
|
+
{
|
|
1021
|
+
if (left.type === right.type) {
|
|
1022
|
+
return { type: left.type, sql: sql `(LOG(${left.sql}) / LOG(${right.sql}))` };
|
|
1023
|
+
}
|
|
1024
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1025
|
+
return { type: 'decimal', sql: sql `(LOG(${left.sql}) / LOG(CAST((${right.sql}) AS DECIMAL)))` };
|
|
1026
|
+
}
|
|
1027
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1028
|
+
return { type: 'decimal', sql: sql `(LOG(CAST((${left.sql}) AS DECIMAL) / LOG(${right.sql}))` };
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
break;
|
|
1032
|
+
case '$pow':
|
|
1033
|
+
case '$atan2':
|
|
1034
|
+
{
|
|
1035
|
+
const op = {
|
|
1036
|
+
'$pow': 'POWER',
|
|
1037
|
+
'$atan2': 'ATAN2',
|
|
1038
|
+
}[expr.type];
|
|
1039
|
+
if (left.type === right.type) {
|
|
1040
|
+
return { type: left.type, sql: sql `${{ literal: op }}((${left.sql}), (${right.sql}))` };
|
|
1041
|
+
}
|
|
1042
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1043
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}((${left.sql}), CAST((${right.sql}) AS DECIMAL))` };
|
|
1044
|
+
}
|
|
1045
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1046
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}(CAST((${left.sql}) AS DECIMAL), (${right.sql}))` };
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
break;
|
|
1050
|
+
case '$trim':
|
|
1051
|
+
case '$ltrim':
|
|
1052
|
+
case '$rtrim':
|
|
1053
|
+
{
|
|
1054
|
+
const op = {
|
|
1055
|
+
'$trim': 'TRIM',
|
|
1056
|
+
'$ltrim': 'LTRIM',
|
|
1057
|
+
'$rtrim': 'RTRIM',
|
|
1058
|
+
}[expr.type];
|
|
1059
|
+
if (left?.type !== 'string' || right?.type !== 'string')
|
|
1060
|
+
return;
|
|
1061
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1062
|
+
}
|
|
1063
|
+
case '$first':
|
|
1064
|
+
case '$last':
|
|
1065
|
+
{
|
|
1066
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1067
|
+
return;
|
|
1068
|
+
if (left?.type === 'string') {
|
|
1069
|
+
const op = {
|
|
1070
|
+
'$first': 'LEFT',
|
|
1071
|
+
'$last': 'RIGHT',
|
|
1072
|
+
}[expr.type];
|
|
1073
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
break;
|
|
1077
|
+
case '$ldrop':
|
|
1078
|
+
case '$rdrop':
|
|
1079
|
+
{
|
|
1080
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1081
|
+
return;
|
|
1082
|
+
if (left?.type === 'string') {
|
|
1083
|
+
const op = {
|
|
1084
|
+
'$ldrop': 'RIGHT',
|
|
1085
|
+
'$rdrop': 'LEFT',
|
|
1086
|
+
}[expr.type];
|
|
1087
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, -(${right.sql}))` };
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (expr instanceof validator.QueryListExpression) {
|
|
1094
|
+
const values = _.compact(_.map(expr.exprs, x => encodeTypedQueryExpression(compiler, parent, x)));
|
|
1095
|
+
if (values.length !== expr.exprs.length)
|
|
1096
|
+
return;
|
|
1097
|
+
switch (expr.type) {
|
|
1098
|
+
case '$add':
|
|
1099
|
+
case '$multiply':
|
|
1100
|
+
{
|
|
1101
|
+
const op = {
|
|
1102
|
+
'$add': '+',
|
|
1103
|
+
'$multiply': '*',
|
|
1104
|
+
}[expr.type];
|
|
1105
|
+
if (_.every(values, x => x.type === 'number')) {
|
|
1106
|
+
return { type: 'number', sql: sql `${{ literal: _.map(values, x => x.sql), separator: ` ${op} ` }}` };
|
|
1107
|
+
}
|
|
1108
|
+
if (_.every(values, x => x.type === 'number' || x.type === 'decimal')) {
|
|
1109
|
+
return { type: 'decimal', sql: sql `${{ literal: _.map(values, x => x.type === 'decimal' ? x.sql : sql `CAST((${x.sql}) AS DECIMAL)`), separator: ` ${op} ` }}` };
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
break;
|
|
1113
|
+
case '$ifNull':
|
|
1114
|
+
{
|
|
1115
|
+
const type = values[0].type;
|
|
1116
|
+
if (_.every(values, x => x.type === type)) {
|
|
1117
|
+
return { type, sql: sql `COALESCE(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
break;
|
|
1121
|
+
case '$concat':
|
|
1122
|
+
{
|
|
1123
|
+
if (_.every(values, x => x.type === 'string')) {
|
|
1124
|
+
return { type: 'string', sql: sql `CONCAT(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (expr instanceof validator.QueryTernaryExpression) {
|
|
1131
|
+
const first = encodeTypedQueryExpression(compiler, parent, expr.first);
|
|
1132
|
+
const second = encodeTypedQueryExpression(compiler, parent, expr.second);
|
|
1133
|
+
const last = encodeTypedQueryExpression(compiler, parent, expr.last);
|
|
1134
|
+
if (!first || !second || !last)
|
|
1135
|
+
return;
|
|
1136
|
+
switch (expr.type) {
|
|
1137
|
+
case '$lpad':
|
|
1138
|
+
case '$rpad':
|
|
1139
|
+
{
|
|
1140
|
+
const op = {
|
|
1141
|
+
'$lpad': 'LPAD',
|
|
1142
|
+
'$rpad': 'RPAD',
|
|
1143
|
+
}[expr.type];
|
|
1144
|
+
if (first?.type !== 'string' || last?.type !== 'string')
|
|
1145
|
+
return;
|
|
1146
|
+
if (!_.includes(['number', 'decimal'], second.type))
|
|
1147
|
+
return;
|
|
1148
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${first.sql}, ${second.sql}, ${last.sql})` };
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (expr instanceof validator.QueryCondExpression) {
|
|
1153
|
+
const branches = _.flatMap(_.map(expr.branch, x => ({
|
|
1154
|
+
case: encodeTypedQueryExpression(compiler, parent, x.case),
|
|
1155
|
+
then: encodeTypedQueryExpression(compiler, parent, x.then),
|
|
1156
|
+
})), x => x.case && x.then ? ({ case: x.case, then: x.then }) : []);
|
|
1157
|
+
if (branches.length !== expr.branch.length)
|
|
1158
|
+
return;
|
|
1159
|
+
const defaultCase = encodeTypedQueryExpression(compiler, parent, expr.default);
|
|
1160
|
+
if (!defaultCase)
|
|
1161
|
+
return;
|
|
1162
|
+
if (!_.every(branches, x => x.case.type === 'boolean' && x.then.type === defaultCase.type))
|
|
1163
|
+
return;
|
|
1164
|
+
return {
|
|
1165
|
+
type: defaultCase.type,
|
|
1166
|
+
sql: sql `CASE ${{
|
|
1167
|
+
literal: _.map(branches, x => sql `WHEN (${x.case.sql}) THEN (${x.then.sql})`),
|
|
1168
|
+
separator: ' '
|
|
1169
|
+
}} ELSE (${defaultCase.sql}) END`,
|
|
1170
|
+
};
|
|
804
1171
|
}
|
|
805
1172
|
};
|
|
806
1173
|
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
@@ -826,57 +1193,19 @@ const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
|
826
1193
|
if (expr instanceof validator.QueryArrayExpression) {
|
|
827
1194
|
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
828
1195
|
}
|
|
829
|
-
const
|
|
830
|
-
if (!
|
|
1196
|
+
const typed = encodeTypedQueryExpression(compiler, parent, expr);
|
|
1197
|
+
if (!typed)
|
|
831
1198
|
throw Error('Invalid expression');
|
|
832
|
-
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
return { sql: element, dimension: dataType.dimension };
|
|
842
|
-
}
|
|
843
|
-
if (expr instanceof validator.QueryValueExpression) {
|
|
844
|
-
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
845
|
-
throw Error('Invalid expression');
|
|
846
|
-
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
847
|
-
}
|
|
1199
|
+
switch (typed.type) {
|
|
1200
|
+
case 'boolean': return sql `to_jsonb(${typed.sql})`;
|
|
1201
|
+
case 'number': return sql `to_jsonb(${typed.sql})`;
|
|
1202
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${typed.sql} AS TEXT))`;
|
|
1203
|
+
case 'string': return sql `to_jsonb(${typed.sql})`;
|
|
1204
|
+
case 'date': return sql `jsonb_build_object(
|
|
1205
|
+
'$date', to_char(${typed.sql} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1206
|
+
)`;
|
|
1207
|
+
default: throw Error('Invalid expression');
|
|
848
1208
|
}
|
|
849
|
-
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
850
|
-
if (_.some(result, x => _.isNil(x)))
|
|
851
|
-
throw Error('Invalid expression');
|
|
852
|
-
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
853
|
-
};
|
|
854
|
-
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
855
|
-
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
856
|
-
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
857
|
-
if (d1 !== d2)
|
|
858
|
-
throw Error('Invalid expression');
|
|
859
|
-
const operatorMap = {
|
|
860
|
-
'$distance': sql `<->`,
|
|
861
|
-
'$innerProduct': sql `<#>`,
|
|
862
|
-
'$negInnerProduct': sql `<#>`,
|
|
863
|
-
'$cosineDistance': sql `<=>`,
|
|
864
|
-
'$rectilinearDistance': sql `<+>`,
|
|
865
|
-
};
|
|
866
|
-
const _expr = sql `
|
|
867
|
-
CAST(
|
|
868
|
-
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
869
|
-
)
|
|
870
|
-
${operatorMap[expr.type]}
|
|
871
|
-
CAST(
|
|
872
|
-
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
873
|
-
)
|
|
874
|
-
`;
|
|
875
|
-
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
876
|
-
};
|
|
877
|
-
const matchType = (first, second) => {
|
|
878
|
-
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
879
|
-
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
880
1209
|
};
|
|
881
1210
|
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
882
1211
|
if (expr instanceof validator.QueryCoditionalExpression) {
|
|
@@ -898,23 +1227,29 @@ const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
|
898
1227
|
'$lt': sql `<`,
|
|
899
1228
|
'$lte': sql `<=`,
|
|
900
1229
|
};
|
|
901
|
-
if (
|
|
902
|
-
|
|
1230
|
+
if (isArrayExpr(expr.left) &&
|
|
1231
|
+
isArrayExpr(expr.right) &&
|
|
903
1232
|
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
904
|
-
const _left =
|
|
905
|
-
const _right =
|
|
906
|
-
const
|
|
907
|
-
if (
|
|
908
|
-
const [l, r] = _.unzip(
|
|
1233
|
+
const _left = mapExpr(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1234
|
+
const _right = mapExpr(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1235
|
+
const zipped = zipExpr(_left, _right) ?? [];
|
|
1236
|
+
if (_left.length === zipped.length) {
|
|
1237
|
+
const [l, r] = _.unzip(zipped);
|
|
909
1238
|
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
910
1239
|
}
|
|
911
1240
|
}
|
|
912
1241
|
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
913
1242
|
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
914
1243
|
if (_left && _right) {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1244
|
+
if (_left.type === _right.type) {
|
|
1245
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1246
|
+
}
|
|
1247
|
+
if (_left.type === 'decimal' && _right.type === 'number') {
|
|
1248
|
+
return sql `CAST((${_left.sql}) AS DOUBLE PRECISION) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1249
|
+
}
|
|
1250
|
+
if (_left.type === 'number' && _right.type === 'decimal') {
|
|
1251
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} CAST((${_right.sql}) AS DOUBLE PRECISION)`;
|
|
1252
|
+
}
|
|
918
1253
|
}
|
|
919
1254
|
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
920
1255
|
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
@@ -926,12 +1261,6 @@ const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
|
926
1261
|
}
|
|
927
1262
|
throw Error('Invalid expression');
|
|
928
1263
|
};
|
|
929
|
-
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
930
|
-
if (expr instanceof validator.QueryDistanceExpression) {
|
|
931
|
-
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
932
|
-
}
|
|
933
|
-
return encodeBooleanExpression(compiler, parent, expr);
|
|
934
|
-
};
|
|
935
1264
|
|
|
936
1265
|
//
|
|
937
1266
|
// storage.ts
|
|
@@ -1098,13 +1427,18 @@ class SqlStorage {
|
|
|
1098
1427
|
const self = this;
|
|
1099
1428
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
1100
1429
|
const _matchesType = self._matchesType(compiler, query);
|
|
1101
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) =>
|
|
1102
|
-
|
|
1430
|
+
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) => {
|
|
1431
|
+
if (!opts?.weight)
|
|
1432
|
+
return { sort: sql `ORDER BY ${self.dialect.random()}` };
|
|
1433
|
+
const weight = encodeTypedQueryExpression(compiler, {
|
|
1103
1434
|
name: fetchName,
|
|
1104
1435
|
className: query.className,
|
|
1105
1436
|
groupMatches: query.groupMatches,
|
|
1106
|
-
}, opts.weight)
|
|
1107
|
-
|
|
1437
|
+
}, opts.weight);
|
|
1438
|
+
if (!weight)
|
|
1439
|
+
throw Error('Invalid expression');
|
|
1440
|
+
return { sort: sql `ORDER BY ${self.dialect.random(weight.sql)}` };
|
|
1441
|
+
});
|
|
1108
1442
|
return (async function* () {
|
|
1109
1443
|
const objects = self.query(_query);
|
|
1110
1444
|
for await (const object of objects) {
|
|
@@ -1983,7 +2317,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1983
2317
|
];
|
|
1984
2318
|
if (!_.isEmpty(groupMatches?.[field])) {
|
|
1985
2319
|
for (const [key, expr] of _.entries(groupMatches[field])) {
|
|
1986
|
-
if (expr instanceof validator.
|
|
2320
|
+
if (expr instanceof validator.QueryZeroParamAccumulator) {
|
|
1987
2321
|
switch (expr.type) {
|
|
1988
2322
|
case '$count':
|
|
1989
2323
|
columns.push(sql `
|
|
@@ -1996,11 +2330,10 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1996
2330
|
break;
|
|
1997
2331
|
}
|
|
1998
2332
|
}
|
|
1999
|
-
else if (expr instanceof validator.
|
|
2333
|
+
else if (expr instanceof validator.QueryUnaryAccumulator) {
|
|
2000
2334
|
if (!expr.expr)
|
|
2001
2335
|
throw Error('Invalid expression');
|
|
2002
|
-
const
|
|
2003
|
-
const value = _.first(exprs)?.sql;
|
|
2336
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.expr) ?? {};
|
|
2004
2337
|
if (!value)
|
|
2005
2338
|
throw Error('Invalid expression');
|
|
2006
2339
|
switch (expr.type) {
|
|
@@ -2043,8 +2376,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
2043
2376
|
}[expr.mode];
|
|
2044
2377
|
if (!expr.input)
|
|
2045
2378
|
throw Error('Invalid expression');
|
|
2046
|
-
const
|
|
2047
|
-
const value = _.first(exprs)?.sql;
|
|
2379
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.input) ?? {};
|
|
2048
2380
|
if (!value)
|
|
2049
2381
|
throw Error('Invalid expression');
|
|
2050
2382
|
columns.push(sql `
|
|
@@ -2632,6 +2964,36 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2632
2964
|
throw Error('Invalid expression');
|
|
2633
2965
|
};
|
|
2634
2966
|
|
|
2967
|
+
//
|
|
2968
|
+
// index.ts
|
|
2969
|
+
//
|
|
2970
|
+
// The MIT License
|
|
2971
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
2972
|
+
//
|
|
2973
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2974
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
2975
|
+
// in the Software without restriction, including without limitation the rights
|
|
2976
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
2977
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
2978
|
+
// furnished to do so, subject to the following conditions:
|
|
2979
|
+
//
|
|
2980
|
+
// The above copyright notice and this permission notice shall be included in
|
|
2981
|
+
// all copies or substantial portions of the Software.
|
|
2982
|
+
//
|
|
2983
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
2984
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
2985
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
2986
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
2987
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
2988
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
2989
|
+
// THE SOFTWARE.
|
|
2990
|
+
//
|
|
2991
|
+
const encodeSortExpression = (compiler, parent, expr) => {
|
|
2992
|
+
if (expr instanceof validator.QueryDistanceExpression) {
|
|
2993
|
+
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
2994
|
+
}
|
|
2995
|
+
};
|
|
2996
|
+
|
|
2635
2997
|
//
|
|
2636
2998
|
// relation.ts
|
|
2637
2999
|
//
|
|
@@ -2703,7 +3065,8 @@ const PostgresDialect = {
|
|
|
2703
3065
|
updateOperation,
|
|
2704
3066
|
selectPopulate,
|
|
2705
3067
|
encodeFieldExpression,
|
|
2706
|
-
|
|
3068
|
+
encodeBooleanExpression,
|
|
3069
|
+
encodeSortExpression,
|
|
2707
3070
|
encodePopulate,
|
|
2708
3071
|
encodeRelation,
|
|
2709
3072
|
encodeSortKey,
|