proto.io 0.0.216 → 0.0.218
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 +2 -2
- package/dist/adapters/file/database.mjs +2 -2
- 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 +511 -148
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +511 -148
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +2 -8
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +3 -3
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +28 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -29
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{base-BO3ZP6EF.d.ts → base-CqeIQTE9.d.ts} +2 -2
- package/dist/internals/base-CqeIQTE9.d.ts.map +1 -0
- package/dist/internals/{chunk-DDkLpKXp.d.ts → chunk-CLKTwfRe.d.ts} +3 -3
- package/dist/internals/chunk-CLKTwfRe.d.ts.map +1 -0
- package/dist/internals/{index-B8TESzd9.js → index-CLKTEIj0.js} +2 -2
- package/dist/internals/index-CLKTEIj0.js.map +1 -0
- package/dist/internals/{index-DF2AfSGK.mjs → index-CZ5fKgiJ.mjs} +159 -166
- package/dist/internals/index-CZ5fKgiJ.mjs.map +1 -0
- package/dist/internals/{index-CYhA8SU8.d.ts → index-Ci8d33k-.d.ts} +3 -9
- package/dist/internals/index-Ci8d33k-.d.ts.map +1 -0
- package/dist/internals/{index-HdMgLYtD.d.ts → index-DQHWdslW.d.ts} +59 -22
- package/dist/internals/index-DQHWdslW.d.ts.map +1 -0
- package/dist/internals/{index-BzDsTt4R.mjs → index-gWcE22mf.mjs} +2 -2
- package/dist/internals/index-gWcE22mf.mjs.map +1 -0
- package/dist/internals/{index-DfnPpl1I.js → index-xHeu-AjT.js} +158 -171
- package/dist/internals/index-xHeu-AjT.js.map +1 -0
- package/dist/internals/{validator-BBjOdLiT.js → validator-B5yHpyvb.js} +743 -36
- package/dist/internals/validator-B5yHpyvb.js.map +1 -0
- package/dist/internals/{validator-LNgZGT_l.mjs → validator-DX2nXeQo.mjs} +736 -35
- package/dist/internals/validator-DX2nXeQo.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/internals/base-BO3ZP6EF.d.ts.map +0 -1
- package/dist/internals/chunk-DDkLpKXp.d.ts.map +0 -1
- package/dist/internals/index-B8TESzd9.js.map +0 -1
- package/dist/internals/index-BzDsTt4R.mjs.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
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import { a as isRelation, i as isPointer, b as isShape, e as decodeUpdateOp, s as shapePaths, c as isPrimitive, T as TObject, _ as _encodeValue, f as dimensionOf, g as _decodeValue, h as _typeof, j as isVector, k as _isTypeof } from '../../internals/index-
|
|
2
|
+
import { a as isRelation, i as isPointer, b as isShape, e as decodeUpdateOp, s as shapePaths, c as isPrimitive, T as TObject, _ as _encodeValue, f as dimensionOf, g as _decodeValue, h as _typeof, j as isVector, k as _isTypeof } from '../../internals/index-gWcE22mf.mjs';
|
|
3
3
|
import { Pool, types } from 'pg';
|
|
4
4
|
import QueryStream from 'pg-query-stream';
|
|
5
5
|
import { asyncStream, IteratorPool } from '@o2ter/utils-js';
|
|
6
6
|
import Decimal from 'decimal.js';
|
|
7
7
|
import { escapeLiteral, escapeIdentifier } from 'pg/lib/utils';
|
|
8
|
-
import { r as resolveColumn, b as resolveDataType$1, d as QueryCoditionalSelector, e as QueryFieldSelector, f as QueryExpressionSelector, g as
|
|
8
|
+
import { r as resolveColumn, b as resolveDataType$1, d as QueryCoditionalSelector, e as QueryFieldSelector, f as QueryExpressionSelector, g as QueryArrayExpression, h as QueryValueExpression, i as QueryKeyExpression, j as QueryCoditionalExpression, k as QueryComparisonExpression, l as QueryNotExpression, m as QueryDistanceExpression, n as QueryZeroParamExpression, o as QueryUnaryExpression, p as QueryBinaryExpression, q as QueryListExpression, s as QueryTernaryExpression, t as QueryCondExpression, Q as QueryValidator, u as QueryZeroParamAccumulator, v as QueryUnaryAccumulator, w as QueryPercentileAccumulator, c as QuerySelector, F as FieldSelectorExpression } from '../../internals/validator-DX2nXeQo.mjs';
|
|
9
9
|
import '@o2ter/crypto-js';
|
|
10
10
|
import { c as PROTO_EVENT } from '../../internals/const-Dkp7Nsv5.mjs';
|
|
11
11
|
import { g as generateId } from '../../internals/random-CYjWDvex.mjs';
|
|
@@ -185,12 +185,12 @@ const _encodeSorting = (includes, populates, sort) => {
|
|
|
185
185
|
return sorting;
|
|
186
186
|
};
|
|
187
187
|
const _defaultInsertOpts = (options) => {
|
|
188
|
-
const
|
|
188
|
+
const id = generateId(options.objectIdSize);
|
|
189
189
|
return {
|
|
190
|
-
_id: sql `${{ value:
|
|
190
|
+
_id: sql `${{ value: id }}`,
|
|
191
191
|
...options.className === 'User' ? {
|
|
192
|
-
_rperm: sql `${{ value: [
|
|
193
|
-
_wperm: sql `${{ value: [
|
|
192
|
+
_rperm: sql `${{ value: [id] }}`,
|
|
193
|
+
_wperm: sql `${{ value: [id] }}`,
|
|
194
194
|
} : {},
|
|
195
195
|
};
|
|
196
196
|
};
|
|
@@ -409,7 +409,7 @@ class QueryCompiler {
|
|
|
409
409
|
return this.dialect.encodeFieldExpression(this, parent, filter.field, filter.expr);
|
|
410
410
|
}
|
|
411
411
|
if (filter instanceof QueryExpressionSelector) {
|
|
412
|
-
return this.dialect.
|
|
412
|
+
return this.dialect.encodeBooleanExpression(this, parent, filter.expr);
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
_selectIncludes(className, includes) {
|
|
@@ -424,7 +424,7 @@ class QueryCompiler {
|
|
|
424
424
|
_encodeSort(sort, parent) {
|
|
425
425
|
if (_.isArray(sort)) {
|
|
426
426
|
return sql `${_.map(sort, ({ expr, order }) => {
|
|
427
|
-
const _expr = this.dialect.
|
|
427
|
+
const _expr = this.dialect.encodeSortExpression(this, parent, expr);
|
|
428
428
|
if (!_expr)
|
|
429
429
|
throw Error('Invalid expression');
|
|
430
430
|
return sql `${_expr} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}`;
|
|
@@ -628,12 +628,12 @@ const encodeType = (colname, dataType, value) => {
|
|
|
628
628
|
break;
|
|
629
629
|
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(_encodeValue(x)))}]::JSONB[]`;
|
|
630
630
|
case 'pointer':
|
|
631
|
-
if (value instanceof TObject && value.
|
|
632
|
-
return sql `${{ value: `${value.className}$${value.
|
|
631
|
+
if (value instanceof TObject && value.id)
|
|
632
|
+
return sql `${{ value: `${value.className}$${value.id}` }}`;
|
|
633
633
|
break;
|
|
634
634
|
case 'relation':
|
|
635
|
-
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.
|
|
636
|
-
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.
|
|
635
|
+
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.id)) {
|
|
636
|
+
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.id}`)) }}`;
|
|
637
637
|
}
|
|
638
638
|
break;
|
|
639
639
|
}
|
|
@@ -704,7 +704,7 @@ const decodeType = (type, value) => {
|
|
|
704
704
|
};
|
|
705
705
|
|
|
706
706
|
//
|
|
707
|
-
//
|
|
707
|
+
// types.ts
|
|
708
708
|
//
|
|
709
709
|
// The MIT License
|
|
710
710
|
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
@@ -727,7 +727,8 @@ const decodeType = (type, value) => {
|
|
|
727
727
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
728
728
|
// THE SOFTWARE.
|
|
729
729
|
//
|
|
730
|
-
const
|
|
730
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
731
|
+
const isArrayExpr = (expr) => {
|
|
731
732
|
if (expr instanceof QueryArrayExpression)
|
|
732
733
|
return true;
|
|
733
734
|
if (expr instanceof QueryValueExpression)
|
|
@@ -741,62 +742,428 @@ const arrayLength = (expr) => {
|
|
|
741
742
|
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
742
743
|
return 0;
|
|
743
744
|
};
|
|
744
|
-
const
|
|
745
|
+
const mapExpr = (expr, callback) => {
|
|
745
746
|
if (expr instanceof QueryArrayExpression)
|
|
746
747
|
return _.map(expr.exprs, x => callback(x));
|
|
747
748
|
if (expr instanceof QueryValueExpression)
|
|
748
749
|
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new QueryValueExpression(x))) : [];
|
|
749
750
|
return [];
|
|
750
751
|
};
|
|
751
|
-
const
|
|
752
|
+
const zipExpr = (lhs, rhs) => {
|
|
753
|
+
const result = [];
|
|
754
|
+
for (const [l, r] of _.zip(lhs, rhs)) {
|
|
755
|
+
if (!l || !r)
|
|
756
|
+
return;
|
|
757
|
+
if (l.type === r.type)
|
|
758
|
+
result.push([l, r]);
|
|
759
|
+
else if (l.type === 'number' && r.type === 'decimal')
|
|
760
|
+
result.push([l, { type: 'decimal', sql: sql `CAST((${r.sql}) AS DECIMAL)` }]);
|
|
761
|
+
else if (l.type === 'decimal' && r.type === 'number')
|
|
762
|
+
result.push([{ type: 'decimal', sql: sql `CAST((${l.sql}) AS DECIMAL)` }, r]);
|
|
763
|
+
else
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
return result;
|
|
767
|
+
};
|
|
768
|
+
const typeCastExpr = (expr, type) => {
|
|
769
|
+
if (!expr)
|
|
770
|
+
return;
|
|
771
|
+
if (expr.type === type)
|
|
772
|
+
return expr;
|
|
773
|
+
if (expr.type === 'number' && type === 'decimal')
|
|
774
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DECIMAL)` };
|
|
775
|
+
if (expr.type === 'decimal' && type === 'number')
|
|
776
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DOUBLE PRECISION)` };
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
//
|
|
780
|
+
// vector.ts
|
|
781
|
+
//
|
|
782
|
+
// The MIT License
|
|
783
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
784
|
+
//
|
|
785
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
786
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
787
|
+
// in the Software without restriction, including without limitation the rights
|
|
788
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
789
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
790
|
+
// furnished to do so, subject to the following conditions:
|
|
791
|
+
//
|
|
792
|
+
// The above copyright notice and this permission notice shall be included in
|
|
793
|
+
// all copies or substantial portions of the Software.
|
|
794
|
+
//
|
|
795
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
796
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
797
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
798
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
799
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
800
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
801
|
+
// THE SOFTWARE.
|
|
802
|
+
//
|
|
803
|
+
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
804
|
+
if (exprs.length === 1) {
|
|
805
|
+
const [expr] = exprs;
|
|
806
|
+
if (expr instanceof QueryKeyExpression) {
|
|
807
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
808
|
+
if (!dataType || !isVector(dataType))
|
|
809
|
+
throw Error('Invalid expression');
|
|
810
|
+
return { sql: element, dimension: dataType.dimension };
|
|
811
|
+
}
|
|
812
|
+
if (expr instanceof QueryValueExpression) {
|
|
813
|
+
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
814
|
+
throw Error('Invalid expression');
|
|
815
|
+
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
const result = _.compact(_.map(exprs, x => typeCastExpr(encodeTypedQueryExpression(compiler, parent, x), 'number')?.sql));
|
|
819
|
+
if (result.length !== exprs.length)
|
|
820
|
+
throw Error('Invalid expression');
|
|
821
|
+
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
822
|
+
};
|
|
823
|
+
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
824
|
+
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
825
|
+
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
826
|
+
if (d1 !== d2)
|
|
827
|
+
throw Error('Invalid expression');
|
|
828
|
+
const operatorMap = {
|
|
829
|
+
'$distance': sql `<->`,
|
|
830
|
+
'$innerProduct': sql `<#>`,
|
|
831
|
+
'$negInnerProduct': sql `<#>`,
|
|
832
|
+
'$cosineDistance': sql `<=>`,
|
|
833
|
+
'$rectilinearDistance': sql `<+>`,
|
|
834
|
+
};
|
|
835
|
+
const _expr = sql `
|
|
836
|
+
CAST(
|
|
837
|
+
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
838
|
+
)
|
|
839
|
+
${operatorMap[expr.type]}
|
|
840
|
+
CAST(
|
|
841
|
+
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
842
|
+
)
|
|
843
|
+
`;
|
|
844
|
+
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
//
|
|
848
|
+
// index.ts
|
|
849
|
+
//
|
|
850
|
+
// The MIT License
|
|
851
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
852
|
+
//
|
|
853
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
854
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
855
|
+
// in the Software without restriction, including without limitation the rights
|
|
856
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
857
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
858
|
+
// furnished to do so, subject to the following conditions:
|
|
859
|
+
//
|
|
860
|
+
// The above copyright notice and this permission notice shall be included in
|
|
861
|
+
// all copies or substantial portions of the Software.
|
|
862
|
+
//
|
|
863
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
864
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
865
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
866
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
867
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
868
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
869
|
+
// THE SOFTWARE.
|
|
870
|
+
//
|
|
752
871
|
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
753
872
|
if (expr instanceof QueryKeyExpression) {
|
|
754
873
|
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
755
874
|
const _dataType = dataType ? _typeof(dataType) : null;
|
|
756
|
-
if (_dataType
|
|
757
|
-
return
|
|
758
|
-
|
|
759
|
-
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
760
|
-
];
|
|
761
|
-
}
|
|
762
|
-
else if (_dataType === 'decimal') {
|
|
763
|
-
return [
|
|
764
|
-
{ type: 'decimal', sql: element },
|
|
765
|
-
{ type: 'number', sql: element },
|
|
766
|
-
];
|
|
767
|
-
}
|
|
768
|
-
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
769
|
-
return [{ type: _dataType, sql: element }];
|
|
770
|
-
}
|
|
875
|
+
if (!_dataType || !_PrimitiveValue.includes(_dataType))
|
|
876
|
+
return;
|
|
877
|
+
return { type: _dataType, sql: element };
|
|
771
878
|
}
|
|
772
879
|
if (expr instanceof QueryValueExpression) {
|
|
773
880
|
if (_.isBoolean(expr.value))
|
|
774
|
-
return
|
|
881
|
+
return { type: 'boolean', sql: sql `${{ value: expr.value }}` };
|
|
775
882
|
if (_.isNumber(expr.value))
|
|
776
|
-
return
|
|
777
|
-
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
778
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
779
|
-
];
|
|
883
|
+
return { type: 'number', sql: sql `${{ value: expr.value }}` };
|
|
780
884
|
if (expr.value instanceof Decimal)
|
|
781
|
-
return
|
|
782
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
783
|
-
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
784
|
-
];
|
|
885
|
+
return { type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` };
|
|
785
886
|
if (_.isString(expr.value))
|
|
786
|
-
return
|
|
887
|
+
return { type: 'string', sql: sql `${{ value: expr.value }}` };
|
|
787
888
|
if (_.isDate(expr.value))
|
|
788
|
-
return
|
|
889
|
+
return { type: 'date', sql: sql `${{ value: expr.value }}` };
|
|
789
890
|
}
|
|
790
891
|
if (expr instanceof QueryCoditionalExpression ||
|
|
791
892
|
expr instanceof QueryComparisonExpression ||
|
|
792
893
|
expr instanceof QueryNotExpression) {
|
|
793
894
|
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
794
895
|
if (value)
|
|
795
|
-
return
|
|
896
|
+
return { type: 'boolean', sql: value };
|
|
796
897
|
}
|
|
797
898
|
if (expr instanceof QueryDistanceExpression) {
|
|
798
899
|
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
799
|
-
return
|
|
900
|
+
return { type: 'number', sql: value };
|
|
901
|
+
}
|
|
902
|
+
if (expr instanceof QueryZeroParamExpression) {
|
|
903
|
+
switch (expr.type) {
|
|
904
|
+
case '$rand': return { type: 'number', sql: sql `RANDOM()` };
|
|
905
|
+
case '$now': return { type: 'date', sql: sql `CAST(NOW() AS TIMESTAMP(3) WITH TIME ZONE)` };
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (expr instanceof QueryUnaryExpression) {
|
|
909
|
+
const value = encodeTypedQueryExpression(compiler, parent, expr.expr);
|
|
910
|
+
if (!value)
|
|
911
|
+
return;
|
|
912
|
+
switch (expr.type) {
|
|
913
|
+
case '$abs':
|
|
914
|
+
case '$neg':
|
|
915
|
+
case '$sqrt':
|
|
916
|
+
case '$cbrt':
|
|
917
|
+
case '$ceil':
|
|
918
|
+
case '$floor':
|
|
919
|
+
case '$round':
|
|
920
|
+
case '$exp':
|
|
921
|
+
case '$ln':
|
|
922
|
+
case '$log10':
|
|
923
|
+
case '$sin':
|
|
924
|
+
case '$cos':
|
|
925
|
+
case '$tan':
|
|
926
|
+
case '$asin':
|
|
927
|
+
case '$acos':
|
|
928
|
+
case '$atan':
|
|
929
|
+
case '$asinh':
|
|
930
|
+
case '$acosh':
|
|
931
|
+
case '$atanh':
|
|
932
|
+
case '$sinh':
|
|
933
|
+
case '$cosh':
|
|
934
|
+
case '$tanh':
|
|
935
|
+
case '$degrees':
|
|
936
|
+
case '$radians':
|
|
937
|
+
case '$sign':
|
|
938
|
+
{
|
|
939
|
+
const op = {
|
|
940
|
+
'$abs': 'ABS',
|
|
941
|
+
'$neg': '-',
|
|
942
|
+
'$sqrt': 'SQRT',
|
|
943
|
+
'$cbrt': 'CBRT',
|
|
944
|
+
'$ceil': 'CEIL',
|
|
945
|
+
'$floor': 'FLOOR',
|
|
946
|
+
'$round': 'ROUND',
|
|
947
|
+
'$exp': 'EXP',
|
|
948
|
+
'$ln': 'LN',
|
|
949
|
+
'$log10': 'LOG10',
|
|
950
|
+
'$sin': 'SIN',
|
|
951
|
+
'$cos': 'COS',
|
|
952
|
+
'$tan': 'TAN',
|
|
953
|
+
'$asin': 'ASIN',
|
|
954
|
+
'$acos': 'ACOS',
|
|
955
|
+
'$atan': 'ATAN',
|
|
956
|
+
'$asinh': 'ASINH',
|
|
957
|
+
'$acosh': 'ACOSH',
|
|
958
|
+
'$atanh': 'ATANH',
|
|
959
|
+
'$sinh': 'SINH',
|
|
960
|
+
'$cosh': 'COSH',
|
|
961
|
+
'$tanh': 'TANH',
|
|
962
|
+
'$degrees': 'DEGREES',
|
|
963
|
+
'$radians': 'RADIANS',
|
|
964
|
+
'$sign': 'SIGN',
|
|
965
|
+
}[expr.type];
|
|
966
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
967
|
+
return;
|
|
968
|
+
return { type: 'number', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
969
|
+
}
|
|
970
|
+
case '$log2':
|
|
971
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
972
|
+
return;
|
|
973
|
+
return { type: value.type, sql: sql `(LOG(${value.sql}) / LOG(2))` };
|
|
974
|
+
case '$size':
|
|
975
|
+
if (!_.includes(['string', 'string[]', 'array'], value.type))
|
|
976
|
+
return;
|
|
977
|
+
return { type: 'number', sql: sql `LENGTH(${value.sql})` };
|
|
978
|
+
case '$lower':
|
|
979
|
+
case '$upper':
|
|
980
|
+
{
|
|
981
|
+
const op = {
|
|
982
|
+
'$lower': 'LOWER',
|
|
983
|
+
'$upper': 'UPPER',
|
|
984
|
+
}[expr.type];
|
|
985
|
+
if (value.type !== 'string')
|
|
986
|
+
return;
|
|
987
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (expr instanceof QueryBinaryExpression) {
|
|
992
|
+
const left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
993
|
+
const right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
994
|
+
if (!left || !right)
|
|
995
|
+
return;
|
|
996
|
+
switch (expr.type) {
|
|
997
|
+
case '$divide':
|
|
998
|
+
case '$subtract':
|
|
999
|
+
{
|
|
1000
|
+
const op = {
|
|
1001
|
+
'$divide': '/',
|
|
1002
|
+
'$subtract': '-',
|
|
1003
|
+
}[expr.type];
|
|
1004
|
+
if (left.type === right.type) {
|
|
1005
|
+
return { type: left.type, sql: sql `(${left.sql}) ${{ literal: op }} (${right.sql})` };
|
|
1006
|
+
}
|
|
1007
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1008
|
+
return { type: 'decimal', sql: sql `CAST((${left.sql}) AS DECIMAL) ${{ literal: op }} (${right.sql})` };
|
|
1009
|
+
}
|
|
1010
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1011
|
+
return { type: 'decimal', sql: sql `(${left.sql}) ${{ literal: op }} CAST((${right.sql}) AS DECIMAL)` };
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
break;
|
|
1015
|
+
case '$log':
|
|
1016
|
+
{
|
|
1017
|
+
if (left.type === right.type) {
|
|
1018
|
+
return { type: left.type, sql: sql `(LOG(${left.sql}) / LOG(${right.sql}))` };
|
|
1019
|
+
}
|
|
1020
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1021
|
+
return { type: 'decimal', sql: sql `(LOG(${left.sql}) / LOG(CAST((${right.sql}) AS DECIMAL)))` };
|
|
1022
|
+
}
|
|
1023
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1024
|
+
return { type: 'decimal', sql: sql `(LOG(CAST((${left.sql}) AS DECIMAL) / LOG(${right.sql}))` };
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
break;
|
|
1028
|
+
case '$pow':
|
|
1029
|
+
case '$atan2':
|
|
1030
|
+
{
|
|
1031
|
+
const op = {
|
|
1032
|
+
'$pow': 'POWER',
|
|
1033
|
+
'$atan2': 'ATAN2',
|
|
1034
|
+
}[expr.type];
|
|
1035
|
+
if (left.type === right.type) {
|
|
1036
|
+
return { type: left.type, sql: sql `${{ literal: op }}((${left.sql}), (${right.sql}))` };
|
|
1037
|
+
}
|
|
1038
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1039
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}((${left.sql}), CAST((${right.sql}) AS DECIMAL))` };
|
|
1040
|
+
}
|
|
1041
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1042
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}(CAST((${left.sql}) AS DECIMAL), (${right.sql}))` };
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
break;
|
|
1046
|
+
case '$trim':
|
|
1047
|
+
case '$ltrim':
|
|
1048
|
+
case '$rtrim':
|
|
1049
|
+
{
|
|
1050
|
+
const op = {
|
|
1051
|
+
'$trim': 'TRIM',
|
|
1052
|
+
'$ltrim': 'LTRIM',
|
|
1053
|
+
'$rtrim': 'RTRIM',
|
|
1054
|
+
}[expr.type];
|
|
1055
|
+
if (left?.type !== 'string' || right?.type !== 'string')
|
|
1056
|
+
return;
|
|
1057
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1058
|
+
}
|
|
1059
|
+
case '$first':
|
|
1060
|
+
case '$last':
|
|
1061
|
+
{
|
|
1062
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1063
|
+
return;
|
|
1064
|
+
if (left?.type === 'string') {
|
|
1065
|
+
const op = {
|
|
1066
|
+
'$first': 'LEFT',
|
|
1067
|
+
'$last': 'RIGHT',
|
|
1068
|
+
}[expr.type];
|
|
1069
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
break;
|
|
1073
|
+
case '$ldrop':
|
|
1074
|
+
case '$rdrop':
|
|
1075
|
+
{
|
|
1076
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1077
|
+
return;
|
|
1078
|
+
if (left?.type === 'string') {
|
|
1079
|
+
const op = {
|
|
1080
|
+
'$ldrop': 'RIGHT',
|
|
1081
|
+
'$rdrop': 'LEFT',
|
|
1082
|
+
}[expr.type];
|
|
1083
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, -(${right.sql}))` };
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
if (expr instanceof QueryListExpression) {
|
|
1090
|
+
const values = _.compact(_.map(expr.exprs, x => encodeTypedQueryExpression(compiler, parent, x)));
|
|
1091
|
+
if (values.length !== expr.exprs.length)
|
|
1092
|
+
return;
|
|
1093
|
+
switch (expr.type) {
|
|
1094
|
+
case '$add':
|
|
1095
|
+
case '$multiply':
|
|
1096
|
+
{
|
|
1097
|
+
const op = {
|
|
1098
|
+
'$add': '+',
|
|
1099
|
+
'$multiply': '*',
|
|
1100
|
+
}[expr.type];
|
|
1101
|
+
if (_.every(values, x => x.type === 'number')) {
|
|
1102
|
+
return { type: 'number', sql: sql `${{ literal: _.map(values, x => x.sql), separator: ` ${op} ` }}` };
|
|
1103
|
+
}
|
|
1104
|
+
if (_.every(values, x => x.type === 'number' || x.type === 'decimal')) {
|
|
1105
|
+
return { type: 'decimal', sql: sql `${{ literal: _.map(values, x => x.type === 'decimal' ? x.sql : sql `CAST((${x.sql}) AS DECIMAL)`), separator: ` ${op} ` }}` };
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
break;
|
|
1109
|
+
case '$ifNull':
|
|
1110
|
+
{
|
|
1111
|
+
const type = values[0].type;
|
|
1112
|
+
if (_.every(values, x => x.type === type)) {
|
|
1113
|
+
return { type, sql: sql `COALESCE(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
break;
|
|
1117
|
+
case '$concat':
|
|
1118
|
+
{
|
|
1119
|
+
if (_.every(values, x => x.type === 'string')) {
|
|
1120
|
+
return { type: 'string', sql: sql `CONCAT(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (expr instanceof QueryTernaryExpression) {
|
|
1127
|
+
const first = encodeTypedQueryExpression(compiler, parent, expr.first);
|
|
1128
|
+
const second = encodeTypedQueryExpression(compiler, parent, expr.second);
|
|
1129
|
+
const last = encodeTypedQueryExpression(compiler, parent, expr.last);
|
|
1130
|
+
if (!first || !second || !last)
|
|
1131
|
+
return;
|
|
1132
|
+
switch (expr.type) {
|
|
1133
|
+
case '$lpad':
|
|
1134
|
+
case '$rpad':
|
|
1135
|
+
{
|
|
1136
|
+
const op = {
|
|
1137
|
+
'$lpad': 'LPAD',
|
|
1138
|
+
'$rpad': 'RPAD',
|
|
1139
|
+
}[expr.type];
|
|
1140
|
+
if (first?.type !== 'string' || last?.type !== 'string')
|
|
1141
|
+
return;
|
|
1142
|
+
if (!_.includes(['number', 'decimal'], second.type))
|
|
1143
|
+
return;
|
|
1144
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${first.sql}, ${second.sql}, ${last.sql})` };
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (expr instanceof QueryCondExpression) {
|
|
1149
|
+
const branches = _.flatMap(_.map(expr.branch, x => ({
|
|
1150
|
+
case: encodeTypedQueryExpression(compiler, parent, x.case),
|
|
1151
|
+
then: encodeTypedQueryExpression(compiler, parent, x.then),
|
|
1152
|
+
})), x => x.case && x.then ? ({ case: x.case, then: x.then }) : []);
|
|
1153
|
+
if (branches.length !== expr.branch.length)
|
|
1154
|
+
return;
|
|
1155
|
+
const defaultCase = encodeTypedQueryExpression(compiler, parent, expr.default);
|
|
1156
|
+
if (!defaultCase)
|
|
1157
|
+
return;
|
|
1158
|
+
if (!_.every(branches, x => x.case.type === 'boolean' && x.then.type === defaultCase.type))
|
|
1159
|
+
return;
|
|
1160
|
+
return {
|
|
1161
|
+
type: defaultCase.type,
|
|
1162
|
+
sql: sql `CASE ${{
|
|
1163
|
+
literal: _.map(branches, x => sql `WHEN (${x.case.sql}) THEN (${x.then.sql})`),
|
|
1164
|
+
separator: ' '
|
|
1165
|
+
}} ELSE (${defaultCase.sql}) END`,
|
|
1166
|
+
};
|
|
800
1167
|
}
|
|
801
1168
|
};
|
|
802
1169
|
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
@@ -822,57 +1189,19 @@ const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
|
822
1189
|
if (expr instanceof QueryArrayExpression) {
|
|
823
1190
|
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
824
1191
|
}
|
|
825
|
-
const
|
|
826
|
-
if (!
|
|
1192
|
+
const typed = encodeTypedQueryExpression(compiler, parent, expr);
|
|
1193
|
+
if (!typed)
|
|
827
1194
|
throw Error('Invalid expression');
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
return { sql: element, dimension: dataType.dimension };
|
|
838
|
-
}
|
|
839
|
-
if (expr instanceof QueryValueExpression) {
|
|
840
|
-
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
841
|
-
throw Error('Invalid expression');
|
|
842
|
-
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
843
|
-
}
|
|
1195
|
+
switch (typed.type) {
|
|
1196
|
+
case 'boolean': return sql `to_jsonb(${typed.sql})`;
|
|
1197
|
+
case 'number': return sql `to_jsonb(${typed.sql})`;
|
|
1198
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${typed.sql} AS TEXT))`;
|
|
1199
|
+
case 'string': return sql `to_jsonb(${typed.sql})`;
|
|
1200
|
+
case 'date': return sql `jsonb_build_object(
|
|
1201
|
+
'$date', to_char(${typed.sql} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1202
|
+
)`;
|
|
1203
|
+
default: throw Error('Invalid expression');
|
|
844
1204
|
}
|
|
845
|
-
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
846
|
-
if (_.some(result, x => _.isNil(x)))
|
|
847
|
-
throw Error('Invalid expression');
|
|
848
|
-
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
849
|
-
};
|
|
850
|
-
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
851
|
-
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
852
|
-
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
853
|
-
if (d1 !== d2)
|
|
854
|
-
throw Error('Invalid expression');
|
|
855
|
-
const operatorMap = {
|
|
856
|
-
'$distance': sql `<->`,
|
|
857
|
-
'$innerProduct': sql `<#>`,
|
|
858
|
-
'$negInnerProduct': sql `<#>`,
|
|
859
|
-
'$cosineDistance': sql `<=>`,
|
|
860
|
-
'$rectilinearDistance': sql `<+>`,
|
|
861
|
-
};
|
|
862
|
-
const _expr = sql `
|
|
863
|
-
CAST(
|
|
864
|
-
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
865
|
-
)
|
|
866
|
-
${operatorMap[expr.type]}
|
|
867
|
-
CAST(
|
|
868
|
-
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
869
|
-
)
|
|
870
|
-
`;
|
|
871
|
-
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
872
|
-
};
|
|
873
|
-
const matchType = (first, second) => {
|
|
874
|
-
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
875
|
-
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
876
1205
|
};
|
|
877
1206
|
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
878
1207
|
if (expr instanceof QueryCoditionalExpression) {
|
|
@@ -894,23 +1223,29 @@ const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
|
894
1223
|
'$lt': sql `<`,
|
|
895
1224
|
'$lte': sql `<=`,
|
|
896
1225
|
};
|
|
897
|
-
if (
|
|
898
|
-
|
|
1226
|
+
if (isArrayExpr(expr.left) &&
|
|
1227
|
+
isArrayExpr(expr.right) &&
|
|
899
1228
|
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
900
|
-
const _left =
|
|
901
|
-
const _right =
|
|
902
|
-
const
|
|
903
|
-
if (
|
|
904
|
-
const [l, r] = _.unzip(
|
|
1229
|
+
const _left = mapExpr(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1230
|
+
const _right = mapExpr(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1231
|
+
const zipped = zipExpr(_left, _right) ?? [];
|
|
1232
|
+
if (_left.length === zipped.length) {
|
|
1233
|
+
const [l, r] = _.unzip(zipped);
|
|
905
1234
|
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
906
1235
|
}
|
|
907
1236
|
}
|
|
908
1237
|
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
909
1238
|
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
910
1239
|
if (_left && _right) {
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1240
|
+
if (_left.type === _right.type) {
|
|
1241
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1242
|
+
}
|
|
1243
|
+
if (_left.type === 'decimal' && _right.type === 'number') {
|
|
1244
|
+
return sql `CAST((${_left.sql}) AS DOUBLE PRECISION) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1245
|
+
}
|
|
1246
|
+
if (_left.type === 'number' && _right.type === 'decimal') {
|
|
1247
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} CAST((${_right.sql}) AS DOUBLE PRECISION)`;
|
|
1248
|
+
}
|
|
914
1249
|
}
|
|
915
1250
|
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
916
1251
|
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
@@ -922,12 +1257,6 @@ const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
|
922
1257
|
}
|
|
923
1258
|
throw Error('Invalid expression');
|
|
924
1259
|
};
|
|
925
|
-
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
926
|
-
if (expr instanceof QueryDistanceExpression) {
|
|
927
|
-
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
928
|
-
}
|
|
929
|
-
return encodeBooleanExpression(compiler, parent, expr);
|
|
930
|
-
};
|
|
931
1260
|
|
|
932
1261
|
//
|
|
933
1262
|
// storage.ts
|
|
@@ -984,7 +1313,7 @@ class SqlStorage {
|
|
|
984
1313
|
const _value = _.get(value, path);
|
|
985
1314
|
if (_.isPlainObject(_value)) {
|
|
986
1315
|
const decoded = this._decodeObject(type.target, _value, matchType);
|
|
987
|
-
if (decoded.
|
|
1316
|
+
if (decoded.id)
|
|
988
1317
|
_.set(result, path, decoded);
|
|
989
1318
|
}
|
|
990
1319
|
}
|
|
@@ -1024,7 +1353,7 @@ class SqlStorage {
|
|
|
1024
1353
|
else if (isPointer(dataType)) {
|
|
1025
1354
|
if (_.isPlainObject(value)) {
|
|
1026
1355
|
const decoded = this._decodeObject(dataType.target, value, matchType);
|
|
1027
|
-
if (decoded.
|
|
1356
|
+
if (decoded.id)
|
|
1028
1357
|
obj[PVK].attributes[key] = decoded;
|
|
1029
1358
|
}
|
|
1030
1359
|
}
|
|
@@ -1094,13 +1423,18 @@ class SqlStorage {
|
|
|
1094
1423
|
const self = this;
|
|
1095
1424
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
1096
1425
|
const _matchesType = self._matchesType(compiler, query);
|
|
1097
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) =>
|
|
1098
|
-
|
|
1426
|
+
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) => {
|
|
1427
|
+
if (!opts?.weight)
|
|
1428
|
+
return { sort: sql `ORDER BY ${self.dialect.random()}` };
|
|
1429
|
+
const weight = encodeTypedQueryExpression(compiler, {
|
|
1099
1430
|
name: fetchName,
|
|
1100
1431
|
className: query.className,
|
|
1101
1432
|
groupMatches: query.groupMatches,
|
|
1102
|
-
}, opts.weight)
|
|
1103
|
-
|
|
1433
|
+
}, opts.weight);
|
|
1434
|
+
if (!weight)
|
|
1435
|
+
throw Error('Invalid expression');
|
|
1436
|
+
return { sort: sql `ORDER BY ${self.dialect.random(weight.sql)}` };
|
|
1437
|
+
});
|
|
1104
1438
|
return (async function* () {
|
|
1105
1439
|
const objects = self.query(_query);
|
|
1106
1440
|
for await (const object of objects) {
|
|
@@ -1112,7 +1446,7 @@ class SqlStorage {
|
|
|
1112
1446
|
const self = this;
|
|
1113
1447
|
const query = sql `
|
|
1114
1448
|
SELECT *
|
|
1115
|
-
FROM (${this._refs(_.pick(this.schema, classNames), object.className, TObject.defaultKeys, sql `${{ value: `${object.className}$${object.
|
|
1449
|
+
FROM (${this._refs(_.pick(this.schema, classNames), object.className, TObject.defaultKeys, sql `${{ value: `${object.className}$${object.id}` }}`)}) AS "$"
|
|
1116
1450
|
${_.isNil(roles) ? sql `` : sql `WHERE ${{ identifier: '$' }}.${{ identifier: '_rperm' }} && ${{ value: roles }}`}
|
|
1117
1451
|
`;
|
|
1118
1452
|
return (async function* () {
|
|
@@ -1703,25 +2037,25 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1703
2037
|
case '$addToSet':
|
|
1704
2038
|
case '$push':
|
|
1705
2039
|
{
|
|
1706
|
-
if (!_.isArray(value) || !_.every(value, x => x instanceof TObject && x.
|
|
2040
|
+
if (!_.isArray(value) || !_.every(value, x => x instanceof TObject && x.id))
|
|
1707
2041
|
break;
|
|
1708
|
-
const
|
|
2042
|
+
const ids = _.uniq(_.map(value, (x) => `${x.className}$${x.id}`));
|
|
1709
2043
|
return sql `ARRAY(
|
|
1710
2044
|
SELECT DISTINCT "$"
|
|
1711
|
-
FROM UNNEST(${{ identifier: column }} || ARRAY[${_.map(
|
|
2045
|
+
FROM UNNEST(${{ identifier: column }} || ARRAY[${_.map(ids, (x) => sql `${{ value: x }}`)}]) "$"
|
|
1712
2046
|
RIGHT JOIN ${{ identifier: dataType.target }} ON "$" = (${{ quote: dataType.target + '$' }} || ${{ identifier: dataType.target }}._id)
|
|
1713
2047
|
)`;
|
|
1714
2048
|
}
|
|
1715
2049
|
case '$removeAll':
|
|
1716
2050
|
{
|
|
1717
|
-
if (!_.isArray(value) || !_.every(value, x => x instanceof TObject && x.
|
|
2051
|
+
if (!_.isArray(value) || !_.every(value, x => x instanceof TObject && x.id))
|
|
1718
2052
|
break;
|
|
1719
|
-
const
|
|
2053
|
+
const ids = _.uniq(_.map(value, (x) => `${x.className}$${x.id}`));
|
|
1720
2054
|
return sql `ARRAY(
|
|
1721
2055
|
SELECT "$"
|
|
1722
2056
|
FROM UNNEST(${{ identifier: column }}) "$"
|
|
1723
2057
|
RIGHT JOIN ${{ identifier: dataType.target }} ON "$" = (${{ quote: dataType.target + '$' }} || ${{ identifier: dataType.target }}._id)
|
|
1724
|
-
WHERE "$" NOT IN (${_.map(
|
|
2058
|
+
WHERE "$" NOT IN (${_.map(ids, (x) => sql `${{ value: x }}`)})
|
|
1725
2059
|
)`;
|
|
1726
2060
|
}
|
|
1727
2061
|
}
|
|
@@ -1979,7 +2313,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1979
2313
|
];
|
|
1980
2314
|
if (!_.isEmpty(groupMatches?.[field])) {
|
|
1981
2315
|
for (const [key, expr] of _.entries(groupMatches[field])) {
|
|
1982
|
-
if (expr instanceof
|
|
2316
|
+
if (expr instanceof QueryZeroParamAccumulator) {
|
|
1983
2317
|
switch (expr.type) {
|
|
1984
2318
|
case '$count':
|
|
1985
2319
|
columns.push(sql `
|
|
@@ -1992,11 +2326,10 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1992
2326
|
break;
|
|
1993
2327
|
}
|
|
1994
2328
|
}
|
|
1995
|
-
else if (expr instanceof
|
|
2329
|
+
else if (expr instanceof QueryUnaryAccumulator) {
|
|
1996
2330
|
if (!expr.expr)
|
|
1997
2331
|
throw Error('Invalid expression');
|
|
1998
|
-
const
|
|
1999
|
-
const value = _.first(exprs)?.sql;
|
|
2332
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.expr) ?? {};
|
|
2000
2333
|
if (!value)
|
|
2001
2334
|
throw Error('Invalid expression');
|
|
2002
2335
|
switch (expr.type) {
|
|
@@ -2039,8 +2372,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
2039
2372
|
}[expr.mode];
|
|
2040
2373
|
if (!expr.input)
|
|
2041
2374
|
throw Error('Invalid expression');
|
|
2042
|
-
const
|
|
2043
|
-
const value = _.first(exprs)?.sql;
|
|
2375
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.input) ?? {};
|
|
2044
2376
|
if (!value)
|
|
2045
2377
|
throw Error('Invalid expression');
|
|
2046
2378
|
columns.push(sql `
|
|
@@ -2223,9 +2555,9 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2223
2555
|
if (_.isNil(expr.value))
|
|
2224
2556
|
return sql `${element} IS NULL`;
|
|
2225
2557
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2226
|
-
if (!(expr.value instanceof TObject) || dataType.target !== expr.value.className || !expr.value.
|
|
2558
|
+
if (!(expr.value instanceof TObject) || dataType.target !== expr.value.className || !expr.value.id)
|
|
2227
2559
|
break;
|
|
2228
|
-
return sql `${element} ${nullSafeEqual()} ${{ value: expr.value.
|
|
2560
|
+
return sql `${element} ${nullSafeEqual()} ${{ value: expr.value.id }}`;
|
|
2229
2561
|
}
|
|
2230
2562
|
return sql `${element} ${nullSafeEqual()} ${encodeValue(expr.value)}`;
|
|
2231
2563
|
}
|
|
@@ -2236,9 +2568,9 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2236
2568
|
if (_.isNil(expr.value))
|
|
2237
2569
|
return sql `${element} IS NOT NULL`;
|
|
2238
2570
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2239
|
-
if (!(expr.value instanceof TObject) || dataType.target !== expr.value.className || !expr.value.
|
|
2571
|
+
if (!(expr.value instanceof TObject) || dataType.target !== expr.value.className || !expr.value.id)
|
|
2240
2572
|
break;
|
|
2241
|
-
return sql `${element} ${nullSafeNotEqual()} ${{ value: expr.value.
|
|
2573
|
+
return sql `${element} ${nullSafeNotEqual()} ${{ value: expr.value.id }}`;
|
|
2242
2574
|
}
|
|
2243
2575
|
return sql `${element} ${nullSafeNotEqual()} ${encodeValue(expr.value)}`;
|
|
2244
2576
|
}
|
|
@@ -2280,8 +2612,8 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2280
2612
|
return sql `${element} ${{ literal: op }} ${encodeValue(expr.value)}`;
|
|
2281
2613
|
}
|
|
2282
2614
|
}
|
|
2283
|
-
else if (!_.isString(dataType) && dataType?.type === 'pointer' && expr.value instanceof TObject && expr.value.
|
|
2284
|
-
return sql `${element} ${{ literal: op }} ${{ value: expr.value.
|
|
2615
|
+
else if (!_.isString(dataType) && dataType?.type === 'pointer' && expr.value instanceof TObject && expr.value.id) {
|
|
2616
|
+
return sql `${element} ${{ literal: op }} ${{ value: expr.value.id }}`;
|
|
2285
2617
|
}
|
|
2286
2618
|
else if (!dataType) {
|
|
2287
2619
|
if (expr.value instanceof Decimal || _.isNumber(expr.value)) {
|
|
@@ -2317,9 +2649,9 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2317
2649
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2318
2650
|
if (_.isNil(value))
|
|
2319
2651
|
return sql `${element} IS NULL`;
|
|
2320
|
-
if (!(value instanceof TObject) || dataType.target !== value.className || !value.
|
|
2652
|
+
if (!(value instanceof TObject) || dataType.target !== value.className || !value.id)
|
|
2321
2653
|
break;
|
|
2322
|
-
return sql `${element} ${nullSafeEqual()} ${{ value: value.
|
|
2654
|
+
return sql `${element} ${nullSafeEqual()} ${{ value: value.id }}`;
|
|
2323
2655
|
}
|
|
2324
2656
|
return sql `${element} ${nullSafeEqual()} ${encodeValue(value)}`;
|
|
2325
2657
|
}
|
|
@@ -2327,12 +2659,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2327
2659
|
const containsNil = _.some(expr.value, x => _.isNil(x));
|
|
2328
2660
|
const values = _.filter(expr.value, x => !_.isNil(x));
|
|
2329
2661
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2330
|
-
if (!_.every(values, x => x instanceof TObject && dataType.target === x.className && x.
|
|
2662
|
+
if (!_.every(values, x => x instanceof TObject && dataType.target === x.className && x.id))
|
|
2331
2663
|
break;
|
|
2332
2664
|
if (containsNil) {
|
|
2333
|
-
return sql `${element} IS NULL OR ${element} IN (${_.map(values, (x) => sql `${{ value: x.
|
|
2665
|
+
return sql `${element} IS NULL OR ${element} IN (${_.map(values, (x) => sql `${{ value: x.id }}`)})`;
|
|
2334
2666
|
}
|
|
2335
|
-
return sql `${element} IN (${_.map(values, (x) => sql `${{ value: x.
|
|
2667
|
+
return sql `${element} IN (${_.map(values, (x) => sql `${{ value: x.id }}`)})`;
|
|
2336
2668
|
}
|
|
2337
2669
|
if (containsNil) {
|
|
2338
2670
|
return sql `${element} IS NULL OR ${element} IN (${_.map(values, x => encodeValue(x))})`;
|
|
@@ -2353,9 +2685,9 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2353
2685
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2354
2686
|
if (_.isNil(value))
|
|
2355
2687
|
return sql `${element} IS NOT NULL`;
|
|
2356
|
-
if (!(value instanceof TObject) || dataType.target !== value.className || !value.
|
|
2688
|
+
if (!(value instanceof TObject) || dataType.target !== value.className || !value.id)
|
|
2357
2689
|
break;
|
|
2358
|
-
return sql `${element} ${nullSafeNotEqual()} ${{ value: value.
|
|
2690
|
+
return sql `${element} ${nullSafeNotEqual()} ${{ value: value.id }}`;
|
|
2359
2691
|
}
|
|
2360
2692
|
return sql `${element} ${nullSafeNotEqual()} ${encodeValue(value)}`;
|
|
2361
2693
|
}
|
|
@@ -2363,12 +2695,12 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2363
2695
|
const containsNil = _.some(expr.value, x => _.isNil(x));
|
|
2364
2696
|
const values = _.filter(expr.value, x => !_.isNil(x));
|
|
2365
2697
|
if (!_.isString(dataType) && dataType?.type === 'pointer') {
|
|
2366
|
-
if (!_.every(values, x => x instanceof TObject && dataType.target === x.className && x.
|
|
2698
|
+
if (!_.every(values, x => x instanceof TObject && dataType.target === x.className && x.id))
|
|
2367
2699
|
break;
|
|
2368
2700
|
if (containsNil) {
|
|
2369
|
-
return sql `${element} IS NOT NULL AND ${element} NOT IN (${_.map(values, (x) => sql `${{ value: x.
|
|
2701
|
+
return sql `${element} IS NOT NULL AND ${element} NOT IN (${_.map(values, (x) => sql `${{ value: x.id }}`)})`;
|
|
2370
2702
|
}
|
|
2371
|
-
return sql `${element} NOT IN (${_.map(values, (x) => sql `${{ value: x.
|
|
2703
|
+
return sql `${element} NOT IN (${_.map(values, (x) => sql `${{ value: x.id }}`)})`;
|
|
2372
2704
|
}
|
|
2373
2705
|
if (containsNil) {
|
|
2374
2706
|
return sql `${element} IS NOT NULL AND ${element} NOT IN (${_.map(values, x => encodeValue(x))})`;
|
|
@@ -2398,14 +2730,14 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2398
2730
|
return sql `${element} ${{ literal: op }} ${{ value: _encodeValue(expr.value) }}`;
|
|
2399
2731
|
}
|
|
2400
2732
|
if (relation && parent.className) {
|
|
2401
|
-
if (!_.every(expr.value, x => x instanceof TObject && relation.target === x.className && x.
|
|
2733
|
+
if (!_.every(expr.value, x => x instanceof TObject && relation.target === x.className && x.id))
|
|
2402
2734
|
break;
|
|
2403
2735
|
const tempName = `_populate_expr_$${compiler.nextIdx()}`;
|
|
2404
2736
|
const populate = _selectRelationPopulate(compiler, { className: parent.className, name: parent.name }, relation.populate, `$${field}`, false);
|
|
2405
2737
|
return sql `ARRAY(
|
|
2406
2738
|
SELECT ${{ identifier: '_id' }}
|
|
2407
2739
|
FROM (${populate}) AS ${{ identifier: tempName }}
|
|
2408
|
-
) ${{ literal: op }} ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.
|
|
2740
|
+
) ${{ literal: op }} ARRAY[${_.map(expr.value, (x) => sql `${{ value: x.id }}`)}]::TEXT[]`;
|
|
2409
2741
|
}
|
|
2410
2742
|
if (!dataType) {
|
|
2411
2743
|
return sql `jsonb_typeof(${element}) ${nullSafeEqual()} 'array' AND ${element} ${{ literal: op }} ${_encodeJsonValue(_encodeValue(expr.value))}`;
|
|
@@ -2628,6 +2960,36 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2628
2960
|
throw Error('Invalid expression');
|
|
2629
2961
|
};
|
|
2630
2962
|
|
|
2963
|
+
//
|
|
2964
|
+
// index.ts
|
|
2965
|
+
//
|
|
2966
|
+
// The MIT License
|
|
2967
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
2968
|
+
//
|
|
2969
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2970
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
2971
|
+
// in the Software without restriction, including without limitation the rights
|
|
2972
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
2973
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
2974
|
+
// furnished to do so, subject to the following conditions:
|
|
2975
|
+
//
|
|
2976
|
+
// The above copyright notice and this permission notice shall be included in
|
|
2977
|
+
// all copies or substantial portions of the Software.
|
|
2978
|
+
//
|
|
2979
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
2980
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
2981
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
2982
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
2983
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
2984
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
2985
|
+
// THE SOFTWARE.
|
|
2986
|
+
//
|
|
2987
|
+
const encodeSortExpression = (compiler, parent, expr) => {
|
|
2988
|
+
if (expr instanceof QueryDistanceExpression) {
|
|
2989
|
+
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2992
|
+
|
|
2631
2993
|
//
|
|
2632
2994
|
// relation.ts
|
|
2633
2995
|
//
|
|
@@ -2661,7 +3023,7 @@ const encodeRelation = (compiler, parent, relatedBy) => {
|
|
|
2661
3023
|
SELECT 1
|
|
2662
3024
|
FROM ${{ identifier: relatedBy.className }} AS ${{ identifier: name }}
|
|
2663
3025
|
${!_.isEmpty(joins) ? { literal: joins, separator: '\n' } : sql ``}
|
|
2664
|
-
WHERE ${_foreign('_id')} = ${{ value: relatedBy.
|
|
3026
|
+
WHERE ${_foreign('_id')} = ${{ value: relatedBy.id }} AND ${sql `(${{ quote: parent.className + '$' }} || ${_local('_id')})`} = ANY(${field})
|
|
2665
3027
|
)`;
|
|
2666
3028
|
};
|
|
2667
3029
|
|
|
@@ -2699,7 +3061,8 @@ const PostgresDialect = {
|
|
|
2699
3061
|
updateOperation,
|
|
2700
3062
|
selectPopulate,
|
|
2701
3063
|
encodeFieldExpression,
|
|
2702
|
-
|
|
3064
|
+
encodeBooleanExpression,
|
|
3065
|
+
encodeSortExpression,
|
|
2703
3066
|
encodePopulate,
|
|
2704
3067
|
encodeRelation,
|
|
2705
3068
|
encodeSortKey,
|