proto.io 0.0.215 → 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 +444 -425
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +445 -426
- 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-Cp5le5dC.d.ts → base-BO3ZP6EF.d.ts} +2 -2
- package/dist/internals/base-BO3ZP6EF.d.ts.map +1 -0
- package/dist/internals/{chunk--Vo2-p-z.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-CZUGnb-y.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-YLrO0f2D.d.ts → index-HdMgLYtD.d.ts} +8 -5
- package/dist/internals/index-HdMgLYtD.d.ts.map +1 -0
- package/dist/internals/{validator-m6wY35c6.js → validator-BBjOdLiT.js} +4 -1
- package/dist/internals/validator-BBjOdLiT.js.map +1 -0
- package/dist/internals/{validator-ChrCrz_C.mjs → validator-LNgZGT_l.mjs} +5 -3
- package/dist/internals/validator-LNgZGT_l.mjs.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-YLrO0f2D.d.ts.map +0 -1
- package/dist/internals/index-ZPbBr9Db.mjs.map +0 -1
- package/dist/internals/validator-ChrCrz_C.mjs.map +0 -1
- package/dist/internals/validator-m6wY35c6.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import { a as isRelation, i as isPointer, b as isShape, e as decodeUpdateOp, s as shapePaths, c as isPrimitive, T as TObject, _ as
|
|
2
|
+
import { a as isRelation, i as isPointer, b as isShape, e as decodeUpdateOp, s as shapePaths, c as isPrimitive, T as TObject, _ as _encodeValue, f as dimensionOf, g as _decodeValue, h as _typeof, j as isVector, k as _isTypeof } from '../../internals/index-BzDsTt4R.mjs';
|
|
3
3
|
import { Pool, types } from 'pg';
|
|
4
4
|
import QueryStream from 'pg-query-stream';
|
|
5
5
|
import { asyncStream, IteratorPool } from '@o2ter/utils-js';
|
|
6
6
|
import Decimal from 'decimal.js';
|
|
7
7
|
import { escapeLiteral, escapeIdentifier } from 'pg/lib/utils';
|
|
8
|
-
import { r as resolveColumn,
|
|
8
|
+
import { r as resolveColumn, b as resolveDataType$1, d as QueryCoditionalSelector, e as QueryFieldSelector, f as QueryExpressionSelector, g as QueryKeyExpression, h as QueryValueExpression, i as QueryCoditionalExpression, j as QueryComparisonExpression, k as QueryNotExpression, l as QueryDistanceExpression, m as QueryArrayExpression, Q as QueryValidator, n as QueryNoParamAccumulator, o as QueryExprAccumulator, p as QueryPercentileAccumulator, c as QuerySelector, F as FieldSelectorExpression } from '../../internals/validator-LNgZGT_l.mjs';
|
|
9
9
|
import '@o2ter/crypto-js';
|
|
10
10
|
import { c as PROTO_EVENT } from '../../internals/const-Dkp7Nsv5.mjs';
|
|
11
11
|
import { g as generateId } from '../../internals/random-CYjWDvex.mjs';
|
|
@@ -536,6 +536,399 @@ class QueryCompiler {
|
|
|
536
536
|
}
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
+
//
|
|
540
|
+
// encode.ts
|
|
541
|
+
//
|
|
542
|
+
// The MIT License
|
|
543
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
544
|
+
//
|
|
545
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
546
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
547
|
+
// in the Software without restriction, including without limitation the rights
|
|
548
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
549
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
550
|
+
// furnished to do so, subject to the following conditions:
|
|
551
|
+
//
|
|
552
|
+
// The above copyright notice and this permission notice shall be included in
|
|
553
|
+
// all copies or substantial portions of the Software.
|
|
554
|
+
//
|
|
555
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
556
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
557
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
558
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
559
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
560
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
561
|
+
// THE SOFTWARE.
|
|
562
|
+
//
|
|
563
|
+
const _encodeJsonValue = (value) => {
|
|
564
|
+
if (_.isArray(value))
|
|
565
|
+
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
566
|
+
if (_.isPlainObject(value))
|
|
567
|
+
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
568
|
+
return sql `to_jsonb(${{ value }})`;
|
|
569
|
+
};
|
|
570
|
+
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
571
|
+
switch (_typeof(dataType)) {
|
|
572
|
+
case 'decimal':
|
|
573
|
+
return sql `jsonb_build_object(
|
|
574
|
+
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
575
|
+
) AS ${{ identifier: colname }}`;
|
|
576
|
+
case 'date':
|
|
577
|
+
return sql `jsonb_build_object(
|
|
578
|
+
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
579
|
+
) AS ${{ identifier: colname }}`;
|
|
580
|
+
default:
|
|
581
|
+
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
const encodeType = (colname, dataType, value) => {
|
|
585
|
+
if (_.isNil(value))
|
|
586
|
+
return sql `NULL`;
|
|
587
|
+
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
588
|
+
case 'boolean':
|
|
589
|
+
if (_.isBoolean(value))
|
|
590
|
+
return sql `${{ value }}`;
|
|
591
|
+
break;
|
|
592
|
+
case 'number':
|
|
593
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
594
|
+
return sql `${{ value }}`;
|
|
595
|
+
if (value instanceof Decimal)
|
|
596
|
+
return sql `${{ value: value.toNumber() }}`;
|
|
597
|
+
break;
|
|
598
|
+
case 'decimal':
|
|
599
|
+
if (_.isNumber(value) && _.isFinite(value))
|
|
600
|
+
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
601
|
+
if (value instanceof Decimal)
|
|
602
|
+
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
603
|
+
break;
|
|
604
|
+
case 'string':
|
|
605
|
+
if (_.isString(value))
|
|
606
|
+
return sql `${{ value }}`;
|
|
607
|
+
break;
|
|
608
|
+
case 'string[]':
|
|
609
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
610
|
+
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
611
|
+
break;
|
|
612
|
+
case 'date':
|
|
613
|
+
if (_.isDate(value))
|
|
614
|
+
return sql `${{ value }}`;
|
|
615
|
+
break;
|
|
616
|
+
case 'object':
|
|
617
|
+
if (_.isPlainObject(value))
|
|
618
|
+
return sql `${{ value: _encodeValue(value) }}`;
|
|
619
|
+
break;
|
|
620
|
+
case 'vector':
|
|
621
|
+
if (!_.isArray(value) || value.length !== dimensionOf(dataType))
|
|
622
|
+
break;
|
|
623
|
+
if (!_.every(value, x => _.isFinite(x)))
|
|
624
|
+
break;
|
|
625
|
+
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
626
|
+
case 'array':
|
|
627
|
+
if (!_.isArray(value))
|
|
628
|
+
break;
|
|
629
|
+
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(_encodeValue(x)))}]::JSONB[]`;
|
|
630
|
+
case 'pointer':
|
|
631
|
+
if (value instanceof TObject && value.objectId)
|
|
632
|
+
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
633
|
+
break;
|
|
634
|
+
case 'relation':
|
|
635
|
+
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.objectId)) {
|
|
636
|
+
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
637
|
+
}
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
throw Error('Invalid data type');
|
|
641
|
+
};
|
|
642
|
+
const decodeType = (type, value) => {
|
|
643
|
+
switch (type) {
|
|
644
|
+
case 'boolean':
|
|
645
|
+
if (_.isBoolean(value))
|
|
646
|
+
return value;
|
|
647
|
+
if (value === 'true')
|
|
648
|
+
return true;
|
|
649
|
+
if (value === 'false')
|
|
650
|
+
return false;
|
|
651
|
+
break;
|
|
652
|
+
case 'number':
|
|
653
|
+
if (_.isNumber(value))
|
|
654
|
+
return value;
|
|
655
|
+
if (_.isString(value)) {
|
|
656
|
+
const float = parseFloat(value);
|
|
657
|
+
return _.isNaN(float) ? null : float;
|
|
658
|
+
}
|
|
659
|
+
break;
|
|
660
|
+
case 'decimal':
|
|
661
|
+
if (_.isString(value) || _.isNumber(value))
|
|
662
|
+
return new Decimal(value);
|
|
663
|
+
if (value instanceof Decimal)
|
|
664
|
+
return value;
|
|
665
|
+
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
666
|
+
return new Decimal(value.$decimal);
|
|
667
|
+
break;
|
|
668
|
+
case 'string':
|
|
669
|
+
if (_.isString(value))
|
|
670
|
+
return value;
|
|
671
|
+
break;
|
|
672
|
+
case 'string[]':
|
|
673
|
+
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
674
|
+
return value;
|
|
675
|
+
break;
|
|
676
|
+
case 'date':
|
|
677
|
+
if (_.isDate(value))
|
|
678
|
+
return value;
|
|
679
|
+
if (_.isString(value)) {
|
|
680
|
+
const date = new Date(value);
|
|
681
|
+
if (isFinite(date.valueOf()))
|
|
682
|
+
return date;
|
|
683
|
+
}
|
|
684
|
+
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
685
|
+
return new Date(value.$date);
|
|
686
|
+
break;
|
|
687
|
+
case 'object':
|
|
688
|
+
if (_.isPlainObject(value))
|
|
689
|
+
return _decodeValue(value);
|
|
690
|
+
break;
|
|
691
|
+
case 'array':
|
|
692
|
+
if (_.isArray(value))
|
|
693
|
+
return _decodeValue(value);
|
|
694
|
+
break;
|
|
695
|
+
case 'vector':
|
|
696
|
+
if (!_.isArray(value))
|
|
697
|
+
break;
|
|
698
|
+
if (_.every(value, x => _.isNumber(x))) {
|
|
699
|
+
return value;
|
|
700
|
+
}
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
return null;
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
//
|
|
707
|
+
// expressions.ts
|
|
708
|
+
//
|
|
709
|
+
// The MIT License
|
|
710
|
+
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
711
|
+
//
|
|
712
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
713
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
714
|
+
// in the Software without restriction, including without limitation the rights
|
|
715
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
716
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
717
|
+
// furnished to do so, subject to the following conditions:
|
|
718
|
+
//
|
|
719
|
+
// The above copyright notice and this permission notice shall be included in
|
|
720
|
+
// all copies or substantial portions of the Software.
|
|
721
|
+
//
|
|
722
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
723
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
724
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
725
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
726
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
727
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
728
|
+
// THE SOFTWARE.
|
|
729
|
+
//
|
|
730
|
+
const isArrayExpression = (expr) => {
|
|
731
|
+
if (expr instanceof QueryArrayExpression)
|
|
732
|
+
return true;
|
|
733
|
+
if (expr instanceof QueryValueExpression)
|
|
734
|
+
return _.isArray(expr.value);
|
|
735
|
+
return false;
|
|
736
|
+
};
|
|
737
|
+
const arrayLength = (expr) => {
|
|
738
|
+
if (expr instanceof QueryArrayExpression)
|
|
739
|
+
return expr.exprs.length;
|
|
740
|
+
if (expr instanceof QueryValueExpression)
|
|
741
|
+
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
742
|
+
return 0;
|
|
743
|
+
};
|
|
744
|
+
const mapExpression = (expr, callback) => {
|
|
745
|
+
if (expr instanceof QueryArrayExpression)
|
|
746
|
+
return _.map(expr.exprs, x => callback(x));
|
|
747
|
+
if (expr instanceof QueryValueExpression)
|
|
748
|
+
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new QueryValueExpression(x))) : [];
|
|
749
|
+
return [];
|
|
750
|
+
};
|
|
751
|
+
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
752
|
+
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
753
|
+
if (expr instanceof QueryKeyExpression) {
|
|
754
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
755
|
+
const _dataType = dataType ? _typeof(dataType) : null;
|
|
756
|
+
if (_dataType === 'number') {
|
|
757
|
+
return [
|
|
758
|
+
{ type: 'number', sql: element },
|
|
759
|
+
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
760
|
+
];
|
|
761
|
+
}
|
|
762
|
+
else if (_dataType === 'decimal') {
|
|
763
|
+
return [
|
|
764
|
+
{ type: 'decimal', sql: element },
|
|
765
|
+
{ type: 'number', sql: element },
|
|
766
|
+
];
|
|
767
|
+
}
|
|
768
|
+
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
769
|
+
return [{ type: _dataType, sql: element }];
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (expr instanceof QueryValueExpression) {
|
|
773
|
+
if (_.isBoolean(expr.value))
|
|
774
|
+
return [{ type: 'boolean', sql: sql `${{ value: expr.value }}` }];
|
|
775
|
+
if (_.isNumber(expr.value))
|
|
776
|
+
return [
|
|
777
|
+
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
778
|
+
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
779
|
+
];
|
|
780
|
+
if (expr.value instanceof Decimal)
|
|
781
|
+
return [
|
|
782
|
+
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
783
|
+
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
784
|
+
];
|
|
785
|
+
if (_.isString(expr.value))
|
|
786
|
+
return [{ type: 'string', sql: sql `${{ value: expr.value }}` }];
|
|
787
|
+
if (_.isDate(expr.value))
|
|
788
|
+
return [{ type: 'date', sql: sql `${{ value: expr.value }}` }];
|
|
789
|
+
}
|
|
790
|
+
if (expr instanceof QueryCoditionalExpression ||
|
|
791
|
+
expr instanceof QueryComparisonExpression ||
|
|
792
|
+
expr instanceof QueryNotExpression) {
|
|
793
|
+
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
794
|
+
if (value)
|
|
795
|
+
return [{ type: 'boolean', sql: value }];
|
|
796
|
+
}
|
|
797
|
+
if (expr instanceof QueryDistanceExpression) {
|
|
798
|
+
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
799
|
+
return [{ type: 'number', sql: value }];
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
803
|
+
if (expr instanceof QueryKeyExpression) {
|
|
804
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
805
|
+
if (dataType && isPrimitive(dataType)) {
|
|
806
|
+
switch (_typeof(dataType)) {
|
|
807
|
+
case 'boolean': return sql `to_jsonb(${element})`;
|
|
808
|
+
case 'number': return sql `to_jsonb(${element})`;
|
|
809
|
+
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
810
|
+
case 'string': return sql `to_jsonb(${element})`;
|
|
811
|
+
case 'string[]': return sql `to_jsonb(${element})`;
|
|
812
|
+
case 'date': return sql `jsonb_build_object(
|
|
813
|
+
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
814
|
+
)`;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return sql `${element}`;
|
|
818
|
+
}
|
|
819
|
+
if (expr instanceof QueryValueExpression) {
|
|
820
|
+
return _encodeJsonValue(_encodeValue(expr.value));
|
|
821
|
+
}
|
|
822
|
+
if (expr instanceof QueryArrayExpression) {
|
|
823
|
+
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
824
|
+
}
|
|
825
|
+
const value = encodeQueryExpression(compiler, parent, expr);
|
|
826
|
+
if (!value)
|
|
827
|
+
throw Error('Invalid expression');
|
|
828
|
+
return sql `to_jsonb(${value})`;
|
|
829
|
+
};
|
|
830
|
+
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
831
|
+
if (exprs.length === 1) {
|
|
832
|
+
const [expr] = exprs;
|
|
833
|
+
if (expr instanceof QueryKeyExpression) {
|
|
834
|
+
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
835
|
+
if (!dataType || !isVector(dataType))
|
|
836
|
+
throw Error('Invalid expression');
|
|
837
|
+
return { sql: element, dimension: dataType.dimension };
|
|
838
|
+
}
|
|
839
|
+
if (expr instanceof QueryValueExpression) {
|
|
840
|
+
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
841
|
+
throw Error('Invalid expression');
|
|
842
|
+
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
846
|
+
if (_.some(result, x => _.isNil(x)))
|
|
847
|
+
throw Error('Invalid expression');
|
|
848
|
+
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
849
|
+
};
|
|
850
|
+
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
851
|
+
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
852
|
+
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
853
|
+
if (d1 !== d2)
|
|
854
|
+
throw Error('Invalid expression');
|
|
855
|
+
const operatorMap = {
|
|
856
|
+
'$distance': sql `<->`,
|
|
857
|
+
'$innerProduct': sql `<#>`,
|
|
858
|
+
'$negInnerProduct': sql `<#>`,
|
|
859
|
+
'$cosineDistance': sql `<=>`,
|
|
860
|
+
'$rectilinearDistance': sql `<+>`,
|
|
861
|
+
};
|
|
862
|
+
const _expr = sql `
|
|
863
|
+
CAST(
|
|
864
|
+
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
865
|
+
)
|
|
866
|
+
${operatorMap[expr.type]}
|
|
867
|
+
CAST(
|
|
868
|
+
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
869
|
+
)
|
|
870
|
+
`;
|
|
871
|
+
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
872
|
+
};
|
|
873
|
+
const matchType = (first, second) => {
|
|
874
|
+
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
875
|
+
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
876
|
+
};
|
|
877
|
+
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
878
|
+
if (expr instanceof QueryCoditionalExpression) {
|
|
879
|
+
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
880
|
+
if (_.isEmpty(queries))
|
|
881
|
+
return;
|
|
882
|
+
switch (expr.type) {
|
|
883
|
+
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
884
|
+
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
885
|
+
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (expr instanceof QueryComparisonExpression) {
|
|
889
|
+
const operatorMap = {
|
|
890
|
+
'$eq': nullSafeEqual(),
|
|
891
|
+
'$ne': nullSafeNotEqual(),
|
|
892
|
+
'$gt': sql `>`,
|
|
893
|
+
'$gte': sql `>=`,
|
|
894
|
+
'$lt': sql `<`,
|
|
895
|
+
'$lte': sql `<=`,
|
|
896
|
+
};
|
|
897
|
+
if (isArrayExpression(expr.left) &&
|
|
898
|
+
isArrayExpression(expr.right) &&
|
|
899
|
+
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
900
|
+
const _left = mapExpression(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
901
|
+
const _right = mapExpression(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
902
|
+
const mapped = _.compact(_.map(_.zip(_left, _right), ([l, r]) => matchType(l, r)));
|
|
903
|
+
if (mapped.length === _left.length) {
|
|
904
|
+
const [l, r] = _.unzip(mapped);
|
|
905
|
+
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
909
|
+
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
910
|
+
if (_left && _right) {
|
|
911
|
+
const matched = matchType(_left, _right);
|
|
912
|
+
if (matched)
|
|
913
|
+
return sql `${matched[0].sql} ${operatorMap[expr.type]} ${matched[1].sql}`;
|
|
914
|
+
}
|
|
915
|
+
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
916
|
+
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
917
|
+
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
918
|
+
}
|
|
919
|
+
if (expr instanceof QueryNotExpression) {
|
|
920
|
+
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
921
|
+
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
922
|
+
}
|
|
923
|
+
throw Error('Invalid expression');
|
|
924
|
+
};
|
|
925
|
+
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
926
|
+
if (expr instanceof QueryDistanceExpression) {
|
|
927
|
+
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
928
|
+
}
|
|
929
|
+
return encodeBooleanExpression(compiler, parent, expr);
|
|
930
|
+
};
|
|
931
|
+
|
|
539
932
|
//
|
|
540
933
|
// storage.ts
|
|
541
934
|
//
|
|
@@ -701,9 +1094,13 @@ class SqlStorage {
|
|
|
701
1094
|
const self = this;
|
|
702
1095
|
const compiler = self._makeCompiler(false, query.extraFilter);
|
|
703
1096
|
const _matchesType = self._matchesType(compiler, query);
|
|
704
|
-
const _query = compiler._selectQuery({ ...query, sort: {} }, {
|
|
705
|
-
sort: sql `ORDER BY ${self.dialect.random(opts
|
|
706
|
-
|
|
1097
|
+
const _query = compiler._selectQuery({ ...query, sort: {} }, ({ fetchName }) => ({
|
|
1098
|
+
sort: sql `ORDER BY ${self.dialect.random(opts?.weight ? _.first(encodeTypedQueryExpression(compiler, {
|
|
1099
|
+
name: fetchName,
|
|
1100
|
+
className: query.className,
|
|
1101
|
+
groupMatches: query.groupMatches,
|
|
1102
|
+
}, opts.weight))?.sql : undefined)}`,
|
|
1103
|
+
}));
|
|
707
1104
|
return (async function* () {
|
|
708
1105
|
const objects = self.query(_query);
|
|
709
1106
|
for await (const object of objects) {
|
|
@@ -947,8 +1344,8 @@ const encodeSortKey = (compiler, parent, key) => {
|
|
|
947
1344
|
const { element } = fetchElement(compiler, parent, key);
|
|
948
1345
|
return element;
|
|
949
1346
|
};
|
|
950
|
-
const random = (
|
|
951
|
-
return
|
|
1347
|
+
const random = (weight) => {
|
|
1348
|
+
return weight ? sql `-ln(random()) / ${weight}` : sql `random()`;
|
|
952
1349
|
};
|
|
953
1350
|
|
|
954
1351
|
//
|
|
@@ -1183,183 +1580,16 @@ class PostgresDriver extends PostgresClientDriver {
|
|
|
1183
1580
|
if (!this.pubsub)
|
|
1184
1581
|
return () => void 0;
|
|
1185
1582
|
const release = this.pubsub.subscribe(({ channel: _channel, payload }) => {
|
|
1186
|
-
if (_channel === channel)
|
|
1187
|
-
callback(payload);
|
|
1188
|
-
});
|
|
1189
|
-
return () => {
|
|
1190
|
-
release();
|
|
1191
|
-
if (this.pubsub?.isEmpty())
|
|
1192
|
-
this._release_pubsub();
|
|
1193
|
-
};
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
//
|
|
1198
|
-
// encode.ts
|
|
1199
|
-
//
|
|
1200
|
-
// The MIT License
|
|
1201
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1202
|
-
//
|
|
1203
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1204
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1205
|
-
// in the Software without restriction, including without limitation the rights
|
|
1206
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1207
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1208
|
-
// furnished to do so, subject to the following conditions:
|
|
1209
|
-
//
|
|
1210
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1211
|
-
// all copies or substantial portions of the Software.
|
|
1212
|
-
//
|
|
1213
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1214
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1215
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1216
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1217
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1218
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1219
|
-
// THE SOFTWARE.
|
|
1220
|
-
//
|
|
1221
|
-
const _encodeJsonValue = (value) => {
|
|
1222
|
-
if (_.isArray(value))
|
|
1223
|
-
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
1224
|
-
if (_.isPlainObject(value))
|
|
1225
|
-
return sql `jsonb_build_object(${_.map(value, (v, k) => sql `${{ value: k }}, ${_encodeJsonValue(v)}`)})`;
|
|
1226
|
-
return sql `to_jsonb(${{ value }})`;
|
|
1227
|
-
};
|
|
1228
|
-
const _jsonPopulateInclude = (className, colname, dataType) => {
|
|
1229
|
-
switch (_typeof(dataType)) {
|
|
1230
|
-
case 'decimal':
|
|
1231
|
-
return sql `jsonb_build_object(
|
|
1232
|
-
'$decimal', CAST(${{ identifier: className }}.${{ identifier: colname }} AS TEXT)
|
|
1233
|
-
) AS ${{ identifier: colname }}`;
|
|
1234
|
-
case 'date':
|
|
1235
|
-
return sql `jsonb_build_object(
|
|
1236
|
-
'$date', to_char(${{ identifier: className }}.${{ identifier: colname }} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1237
|
-
) AS ${{ identifier: colname }}`;
|
|
1238
|
-
default:
|
|
1239
|
-
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
1240
|
-
}
|
|
1241
|
-
};
|
|
1242
|
-
const encodeType = (colname, dataType, value) => {
|
|
1243
|
-
if (_.isNil(value))
|
|
1244
|
-
return sql `NULL`;
|
|
1245
|
-
switch (_.isString(dataType) ? dataType : dataType.type) {
|
|
1246
|
-
case 'boolean':
|
|
1247
|
-
if (_.isBoolean(value))
|
|
1248
|
-
return sql `${{ value }}`;
|
|
1249
|
-
break;
|
|
1250
|
-
case 'number':
|
|
1251
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1252
|
-
return sql `${{ value }}`;
|
|
1253
|
-
if (value instanceof Decimal)
|
|
1254
|
-
return sql `${{ value: value.toNumber() }}`;
|
|
1255
|
-
break;
|
|
1256
|
-
case 'decimal':
|
|
1257
|
-
if (_.isNumber(value) && _.isFinite(value))
|
|
1258
|
-
return sql `CAST(${{ quote: (new Decimal(value)).toString() }} AS DECIMAL)`;
|
|
1259
|
-
if (value instanceof Decimal)
|
|
1260
|
-
return sql `CAST(${{ quote: value.toString() }} AS DECIMAL)`;
|
|
1261
|
-
break;
|
|
1262
|
-
case 'string':
|
|
1263
|
-
if (_.isString(value))
|
|
1264
|
-
return sql `${{ value }}`;
|
|
1265
|
-
break;
|
|
1266
|
-
case 'string[]':
|
|
1267
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1268
|
-
return sql `ARRAY[${_.map(value, x => sql `${{ value: x }}`)}]::TEXT[]`;
|
|
1269
|
-
break;
|
|
1270
|
-
case 'date':
|
|
1271
|
-
if (_.isDate(value))
|
|
1272
|
-
return sql `${{ value }}`;
|
|
1273
|
-
break;
|
|
1274
|
-
case 'object':
|
|
1275
|
-
if (_.isPlainObject(value))
|
|
1276
|
-
return sql `${{ value: _encodeValue(value) }}`;
|
|
1277
|
-
break;
|
|
1278
|
-
case 'vector':
|
|
1279
|
-
if (!_.isArray(value) || value.length !== dimensionOf(dataType))
|
|
1280
|
-
break;
|
|
1281
|
-
if (!_.every(value, x => _.isFinite(x)))
|
|
1282
|
-
break;
|
|
1283
|
-
return sql `${{ value }}::DOUBLE PRECISION[]`;
|
|
1284
|
-
case 'array':
|
|
1285
|
-
if (!_.isArray(value))
|
|
1286
|
-
break;
|
|
1287
|
-
return sql `ARRAY[${_.map(value, x => _encodeJsonValue(_encodeValue(x)))}]::JSONB[]`;
|
|
1288
|
-
case 'pointer':
|
|
1289
|
-
if (value instanceof TObject && value.objectId)
|
|
1290
|
-
return sql `${{ value: `${value.className}$${value.objectId}` }}`;
|
|
1291
|
-
break;
|
|
1292
|
-
case 'relation':
|
|
1293
|
-
if (_.isArray(value) && _.every(value, x => x instanceof TObject && x.objectId)) {
|
|
1294
|
-
return sql `${{ value: _.uniq(_.map(value, (x) => `${x.className}$${x.objectId}`)) }}`;
|
|
1295
|
-
}
|
|
1296
|
-
break;
|
|
1297
|
-
}
|
|
1298
|
-
throw Error('Invalid data type');
|
|
1299
|
-
};
|
|
1300
|
-
const decodeType = (type, value) => {
|
|
1301
|
-
switch (type) {
|
|
1302
|
-
case 'boolean':
|
|
1303
|
-
if (_.isBoolean(value))
|
|
1304
|
-
return value;
|
|
1305
|
-
if (value === 'true')
|
|
1306
|
-
return true;
|
|
1307
|
-
if (value === 'false')
|
|
1308
|
-
return false;
|
|
1309
|
-
break;
|
|
1310
|
-
case 'number':
|
|
1311
|
-
if (_.isNumber(value))
|
|
1312
|
-
return value;
|
|
1313
|
-
if (_.isString(value)) {
|
|
1314
|
-
const float = parseFloat(value);
|
|
1315
|
-
return _.isNaN(float) ? null : float;
|
|
1316
|
-
}
|
|
1317
|
-
break;
|
|
1318
|
-
case 'decimal':
|
|
1319
|
-
if (_.isString(value) || _.isNumber(value))
|
|
1320
|
-
return new Decimal(value);
|
|
1321
|
-
if (value instanceof Decimal)
|
|
1322
|
-
return value;
|
|
1323
|
-
if (_.isPlainObject(value) && _.isString(value.$decimal))
|
|
1324
|
-
return new Decimal(value.$decimal);
|
|
1325
|
-
break;
|
|
1326
|
-
case 'string':
|
|
1327
|
-
if (_.isString(value))
|
|
1328
|
-
return value;
|
|
1329
|
-
break;
|
|
1330
|
-
case 'string[]':
|
|
1331
|
-
if (_.isArray(value) && _.every(value, x => _.isString(x)))
|
|
1332
|
-
return value;
|
|
1333
|
-
break;
|
|
1334
|
-
case 'date':
|
|
1335
|
-
if (_.isDate(value))
|
|
1336
|
-
return value;
|
|
1337
|
-
if (_.isString(value)) {
|
|
1338
|
-
const date = new Date(value);
|
|
1339
|
-
if (isFinite(date.valueOf()))
|
|
1340
|
-
return date;
|
|
1341
|
-
}
|
|
1342
|
-
if (_.isPlainObject(value) && _.isString(value.$date))
|
|
1343
|
-
return new Date(value.$date);
|
|
1344
|
-
break;
|
|
1345
|
-
case 'object':
|
|
1346
|
-
if (_.isPlainObject(value))
|
|
1347
|
-
return _decodeValue(value);
|
|
1348
|
-
break;
|
|
1349
|
-
case 'array':
|
|
1350
|
-
if (_.isArray(value))
|
|
1351
|
-
return _decodeValue(value);
|
|
1352
|
-
break;
|
|
1353
|
-
case 'vector':
|
|
1354
|
-
if (!_.isArray(value))
|
|
1355
|
-
break;
|
|
1356
|
-
if (_.every(value, x => _.isNumber(x))) {
|
|
1357
|
-
return value;
|
|
1358
|
-
}
|
|
1359
|
-
break;
|
|
1583
|
+
if (_channel === channel)
|
|
1584
|
+
callback(payload);
|
|
1585
|
+
});
|
|
1586
|
+
return () => {
|
|
1587
|
+
release();
|
|
1588
|
+
if (this.pubsub?.isEmpty())
|
|
1589
|
+
this._release_pubsub();
|
|
1590
|
+
};
|
|
1360
1591
|
}
|
|
1361
|
-
|
|
1362
|
-
};
|
|
1592
|
+
}
|
|
1363
1593
|
|
|
1364
1594
|
//
|
|
1365
1595
|
// update.ts
|
|
@@ -1647,232 +1877,6 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1647
1877
|
throw Error('Invalid update operation');
|
|
1648
1878
|
};
|
|
1649
1879
|
|
|
1650
|
-
//
|
|
1651
|
-
// expressions.ts
|
|
1652
|
-
//
|
|
1653
|
-
// The MIT License
|
|
1654
|
-
// Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
|
|
1655
|
-
//
|
|
1656
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1657
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1658
|
-
// in the Software without restriction, including without limitation the rights
|
|
1659
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1660
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1661
|
-
// furnished to do so, subject to the following conditions:
|
|
1662
|
-
//
|
|
1663
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1664
|
-
// all copies or substantial portions of the Software.
|
|
1665
|
-
//
|
|
1666
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1667
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1668
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1669
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1670
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1671
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1672
|
-
// THE SOFTWARE.
|
|
1673
|
-
//
|
|
1674
|
-
const isArrayExpression = (expr) => {
|
|
1675
|
-
if (expr instanceof QueryArrayExpression)
|
|
1676
|
-
return true;
|
|
1677
|
-
if (expr instanceof QueryValueExpression)
|
|
1678
|
-
return _.isArray(expr.value);
|
|
1679
|
-
return false;
|
|
1680
|
-
};
|
|
1681
|
-
const arrayLength = (expr) => {
|
|
1682
|
-
if (expr instanceof QueryArrayExpression)
|
|
1683
|
-
return expr.exprs.length;
|
|
1684
|
-
if (expr instanceof QueryValueExpression)
|
|
1685
|
-
return _.isArray(expr.value) ? expr.value.length : 0;
|
|
1686
|
-
return 0;
|
|
1687
|
-
};
|
|
1688
|
-
const mapExpression = (expr, callback) => {
|
|
1689
|
-
if (expr instanceof QueryArrayExpression)
|
|
1690
|
-
return _.map(expr.exprs, x => callback(x));
|
|
1691
|
-
if (expr instanceof QueryValueExpression)
|
|
1692
|
-
return _.isArray(expr.value) ? _.map(expr.value, x => callback(new QueryValueExpression(x))) : [];
|
|
1693
|
-
return [];
|
|
1694
|
-
};
|
|
1695
|
-
const _PrimitiveValue = ['boolean', 'number', 'decimal', 'string', 'date'];
|
|
1696
|
-
const encodeTypedQueryExpression = (compiler, parent, expr) => {
|
|
1697
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1698
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1699
|
-
const _dataType = dataType ? _typeof(dataType) : null;
|
|
1700
|
-
if (_dataType === 'number') {
|
|
1701
|
-
return [
|
|
1702
|
-
{ type: 'number', sql: element },
|
|
1703
|
-
{ type: 'decimal', sql: sql `CAST((${element}) AS DECIMAL)` },
|
|
1704
|
-
];
|
|
1705
|
-
}
|
|
1706
|
-
else if (_dataType === 'decimal') {
|
|
1707
|
-
return [
|
|
1708
|
-
{ type: 'decimal', sql: element },
|
|
1709
|
-
{ type: 'number', sql: element },
|
|
1710
|
-
];
|
|
1711
|
-
}
|
|
1712
|
-
else if (_dataType && _PrimitiveValue.includes(_dataType)) {
|
|
1713
|
-
return [{ type: _dataType, sql: element }];
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
if (expr instanceof QueryValueExpression) {
|
|
1717
|
-
if (_.isBoolean(expr.value))
|
|
1718
|
-
return [{ type: 'boolean', sql: sql `${{ value: expr.value }}` }];
|
|
1719
|
-
if (_.isNumber(expr.value))
|
|
1720
|
-
return [
|
|
1721
|
-
{ type: 'number', sql: sql `${{ value: expr.value }}` },
|
|
1722
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: (new Decimal(expr.value)).toString() }} AS DECIMAL)` },
|
|
1723
|
-
];
|
|
1724
|
-
if (expr.value instanceof Decimal)
|
|
1725
|
-
return [
|
|
1726
|
-
{ type: 'decimal', sql: sql `CAST(${{ quote: expr.value.toString() }} AS DECIMAL)` },
|
|
1727
|
-
{ type: 'number', sql: sql `${{ value: expr.value.toNumber() }}` },
|
|
1728
|
-
];
|
|
1729
|
-
if (_.isString(expr.value))
|
|
1730
|
-
return [{ type: 'string', sql: sql `${{ value: expr.value }}` }];
|
|
1731
|
-
if (_.isDate(expr.value))
|
|
1732
|
-
return [{ type: 'date', sql: sql `${{ value: expr.value }}` }];
|
|
1733
|
-
}
|
|
1734
|
-
if (expr instanceof QueryCoditionalExpression ||
|
|
1735
|
-
expr instanceof QueryComparisonExpression ||
|
|
1736
|
-
expr instanceof QueryNotExpression) {
|
|
1737
|
-
const value = encodeBooleanExpression(compiler, parent, expr);
|
|
1738
|
-
if (value)
|
|
1739
|
-
return [{ type: 'boolean', sql: value }];
|
|
1740
|
-
}
|
|
1741
|
-
if (expr instanceof QueryDistanceExpression) {
|
|
1742
|
-
const value = encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1743
|
-
return [{ type: 'number', sql: value }];
|
|
1744
|
-
}
|
|
1745
|
-
};
|
|
1746
|
-
const encodeJsonQueryExpression = (compiler, parent, expr) => {
|
|
1747
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1748
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1749
|
-
if (dataType && isPrimitive(dataType)) {
|
|
1750
|
-
switch (_typeof(dataType)) {
|
|
1751
|
-
case 'boolean': return sql `to_jsonb(${element})`;
|
|
1752
|
-
case 'number': return sql `to_jsonb(${element})`;
|
|
1753
|
-
case 'decimal': return sql `jsonb_build_object('$decimal', CAST(${element} AS TEXT))`;
|
|
1754
|
-
case 'string': return sql `to_jsonb(${element})`;
|
|
1755
|
-
case 'string[]': return sql `to_jsonb(${element})`;
|
|
1756
|
-
case 'date': return sql `jsonb_build_object(
|
|
1757
|
-
'$date', to_char(${element} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')
|
|
1758
|
-
)`;
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
|
-
return sql `${element}`;
|
|
1762
|
-
}
|
|
1763
|
-
if (expr instanceof QueryValueExpression) {
|
|
1764
|
-
return _encodeJsonValue(_encodeValue(expr.value));
|
|
1765
|
-
}
|
|
1766
|
-
if (expr instanceof QueryArrayExpression) {
|
|
1767
|
-
return sql `jsonb_build_array(${_.map(expr.exprs, x => encodeJsonQueryExpression(compiler, parent, x))})`;
|
|
1768
|
-
}
|
|
1769
|
-
const value = encodeQueryExpression(compiler, parent, expr);
|
|
1770
|
-
if (!value)
|
|
1771
|
-
throw Error('Invalid expression');
|
|
1772
|
-
return sql `to_jsonb(${value})`;
|
|
1773
|
-
};
|
|
1774
|
-
const encodeVectorExpression = (compiler, parent, exprs) => {
|
|
1775
|
-
if (exprs.length === 1) {
|
|
1776
|
-
const [expr] = exprs;
|
|
1777
|
-
if (expr instanceof QueryKeyExpression) {
|
|
1778
|
-
const { element, dataType } = fetchElement(compiler, parent, expr.key);
|
|
1779
|
-
if (!dataType || !isVector(dataType))
|
|
1780
|
-
throw Error('Invalid expression');
|
|
1781
|
-
return { sql: element, dimension: dataType.dimension };
|
|
1782
|
-
}
|
|
1783
|
-
if (expr instanceof QueryValueExpression) {
|
|
1784
|
-
if (!_.isArray(expr.value) || !_.every(expr.value, x => _.isFinite(x)))
|
|
1785
|
-
throw Error('Invalid expression');
|
|
1786
|
-
return { sql: sql `${{ value: expr.value }}::DOUBLE PRECISION[]`, dimension: expr.value.length };
|
|
1787
|
-
}
|
|
1788
|
-
}
|
|
1789
|
-
const result = _.map(exprs, x => _.find(encodeTypedQueryExpression(compiler, parent, x), e => e.type === 'number')?.sql);
|
|
1790
|
-
if (_.some(result, x => _.isNil(x)))
|
|
1791
|
-
throw Error('Invalid expression');
|
|
1792
|
-
return { sql: sql `ARRAY[${_.map(result, x => sql `COALESCE(${x}, 0)`)}]`, dimension: result.length };
|
|
1793
|
-
};
|
|
1794
|
-
const encodeDistanceQueryExpression = (compiler, parent, expr) => {
|
|
1795
|
-
const { sql: left, dimension: d1 } = encodeVectorExpression(compiler, parent, expr.left);
|
|
1796
|
-
const { sql: right, dimension: d2 } = encodeVectorExpression(compiler, parent, expr.right);
|
|
1797
|
-
if (d1 !== d2)
|
|
1798
|
-
throw Error('Invalid expression');
|
|
1799
|
-
const operatorMap = {
|
|
1800
|
-
'$distance': sql `<->`,
|
|
1801
|
-
'$innerProduct': sql `<#>`,
|
|
1802
|
-
'$negInnerProduct': sql `<#>`,
|
|
1803
|
-
'$cosineDistance': sql `<=>`,
|
|
1804
|
-
'$rectilinearDistance': sql `<+>`,
|
|
1805
|
-
};
|
|
1806
|
-
const _expr = sql `
|
|
1807
|
-
CAST(
|
|
1808
|
-
${left} AS VECTOR(${{ literal: `${d1}` }})
|
|
1809
|
-
)
|
|
1810
|
-
${operatorMap[expr.type]}
|
|
1811
|
-
CAST(
|
|
1812
|
-
${right} AS VECTOR(${{ literal: `${d2}` }})
|
|
1813
|
-
)
|
|
1814
|
-
`;
|
|
1815
|
-
return expr.type === '$innerProduct' ? sql `-1 * (${_expr})` : _expr;
|
|
1816
|
-
};
|
|
1817
|
-
const matchType = (first, second) => {
|
|
1818
|
-
const found = _.find(first, l => _.some(second, r => l.type === r.type));
|
|
1819
|
-
return found ? [found, _.find(second, r => r.type === found.type)] : undefined;
|
|
1820
|
-
};
|
|
1821
|
-
const encodeBooleanExpression = (compiler, parent, expr) => {
|
|
1822
|
-
if (expr instanceof QueryCoditionalExpression) {
|
|
1823
|
-
const queries = _.compact(_.map(expr.exprs, x => encodeBooleanExpression(compiler, parent, x)));
|
|
1824
|
-
if (_.isEmpty(queries))
|
|
1825
|
-
return;
|
|
1826
|
-
switch (expr.type) {
|
|
1827
|
-
case '$and': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' AND ' }})`;
|
|
1828
|
-
case '$nor': return sql `(${{ literal: _.map(queries, x => sql `NOT (${x})`), separator: ' AND ' }})`;
|
|
1829
|
-
case '$or': return sql `(${{ literal: _.map(queries, x => sql `(${x})`), separator: ' OR ' }})`;
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
if (expr instanceof QueryComparisonExpression) {
|
|
1833
|
-
const operatorMap = {
|
|
1834
|
-
'$eq': nullSafeEqual(),
|
|
1835
|
-
'$ne': nullSafeNotEqual(),
|
|
1836
|
-
'$gt': sql `>`,
|
|
1837
|
-
'$gte': sql `>=`,
|
|
1838
|
-
'$lt': sql `<`,
|
|
1839
|
-
'$lte': sql `<=`,
|
|
1840
|
-
};
|
|
1841
|
-
if (isArrayExpression(expr.left) &&
|
|
1842
|
-
isArrayExpression(expr.right) &&
|
|
1843
|
-
arrayLength(expr.left) === arrayLength(expr.right)) {
|
|
1844
|
-
const _left = mapExpression(expr.left, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1845
|
-
const _right = mapExpression(expr.right, x => encodeTypedQueryExpression(compiler, parent, x));
|
|
1846
|
-
const mapped = _.compact(_.map(_.zip(_left, _right), ([l, r]) => matchType(l, r)));
|
|
1847
|
-
if (mapped.length === _left.length) {
|
|
1848
|
-
const [l, r] = _.unzip(mapped);
|
|
1849
|
-
return sql `(${_.map(l, x => x.sql)}) ${operatorMap[expr.type]} (${_.map(r, x => x.sql)})`;
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
const _left = encodeTypedQueryExpression(compiler, parent, expr.left);
|
|
1853
|
-
const _right = encodeTypedQueryExpression(compiler, parent, expr.right);
|
|
1854
|
-
if (_left && _right) {
|
|
1855
|
-
const matched = matchType(_left, _right);
|
|
1856
|
-
if (matched)
|
|
1857
|
-
return sql `${matched[0].sql} ${operatorMap[expr.type]} ${matched[1].sql}`;
|
|
1858
|
-
}
|
|
1859
|
-
const _left2 = encodeJsonQueryExpression(compiler, parent, expr.left);
|
|
1860
|
-
const _right2 = encodeJsonQueryExpression(compiler, parent, expr.right);
|
|
1861
|
-
return sql `${_left2} ${operatorMap[expr.type]} ${_right2}`;
|
|
1862
|
-
}
|
|
1863
|
-
if (expr instanceof QueryNotExpression) {
|
|
1864
|
-
const _expr = encodeBooleanExpression(compiler, parent, expr.expr);
|
|
1865
|
-
return _expr ? sql `NOT (${_expr})` : undefined;
|
|
1866
|
-
}
|
|
1867
|
-
throw Error('Invalid expression');
|
|
1868
|
-
};
|
|
1869
|
-
const encodeQueryExpression = (compiler, parent, expr) => {
|
|
1870
|
-
if (expr instanceof QueryDistanceExpression) {
|
|
1871
|
-
return encodeDistanceQueryExpression(compiler, parent, expr);
|
|
1872
|
-
}
|
|
1873
|
-
return encodeBooleanExpression(compiler, parent, expr);
|
|
1874
|
-
};
|
|
1875
|
-
|
|
1876
1880
|
//
|
|
1877
1881
|
// populate.ts
|
|
1878
1882
|
//
|
|
@@ -1989,29 +1993,44 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
1989
1993
|
}
|
|
1990
1994
|
}
|
|
1991
1995
|
else if (expr instanceof QueryExprAccumulator) {
|
|
1992
|
-
const op = {
|
|
1993
|
-
'$max': 'MAX',
|
|
1994
|
-
'$min': 'MIN',
|
|
1995
|
-
'$avg': 'AVG',
|
|
1996
|
-
'$sum': 'SUM',
|
|
1997
|
-
'$stdDevPop': 'STDDEV_POP',
|
|
1998
|
-
'$stdDevSamp': 'STDDEV_SAMP',
|
|
1999
|
-
'$varPop': 'VAR_POP',
|
|
2000
|
-
'$varSamp': 'VAR_SAMP',
|
|
2001
|
-
}[expr.type];
|
|
2002
1996
|
if (!expr.expr)
|
|
2003
1997
|
throw Error('Invalid expression');
|
|
2004
1998
|
const exprs = encodeTypedQueryExpression(compiler, populate, expr.expr);
|
|
2005
1999
|
const value = _.first(exprs)?.sql;
|
|
2006
2000
|
if (!value)
|
|
2007
2001
|
throw Error('Invalid expression');
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2002
|
+
switch (expr.type) {
|
|
2003
|
+
case '$most':
|
|
2004
|
+
columns.push(sql `
|
|
2005
|
+
(
|
|
2006
|
+
SELECT MODE() WITHIN GROUP (ORDER BY (${value})) FROM (
|
|
2007
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2008
|
+
) ${{ identifier: populate.name }}
|
|
2009
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2010
|
+
`);
|
|
2011
|
+
break;
|
|
2012
|
+
default:
|
|
2013
|
+
{
|
|
2014
|
+
const op = {
|
|
2015
|
+
'$max': 'MAX',
|
|
2016
|
+
'$min': 'MIN',
|
|
2017
|
+
'$avg': 'AVG',
|
|
2018
|
+
'$sum': 'SUM',
|
|
2019
|
+
'$stdDevPop': 'STDDEV_POP',
|
|
2020
|
+
'$stdDevSamp': 'STDDEV_SAMP',
|
|
2021
|
+
'$varPop': 'VAR_POP',
|
|
2022
|
+
'$varSamp': 'VAR_SAMP',
|
|
2023
|
+
}[expr.type];
|
|
2024
|
+
columns.push(sql `
|
|
2025
|
+
(
|
|
2026
|
+
SELECT ${{ literal: op }}(${value}) FROM (
|
|
2027
|
+
${_selectRelationPopulate(compiler, parent, populate, field, false)}
|
|
2028
|
+
) ${{ identifier: populate.name }}
|
|
2029
|
+
) AS ${{ identifier: `${field}.${key}` }}
|
|
2030
|
+
`);
|
|
2031
|
+
}
|
|
2032
|
+
break;
|
|
2033
|
+
}
|
|
2015
2034
|
}
|
|
2016
2035
|
else if (expr instanceof QueryPercentileAccumulator) {
|
|
2017
2036
|
const op = {
|