proto.io 0.0.214 → 0.0.216
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 +4 -8
- package/dist/adapters/storage/progres.js +457 -416
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +458 -417
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.mjs +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -11
- package/dist/index.mjs.map +1 -1
- package/dist/internals/{base-E1b8J-Fs.d.ts → base-BO3ZP6EF.d.ts} +2 -2
- package/dist/internals/base-BO3ZP6EF.d.ts.map +1 -0
- package/dist/internals/{chunk-D1bD7otk.d.ts → chunk-DDkLpKXp.d.ts} +3 -3
- package/dist/internals/chunk-DDkLpKXp.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-SSEdPyhp.d.ts → index-CYhA8SU8.d.ts} +2 -2
- package/dist/internals/index-CYhA8SU8.d.ts.map +1 -0
- package/dist/internals/{index-ZPbBr9Db.mjs → index-DF2AfSGK.mjs} +2 -2
- package/dist/internals/index-DF2AfSGK.mjs.map +1 -0
- package/dist/internals/index-DfnPpl1I.js.map +1 -1
- package/dist/internals/{index-CbjY-gJ7.d.ts → index-HdMgLYtD.d.ts} +14 -5
- package/dist/internals/index-HdMgLYtD.d.ts.map +1 -0
- package/dist/internals/{validator-DoRPoIs2.js → validator-BBjOdLiT.js} +42 -3
- package/dist/internals/validator-BBjOdLiT.js.map +1 -0
- package/dist/internals/{validator-Dxmbwa3P.mjs → validator-LNgZGT_l.mjs} +42 -5
- package/dist/internals/validator-LNgZGT_l.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/internals/base-E1b8J-Fs.d.ts.map +0 -1
- package/dist/internals/chunk-D1bD7otk.d.ts.map +0 -1
- package/dist/internals/index-CbjY-gJ7.d.ts.map +0 -1
- package/dist/internals/index-SSEdPyhp.d.ts.map +0 -1
- package/dist/internals/index-ZPbBr9Db.mjs.map +0 -1
- package/dist/internals/validator-DoRPoIs2.js.map +0 -1
- package/dist/internals/validator-Dxmbwa3P.mjs.map +0 -1
|
@@ -9,7 +9,7 @@ var QueryStream = require('pg-query-stream');
|
|
|
9
9
|
var utilsJs = require('@o2ter/utils-js');
|
|
10
10
|
var Decimal = require('decimal.js');
|
|
11
11
|
var utils = require('pg/lib/utils');
|
|
12
|
-
var validator = require('../../internals/validator-
|
|
12
|
+
var validator = require('../../internals/validator-BBjOdLiT.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');
|
|
@@ -540,6 +540,399 @@ 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
|
+
// expressions.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 isArrayExpression = (expr) => {
|
|
735
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
736
|
+
return true;
|
|
737
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
738
|
+
return _.isArray(expr.value);
|
|
739
|
+
return false;
|
|
740
|
+
};
|
|
741
|
+
const arrayLength = (expr) => {
|
|
742
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
743
|
+
return expr.exprs.length;
|
|
744
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
745
|
+
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
746
|
+
return 0;
|
|
747
|
+
};
|
|
748
|
+
const mapExpression = (expr, callback) => {
|
|
749
|
+
if (expr instanceof validator.QueryArrayExpression)
|
|
750
|
+
return _.map(expr.exprs, x => callback(x));
|
|
751
|
+
if (expr instanceof validator.QueryValueExpression)
|
|
752
|
+
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new validator.QueryValueExpression(x))) : [];
|
|
753
|
+
return [];
|
|
754
|
+
};
|
|
755
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
756
|
+
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
757
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
758
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
759
|
+
const _dataType = dataType ? index._typeof(dataType) : null;
|
|
760
|
+
if (_dataType === 'number') {
|
|
761
|
+
return [
|
|
762
|
+
{ type: 'number', sql: element },
|
|
763
|
+
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
764
|
+
];
|
|
765
|
+
}
|
|
766
|
+
else if (_dataType === 'decimal') {
|
|
767
|
+
return [
|
|
768
|
+
{ type: 'decimal', sql: element },
|
|
769
|
+
{ type: 'number', sql: element },
|
|
770
|
+
];
|
|
771
|
+
}
|
|
772
|
+
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
773
|
+
return [{ type: _dataType, sql: element }];
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
777
|
+
if (_.isBoolean(expr.value))
|
|
778
|
+
return [{ type: 'boolean', sql: sql `${{ value: expr.value }}` }];
|
|
779
|
+
if (_.isNumber(expr.value))
|
|
780
|
+
return [
|
|
781
|
+
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
782
|
+
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
783
|
+
];
|
|
784
|
+
if (expr.value instanceof Decimal)
|
|
785
|
+
return [
|
|
786
|
+
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
787
|
+
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
788
|
+
];
|
|
789
|
+
if (_.isString(expr.value))
|
|
790
|
+
return [{ type: 'string', sql: sql `${{ value: expr.value }}` }];
|
|
791
|
+
if (_.isDate(expr.value))
|
|
792
|
+
return [{ type: 'date', sql: sql `${{ value: expr.value }}` }];
|
|
793
|
+
}
|
|
794
|
+
if (expr instanceof validator.QueryCoditionalExpression ||
|
|
795
|
+
expr instanceof validator.QueryComparisonExpression ||
|
|
796
|
+
expr instanceof validator.QueryNotExpression) {
|
|
797
|
+
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
798
|
+
if (value)
|
|
799
|
+
return [{ type: 'boolean', sql: value }];
|
|
800
|
+
}
|
|
801
|
+
if (expr instanceof validator.QueryDistanceExpression) {
|
|
802
|
+
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
803
|
+
return [{ type: 'number', sql: value }];
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
807
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
808
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
809
|
+
if (dataType && index.isPrimitive(dataType)) {
|
|
810
|
+
switch (index._typeof(dataType)) {
|
|
811
|
+
case 'boolean': return sql `to_jsonb(${element})`;
|
|
812
|
+
case 'number': return sql `to_jsonb(${element})`;
|
|
813
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
814
|
+
case 'string': return sql `to_jsonb(${element})`;
|
|
815
|
+
case 'string[]': return sql `to_jsonb(${element})`;
|
|
816
|
+
case 'date': return sql `jsonb_build_object(
|
|
817
|
+
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
818
|
+
)`;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return sql `${element}`;
|
|
822
|
+
}
|
|
823
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
824
|
+
return _encodeJsonValue(index._encodeValue(expr.value));
|
|
825
|
+
}
|
|
826
|
+
if (expr instanceof validator.QueryArrayExpression) {
|
|
827
|
+
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
828
|
+
}
|
|
829
|
+
const value = encodeQueryExpression(compiler, parent, expr);
|
|
830
|
+
if (!value)
|
|
831
|
+
throw Error('Invalid expression');
|
|
832
|
+
return sql `to_jsonb(${value})`;
|
|
833
|
+
};
|
|
834
|
+
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
835
|
+
if (exprs.length === 1) {
|
|
836
|
+
const [expr] = exprs;
|
|
837
|
+
if (expr instanceof validator.QueryKeyExpression) {
|
|
838
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
839
|
+
if (!dataType || !index.isVector(dataType))
|
|
840
|
+
throw Error('Invalid expression');
|
|
841
|
+
return { sql: element, dimension: dataType.dimension };
|
|
842
|
+
}
|
|
843
|
+
if (expr instanceof validator.QueryValueExpression) {
|
|
844
|
+
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
845
|
+
throw Error('Invalid expression');
|
|
846
|
+
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
850
|
+
if (_.some(result, x => _.isNil(x)))
|
|
851
|
+
throw Error('Invalid expression');
|
|
852
|
+
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
853
|
+
};
|
|
854
|
+
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
855
|
+
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
856
|
+
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
857
|
+
if (d1 !== d2)
|
|
858
|
+
throw Error('Invalid expression');
|
|
859
|
+
const operatorMap = {
|
|
860
|
+
'$distance': sql `<->`,
|
|
861
|
+
'$innerProduct': sql `<#>`,
|
|
862
|
+
'$negInnerProduct': sql `<#>`,
|
|
863
|
+
'$cosineDistance': sql `<=>`,
|
|
864
|
+
'$rectilinearDistance': sql `<+>`,
|
|
865
|
+
};
|
|
866
|
+
const _expr = sql `
|
|
867
|
+
CAST(
|
|
868
|
+
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
869
|
+
)
|
|
870
|
+
${operatorMap[expr.type]}
|
|
871
|
+
CAST(
|
|
872
|
+
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
873
|
+
)
|
|
874
|
+
`;
|
|
875
|
+
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
876
|
+
};
|
|
877
|
+
const matchType = (first, second) => {
|
|
878
|
+
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
879
|
+
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
880
|
+
};
|
|
881
|
+
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
882
|
+
if (expr instanceof validator.QueryCoditionalExpression) {
|
|
883
|
+
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
884
|
+
if (_.isEmpty(queries))
|
|
885
|
+
return;
|
|
886
|
+
switch (expr.type) {
|
|
887
|
+
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
888
|
+
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
889
|
+
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (expr instanceof validator.QueryComparisonExpression) {
|
|
893
|
+
const operatorMap = {
|
|
894
|
+
'$eq': nullSafeEqual(),
|
|
895
|
+
'$ne': nullSafeNotEqual(),
|
|
896
|
+
'$gt': sql `>`,
|
|
897
|
+
'$gte': sql `>=`,
|
|
898
|
+
'$lt': sql `<`,
|
|
899
|
+
'$lte': sql `<=`,
|
|
900
|
+
};
|
|
901
|
+
if (isArrayExpression(expr.left) &&
|
|
902
|
+
isArrayExpression(expr.right) &&
|
|
903
|
+
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
904
|
+
const _left = mapExpression(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
905
|
+
const _right = mapExpression(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
906
|
+
const mapped = _.compact(_.map(_.zip(_left, _right), ([l, r]) => matchType(l, r)));
|
|
907
|
+
if (mapped.length === _left.length) {
|
|
908
|
+
const [l, r] = _.unzip(mapped);
|
|
909
|
+
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
913
|
+
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
914
|
+
if (_left && _right) {
|
|
915
|
+
const matched = matchType(_left, _right);
|
|
916
|
+
if (matched)
|
|
917
|
+
return sql `${matched[0].sql} ${operatorMap[expr.type]} ${matched[1].sql}`;
|
|
918
|
+
}
|
|
919
|
+
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
920
|
+
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
921
|
+
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
922
|
+
}
|
|
923
|
+
if (expr instanceof validator.QueryNotExpression) {
|
|
924
|
+
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
925
|
+
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
926
|
+
}
|
|
927
|
+
throw Error('Invalid expression');
|
|
928
|
+
};
|
|
929
|
+
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
930
|
+
if (expr instanceof validator.QueryDistanceExpression) {
|
|
931
|
+
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
932
|
+
}
|
|
933
|
+
return encodeBooleanExpression(compiler, parent, expr);
|
|
934
|
+
};
|
|
935
|
+
|
|
543
936
|
//
|
|
544
937
|
// storage.ts
|
|
545
938
|
//
|
|
@@ -705,9 +1098,13 @@ class SqlStorage {
|
|
|
705
1098
|
const self = this;
|
|
706
1099
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
707
1100
|
const _matchesType = self._matchesType(compiler, query);
|
|
708
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, {
|
|
709
|
-
sort: sql `ORDER BY ${self.dialect.random(opts
|
|
710
|
-
|
|
1101
|
+
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) => ({
|
|
1102
|
+
sort: sql `ORDER BY ${self.dialect.random(opts?.weight ? _.first(encodeTypedQueryExpression(compiler, {
|
|
1103
|
+
name: fetchName,
|
|
1104
|
+
className: query.className,
|
|
1105
|
+
groupMatches: query.groupMatches,
|
|
1106
|
+
}, opts.weight))?.sql : undefined)}`,
|
|
1107
|
+
}));
|
|
711
1108
|
return (async function* () {
|
|
712
1109
|
const objects = self.query(_query);
|
|
713
1110
|
for await (const object of objects) {
|
|
@@ -951,8 +1348,8 @@ const encodeSortKey = (compiler, parent, key) => {
|
|
|
951
1348
|
const { element } = fetchElement(compiler, parent, key);
|
|
952
1349
|
return element;
|
|
953
1350
|
};
|
|
954
|
-
const random = (
|
|
955
|
-
return
|
|
1351
|
+
const random = (weight) => {
|
|
1352
|
+
return weight ? sql `-ln(random()) / ${weight}` : sql `random()`;
|
|
956
1353
|
};
|
|
957
1354
|
|
|
958
1355
|
//
|
|
@@ -1190,180 +1587,13 @@ class PostgresDriver extends PostgresClientDriver {
|
|
|
1190
1587
|
if (_channel === channel)
|
|
1191
1588
|
callback(payload);
|
|
1192
1589
|
});
|
|
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;
|
|
1590
|
+
return () => {
|
|
1591
|
+
release();
|
|
1592
|
+
if (this.pubsub?.isEmpty())
|
|
1593
|
+
this._release_pubsub();
|
|
1594
|
+
};
|
|
1364
1595
|
}
|
|
1365
|
-
|
|
1366
|
-
};
|
|
1596
|
+
}
|
|
1367
1597
|
|
|
1368
1598
|
//
|
|
1369
1599
|
// update.ts
|
|
@@ -1651,232 +1881,6 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1651
1881
|
throw Error('Invalid update operation');
|
|
1652
1882
|
};
|
|
1653
1883
|
|
|
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
1884
|
//
|
|
1881
1885
|
// populate.ts
|
|
1882
1886
|
//
|
|
@@ -1993,30 +1997,67 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1993
1997
|
}
|
|
1994
1998
|
}
|
|
1995
1999
|
else if (expr instanceof validator.QueryExprAccumulator) {
|
|
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];
|
|
2006
2000
|
if (!expr.expr)
|
|
2007
2001
|
throw Error('Invalid expression');
|
|
2008
2002
|
const exprs = encodeTypedQueryExpression(compiler, populate, expr.expr);
|
|
2009
2003
|
const value = _.first(exprs)?.sql;
|
|
2004
|
+
if (!value)
|
|
2005
|
+
throw Error('Invalid expression');
|
|
2006
|
+
switch (expr.type) {
|
|
2007
|
+
case '$most':
|
|
2008
|
+
columns.push(sql `
|
|
2009
|
+
(
|
|
2010
|
+
SELECT MODE() WITHIN GROUP (ORDER BY (${value})) FROM (
|
|
2011
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2012
|
+
) ${{ identifier: populate.name }}
|
|
2013
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2014
|
+
`);
|
|
2015
|
+
break;
|
|
2016
|
+
default:
|
|
2017
|
+
{
|
|
2018
|
+
const op = {
|
|
2019
|
+
'$max': 'MAX',
|
|
2020
|
+
'$min': 'MIN',
|
|
2021
|
+
'$avg': 'AVG',
|
|
2022
|
+
'$sum': 'SUM',
|
|
2023
|
+
'$stdDevPop': 'STDDEV_POP',
|
|
2024
|
+
'$stdDevSamp': 'STDDEV_SAMP',
|
|
2025
|
+
'$varPop': 'VAR_POP',
|
|
2026
|
+
'$varSamp': 'VAR_SAMP',
|
|
2027
|
+
}[expr.type];
|
|
2028
|
+
columns.push(sql `
|
|
2029
|
+
(
|
|
2030
|
+
SELECT ${{ literal: op }}(${value}) FROM (
|
|
2031
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2032
|
+
) ${{ identifier: populate.name }}
|
|
2033
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2034
|
+
`);
|
|
2035
|
+
}
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
else if (expr instanceof validator.QueryPercentileAccumulator) {
|
|
2040
|
+
const op = {
|
|
2041
|
+
'discrete': 'PERCENTILE_DISC',
|
|
2042
|
+
'continuous': 'PERCENTILE_CONT',
|
|
2043
|
+
}[expr.mode];
|
|
2044
|
+
if (!expr.input)
|
|
2045
|
+
throw Error('Invalid expression');
|
|
2046
|
+
const exprs = encodeTypedQueryExpression(compiler, populate, expr.input);
|
|
2047
|
+
const value = _.first(exprs)?.sql;
|
|
2010
2048
|
if (!value)
|
|
2011
2049
|
throw Error('Invalid expression');
|
|
2012
2050
|
columns.push(sql `
|
|
2013
2051
|
(
|
|
2014
|
-
SELECT ${{ literal: op }}(${value}) FROM (
|
|
2052
|
+
SELECT ${{ literal: op }}(${{ value: expr.p }}) WITHIN GROUP (ORDER BY (${value})) FROM (
|
|
2015
2053
|
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2016
2054
|
) ${{ identifier: populate.name }}
|
|
2017
2055
|
) AS ${{ identifier: `${field}.${key}` }}
|
|
2018
2056
|
`);
|
|
2019
2057
|
}
|
|
2058
|
+
else {
|
|
2059
|
+
throw Error('Invalid expression');
|
|
2060
|
+
}
|
|
2020
2061
|
}
|
|
2021
2062
|
}
|
|
2022
2063
|
else {
|