proto.io 0.0.215 → 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 +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 +8 -10
- package/dist/adapters/storage/progres.js +833 -451
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +834 -452
- 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 +3 -3
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +9 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -12
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{base-Cp5le5dC.d.ts → base-CWnOBKD5.d.ts} +2 -2
- package/dist/internals/base-CWnOBKD5.d.ts.map +1 -0
- package/dist/internals/{chunk--Vo2-p-z.d.ts → chunk-CNNSQpRF.d.ts} +3 -3
- package/dist/internals/chunk-CNNSQpRF.d.ts.map +1 -0
- package/dist/internals/{index-CZUGnb-y.d.ts → index-BRIlS3mY.d.ts} +3 -9
- package/dist/internals/index-BRIlS3mY.d.ts.map +1 -0
- package/dist/internals/{index-YLrO0f2D.d.ts → index-Bs8n7Q8f.d.ts} +60 -20
- package/dist/internals/index-Bs8n7Q8f.d.ts.map +1 -0
- package/dist/internals/{index-al1N-qi7.mjs → index-BzDsTt4R.mjs} +2 -2
- package/dist/internals/{index-al1N-qi7.mjs.map → index-BzDsTt4R.mjs.map} +1 -1
- package/dist/internals/{index-ZPbBr9Db.mjs → index-DfqABzjr.mjs} +135 -142
- 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-ChrCrz_C.mjs → validator-CEcBF4Cn.mjs} +730 -27
- package/dist/internals/validator-CEcBF4Cn.mjs.map +1 -0
- package/dist/internals/{validator-m6wY35c6.js → validator-mcBCJP4P.js} +737 -27
- package/dist/internals/validator-mcBCJP4P.js.map +1 -0
- package/package.json +1 -1
- package/dist/internals/base-Cp5le5dC.d.ts.map +0 -1
- package/dist/internals/chunk--Vo2-p-z.d.ts.map +0 -1
- package/dist/internals/index-CZUGnb-y.d.ts.map +0 -1
- package/dist/internals/index-DfnPpl1I.js.map +0 -1
- package/dist/internals/index-YLrO0f2D.d.ts.map +0 -1
- package/dist/internals/index-ZPbBr9Db.mjs.map +0 -1
- package/dist/internals/validator-ChrCrz_C.mjs.map +0 -1
- package/dist/internals/validator-m6wY35c6.js.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
|
|
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-BzDsTt4R.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,
|
|
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-CEcBF4Cn.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';
|
|
@@ -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' }}`;
|
|
@@ -536,6 +536,728 @@ class QueryCompiler {
|
|
|
536
536
|
}
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
+
//
|
|
540
|
+
// encode.ts
|
|
541
|
+
//
|
|
542
|
+
// The MIT License
|
|
543
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
544
|
+
//
|
|
545
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
546
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
547
|
+
// in the Software without restriction, including without limitation the rights
|
|
548
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
549
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
550
|
+
// furnished to do so, subject to the following conditions:
|
|
551
|
+
//
|
|
552
|
+
// The above copyright notice and this permission notice shall be included in
|
|
553
|
+
// all copies or substantial portions of the Software.
|
|
554
|
+
//
|
|
555
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
556
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
557
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
558
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
559
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
560
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
561
|
+
// THE SOFTWARE.
|
|
562
|
+
//
|
|
563
|
+
const _encodeJsonValue = (value) => {
|
|
564
|
+
if (_.isArray(value))
|
|
565
|
+
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
566
|
+
if (_.isPlainObject(value))
|
|
567
|
+
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
568
|
+
return sql `to_jsonb(${{ value }})`;
|
|
569
|
+
};
|
|
570
|
+
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
571
|
+
switch (_typeof(dataType)) {
|
|
572
|
+
case 'decimal':
|
|
573
|
+
return sql `jsonb_build_object(
|
|
574
|
+
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
575
|
+
) AS ${{ identifier: colname }}`;
|
|
576
|
+
case 'date':
|
|
577
|
+
return sql `jsonb_build_object(
|
|
578
|
+
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
579
|
+
) AS ${{ identifier: colname }}`;
|
|
580
|
+
default:
|
|
581
|
+
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
const encodeType = (colname, dataType, value) => {
|
|
585
|
+
if (_.isNil(value))
|
|
586
|
+
return sql `NULL`;
|
|
587
|
+
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
588
|
+
case 'boolean':
|
|
589
|
+
if (_.isBoolean(value))
|
|
590
|
+
return sql `${{ value }}`;
|
|
591
|
+
break;
|
|
592
|
+
case 'number':
|
|
593
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
594
|
+
return sql `${{ value }}`;
|
|
595
|
+
if (value instanceof Decimal)
|
|
596
|
+
return sql `${{ value: value.toNumber() }}`;
|
|
597
|
+
break;
|
|
598
|
+
case 'decimal':
|
|
599
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
600
|
+
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
601
|
+
if (value instanceof Decimal)
|
|
602
|
+
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
603
|
+
break;
|
|
604
|
+
case 'string':
|
|
605
|
+
if (_.isString(value))
|
|
606
|
+
return sql `${{ value }}`;
|
|
607
|
+
break;
|
|
608
|
+
case 'string[]':
|
|
609
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
610
|
+
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
611
|
+
break;
|
|
612
|
+
case 'date':
|
|
613
|
+
if (_.isDate(value))
|
|
614
|
+
return sql `${{ value }}`;
|
|
615
|
+
break;
|
|
616
|
+
case 'object':
|
|
617
|
+
if (_.isPlainObject(value))
|
|
618
|
+
return sql `${{ value: _encodeValue(value) }}`;
|
|
619
|
+
break;
|
|
620
|
+
case 'vector':
|
|
621
|
+
if (!_.isArray(value) || value.length !== dimensionOf(dataType))
|
|
622
|
+
break;
|
|
623
|
+
if (!_.every(value, x => _.isFinite(x)))
|
|
624
|
+
break;
|
|
625
|
+
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
626
|
+
case 'array':
|
|
627
|
+
if (!_.isArray(value))
|
|
628
|
+
break;
|
|
629
|
+
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(_encodeValue(x)))}]::JSONB[]`;
|
|
630
|
+
case 'pointer':
|
|
631
|
+
if (value instanceof TObject && value.objectId)
|
|
632
|
+
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
633
|
+
break;
|
|
634
|
+
case 'relation':
|
|
635
|
+
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.objectId)) {
|
|
636
|
+
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
637
|
+
}
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
throw Error('Invalid data type');
|
|
641
|
+
};
|
|
642
|
+
const decodeType = (type, value) => {
|
|
643
|
+
switch (type) {
|
|
644
|
+
case 'boolean':
|
|
645
|
+
if (_.isBoolean(value))
|
|
646
|
+
return value;
|
|
647
|
+
if (value === 'true')
|
|
648
|
+
return true;
|
|
649
|
+
if (value === 'false')
|
|
650
|
+
return false;
|
|
651
|
+
break;
|
|
652
|
+
case 'number':
|
|
653
|
+
if (_.isNumber(value))
|
|
654
|
+
return value;
|
|
655
|
+
if (_.isString(value)) {
|
|
656
|
+
const float = parseFloat(value);
|
|
657
|
+
return _.isNaN(float) ? null : float;
|
|
658
|
+
}
|
|
659
|
+
break;
|
|
660
|
+
case 'decimal':
|
|
661
|
+
if (_.isString(value) || _.isNumber(value))
|
|
662
|
+
return new Decimal(value);
|
|
663
|
+
if (value instanceof Decimal)
|
|
664
|
+
return value;
|
|
665
|
+
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
666
|
+
return new Decimal(value.$decimal);
|
|
667
|
+
break;
|
|
668
|
+
case 'string':
|
|
669
|
+
if (_.isString(value))
|
|
670
|
+
return value;
|
|
671
|
+
break;
|
|
672
|
+
case 'string[]':
|
|
673
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
674
|
+
return value;
|
|
675
|
+
break;
|
|
676
|
+
case 'date':
|
|
677
|
+
if (_.isDate(value))
|
|
678
|
+
return value;
|
|
679
|
+
if (_.isString(value)) {
|
|
680
|
+
const date = new Date(value);
|
|
681
|
+
if (isFinite(date.valueOf()))
|
|
682
|
+
return date;
|
|
683
|
+
}
|
|
684
|
+
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
685
|
+
return new Date(value.$date);
|
|
686
|
+
break;
|
|
687
|
+
case 'object':
|
|
688
|
+
if (_.isPlainObject(value))
|
|
689
|
+
return _decodeValue(value);
|
|
690
|
+
break;
|
|
691
|
+
case 'array':
|
|
692
|
+
if (_.isArray(value))
|
|
693
|
+
return _decodeValue(value);
|
|
694
|
+
break;
|
|
695
|
+
case 'vector':
|
|
696
|
+
if (!_.isArray(value))
|
|
697
|
+
break;
|
|
698
|
+
if (_.every(value, x => _.isNumber(x))) {
|
|
699
|
+
return value;
|
|
700
|
+
}
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
return null;
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
//
|
|
707
|
+
// types.ts
|
|
708
|
+
//
|
|
709
|
+
// The MIT License
|
|
710
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
711
|
+
//
|
|
712
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
713
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
714
|
+
// in the Software without restriction, including without limitation the rights
|
|
715
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
716
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
717
|
+
// furnished to do so, subject to the following conditions:
|
|
718
|
+
//
|
|
719
|
+
// The above copyright notice and this permission notice shall be included in
|
|
720
|
+
// all copies or substantial portions of the Software.
|
|
721
|
+
//
|
|
722
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
723
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
724
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
725
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
726
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
727
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
728
|
+
// THE SOFTWARE.
|
|
729
|
+
//
|
|
730
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
731
|
+
const isArrayExpr = (expr) => {
|
|
732
|
+
if (expr instanceof QueryArrayExpression)
|
|
733
|
+
return true;
|
|
734
|
+
if (expr instanceof QueryValueExpression)
|
|
735
|
+
return _.isArray(expr.value);
|
|
736
|
+
return false;
|
|
737
|
+
};
|
|
738
|
+
const arrayLength = (expr) => {
|
|
739
|
+
if (expr instanceof QueryArrayExpression)
|
|
740
|
+
return expr.exprs.length;
|
|
741
|
+
if (expr instanceof QueryValueExpression)
|
|
742
|
+
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
743
|
+
return 0;
|
|
744
|
+
};
|
|
745
|
+
const mapExpr = (expr, callback) => {
|
|
746
|
+
if (expr instanceof QueryArrayExpression)
|
|
747
|
+
return _.map(expr.exprs, x => callback(x));
|
|
748
|
+
if (expr instanceof QueryValueExpression)
|
|
749
|
+
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new QueryValueExpression(x))) : [];
|
|
750
|
+
return [];
|
|
751
|
+
};
|
|
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
|
+
//
|
|
871
|
+
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
872
|
+
if (expr instanceof QueryKeyExpression) {
|
|
873
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
874
|
+
const _dataType = dataType ? _typeof(dataType) : null;
|
|
875
|
+
if (!_dataType || !_PrimitiveValue.includes(_dataType))
|
|
876
|
+
return;
|
|
877
|
+
return { type: _dataType, sql: element };
|
|
878
|
+
}
|
|
879
|
+
if (expr instanceof QueryValueExpression) {
|
|
880
|
+
if (_.isBoolean(expr.value))
|
|
881
|
+
return { type: 'boolean', sql: sql `${{ value: expr.value }}` };
|
|
882
|
+
if (_.isNumber(expr.value))
|
|
883
|
+
return { type: 'number', sql: sql `${{ value: expr.value }}` };
|
|
884
|
+
if (expr.value instanceof Decimal)
|
|
885
|
+
return { type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` };
|
|
886
|
+
if (_.isString(expr.value))
|
|
887
|
+
return { type: 'string', sql: sql `${{ value: expr.value }}` };
|
|
888
|
+
if (_.isDate(expr.value))
|
|
889
|
+
return { type: 'date', sql: sql `${{ value: expr.value }}` };
|
|
890
|
+
}
|
|
891
|
+
if (expr instanceof QueryCoditionalExpression ||
|
|
892
|
+
expr instanceof QueryComparisonExpression ||
|
|
893
|
+
expr instanceof QueryNotExpression) {
|
|
894
|
+
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
895
|
+
if (value)
|
|
896
|
+
return { type: 'boolean', sql: value };
|
|
897
|
+
}
|
|
898
|
+
if (expr instanceof QueryDistanceExpression) {
|
|
899
|
+
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
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
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
1170
|
+
if (expr instanceof QueryKeyExpression) {
|
|
1171
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1172
|
+
if (dataType && isPrimitive(dataType)) {
|
|
1173
|
+
switch (_typeof(dataType)) {
|
|
1174
|
+
case 'boolean': return sql `to_jsonb(${element})`;
|
|
1175
|
+
case 'number': return sql `to_jsonb(${element})`;
|
|
1176
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
1177
|
+
case 'string': return sql `to_jsonb(${element})`;
|
|
1178
|
+
case 'string[]': return sql `to_jsonb(${element})`;
|
|
1179
|
+
case 'date': return sql `jsonb_build_object(
|
|
1180
|
+
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1181
|
+
)`;
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
return sql `${element}`;
|
|
1185
|
+
}
|
|
1186
|
+
if (expr instanceof QueryValueExpression) {
|
|
1187
|
+
return _encodeJsonValue(_encodeValue(expr.value));
|
|
1188
|
+
}
|
|
1189
|
+
if (expr instanceof QueryArrayExpression) {
|
|
1190
|
+
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
1191
|
+
}
|
|
1192
|
+
const typed = encodeTypedQueryExpression(compiler, parent, expr);
|
|
1193
|
+
if (!typed)
|
|
1194
|
+
throw Error('Invalid expression');
|
|
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');
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
1207
|
+
if (expr instanceof QueryCoditionalExpression) {
|
|
1208
|
+
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
1209
|
+
if (_.isEmpty(queries))
|
|
1210
|
+
return;
|
|
1211
|
+
switch (expr.type) {
|
|
1212
|
+
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
1213
|
+
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
1214
|
+
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if (expr instanceof QueryComparisonExpression) {
|
|
1218
|
+
const operatorMap = {
|
|
1219
|
+
'$eq': nullSafeEqual(),
|
|
1220
|
+
'$ne': nullSafeNotEqual(),
|
|
1221
|
+
'$gt': sql `>`,
|
|
1222
|
+
'$gte': sql `>=`,
|
|
1223
|
+
'$lt': sql `<`,
|
|
1224
|
+
'$lte': sql `<=`,
|
|
1225
|
+
};
|
|
1226
|
+
if (isArrayExpr(expr.left) &&
|
|
1227
|
+
isArrayExpr(expr.right) &&
|
|
1228
|
+
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
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);
|
|
1234
|
+
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
1238
|
+
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
1239
|
+
if (_left && _right) {
|
|
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
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
1251
|
+
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
1252
|
+
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
1253
|
+
}
|
|
1254
|
+
if (expr instanceof QueryNotExpression) {
|
|
1255
|
+
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
1256
|
+
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
1257
|
+
}
|
|
1258
|
+
throw Error('Invalid expression');
|
|
1259
|
+
};
|
|
1260
|
+
|
|
539
1261
|
//
|
|
540
1262
|
// storage.ts
|
|
541
1263
|
//
|
|
@@ -701,8 +1423,17 @@ class SqlStorage {
|
|
|
701
1423
|
const self = this;
|
|
702
1424
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
703
1425
|
const _matchesType = self._matchesType(compiler, query);
|
|
704
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, {
|
|
705
|
-
|
|
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, {
|
|
1430
|
+
name: fetchName,
|
|
1431
|
+
className: query.className,
|
|
1432
|
+
groupMatches: query.groupMatches,
|
|
1433
|
+
}, opts.weight);
|
|
1434
|
+
if (!weight)
|
|
1435
|
+
throw Error('Invalid expression');
|
|
1436
|
+
return { sort: sql `ORDER BY ${self.dialect.random(weight.sql)}` };
|
|
706
1437
|
});
|
|
707
1438
|
return (async function* () {
|
|
708
1439
|
const objects = self.query(_query);
|
|
@@ -947,8 +1678,8 @@ const encodeSortKey = (compiler, parent, key) => {
|
|
|
947
1678
|
const { element } = fetchElement(compiler, parent, key);
|
|
948
1679
|
return element;
|
|
949
1680
|
};
|
|
950
|
-
const random = (
|
|
951
|
-
return
|
|
1681
|
+
const random = (weight) => {
|
|
1682
|
+
return weight ? sql `-ln(random()) / ${weight}` : sql `random()`;
|
|
952
1683
|
};
|
|
953
1684
|
|
|
954
1685
|
//
|
|
@@ -1165,201 +1896,34 @@ class PostgresDriver extends PostgresClientDriver {
|
|
|
1165
1896
|
this.database = database;
|
|
1166
1897
|
}
|
|
1167
1898
|
async shutdown() {
|
|
1168
|
-
await this._release_pubsub();
|
|
1169
|
-
await this.database.end();
|
|
1170
|
-
}
|
|
1171
|
-
_init_pubsub() {
|
|
1172
|
-
if (this.pubsub || this.database.ending || this.database.ended)
|
|
1173
|
-
return;
|
|
1174
|
-
this.pubsub = new PostgresPubSub(this.database.connect());
|
|
1175
|
-
}
|
|
1176
|
-
async _release_pubsub() {
|
|
1177
|
-
const pubsub = this.pubsub;
|
|
1178
|
-
this.pubsub = undefined;
|
|
1179
|
-
await pubsub?.shutdown();
|
|
1180
|
-
}
|
|
1181
|
-
subscribe(channel, callback) {
|
|
1182
|
-
this._init_pubsub();
|
|
1183
|
-
if (!this.pubsub)
|
|
1184
|
-
return () => void 0;
|
|
1185
|
-
const release = this.pubsub.subscribe(({ channel: _channel, payload }) => {
|
|
1186
|
-
if (_channel === channel)
|
|
1187
|
-
callback(payload);
|
|
1188
|
-
});
|
|
1189
|
-
return () => {
|
|
1190
|
-
release();
|
|
1191
|
-
if (this.pubsub?.isEmpty())
|
|
1192
|
-
this._release_pubsub();
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
//
|
|
1198
|
-
// encode.ts
|
|
1199
|
-
//
|
|
1200
|
-
// The MIT License
|
|
1201
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1202
|
-
//
|
|
1203
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1204
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1205
|
-
// in the Software without restriction, including without limitation the rights
|
|
1206
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1207
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1208
|
-
// furnished to do so, subject to the following conditions:
|
|
1209
|
-
//
|
|
1210
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1211
|
-
// all copies or substantial portions of the Software.
|
|
1212
|
-
//
|
|
1213
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1214
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1215
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1216
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1217
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1218
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1219
|
-
// THE SOFTWARE.
|
|
1220
|
-
//
|
|
1221
|
-
const _encodeJsonValue = (value) => {
|
|
1222
|
-
if (_.isArray(value))
|
|
1223
|
-
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
1224
|
-
if (_.isPlainObject(value))
|
|
1225
|
-
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
1226
|
-
return sql `to_jsonb(${{ value }})`;
|
|
1227
|
-
};
|
|
1228
|
-
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
1229
|
-
switch (_typeof(dataType)) {
|
|
1230
|
-
case 'decimal':
|
|
1231
|
-
return sql `jsonb_build_object(
|
|
1232
|
-
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
1233
|
-
) AS ${{ identifier: colname }}`;
|
|
1234
|
-
case 'date':
|
|
1235
|
-
return sql `jsonb_build_object(
|
|
1236
|
-
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1237
|
-
) AS ${{ identifier: colname }}`;
|
|
1238
|
-
default:
|
|
1239
|
-
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
const encodeType = (colname, dataType, value) => {
|
|
1243
|
-
if (_.isNil(value))
|
|
1244
|
-
return sql `NULL`;
|
|
1245
|
-
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
1246
|
-
case 'boolean':
|
|
1247
|
-
if (_.isBoolean(value))
|
|
1248
|
-
return sql `${{ value }}`;
|
|
1249
|
-
break;
|
|
1250
|
-
case 'number':
|
|
1251
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1252
|
-
return sql `${{ value }}`;
|
|
1253
|
-
if (value instanceof Decimal)
|
|
1254
|
-
return sql `${{ value: value.toNumber() }}`;
|
|
1255
|
-
break;
|
|
1256
|
-
case 'decimal':
|
|
1257
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1258
|
-
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
1259
|
-
if (value instanceof Decimal)
|
|
1260
|
-
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
1261
|
-
break;
|
|
1262
|
-
case 'string':
|
|
1263
|
-
if (_.isString(value))
|
|
1264
|
-
return sql `${{ value }}`;
|
|
1265
|
-
break;
|
|
1266
|
-
case 'string[]':
|
|
1267
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1268
|
-
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
1269
|
-
break;
|
|
1270
|
-
case 'date':
|
|
1271
|
-
if (_.isDate(value))
|
|
1272
|
-
return sql `${{ value }}`;
|
|
1273
|
-
break;
|
|
1274
|
-
case 'object':
|
|
1275
|
-
if (_.isPlainObject(value))
|
|
1276
|
-
return sql `${{ value: _encodeValue(value) }}`;
|
|
1277
|
-
break;
|
|
1278
|
-
case 'vector':
|
|
1279
|
-
if (!_.isArray(value) || value.length !== dimensionOf(dataType))
|
|
1280
|
-
break;
|
|
1281
|
-
if (!_.every(value, x => _.isFinite(x)))
|
|
1282
|
-
break;
|
|
1283
|
-
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
1284
|
-
case 'array':
|
|
1285
|
-
if (!_.isArray(value))
|
|
1286
|
-
break;
|
|
1287
|
-
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(_encodeValue(x)))}]::JSONB[]`;
|
|
1288
|
-
case 'pointer':
|
|
1289
|
-
if (value instanceof TObject && value.objectId)
|
|
1290
|
-
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
1291
|
-
break;
|
|
1292
|
-
case 'relation':
|
|
1293
|
-
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.objectId)) {
|
|
1294
|
-
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
1295
|
-
}
|
|
1296
|
-
break;
|
|
1297
|
-
}
|
|
1298
|
-
throw Error('Invalid data type');
|
|
1299
|
-
};
|
|
1300
|
-
const decodeType = (type, value) => {
|
|
1301
|
-
switch (type) {
|
|
1302
|
-
case 'boolean':
|
|
1303
|
-
if (_.isBoolean(value))
|
|
1304
|
-
return value;
|
|
1305
|
-
if (value === 'true')
|
|
1306
|
-
return true;
|
|
1307
|
-
if (value === 'false')
|
|
1308
|
-
return false;
|
|
1309
|
-
break;
|
|
1310
|
-
case 'number':
|
|
1311
|
-
if (_.isNumber(value))
|
|
1312
|
-
return value;
|
|
1313
|
-
if (_.isString(value)) {
|
|
1314
|
-
const float = parseFloat(value);
|
|
1315
|
-
return _.isNaN(float) ? null : float;
|
|
1316
|
-
}
|
|
1317
|
-
break;
|
|
1318
|
-
case 'decimal':
|
|
1319
|
-
if (_.isString(value) || _.isNumber(value))
|
|
1320
|
-
return new Decimal(value);
|
|
1321
|
-
if (value instanceof Decimal)
|
|
1322
|
-
return value;
|
|
1323
|
-
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
1324
|
-
return new Decimal(value.$decimal);
|
|
1325
|
-
break;
|
|
1326
|
-
case 'string':
|
|
1327
|
-
if (_.isString(value))
|
|
1328
|
-
return value;
|
|
1329
|
-
break;
|
|
1330
|
-
case 'string[]':
|
|
1331
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1332
|
-
return value;
|
|
1333
|
-
break;
|
|
1334
|
-
case 'date':
|
|
1335
|
-
if (_.isDate(value))
|
|
1336
|
-
return value;
|
|
1337
|
-
if (_.isString(value)) {
|
|
1338
|
-
const date = new Date(value);
|
|
1339
|
-
if (isFinite(date.valueOf()))
|
|
1340
|
-
return date;
|
|
1341
|
-
}
|
|
1342
|
-
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
1343
|
-
return new Date(value.$date);
|
|
1344
|
-
break;
|
|
1345
|
-
case 'object':
|
|
1346
|
-
if (_.isPlainObject(value))
|
|
1347
|
-
return _decodeValue(value);
|
|
1348
|
-
break;
|
|
1349
|
-
case 'array':
|
|
1350
|
-
if (_.isArray(value))
|
|
1351
|
-
return _decodeValue(value);
|
|
1352
|
-
break;
|
|
1353
|
-
case 'vector':
|
|
1354
|
-
if (!_.isArray(value))
|
|
1355
|
-
break;
|
|
1356
|
-
if (_.every(value, x => _.isNumber(x))) {
|
|
1357
|
-
return value;
|
|
1358
|
-
}
|
|
1359
|
-
break;
|
|
1899
|
+
await this._release_pubsub();
|
|
1900
|
+
await this.database.end();
|
|
1360
1901
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1902
|
+
_init_pubsub() {
|
|
1903
|
+
if (this.pubsub || this.database.ending || this.database.ended)
|
|
1904
|
+
return;
|
|
1905
|
+
this.pubsub = new PostgresPubSub(this.database.connect());
|
|
1906
|
+
}
|
|
1907
|
+
async _release_pubsub() {
|
|
1908
|
+
const pubsub = this.pubsub;
|
|
1909
|
+
this.pubsub = undefined;
|
|
1910
|
+
await pubsub?.shutdown();
|
|
1911
|
+
}
|
|
1912
|
+
subscribe(channel, callback) {
|
|
1913
|
+
this._init_pubsub();
|
|
1914
|
+
if (!this.pubsub)
|
|
1915
|
+
return () => void 0;
|
|
1916
|
+
const release = this.pubsub.subscribe(({ channel: _channel, payload }) => {
|
|
1917
|
+
if (_channel === channel)
|
|
1918
|
+
callback(payload);
|
|
1919
|
+
});
|
|
1920
|
+
return () => {
|
|
1921
|
+
release();
|
|
1922
|
+
if (this.pubsub?.isEmpty())
|
|
1923
|
+
this._release_pubsub();
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1363
1927
|
|
|
1364
1928
|
//
|
|
1365
1929
|
// update.ts
|
|
@@ -1647,232 +2211,6 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1647
2211
|
throw Error('Invalid update operation');
|
|
1648
2212
|
};
|
|
1649
2213
|
|
|
1650
|
-
//
|
|
1651
|
-
// expressions.ts
|
|
1652
|
-
//
|
|
1653
|
-
// The MIT License
|
|
1654
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1655
|
-
//
|
|
1656
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1657
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1658
|
-
// in the Software without restriction, including without limitation the rights
|
|
1659
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1660
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1661
|
-
// furnished to do so, subject to the following conditions:
|
|
1662
|
-
//
|
|
1663
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1664
|
-
// all copies or substantial portions of the Software.
|
|
1665
|
-
//
|
|
1666
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1667
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1668
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1669
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1670
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1671
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1672
|
-
// THE SOFTWARE.
|
|
1673
|
-
//
|
|
1674
|
-
const isArrayExpression = (expr) => {
|
|
1675
|
-
if (expr instanceof QueryArrayExpression)
|
|
1676
|
-
return true;
|
|
1677
|
-
if (expr instanceof QueryValueExpression)
|
|
1678
|
-
return _.isArray(expr.value);
|
|
1679
|
-
return false;
|
|
1680
|
-
};
|
|
1681
|
-
const arrayLength = (expr) => {
|
|
1682
|
-
if (expr instanceof QueryArrayExpression)
|
|
1683
|
-
return expr.exprs.length;
|
|
1684
|
-
if (expr instanceof QueryValueExpression)
|
|
1685
|
-
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
1686
|
-
return 0;
|
|
1687
|
-
};
|
|
1688
|
-
const mapExpression = (expr, callback) => {
|
|
1689
|
-
if (expr instanceof QueryArrayExpression)
|
|
1690
|
-
return _.map(expr.exprs, x => callback(x));
|
|
1691
|
-
if (expr instanceof QueryValueExpression)
|
|
1692
|
-
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new QueryValueExpression(x))) : [];
|
|
1693
|
-
return [];
|
|
1694
|
-
};
|
|
1695
|
-
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
1696
|
-
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
1697
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1698
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1699
|
-
const _dataType = dataType ? _typeof(dataType) : null;
|
|
1700
|
-
if (_dataType === 'number') {
|
|
1701
|
-
return [
|
|
1702
|
-
{ type: 'number', sql: element },
|
|
1703
|
-
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
1704
|
-
];
|
|
1705
|
-
}
|
|
1706
|
-
else if (_dataType === 'decimal') {
|
|
1707
|
-
return [
|
|
1708
|
-
{ type: 'decimal', sql: element },
|
|
1709
|
-
{ type: 'number', sql: element },
|
|
1710
|
-
];
|
|
1711
|
-
}
|
|
1712
|
-
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
1713
|
-
return [{ type: _dataType, sql: element }];
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
if (expr instanceof QueryValueExpression) {
|
|
1717
|
-
if (_.isBoolean(expr.value))
|
|
1718
|
-
return [{ type: 'boolean', sql: sql `${{ value: expr.value }}` }];
|
|
1719
|
-
if (_.isNumber(expr.value))
|
|
1720
|
-
return [
|
|
1721
|
-
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
1722
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
1723
|
-
];
|
|
1724
|
-
if (expr.value instanceof Decimal)
|
|
1725
|
-
return [
|
|
1726
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
1727
|
-
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
1728
|
-
];
|
|
1729
|
-
if (_.isString(expr.value))
|
|
1730
|
-
return [{ type: 'string', sql: sql `${{ value: expr.value }}` }];
|
|
1731
|
-
if (_.isDate(expr.value))
|
|
1732
|
-
return [{ type: 'date', sql: sql `${{ value: expr.value }}` }];
|
|
1733
|
-
}
|
|
1734
|
-
if (expr instanceof QueryCoditionalExpression ||
|
|
1735
|
-
expr instanceof QueryComparisonExpression ||
|
|
1736
|
-
expr instanceof QueryNotExpression) {
|
|
1737
|
-
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
1738
|
-
if (value)
|
|
1739
|
-
return [{ type: 'boolean', sql: value }];
|
|
1740
|
-
}
|
|
1741
|
-
if (expr instanceof QueryDistanceExpression) {
|
|
1742
|
-
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1743
|
-
return [{ type: 'number', sql: value }];
|
|
1744
|
-
}
|
|
1745
|
-
};
|
|
1746
|
-
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
1747
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1748
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1749
|
-
if (dataType && isPrimitive(dataType)) {
|
|
1750
|
-
switch (_typeof(dataType)) {
|
|
1751
|
-
case 'boolean': return sql `to_jsonb(${element})`;
|
|
1752
|
-
case 'number': return sql `to_jsonb(${element})`;
|
|
1753
|
-
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
1754
|
-
case 'string': return sql `to_jsonb(${element})`;
|
|
1755
|
-
case 'string[]': return sql `to_jsonb(${element})`;
|
|
1756
|
-
case 'date': return sql `jsonb_build_object(
|
|
1757
|
-
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1758
|
-
)`;
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
return sql `${element}`;
|
|
1762
|
-
}
|
|
1763
|
-
if (expr instanceof QueryValueExpression) {
|
|
1764
|
-
return _encodeJsonValue(_encodeValue(expr.value));
|
|
1765
|
-
}
|
|
1766
|
-
if (expr instanceof QueryArrayExpression) {
|
|
1767
|
-
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
1768
|
-
}
|
|
1769
|
-
const value = encodeQueryExpression(compiler, parent, expr);
|
|
1770
|
-
if (!value)
|
|
1771
|
-
throw Error('Invalid expression');
|
|
1772
|
-
return sql `to_jsonb(${value})`;
|
|
1773
|
-
};
|
|
1774
|
-
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
1775
|
-
if (exprs.length === 1) {
|
|
1776
|
-
const [expr] = exprs;
|
|
1777
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1778
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1779
|
-
if (!dataType || !isVector(dataType))
|
|
1780
|
-
throw Error('Invalid expression');
|
|
1781
|
-
return { sql: element, dimension: dataType.dimension };
|
|
1782
|
-
}
|
|
1783
|
-
if (expr instanceof QueryValueExpression) {
|
|
1784
|
-
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
1785
|
-
throw Error('Invalid expression');
|
|
1786
|
-
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
1790
|
-
if (_.some(result, x => _.isNil(x)))
|
|
1791
|
-
throw Error('Invalid expression');
|
|
1792
|
-
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
1793
|
-
};
|
|
1794
|
-
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
1795
|
-
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
1796
|
-
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
1797
|
-
if (d1 !== d2)
|
|
1798
|
-
throw Error('Invalid expression');
|
|
1799
|
-
const operatorMap = {
|
|
1800
|
-
'$distance': sql `<->`,
|
|
1801
|
-
'$innerProduct': sql `<#>`,
|
|
1802
|
-
'$negInnerProduct': sql `<#>`,
|
|
1803
|
-
'$cosineDistance': sql `<=>`,
|
|
1804
|
-
'$rectilinearDistance': sql `<+>`,
|
|
1805
|
-
};
|
|
1806
|
-
const _expr = sql `
|
|
1807
|
-
CAST(
|
|
1808
|
-
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
1809
|
-
)
|
|
1810
|
-
${operatorMap[expr.type]}
|
|
1811
|
-
CAST(
|
|
1812
|
-
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
1813
|
-
)
|
|
1814
|
-
`;
|
|
1815
|
-
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
1816
|
-
};
|
|
1817
|
-
const matchType = (first, second) => {
|
|
1818
|
-
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
1819
|
-
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
1820
|
-
};
|
|
1821
|
-
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
1822
|
-
if (expr instanceof QueryCoditionalExpression) {
|
|
1823
|
-
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
1824
|
-
if (_.isEmpty(queries))
|
|
1825
|
-
return;
|
|
1826
|
-
switch (expr.type) {
|
|
1827
|
-
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
1828
|
-
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
1829
|
-
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
if (expr instanceof QueryComparisonExpression) {
|
|
1833
|
-
const operatorMap = {
|
|
1834
|
-
'$eq': nullSafeEqual(),
|
|
1835
|
-
'$ne': nullSafeNotEqual(),
|
|
1836
|
-
'$gt': sql `>`,
|
|
1837
|
-
'$gte': sql `>=`,
|
|
1838
|
-
'$lt': sql `<`,
|
|
1839
|
-
'$lte': sql `<=`,
|
|
1840
|
-
};
|
|
1841
|
-
if (isArrayExpression(expr.left) &&
|
|
1842
|
-
isArrayExpression(expr.right) &&
|
|
1843
|
-
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
1844
|
-
const _left = mapExpression(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1845
|
-
const _right = mapExpression(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1846
|
-
const mapped = _.compact(_.map(_.zip(_left, _right), ([l, r]) => matchType(l, r)));
|
|
1847
|
-
if (mapped.length === _left.length) {
|
|
1848
|
-
const [l, r] = _.unzip(mapped);
|
|
1849
|
-
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
1853
|
-
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
1854
|
-
if (_left && _right) {
|
|
1855
|
-
const matched = matchType(_left, _right);
|
|
1856
|
-
if (matched)
|
|
1857
|
-
return sql `${matched[0].sql} ${operatorMap[expr.type]} ${matched[1].sql}`;
|
|
1858
|
-
}
|
|
1859
|
-
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
1860
|
-
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
1861
|
-
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
1862
|
-
}
|
|
1863
|
-
if (expr instanceof QueryNotExpression) {
|
|
1864
|
-
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
1865
|
-
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
1866
|
-
}
|
|
1867
|
-
throw Error('Invalid expression');
|
|
1868
|
-
};
|
|
1869
|
-
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
1870
|
-
if (expr instanceof QueryDistanceExpression) {
|
|
1871
|
-
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1872
|
-
}
|
|
1873
|
-
return encodeBooleanExpression(compiler, parent, expr);
|
|
1874
|
-
};
|
|
1875
|
-
|
|
1876
2214
|
//
|
|
1877
2215
|
// populate.ts
|
|
1878
2216
|
//
|
|
@@ -1975,7 +2313,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1975
2313
|
];
|
|
1976
2314
|
if (!_.isEmpty(groupMatches?.[field])) {
|
|
1977
2315
|
for (const [key, expr] of _.entries(groupMatches[field])) {
|
|
1978
|
-
if (expr instanceof
|
|
2316
|
+
if (expr instanceof QueryZeroParamAccumulator) {
|
|
1979
2317
|
switch (expr.type) {
|
|
1980
2318
|
case '$count':
|
|
1981
2319
|
columns.push(sql `
|
|
@@ -1988,30 +2326,44 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1988
2326
|
break;
|
|
1989
2327
|
}
|
|
1990
2328
|
}
|
|
1991
|
-
else if (expr instanceof
|
|
1992
|
-
const op = {
|
|
1993
|
-
'$max': 'MAX',
|
|
1994
|
-
'$min': 'MIN',
|
|
1995
|
-
'$avg': 'AVG',
|
|
1996
|
-
'$sum': 'SUM',
|
|
1997
|
-
'$stdDevPop': 'STDDEV_POP',
|
|
1998
|
-
'$stdDevSamp': 'STDDEV_SAMP',
|
|
1999
|
-
'$varPop': 'VAR_POP',
|
|
2000
|
-
'$varSamp': 'VAR_SAMP',
|
|
2001
|
-
}[expr.type];
|
|
2329
|
+
else if (expr instanceof QueryUnaryAccumulator) {
|
|
2002
2330
|
if (!expr.expr)
|
|
2003
2331
|
throw Error('Invalid expression');
|
|
2004
|
-
const
|
|
2005
|
-
const value = _.first(exprs)?.sql;
|
|
2332
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.expr) ?? {};
|
|
2006
2333
|
if (!value)
|
|
2007
2334
|
throw Error('Invalid expression');
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2335
|
+
switch (expr.type) {
|
|
2336
|
+
case '$most':
|
|
2337
|
+
columns.push(sql `
|
|
2338
|
+
(
|
|
2339
|
+
SELECT MODE() WITHIN GROUP (ORDER BY (${value})) FROM (
|
|
2340
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2341
|
+
) ${{ identifier: populate.name }}
|
|
2342
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2343
|
+
`);
|
|
2344
|
+
break;
|
|
2345
|
+
default:
|
|
2346
|
+
{
|
|
2347
|
+
const op = {
|
|
2348
|
+
'$max': 'MAX',
|
|
2349
|
+
'$min': 'MIN',
|
|
2350
|
+
'$avg': 'AVG',
|
|
2351
|
+
'$sum': 'SUM',
|
|
2352
|
+
'$stdDevPop': 'STDDEV_POP',
|
|
2353
|
+
'$stdDevSamp': 'STDDEV_SAMP',
|
|
2354
|
+
'$varPop': 'VAR_POP',
|
|
2355
|
+
'$varSamp': 'VAR_SAMP',
|
|
2356
|
+
}[expr.type];
|
|
2357
|
+
columns.push(sql `
|
|
2358
|
+
(
|
|
2359
|
+
SELECT ${{ literal: op }}(${value}) FROM (
|
|
2360
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2361
|
+
) ${{ identifier: populate.name }}
|
|
2362
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2363
|
+
`);
|
|
2364
|
+
}
|
|
2365
|
+
break;
|
|
2366
|
+
}
|
|
2015
2367
|
}
|
|
2016
2368
|
else if (expr instanceof QueryPercentileAccumulator) {
|
|
2017
2369
|
const op = {
|
|
@@ -2020,8 +2372,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
2020
2372
|
}[expr.mode];
|
|
2021
2373
|
if (!expr.input)
|
|
2022
2374
|
throw Error('Invalid expression');
|
|
2023
|
-
const
|
|
2024
|
-
const value = _.first(exprs)?.sql;
|
|
2375
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.input) ?? {};
|
|
2025
2376
|
if (!value)
|
|
2026
2377
|
throw Error('Invalid expression');
|
|
2027
2378
|
columns.push(sql `
|
|
@@ -2609,6 +2960,36 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2609
2960
|
throw Error('Invalid expression');
|
|
2610
2961
|
};
|
|
2611
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
|
+
|
|
2612
2993
|
//
|
|
2613
2994
|
// relation.ts
|
|
2614
2995
|
//
|
|
@@ -2680,7 +3061,8 @@ const PostgresDialect = {
|
|
|
2680
3061
|
updateOperation,
|
|
2681
3062
|
selectPopulate,
|
|
2682
3063
|
encodeFieldExpression,
|
|
2683
|
-
|
|
3064
|
+
encodeBooleanExpression,
|
|
3065
|
+
encodeSortExpression,
|
|
2684
3066
|
encodePopulate,
|
|
2685
3067
|
encodeRelation,
|
|
2686
3068
|
encodeSortKey,
|