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
|
@@ -9,7 +9,7 @@ var QueryStream = require('pg-query-stream');
|
|
|
9
9
|
var utilsJs = require('@o2ter/utils-js');
|
|
10
10
|
var Decimal = require('decimal.js');
|
|
11
11
|
var utils = require('pg/lib/utils');
|
|
12
|
-
var validator = require('../../internals/validator-
|
|
12
|
+
var validator = require('../../internals/validator-mcBCJP4P.js');
|
|
13
13
|
require('@o2ter/crypto-js');
|
|
14
14
|
var _const = require('../../internals/const-C3I6cfav.js');
|
|
15
15
|
var random$1 = require('../../internals/random-nkOQ9U6S.js');
|
|
@@ -413,7 +413,7 @@ class QueryCompiler {
|
|
|
413
413
|
return this.dialect.encodeFieldExpression(this, parent, filter.field, filter.expr);
|
|
414
414
|
}
|
|
415
415
|
if (filter instanceof validator.QueryExpressionSelector) {
|
|
416
|
-
return this.dialect.
|
|
416
|
+
return this.dialect.encodeBooleanExpression(this, parent, filter.expr);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
_selectIncludes(className, includes) {
|
|
@@ -428,7 +428,7 @@ class QueryCompiler {
|
|
|
428
428
|
_encodeSort(sort, parent) {
|
|
429
429
|
if (_.isArray(sort)) {
|
|
430
430
|
return sql `${_.map(sort, ({ expr, order }) => {
|
|
431
|
-
const _expr = this.dialect.
|
|
431
|
+
const _expr = this.dialect.encodeSortExpression(this, parent, expr);
|
|
432
432
|
if (!_expr)
|
|
433
433
|
throw Error('Invalid expression');
|
|
434
434
|
return sql `${_expr} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}`;
|
|
@@ -540,6 +540,728 @@ class QueryCompiler {
|
|
|
540
540
|
}
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
+
//
|
|
544
|
+
// encode.ts
|
|
545
|
+
//
|
|
546
|
+
// The MIT License
|
|
547
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
548
|
+
//
|
|
549
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
550
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
551
|
+
// in the Software without restriction, including without limitation the rights
|
|
552
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
553
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
554
|
+
// furnished to do so, subject to the following conditions:
|
|
555
|
+
//
|
|
556
|
+
// The above copyright notice and this permission notice shall be included in
|
|
557
|
+
// all copies or substantial portions of the Software.
|
|
558
|
+
//
|
|
559
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
560
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
561
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
562
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
563
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
564
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
565
|
+
// THE SOFTWARE.
|
|
566
|
+
//
|
|
567
|
+
const _encodeJsonValue = (value) => {
|
|
568
|
+
if (_.isArray(value))
|
|
569
|
+
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
570
|
+
if (_.isPlainObject(value))
|
|
571
|
+
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
572
|
+
return sql `to_jsonb(${{ value }})`;
|
|
573
|
+
};
|
|
574
|
+
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
575
|
+
switch (index._typeof(dataType)) {
|
|
576
|
+
case 'decimal':
|
|
577
|
+
return sql `jsonb_build_object(
|
|
578
|
+
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
579
|
+
) AS ${{ identifier: colname }}`;
|
|
580
|
+
case 'date':
|
|
581
|
+
return sql `jsonb_build_object(
|
|
582
|
+
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
583
|
+
) AS ${{ identifier: colname }}`;
|
|
584
|
+
default:
|
|
585
|
+
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
const encodeType = (colname, dataType, value) => {
|
|
589
|
+
if (_.isNil(value))
|
|
590
|
+
return sql `NULL`;
|
|
591
|
+
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
592
|
+
case 'boolean':
|
|
593
|
+
if (_.isBoolean(value))
|
|
594
|
+
return sql `${{ value }}`;
|
|
595
|
+
break;
|
|
596
|
+
case 'number':
|
|
597
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
598
|
+
return sql `${{ value }}`;
|
|
599
|
+
if (value instanceof Decimal)
|
|
600
|
+
return sql `${{ value: value.toNumber() }}`;
|
|
601
|
+
break;
|
|
602
|
+
case 'decimal':
|
|
603
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
604
|
+
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
605
|
+
if (value instanceof Decimal)
|
|
606
|
+
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
607
|
+
break;
|
|
608
|
+
case 'string':
|
|
609
|
+
if (_.isString(value))
|
|
610
|
+
return sql `${{ value }}`;
|
|
611
|
+
break;
|
|
612
|
+
case 'string[]':
|
|
613
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
614
|
+
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
615
|
+
break;
|
|
616
|
+
case 'date':
|
|
617
|
+
if (_.isDate(value))
|
|
618
|
+
return sql `${{ value }}`;
|
|
619
|
+
break;
|
|
620
|
+
case 'object':
|
|
621
|
+
if (_.isPlainObject(value))
|
|
622
|
+
return sql `${{ value: index._encodeValue(value) }}`;
|
|
623
|
+
break;
|
|
624
|
+
case 'vector':
|
|
625
|
+
if (!_.isArray(value) || value.length !== index.dimensionOf(dataType))
|
|
626
|
+
break;
|
|
627
|
+
if (!_.every(value, x => _.isFinite(x)))
|
|
628
|
+
break;
|
|
629
|
+
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
630
|
+
case 'array':
|
|
631
|
+
if (!_.isArray(value))
|
|
632
|
+
break;
|
|
633
|
+
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(index._encodeValue(x)))}]::JSONB[]`;
|
|
634
|
+
case 'pointer':
|
|
635
|
+
if (value instanceof index.TObject && value.objectId)
|
|
636
|
+
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
637
|
+
break;
|
|
638
|
+
case 'relation':
|
|
639
|
+
if (_.isArray(value) && _.every(value, x => x instanceof index.TObject && x.objectId)) {
|
|
640
|
+
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
throw Error('Invalid data type');
|
|
645
|
+
};
|
|
646
|
+
const decodeType = (type, value) => {
|
|
647
|
+
switch (type) {
|
|
648
|
+
case 'boolean':
|
|
649
|
+
if (_.isBoolean(value))
|
|
650
|
+
return value;
|
|
651
|
+
if (value === 'true')
|
|
652
|
+
return true;
|
|
653
|
+
if (value === 'false')
|
|
654
|
+
return false;
|
|
655
|
+
break;
|
|
656
|
+
case 'number':
|
|
657
|
+
if (_.isNumber(value))
|
|
658
|
+
return value;
|
|
659
|
+
if (_.isString(value)) {
|
|
660
|
+
const float = parseFloat(value);
|
|
661
|
+
return _.isNaN(float) ? null : float;
|
|
662
|
+
}
|
|
663
|
+
break;
|
|
664
|
+
case 'decimal':
|
|
665
|
+
if (_.isString(value) || _.isNumber(value))
|
|
666
|
+
return new Decimal(value);
|
|
667
|
+
if (value instanceof Decimal)
|
|
668
|
+
return value;
|
|
669
|
+
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
670
|
+
return new Decimal(value.$decimal);
|
|
671
|
+
break;
|
|
672
|
+
case 'string':
|
|
673
|
+
if (_.isString(value))
|
|
674
|
+
return value;
|
|
675
|
+
break;
|
|
676
|
+
case 'string[]':
|
|
677
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
678
|
+
return value;
|
|
679
|
+
break;
|
|
680
|
+
case 'date':
|
|
681
|
+
if (_.isDate(value))
|
|
682
|
+
return value;
|
|
683
|
+
if (_.isString(value)) {
|
|
684
|
+
const date = new Date(value);
|
|
685
|
+
if (isFinite(date.valueOf()))
|
|
686
|
+
return date;
|
|
687
|
+
}
|
|
688
|
+
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
689
|
+
return new Date(value.$date);
|
|
690
|
+
break;
|
|
691
|
+
case 'object':
|
|
692
|
+
if (_.isPlainObject(value))
|
|
693
|
+
return index._decodeValue(value);
|
|
694
|
+
break;
|
|
695
|
+
case 'array':
|
|
696
|
+
if (_.isArray(value))
|
|
697
|
+
return index._decodeValue(value);
|
|
698
|
+
break;
|
|
699
|
+
case 'vector':
|
|
700
|
+
if (!_.isArray(value))
|
|
701
|
+
break;
|
|
702
|
+
if (_.every(value, x => _.isNumber(x))) {
|
|
703
|
+
return value;
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
//
|
|
711
|
+
// types.ts
|
|
712
|
+
//
|
|
713
|
+
// The MIT License
|
|
714
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
715
|
+
//
|
|
716
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
717
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
718
|
+
// in the Software without restriction, including without limitation the rights
|
|
719
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
720
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
721
|
+
// furnished to do so, subject to the following conditions:
|
|
722
|
+
//
|
|
723
|
+
// The above copyright notice and this permission notice shall be included in
|
|
724
|
+
// all copies or substantial portions of the Software.
|
|
725
|
+
//
|
|
726
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
727
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
728
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
729
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
730
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
731
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
732
|
+
// THE SOFTWARE.
|
|
733
|
+
//
|
|
734
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
735
|
+
const isArrayExpr = (expr) => {
|
|
736
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
737
|
+
return true;
|
|
738
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
739
|
+
return _.isArray(expr.value);
|
|
740
|
+
return false;
|
|
741
|
+
};
|
|
742
|
+
const arrayLength = (expr) => {
|
|
743
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
744
|
+
return expr.exprs.length;
|
|
745
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
746
|
+
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
747
|
+
return 0;
|
|
748
|
+
};
|
|
749
|
+
const mapExpr = (expr, callback) => {
|
|
750
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
751
|
+
return _.map(expr.exprs, x => callback(x));
|
|
752
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
753
|
+
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new validator.QueryValueExpression(x))) : [];
|
|
754
|
+
return [];
|
|
755
|
+
};
|
|
756
|
+
const zipExpr = (lhs, rhs) => {
|
|
757
|
+
const result = [];
|
|
758
|
+
for (const [l, r] of _.zip(lhs, rhs)) {
|
|
759
|
+
if (!l || !r)
|
|
760
|
+
return;
|
|
761
|
+
if (l.type === r.type)
|
|
762
|
+
result.push([l, r]);
|
|
763
|
+
else if (l.type === 'number' && r.type === 'decimal')
|
|
764
|
+
result.push([l, { type: 'decimal', sql: sql `CAST((${r.sql}) AS DECIMAL)` }]);
|
|
765
|
+
else if (l.type === 'decimal' && r.type === 'number')
|
|
766
|
+
result.push([{ type: 'decimal', sql: sql `CAST((${l.sql}) AS DECIMAL)` }, r]);
|
|
767
|
+
else
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
return result;
|
|
771
|
+
};
|
|
772
|
+
const typeCastExpr = (expr, type) => {
|
|
773
|
+
if (!expr)
|
|
774
|
+
return;
|
|
775
|
+
if (expr.type === type)
|
|
776
|
+
return expr;
|
|
777
|
+
if (expr.type === 'number' && type === 'decimal')
|
|
778
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DECIMAL)` };
|
|
779
|
+
if (expr.type === 'decimal' && type === 'number')
|
|
780
|
+
return { type, sql: sql `CAST((${expr.sql}) AS DOUBLE PRECISION)` };
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
//
|
|
784
|
+
// vector.ts
|
|
785
|
+
//
|
|
786
|
+
// The MIT License
|
|
787
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
788
|
+
//
|
|
789
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
790
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
791
|
+
// in the Software without restriction, including without limitation the rights
|
|
792
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
793
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
794
|
+
// furnished to do so, subject to the following conditions:
|
|
795
|
+
//
|
|
796
|
+
// The above copyright notice and this permission notice shall be included in
|
|
797
|
+
// all copies or substantial portions of the Software.
|
|
798
|
+
//
|
|
799
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
800
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
801
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
802
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
803
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
804
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
805
|
+
// THE SOFTWARE.
|
|
806
|
+
//
|
|
807
|
+
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
808
|
+
if (exprs.length === 1) {
|
|
809
|
+
const [expr] = exprs;
|
|
810
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
811
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
812
|
+
if (!dataType || !index.isVector(dataType))
|
|
813
|
+
throw Error('Invalid expression');
|
|
814
|
+
return { sql: element, dimension: dataType.dimension };
|
|
815
|
+
}
|
|
816
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
817
|
+
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
818
|
+
throw Error('Invalid expression');
|
|
819
|
+
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const result = _.compact(_.map(exprs, x => typeCastExpr(encodeTypedQueryExpression(compiler, parent, x), 'number')?.sql));
|
|
823
|
+
if (result.length !== exprs.length)
|
|
824
|
+
throw Error('Invalid expression');
|
|
825
|
+
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
826
|
+
};
|
|
827
|
+
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
828
|
+
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
829
|
+
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
830
|
+
if (d1 !== d2)
|
|
831
|
+
throw Error('Invalid expression');
|
|
832
|
+
const operatorMap = {
|
|
833
|
+
'$distance': sql `<->`,
|
|
834
|
+
'$innerProduct': sql `<#>`,
|
|
835
|
+
'$negInnerProduct': sql `<#>`,
|
|
836
|
+
'$cosineDistance': sql `<=>`,
|
|
837
|
+
'$rectilinearDistance': sql `<+>`,
|
|
838
|
+
};
|
|
839
|
+
const _expr = sql `
|
|
840
|
+
CAST(
|
|
841
|
+
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
842
|
+
)
|
|
843
|
+
${operatorMap[expr.type]}
|
|
844
|
+
CAST(
|
|
845
|
+
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
846
|
+
)
|
|
847
|
+
`;
|
|
848
|
+
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
//
|
|
852
|
+
// index.ts
|
|
853
|
+
//
|
|
854
|
+
// The MIT License
|
|
855
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
856
|
+
//
|
|
857
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
858
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
859
|
+
// in the Software without restriction, including without limitation the rights
|
|
860
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
861
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
862
|
+
// furnished to do so, subject to the following conditions:
|
|
863
|
+
//
|
|
864
|
+
// The above copyright notice and this permission notice shall be included in
|
|
865
|
+
// all copies or substantial portions of the Software.
|
|
866
|
+
//
|
|
867
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
868
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
869
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
870
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
871
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
872
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
873
|
+
// THE SOFTWARE.
|
|
874
|
+
//
|
|
875
|
+
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
876
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
877
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
878
|
+
const _dataType = dataType ? index._typeof(dataType) : null;
|
|
879
|
+
if (!_dataType || !_PrimitiveValue.includes(_dataType))
|
|
880
|
+
return;
|
|
881
|
+
return { type: _dataType, sql: element };
|
|
882
|
+
}
|
|
883
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
884
|
+
if (_.isBoolean(expr.value))
|
|
885
|
+
return { type: 'boolean', sql: sql `${{ value: expr.value }}` };
|
|
886
|
+
if (_.isNumber(expr.value))
|
|
887
|
+
return { type: 'number', sql: sql `${{ value: expr.value }}` };
|
|
888
|
+
if (expr.value instanceof Decimal)
|
|
889
|
+
return { type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` };
|
|
890
|
+
if (_.isString(expr.value))
|
|
891
|
+
return { type: 'string', sql: sql `${{ value: expr.value }}` };
|
|
892
|
+
if (_.isDate(expr.value))
|
|
893
|
+
return { type: 'date', sql: sql `${{ value: expr.value }}` };
|
|
894
|
+
}
|
|
895
|
+
if (expr instanceof validator.QueryCoditionalExpression ||
|
|
896
|
+
expr instanceof validator.QueryComparisonExpression ||
|
|
897
|
+
expr instanceof validator.QueryNotExpression) {
|
|
898
|
+
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
899
|
+
if (value)
|
|
900
|
+
return { type: 'boolean', sql: value };
|
|
901
|
+
}
|
|
902
|
+
if (expr instanceof validator.QueryDistanceExpression) {
|
|
903
|
+
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
904
|
+
return { type: 'number', sql: value };
|
|
905
|
+
}
|
|
906
|
+
if (expr instanceof validator.QueryZeroParamExpression) {
|
|
907
|
+
switch (expr.type) {
|
|
908
|
+
case '$rand': return { type: 'number', sql: sql `RANDOM()` };
|
|
909
|
+
case '$now': return { type: 'date', sql: sql `CAST(NOW() AS TIMESTAMP(3) WITH TIME ZONE)` };
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (expr instanceof validator.QueryUnaryExpression) {
|
|
913
|
+
const value = encodeTypedQueryExpression(compiler, parent, expr.expr);
|
|
914
|
+
if (!value)
|
|
915
|
+
return;
|
|
916
|
+
switch (expr.type) {
|
|
917
|
+
case '$abs':
|
|
918
|
+
case '$neg':
|
|
919
|
+
case '$sqrt':
|
|
920
|
+
case '$cbrt':
|
|
921
|
+
case '$ceil':
|
|
922
|
+
case '$floor':
|
|
923
|
+
case '$round':
|
|
924
|
+
case '$exp':
|
|
925
|
+
case '$ln':
|
|
926
|
+
case '$log10':
|
|
927
|
+
case '$sin':
|
|
928
|
+
case '$cos':
|
|
929
|
+
case '$tan':
|
|
930
|
+
case '$asin':
|
|
931
|
+
case '$acos':
|
|
932
|
+
case '$atan':
|
|
933
|
+
case '$asinh':
|
|
934
|
+
case '$acosh':
|
|
935
|
+
case '$atanh':
|
|
936
|
+
case '$sinh':
|
|
937
|
+
case '$cosh':
|
|
938
|
+
case '$tanh':
|
|
939
|
+
case '$degrees':
|
|
940
|
+
case '$radians':
|
|
941
|
+
case '$sign':
|
|
942
|
+
{
|
|
943
|
+
const op = {
|
|
944
|
+
'$abs': 'ABS',
|
|
945
|
+
'$neg': '-',
|
|
946
|
+
'$sqrt': 'SQRT',
|
|
947
|
+
'$cbrt': 'CBRT',
|
|
948
|
+
'$ceil': 'CEIL',
|
|
949
|
+
'$floor': 'FLOOR',
|
|
950
|
+
'$round': 'ROUND',
|
|
951
|
+
'$exp': 'EXP',
|
|
952
|
+
'$ln': 'LN',
|
|
953
|
+
'$log10': 'LOG10',
|
|
954
|
+
'$sin': 'SIN',
|
|
955
|
+
'$cos': 'COS',
|
|
956
|
+
'$tan': 'TAN',
|
|
957
|
+
'$asin': 'ASIN',
|
|
958
|
+
'$acos': 'ACOS',
|
|
959
|
+
'$atan': 'ATAN',
|
|
960
|
+
'$asinh': 'ASINH',
|
|
961
|
+
'$acosh': 'ACOSH',
|
|
962
|
+
'$atanh': 'ATANH',
|
|
963
|
+
'$sinh': 'SINH',
|
|
964
|
+
'$cosh': 'COSH',
|
|
965
|
+
'$tanh': 'TANH',
|
|
966
|
+
'$degrees': 'DEGREES',
|
|
967
|
+
'$radians': 'RADIANS',
|
|
968
|
+
'$sign': 'SIGN',
|
|
969
|
+
}[expr.type];
|
|
970
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
971
|
+
return;
|
|
972
|
+
return { type: 'number', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
973
|
+
}
|
|
974
|
+
case '$log2':
|
|
975
|
+
if (!_.includes(['number', 'decimal'], value.type))
|
|
976
|
+
return;
|
|
977
|
+
return { type: value.type, sql: sql `(LOG(${value.sql}) / LOG(2))` };
|
|
978
|
+
case '$size':
|
|
979
|
+
if (!_.includes(['string', 'string[]', 'array'], value.type))
|
|
980
|
+
return;
|
|
981
|
+
return { type: 'number', sql: sql `LENGTH(${value.sql})` };
|
|
982
|
+
case '$lower':
|
|
983
|
+
case '$upper':
|
|
984
|
+
{
|
|
985
|
+
const op = {
|
|
986
|
+
'$lower': 'LOWER',
|
|
987
|
+
'$upper': 'UPPER',
|
|
988
|
+
}[expr.type];
|
|
989
|
+
if (value.type !== 'string')
|
|
990
|
+
return;
|
|
991
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${value.sql})` };
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (expr instanceof validator.QueryBinaryExpression) {
|
|
996
|
+
const left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
997
|
+
const right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
998
|
+
if (!left || !right)
|
|
999
|
+
return;
|
|
1000
|
+
switch (expr.type) {
|
|
1001
|
+
case '$divide':
|
|
1002
|
+
case '$subtract':
|
|
1003
|
+
{
|
|
1004
|
+
const op = {
|
|
1005
|
+
'$divide': '/',
|
|
1006
|
+
'$subtract': '-',
|
|
1007
|
+
}[expr.type];
|
|
1008
|
+
if (left.type === right.type) {
|
|
1009
|
+
return { type: left.type, sql: sql `(${left.sql}) ${{ literal: op }} (${right.sql})` };
|
|
1010
|
+
}
|
|
1011
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1012
|
+
return { type: 'decimal', sql: sql `CAST((${left.sql}) AS DECIMAL) ${{ literal: op }} (${right.sql})` };
|
|
1013
|
+
}
|
|
1014
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1015
|
+
return { type: 'decimal', sql: sql `(${left.sql}) ${{ literal: op }} CAST((${right.sql}) AS DECIMAL)` };
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
case '$log':
|
|
1020
|
+
{
|
|
1021
|
+
if (left.type === right.type) {
|
|
1022
|
+
return { type: left.type, sql: sql `(LOG(${left.sql}) / LOG(${right.sql}))` };
|
|
1023
|
+
}
|
|
1024
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1025
|
+
return { type: 'decimal', sql: sql `(LOG(${left.sql}) / LOG(CAST((${right.sql}) AS DECIMAL)))` };
|
|
1026
|
+
}
|
|
1027
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1028
|
+
return { type: 'decimal', sql: sql `(LOG(CAST((${left.sql}) AS DECIMAL) / LOG(${right.sql}))` };
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
break;
|
|
1032
|
+
case '$pow':
|
|
1033
|
+
case '$atan2':
|
|
1034
|
+
{
|
|
1035
|
+
const op = {
|
|
1036
|
+
'$pow': 'POWER',
|
|
1037
|
+
'$atan2': 'ATAN2',
|
|
1038
|
+
}[expr.type];
|
|
1039
|
+
if (left.type === right.type) {
|
|
1040
|
+
return { type: left.type, sql: sql `${{ literal: op }}((${left.sql}), (${right.sql}))` };
|
|
1041
|
+
}
|
|
1042
|
+
if (left.type === 'decimal' && right.type === 'number') {
|
|
1043
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}((${left.sql}), CAST((${right.sql}) AS DECIMAL))` };
|
|
1044
|
+
}
|
|
1045
|
+
if (left.type === 'number' && right.type === 'decimal') {
|
|
1046
|
+
return { type: 'decimal', sql: sql `${{ literal: op }}(CAST((${left.sql}) AS DECIMAL), (${right.sql}))` };
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
break;
|
|
1050
|
+
case '$trim':
|
|
1051
|
+
case '$ltrim':
|
|
1052
|
+
case '$rtrim':
|
|
1053
|
+
{
|
|
1054
|
+
const op = {
|
|
1055
|
+
'$trim': 'TRIM',
|
|
1056
|
+
'$ltrim': 'LTRIM',
|
|
1057
|
+
'$rtrim': 'RTRIM',
|
|
1058
|
+
}[expr.type];
|
|
1059
|
+
if (left?.type !== 'string' || right?.type !== 'string')
|
|
1060
|
+
return;
|
|
1061
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1062
|
+
}
|
|
1063
|
+
case '$first':
|
|
1064
|
+
case '$last':
|
|
1065
|
+
{
|
|
1066
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1067
|
+
return;
|
|
1068
|
+
if (left?.type === 'string') {
|
|
1069
|
+
const op = {
|
|
1070
|
+
'$first': 'LEFT',
|
|
1071
|
+
'$last': 'RIGHT',
|
|
1072
|
+
}[expr.type];
|
|
1073
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, ${right.sql})` };
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
break;
|
|
1077
|
+
case '$ldrop':
|
|
1078
|
+
case '$rdrop':
|
|
1079
|
+
{
|
|
1080
|
+
if (!_.includes(['number', 'decimal'], right.type))
|
|
1081
|
+
return;
|
|
1082
|
+
if (left?.type === 'string') {
|
|
1083
|
+
const op = {
|
|
1084
|
+
'$ldrop': 'RIGHT',
|
|
1085
|
+
'$rdrop': 'LEFT',
|
|
1086
|
+
}[expr.type];
|
|
1087
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${left.sql}, -(${right.sql}))` };
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
break;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (expr instanceof validator.QueryListExpression) {
|
|
1094
|
+
const values = _.compact(_.map(expr.exprs, x => encodeTypedQueryExpression(compiler, parent, x)));
|
|
1095
|
+
if (values.length !== expr.exprs.length)
|
|
1096
|
+
return;
|
|
1097
|
+
switch (expr.type) {
|
|
1098
|
+
case '$add':
|
|
1099
|
+
case '$multiply':
|
|
1100
|
+
{
|
|
1101
|
+
const op = {
|
|
1102
|
+
'$add': '+',
|
|
1103
|
+
'$multiply': '*',
|
|
1104
|
+
}[expr.type];
|
|
1105
|
+
if (_.every(values, x => x.type === 'number')) {
|
|
1106
|
+
return { type: 'number', sql: sql `${{ literal: _.map(values, x => x.sql), separator: ` ${op} ` }}` };
|
|
1107
|
+
}
|
|
1108
|
+
if (_.every(values, x => x.type === 'number' || x.type === 'decimal')) {
|
|
1109
|
+
return { type: 'decimal', sql: sql `${{ literal: _.map(values, x => x.type === 'decimal' ? x.sql : sql `CAST((${x.sql}) AS DECIMAL)`), separator: ` ${op} ` }}` };
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
break;
|
|
1113
|
+
case '$ifNull':
|
|
1114
|
+
{
|
|
1115
|
+
const type = values[0].type;
|
|
1116
|
+
if (_.every(values, x => x.type === type)) {
|
|
1117
|
+
return { type, sql: sql `COALESCE(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
break;
|
|
1121
|
+
case '$concat':
|
|
1122
|
+
{
|
|
1123
|
+
if (_.every(values, x => x.type === 'string')) {
|
|
1124
|
+
return { type: 'string', sql: sql `CONCAT(${{ literal: _.map(values, x => x.sql) }})` };
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
break;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (expr instanceof validator.QueryTernaryExpression) {
|
|
1131
|
+
const first = encodeTypedQueryExpression(compiler, parent, expr.first);
|
|
1132
|
+
const second = encodeTypedQueryExpression(compiler, parent, expr.second);
|
|
1133
|
+
const last = encodeTypedQueryExpression(compiler, parent, expr.last);
|
|
1134
|
+
if (!first || !second || !last)
|
|
1135
|
+
return;
|
|
1136
|
+
switch (expr.type) {
|
|
1137
|
+
case '$lpad':
|
|
1138
|
+
case '$rpad':
|
|
1139
|
+
{
|
|
1140
|
+
const op = {
|
|
1141
|
+
'$lpad': 'LPAD',
|
|
1142
|
+
'$rpad': 'RPAD',
|
|
1143
|
+
}[expr.type];
|
|
1144
|
+
if (first?.type !== 'string' || last?.type !== 'string')
|
|
1145
|
+
return;
|
|
1146
|
+
if (!_.includes(['number', 'decimal'], second.type))
|
|
1147
|
+
return;
|
|
1148
|
+
return { type: 'string', sql: sql `${{ literal: op }}(${first.sql}, ${second.sql}, ${last.sql})` };
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (expr instanceof validator.QueryCondExpression) {
|
|
1153
|
+
const branches = _.flatMap(_.map(expr.branch, x => ({
|
|
1154
|
+
case: encodeTypedQueryExpression(compiler, parent, x.case),
|
|
1155
|
+
then: encodeTypedQueryExpression(compiler, parent, x.then),
|
|
1156
|
+
})), x => x.case && x.then ? ({ case: x.case, then: x.then }) : []);
|
|
1157
|
+
if (branches.length !== expr.branch.length)
|
|
1158
|
+
return;
|
|
1159
|
+
const defaultCase = encodeTypedQueryExpression(compiler, parent, expr.default);
|
|
1160
|
+
if (!defaultCase)
|
|
1161
|
+
return;
|
|
1162
|
+
if (!_.every(branches, x => x.case.type === 'boolean' && x.then.type === defaultCase.type))
|
|
1163
|
+
return;
|
|
1164
|
+
return {
|
|
1165
|
+
type: defaultCase.type,
|
|
1166
|
+
sql: sql `CASE ${{
|
|
1167
|
+
literal: _.map(branches, x => sql `WHEN (${x.case.sql}) THEN (${x.then.sql})`),
|
|
1168
|
+
separator: ' '
|
|
1169
|
+
}} ELSE (${defaultCase.sql}) END`,
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
1174
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
1175
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1176
|
+
if (dataType && index.isPrimitive(dataType)) {
|
|
1177
|
+
switch (index._typeof(dataType)) {
|
|
1178
|
+
case 'boolean': return sql `to_jsonb(${element})`;
|
|
1179
|
+
case 'number': return sql `to_jsonb(${element})`;
|
|
1180
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
1181
|
+
case 'string': return sql `to_jsonb(${element})`;
|
|
1182
|
+
case 'string[]': return sql `to_jsonb(${element})`;
|
|
1183
|
+
case 'date': return sql `jsonb_build_object(
|
|
1184
|
+
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1185
|
+
)`;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return sql `${element}`;
|
|
1189
|
+
}
|
|
1190
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
1191
|
+
return _encodeJsonValue(index._encodeValue(expr.value));
|
|
1192
|
+
}
|
|
1193
|
+
if (expr instanceof validator.QueryArrayExpression) {
|
|
1194
|
+
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
1195
|
+
}
|
|
1196
|
+
const typed = encodeTypedQueryExpression(compiler, parent, expr);
|
|
1197
|
+
if (!typed)
|
|
1198
|
+
throw Error('Invalid expression');
|
|
1199
|
+
switch (typed.type) {
|
|
1200
|
+
case 'boolean': return sql `to_jsonb(${typed.sql})`;
|
|
1201
|
+
case 'number': return sql `to_jsonb(${typed.sql})`;
|
|
1202
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${typed.sql} AS TEXT))`;
|
|
1203
|
+
case 'string': return sql `to_jsonb(${typed.sql})`;
|
|
1204
|
+
case 'date': return sql `jsonb_build_object(
|
|
1205
|
+
'$date', to_char(${typed.sql} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1206
|
+
)`;
|
|
1207
|
+
default: throw Error('Invalid expression');
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
1211
|
+
if (expr instanceof validator.QueryCoditionalExpression) {
|
|
1212
|
+
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
1213
|
+
if (_.isEmpty(queries))
|
|
1214
|
+
return;
|
|
1215
|
+
switch (expr.type) {
|
|
1216
|
+
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
1217
|
+
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
1218
|
+
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (expr instanceof validator.QueryComparisonExpression) {
|
|
1222
|
+
const operatorMap = {
|
|
1223
|
+
'$eq': nullSafeEqual(),
|
|
1224
|
+
'$ne': nullSafeNotEqual(),
|
|
1225
|
+
'$gt': sql `>`,
|
|
1226
|
+
'$gte': sql `>=`,
|
|
1227
|
+
'$lt': sql `<`,
|
|
1228
|
+
'$lte': sql `<=`,
|
|
1229
|
+
};
|
|
1230
|
+
if (isArrayExpr(expr.left) &&
|
|
1231
|
+
isArrayExpr(expr.right) &&
|
|
1232
|
+
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
1233
|
+
const _left = mapExpr(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1234
|
+
const _right = mapExpr(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1235
|
+
const zipped = zipExpr(_left, _right) ?? [];
|
|
1236
|
+
if (_left.length === zipped.length) {
|
|
1237
|
+
const [l, r] = _.unzip(zipped);
|
|
1238
|
+
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
1242
|
+
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
1243
|
+
if (_left && _right) {
|
|
1244
|
+
if (_left.type === _right.type) {
|
|
1245
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1246
|
+
}
|
|
1247
|
+
if (_left.type === 'decimal' && _right.type === 'number') {
|
|
1248
|
+
return sql `CAST((${_left.sql}) AS DOUBLE PRECISION) ${operatorMap[expr.type]} (${_right.sql})`;
|
|
1249
|
+
}
|
|
1250
|
+
if (_left.type === 'number' && _right.type === 'decimal') {
|
|
1251
|
+
return sql `(${_left.sql}) ${operatorMap[expr.type]} CAST((${_right.sql}) AS DOUBLE PRECISION)`;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
1255
|
+
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
1256
|
+
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
1257
|
+
}
|
|
1258
|
+
if (expr instanceof validator.QueryNotExpression) {
|
|
1259
|
+
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
1260
|
+
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
1261
|
+
}
|
|
1262
|
+
throw Error('Invalid expression');
|
|
1263
|
+
};
|
|
1264
|
+
|
|
543
1265
|
//
|
|
544
1266
|
// storage.ts
|
|
545
1267
|
//
|
|
@@ -705,8 +1427,17 @@ class SqlStorage {
|
|
|
705
1427
|
const self = this;
|
|
706
1428
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
707
1429
|
const _matchesType = self._matchesType(compiler, query);
|
|
708
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, {
|
|
709
|
-
|
|
1430
|
+
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) => {
|
|
1431
|
+
if (!opts?.weight)
|
|
1432
|
+
return { sort: sql `ORDER BY ${self.dialect.random()}` };
|
|
1433
|
+
const weight = encodeTypedQueryExpression(compiler, {
|
|
1434
|
+
name: fetchName,
|
|
1435
|
+
className: query.className,
|
|
1436
|
+
groupMatches: query.groupMatches,
|
|
1437
|
+
}, opts.weight);
|
|
1438
|
+
if (!weight)
|
|
1439
|
+
throw Error('Invalid expression');
|
|
1440
|
+
return { sort: sql `ORDER BY ${self.dialect.random(weight.sql)}` };
|
|
710
1441
|
});
|
|
711
1442
|
return (async function* () {
|
|
712
1443
|
const objects = self.query(_query);
|
|
@@ -951,8 +1682,8 @@ const encodeSortKey = (compiler, parent, key) => {
|
|
|
951
1682
|
const { element } = fetchElement(compiler, parent, key);
|
|
952
1683
|
return element;
|
|
953
1684
|
};
|
|
954
|
-
const random = (
|
|
955
|
-
return
|
|
1685
|
+
const random = (weight) => {
|
|
1686
|
+
return weight ? sql `-ln(random()) / ${weight}` : sql `random()`;
|
|
956
1687
|
};
|
|
957
1688
|
|
|
958
1689
|
//
|
|
@@ -1169,201 +1900,34 @@ class PostgresDriver extends PostgresClientDriver {
|
|
|
1169
1900
|
this.database = database;
|
|
1170
1901
|
}
|
|
1171
1902
|
async shutdown() {
|
|
1172
|
-
await this._release_pubsub();
|
|
1173
|
-
await this.database.end();
|
|
1174
|
-
}
|
|
1175
|
-
_init_pubsub() {
|
|
1176
|
-
if (this.pubsub || this.database.ending || this.database.ended)
|
|
1177
|
-
return;
|
|
1178
|
-
this.pubsub = new PostgresPubSub(this.database.connect());
|
|
1179
|
-
}
|
|
1180
|
-
async _release_pubsub() {
|
|
1181
|
-
const pubsub = this.pubsub;
|
|
1182
|
-
this.pubsub = undefined;
|
|
1183
|
-
await pubsub?.shutdown();
|
|
1184
|
-
}
|
|
1185
|
-
subscribe(channel, callback) {
|
|
1186
|
-
this._init_pubsub();
|
|
1187
|
-
if (!this.pubsub)
|
|
1188
|
-
return () => void 0;
|
|
1189
|
-
const release = this.pubsub.subscribe(({ channel: _channel, payload }) => {
|
|
1190
|
-
if (_channel === channel)
|
|
1191
|
-
callback(payload);
|
|
1192
|
-
});
|
|
1193
|
-
return () => {
|
|
1194
|
-
release();
|
|
1195
|
-
if (this.pubsub?.isEmpty())
|
|
1196
|
-
this._release_pubsub();
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
//
|
|
1202
|
-
// encode.ts
|
|
1203
|
-
//
|
|
1204
|
-
// The MIT License
|
|
1205
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1206
|
-
//
|
|
1207
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1208
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1209
|
-
// in the Software without restriction, including without limitation the rights
|
|
1210
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1211
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1212
|
-
// furnished to do so, subject to the following conditions:
|
|
1213
|
-
//
|
|
1214
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1215
|
-
// all copies or substantial portions of the Software.
|
|
1216
|
-
//
|
|
1217
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1218
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1219
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1220
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1221
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1222
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1223
|
-
// THE SOFTWARE.
|
|
1224
|
-
//
|
|
1225
|
-
const _encodeJsonValue = (value) => {
|
|
1226
|
-
if (_.isArray(value))
|
|
1227
|
-
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
1228
|
-
if (_.isPlainObject(value))
|
|
1229
|
-
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
1230
|
-
return sql `to_jsonb(${{ value }})`;
|
|
1231
|
-
};
|
|
1232
|
-
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
1233
|
-
switch (index._typeof(dataType)) {
|
|
1234
|
-
case 'decimal':
|
|
1235
|
-
return sql `jsonb_build_object(
|
|
1236
|
-
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
1237
|
-
) AS ${{ identifier: colname }}`;
|
|
1238
|
-
case 'date':
|
|
1239
|
-
return sql `jsonb_build_object(
|
|
1240
|
-
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1241
|
-
) AS ${{ identifier: colname }}`;
|
|
1242
|
-
default:
|
|
1243
|
-
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
1244
|
-
}
|
|
1245
|
-
};
|
|
1246
|
-
const encodeType = (colname, dataType, value) => {
|
|
1247
|
-
if (_.isNil(value))
|
|
1248
|
-
return sql `NULL`;
|
|
1249
|
-
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
1250
|
-
case 'boolean':
|
|
1251
|
-
if (_.isBoolean(value))
|
|
1252
|
-
return sql `${{ value }}`;
|
|
1253
|
-
break;
|
|
1254
|
-
case 'number':
|
|
1255
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1256
|
-
return sql `${{ value }}`;
|
|
1257
|
-
if (value instanceof Decimal)
|
|
1258
|
-
return sql `${{ value: value.toNumber() }}`;
|
|
1259
|
-
break;
|
|
1260
|
-
case 'decimal':
|
|
1261
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1262
|
-
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
1263
|
-
if (value instanceof Decimal)
|
|
1264
|
-
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
1265
|
-
break;
|
|
1266
|
-
case 'string':
|
|
1267
|
-
if (_.isString(value))
|
|
1268
|
-
return sql `${{ value }}`;
|
|
1269
|
-
break;
|
|
1270
|
-
case 'string[]':
|
|
1271
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1272
|
-
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
1273
|
-
break;
|
|
1274
|
-
case 'date':
|
|
1275
|
-
if (_.isDate(value))
|
|
1276
|
-
return sql `${{ value }}`;
|
|
1277
|
-
break;
|
|
1278
|
-
case 'object':
|
|
1279
|
-
if (_.isPlainObject(value))
|
|
1280
|
-
return sql `${{ value: index._encodeValue(value) }}`;
|
|
1281
|
-
break;
|
|
1282
|
-
case 'vector':
|
|
1283
|
-
if (!_.isArray(value) || value.length !== index.dimensionOf(dataType))
|
|
1284
|
-
break;
|
|
1285
|
-
if (!_.every(value, x => _.isFinite(x)))
|
|
1286
|
-
break;
|
|
1287
|
-
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
1288
|
-
case 'array':
|
|
1289
|
-
if (!_.isArray(value))
|
|
1290
|
-
break;
|
|
1291
|
-
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(index._encodeValue(x)))}]::JSONB[]`;
|
|
1292
|
-
case 'pointer':
|
|
1293
|
-
if (value instanceof index.TObject && value.objectId)
|
|
1294
|
-
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
1295
|
-
break;
|
|
1296
|
-
case 'relation':
|
|
1297
|
-
if (_.isArray(value) && _.every(value, x => x instanceof index.TObject && x.objectId)) {
|
|
1298
|
-
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
1299
|
-
}
|
|
1300
|
-
break;
|
|
1301
|
-
}
|
|
1302
|
-
throw Error('Invalid data type');
|
|
1303
|
-
};
|
|
1304
|
-
const decodeType = (type, value) => {
|
|
1305
|
-
switch (type) {
|
|
1306
|
-
case 'boolean':
|
|
1307
|
-
if (_.isBoolean(value))
|
|
1308
|
-
return value;
|
|
1309
|
-
if (value === 'true')
|
|
1310
|
-
return true;
|
|
1311
|
-
if (value === 'false')
|
|
1312
|
-
return false;
|
|
1313
|
-
break;
|
|
1314
|
-
case 'number':
|
|
1315
|
-
if (_.isNumber(value))
|
|
1316
|
-
return value;
|
|
1317
|
-
if (_.isString(value)) {
|
|
1318
|
-
const float = parseFloat(value);
|
|
1319
|
-
return _.isNaN(float) ? null : float;
|
|
1320
|
-
}
|
|
1321
|
-
break;
|
|
1322
|
-
case 'decimal':
|
|
1323
|
-
if (_.isString(value) || _.isNumber(value))
|
|
1324
|
-
return new Decimal(value);
|
|
1325
|
-
if (value instanceof Decimal)
|
|
1326
|
-
return value;
|
|
1327
|
-
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
1328
|
-
return new Decimal(value.$decimal);
|
|
1329
|
-
break;
|
|
1330
|
-
case 'string':
|
|
1331
|
-
if (_.isString(value))
|
|
1332
|
-
return value;
|
|
1333
|
-
break;
|
|
1334
|
-
case 'string[]':
|
|
1335
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1336
|
-
return value;
|
|
1337
|
-
break;
|
|
1338
|
-
case 'date':
|
|
1339
|
-
if (_.isDate(value))
|
|
1340
|
-
return value;
|
|
1341
|
-
if (_.isString(value)) {
|
|
1342
|
-
const date = new Date(value);
|
|
1343
|
-
if (isFinite(date.valueOf()))
|
|
1344
|
-
return date;
|
|
1345
|
-
}
|
|
1346
|
-
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
1347
|
-
return new Date(value.$date);
|
|
1348
|
-
break;
|
|
1349
|
-
case 'object':
|
|
1350
|
-
if (_.isPlainObject(value))
|
|
1351
|
-
return index._decodeValue(value);
|
|
1352
|
-
break;
|
|
1353
|
-
case 'array':
|
|
1354
|
-
if (_.isArray(value))
|
|
1355
|
-
return index._decodeValue(value);
|
|
1356
|
-
break;
|
|
1357
|
-
case 'vector':
|
|
1358
|
-
if (!_.isArray(value))
|
|
1359
|
-
break;
|
|
1360
|
-
if (_.every(value, x => _.isNumber(x))) {
|
|
1361
|
-
return value;
|
|
1362
|
-
}
|
|
1363
|
-
break;
|
|
1903
|
+
await this._release_pubsub();
|
|
1904
|
+
await this.database.end();
|
|
1364
1905
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1906
|
+
_init_pubsub() {
|
|
1907
|
+
if (this.pubsub || this.database.ending || this.database.ended)
|
|
1908
|
+
return;
|
|
1909
|
+
this.pubsub = new PostgresPubSub(this.database.connect());
|
|
1910
|
+
}
|
|
1911
|
+
async _release_pubsub() {
|
|
1912
|
+
const pubsub = this.pubsub;
|
|
1913
|
+
this.pubsub = undefined;
|
|
1914
|
+
await pubsub?.shutdown();
|
|
1915
|
+
}
|
|
1916
|
+
subscribe(channel, callback) {
|
|
1917
|
+
this._init_pubsub();
|
|
1918
|
+
if (!this.pubsub)
|
|
1919
|
+
return () => void 0;
|
|
1920
|
+
const release = this.pubsub.subscribe(({ channel: _channel, payload }) => {
|
|
1921
|
+
if (_channel === channel)
|
|
1922
|
+
callback(payload);
|
|
1923
|
+
});
|
|
1924
|
+
return () => {
|
|
1925
|
+
release();
|
|
1926
|
+
if (this.pubsub?.isEmpty())
|
|
1927
|
+
this._release_pubsub();
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1367
1931
|
|
|
1368
1932
|
//
|
|
1369
1933
|
// update.ts
|
|
@@ -1651,232 +2215,6 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1651
2215
|
throw Error('Invalid update operation');
|
|
1652
2216
|
};
|
|
1653
2217
|
|
|
1654
|
-
//
|
|
1655
|
-
// expressions.ts
|
|
1656
|
-
//
|
|
1657
|
-
// The MIT License
|
|
1658
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1659
|
-
//
|
|
1660
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1661
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1662
|
-
// in the Software without restriction, including without limitation the rights
|
|
1663
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1664
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1665
|
-
// furnished to do so, subject to the following conditions:
|
|
1666
|
-
//
|
|
1667
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1668
|
-
// all copies or substantial portions of the Software.
|
|
1669
|
-
//
|
|
1670
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1671
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1672
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1673
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1674
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1675
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1676
|
-
// THE SOFTWARE.
|
|
1677
|
-
//
|
|
1678
|
-
const isArrayExpression = (expr) => {
|
|
1679
|
-
if (expr instanceof validator.QueryArrayExpression)
|
|
1680
|
-
return true;
|
|
1681
|
-
if (expr instanceof validator.QueryValueExpression)
|
|
1682
|
-
return _.isArray(expr.value);
|
|
1683
|
-
return false;
|
|
1684
|
-
};
|
|
1685
|
-
const arrayLength = (expr) => {
|
|
1686
|
-
if (expr instanceof validator.QueryArrayExpression)
|
|
1687
|
-
return expr.exprs.length;
|
|
1688
|
-
if (expr instanceof validator.QueryValueExpression)
|
|
1689
|
-
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
1690
|
-
return 0;
|
|
1691
|
-
};
|
|
1692
|
-
const mapExpression = (expr, callback) => {
|
|
1693
|
-
if (expr instanceof validator.QueryArrayExpression)
|
|
1694
|
-
return _.map(expr.exprs, x => callback(x));
|
|
1695
|
-
if (expr instanceof validator.QueryValueExpression)
|
|
1696
|
-
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new validator.QueryValueExpression(x))) : [];
|
|
1697
|
-
return [];
|
|
1698
|
-
};
|
|
1699
|
-
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
1700
|
-
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
1701
|
-
if (expr instanceof validator.QueryKeyExpression) {
|
|
1702
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1703
|
-
const _dataType = dataType ? index._typeof(dataType) : null;
|
|
1704
|
-
if (_dataType === 'number') {
|
|
1705
|
-
return [
|
|
1706
|
-
{ type: 'number', sql: element },
|
|
1707
|
-
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
1708
|
-
];
|
|
1709
|
-
}
|
|
1710
|
-
else if (_dataType === 'decimal') {
|
|
1711
|
-
return [
|
|
1712
|
-
{ type: 'decimal', sql: element },
|
|
1713
|
-
{ type: 'number', sql: element },
|
|
1714
|
-
];
|
|
1715
|
-
}
|
|
1716
|
-
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
1717
|
-
return [{ type: _dataType, sql: element }];
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
if (expr instanceof validator.QueryValueExpression) {
|
|
1721
|
-
if (_.isBoolean(expr.value))
|
|
1722
|
-
return [{ type: 'boolean', sql: sql `${{ value: expr.value }}` }];
|
|
1723
|
-
if (_.isNumber(expr.value))
|
|
1724
|
-
return [
|
|
1725
|
-
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
1726
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
1727
|
-
];
|
|
1728
|
-
if (expr.value instanceof Decimal)
|
|
1729
|
-
return [
|
|
1730
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
1731
|
-
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
1732
|
-
];
|
|
1733
|
-
if (_.isString(expr.value))
|
|
1734
|
-
return [{ type: 'string', sql: sql `${{ value: expr.value }}` }];
|
|
1735
|
-
if (_.isDate(expr.value))
|
|
1736
|
-
return [{ type: 'date', sql: sql `${{ value: expr.value }}` }];
|
|
1737
|
-
}
|
|
1738
|
-
if (expr instanceof validator.QueryCoditionalExpression ||
|
|
1739
|
-
expr instanceof validator.QueryComparisonExpression ||
|
|
1740
|
-
expr instanceof validator.QueryNotExpression) {
|
|
1741
|
-
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
1742
|
-
if (value)
|
|
1743
|
-
return [{ type: 'boolean', sql: value }];
|
|
1744
|
-
}
|
|
1745
|
-
if (expr instanceof validator.QueryDistanceExpression) {
|
|
1746
|
-
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1747
|
-
return [{ type: 'number', sql: value }];
|
|
1748
|
-
}
|
|
1749
|
-
};
|
|
1750
|
-
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
1751
|
-
if (expr instanceof validator.QueryKeyExpression) {
|
|
1752
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1753
|
-
if (dataType && index.isPrimitive(dataType)) {
|
|
1754
|
-
switch (index._typeof(dataType)) {
|
|
1755
|
-
case 'boolean': return sql `to_jsonb(${element})`;
|
|
1756
|
-
case 'number': return sql `to_jsonb(${element})`;
|
|
1757
|
-
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
1758
|
-
case 'string': return sql `to_jsonb(${element})`;
|
|
1759
|
-
case 'string[]': return sql `to_jsonb(${element})`;
|
|
1760
|
-
case 'date': return sql `jsonb_build_object(
|
|
1761
|
-
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1762
|
-
)`;
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
return sql `${element}`;
|
|
1766
|
-
}
|
|
1767
|
-
if (expr instanceof validator.QueryValueExpression) {
|
|
1768
|
-
return _encodeJsonValue(index._encodeValue(expr.value));
|
|
1769
|
-
}
|
|
1770
|
-
if (expr instanceof validator.QueryArrayExpression) {
|
|
1771
|
-
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
1772
|
-
}
|
|
1773
|
-
const value = encodeQueryExpression(compiler, parent, expr);
|
|
1774
|
-
if (!value)
|
|
1775
|
-
throw Error('Invalid expression');
|
|
1776
|
-
return sql `to_jsonb(${value})`;
|
|
1777
|
-
};
|
|
1778
|
-
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
1779
|
-
if (exprs.length === 1) {
|
|
1780
|
-
const [expr] = exprs;
|
|
1781
|
-
if (expr instanceof validator.QueryKeyExpression) {
|
|
1782
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1783
|
-
if (!dataType || !index.isVector(dataType))
|
|
1784
|
-
throw Error('Invalid expression');
|
|
1785
|
-
return { sql: element, dimension: dataType.dimension };
|
|
1786
|
-
}
|
|
1787
|
-
if (expr instanceof validator.QueryValueExpression) {
|
|
1788
|
-
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
1789
|
-
throw Error('Invalid expression');
|
|
1790
|
-
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
1794
|
-
if (_.some(result, x => _.isNil(x)))
|
|
1795
|
-
throw Error('Invalid expression');
|
|
1796
|
-
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
1797
|
-
};
|
|
1798
|
-
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
1799
|
-
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
1800
|
-
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
1801
|
-
if (d1 !== d2)
|
|
1802
|
-
throw Error('Invalid expression');
|
|
1803
|
-
const operatorMap = {
|
|
1804
|
-
'$distance': sql `<->`,
|
|
1805
|
-
'$innerProduct': sql `<#>`,
|
|
1806
|
-
'$negInnerProduct': sql `<#>`,
|
|
1807
|
-
'$cosineDistance': sql `<=>`,
|
|
1808
|
-
'$rectilinearDistance': sql `<+>`,
|
|
1809
|
-
};
|
|
1810
|
-
const _expr = sql `
|
|
1811
|
-
CAST(
|
|
1812
|
-
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
1813
|
-
)
|
|
1814
|
-
${operatorMap[expr.type]}
|
|
1815
|
-
CAST(
|
|
1816
|
-
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
1817
|
-
)
|
|
1818
|
-
`;
|
|
1819
|
-
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
1820
|
-
};
|
|
1821
|
-
const matchType = (first, second) => {
|
|
1822
|
-
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
1823
|
-
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
1824
|
-
};
|
|
1825
|
-
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
1826
|
-
if (expr instanceof validator.QueryCoditionalExpression) {
|
|
1827
|
-
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
1828
|
-
if (_.isEmpty(queries))
|
|
1829
|
-
return;
|
|
1830
|
-
switch (expr.type) {
|
|
1831
|
-
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
1832
|
-
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
1833
|
-
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
if (expr instanceof validator.QueryComparisonExpression) {
|
|
1837
|
-
const operatorMap = {
|
|
1838
|
-
'$eq': nullSafeEqual(),
|
|
1839
|
-
'$ne': nullSafeNotEqual(),
|
|
1840
|
-
'$gt': sql `>`,
|
|
1841
|
-
'$gte': sql `>=`,
|
|
1842
|
-
'$lt': sql `<`,
|
|
1843
|
-
'$lte': sql `<=`,
|
|
1844
|
-
};
|
|
1845
|
-
if (isArrayExpression(expr.left) &&
|
|
1846
|
-
isArrayExpression(expr.right) &&
|
|
1847
|
-
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
1848
|
-
const _left = mapExpression(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1849
|
-
const _right = mapExpression(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1850
|
-
const mapped = _.compact(_.map(_.zip(_left, _right), ([l, r]) => matchType(l, r)));
|
|
1851
|
-
if (mapped.length === _left.length) {
|
|
1852
|
-
const [l, r] = _.unzip(mapped);
|
|
1853
|
-
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
1857
|
-
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
1858
|
-
if (_left && _right) {
|
|
1859
|
-
const matched = matchType(_left, _right);
|
|
1860
|
-
if (matched)
|
|
1861
|
-
return sql `${matched[0].sql} ${operatorMap[expr.type]} ${matched[1].sql}`;
|
|
1862
|
-
}
|
|
1863
|
-
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
1864
|
-
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
1865
|
-
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
1866
|
-
}
|
|
1867
|
-
if (expr instanceof validator.QueryNotExpression) {
|
|
1868
|
-
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
1869
|
-
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
1870
|
-
}
|
|
1871
|
-
throw Error('Invalid expression');
|
|
1872
|
-
};
|
|
1873
|
-
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
1874
|
-
if (expr instanceof validator.QueryDistanceExpression) {
|
|
1875
|
-
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1876
|
-
}
|
|
1877
|
-
return encodeBooleanExpression(compiler, parent, expr);
|
|
1878
|
-
};
|
|
1879
|
-
|
|
1880
2218
|
//
|
|
1881
2219
|
// populate.ts
|
|
1882
2220
|
//
|
|
@@ -1979,7 +2317,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1979
2317
|
];
|
|
1980
2318
|
if (!_.isEmpty(groupMatches?.[field])) {
|
|
1981
2319
|
for (const [key, expr] of _.entries(groupMatches[field])) {
|
|
1982
|
-
if (expr instanceof validator.
|
|
2320
|
+
if (expr instanceof validator.QueryZeroParamAccumulator) {
|
|
1983
2321
|
switch (expr.type) {
|
|
1984
2322
|
case '$count':
|
|
1985
2323
|
columns.push(sql `
|
|
@@ -1992,30 +2330,44 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1992
2330
|
break;
|
|
1993
2331
|
}
|
|
1994
2332
|
}
|
|
1995
|
-
else if (expr instanceof validator.
|
|
1996
|
-
const op = {
|
|
1997
|
-
'$max': 'MAX',
|
|
1998
|
-
'$min': 'MIN',
|
|
1999
|
-
'$avg': 'AVG',
|
|
2000
|
-
'$sum': 'SUM',
|
|
2001
|
-
'$stdDevPop': 'STDDEV_POP',
|
|
2002
|
-
'$stdDevSamp': 'STDDEV_SAMP',
|
|
2003
|
-
'$varPop': 'VAR_POP',
|
|
2004
|
-
'$varSamp': 'VAR_SAMP',
|
|
2005
|
-
}[expr.type];
|
|
2333
|
+
else if (expr instanceof validator.QueryUnaryAccumulator) {
|
|
2006
2334
|
if (!expr.expr)
|
|
2007
2335
|
throw Error('Invalid expression');
|
|
2008
|
-
const
|
|
2009
|
-
const value = _.first(exprs)?.sql;
|
|
2336
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.expr) ?? {};
|
|
2010
2337
|
if (!value)
|
|
2011
2338
|
throw Error('Invalid expression');
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2339
|
+
switch (expr.type) {
|
|
2340
|
+
case '$most':
|
|
2341
|
+
columns.push(sql `
|
|
2342
|
+
(
|
|
2343
|
+
SELECT MODE() WITHIN GROUP (ORDER BY (${value})) FROM (
|
|
2344
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2345
|
+
) ${{ identifier: populate.name }}
|
|
2346
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2347
|
+
`);
|
|
2348
|
+
break;
|
|
2349
|
+
default:
|
|
2350
|
+
{
|
|
2351
|
+
const op = {
|
|
2352
|
+
'$max': 'MAX',
|
|
2353
|
+
'$min': 'MIN',
|
|
2354
|
+
'$avg': 'AVG',
|
|
2355
|
+
'$sum': 'SUM',
|
|
2356
|
+
'$stdDevPop': 'STDDEV_POP',
|
|
2357
|
+
'$stdDevSamp': 'STDDEV_SAMP',
|
|
2358
|
+
'$varPop': 'VAR_POP',
|
|
2359
|
+
'$varSamp': 'VAR_SAMP',
|
|
2360
|
+
}[expr.type];
|
|
2361
|
+
columns.push(sql `
|
|
2362
|
+
(
|
|
2363
|
+
SELECT ${{ literal: op }}(${value}) FROM (
|
|
2364
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2365
|
+
) ${{ identifier: populate.name }}
|
|
2366
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2367
|
+
`);
|
|
2368
|
+
}
|
|
2369
|
+
break;
|
|
2370
|
+
}
|
|
2019
2371
|
}
|
|
2020
2372
|
else if (expr instanceof validator.QueryPercentileAccumulator) {
|
|
2021
2373
|
const op = {
|
|
@@ -2024,8 +2376,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
2024
2376
|
}[expr.mode];
|
|
2025
2377
|
if (!expr.input)
|
|
2026
2378
|
throw Error('Invalid expression');
|
|
2027
|
-
const
|
|
2028
|
-
const value = _.first(exprs)?.sql;
|
|
2379
|
+
const { sql: value } = encodeTypedQueryExpression(compiler, populate, expr.input) ?? {};
|
|
2029
2380
|
if (!value)
|
|
2030
2381
|
throw Error('Invalid expression');
|
|
2031
2382
|
columns.push(sql `
|
|
@@ -2613,6 +2964,36 @@ const encodeFieldExpression = (compiler, parent, field, expr) => {
|
|
|
2613
2964
|
throw Error('Invalid expression');
|
|
2614
2965
|
};
|
|
2615
2966
|
|
|
2967
|
+
//
|
|
2968
|
+
// index.ts
|
|
2969
|
+
//
|
|
2970
|
+
// The MIT License
|
|
2971
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
2972
|
+
//
|
|
2973
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2974
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
2975
|
+
// in the Software without restriction, including without limitation the rights
|
|
2976
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
2977
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
2978
|
+
// furnished to do so, subject to the following conditions:
|
|
2979
|
+
//
|
|
2980
|
+
// The above copyright notice and this permission notice shall be included in
|
|
2981
|
+
// all copies or substantial portions of the Software.
|
|
2982
|
+
//
|
|
2983
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
2984
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
2985
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
2986
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
2987
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
2988
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
2989
|
+
// THE SOFTWARE.
|
|
2990
|
+
//
|
|
2991
|
+
const encodeSortExpression = (compiler, parent, expr) => {
|
|
2992
|
+
if (expr instanceof validator.QueryDistanceExpression) {
|
|
2993
|
+
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
2994
|
+
}
|
|
2995
|
+
};
|
|
2996
|
+
|
|
2616
2997
|
//
|
|
2617
2998
|
// relation.ts
|
|
2618
2999
|
//
|
|
@@ -2684,7 +3065,8 @@ const PostgresDialect = {
|
|
|
2684
3065
|
updateOperation,
|
|
2685
3066
|
selectPopulate,
|
|
2686
3067
|
encodeFieldExpression,
|
|
2687
|
-
|
|
3068
|
+
encodeBooleanExpression,
|
|
3069
|
+
encodeSortExpression,
|
|
2688
3070
|
encodePopulate,
|
|
2689
3071
|
encodeRelation,
|
|
2690
3072
|
encodeSortKey,
|