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.
Files changed (43) hide show
  1. package/dist/adapters/file/aliyun-oss.d.ts +3 -3
  2. package/dist/adapters/file/database.d.ts +2 -2
  3. package/dist/adapters/file/database.js +1 -1
  4. package/dist/adapters/file/database.mjs +2 -2
  5. package/dist/adapters/file/filesystem.d.ts +3 -3
  6. package/dist/adapters/file/google-cloud-storage.d.ts +3 -3
  7. package/dist/adapters/storage/progres.d.ts +4 -8
  8. package/dist/adapters/storage/progres.js +457 -416
  9. package/dist/adapters/storage/progres.js.map +1 -1
  10. package/dist/adapters/storage/progres.mjs +458 -417
  11. package/dist/adapters/storage/progres.mjs.map +1 -1
  12. package/dist/client.d.ts +3 -3
  13. package/dist/client.mjs +3 -3
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.js +7 -8
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +10 -11
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/internals/{base-E1b8J-Fs.d.ts → base-BO3ZP6EF.d.ts} +2 -2
  20. package/dist/internals/base-BO3ZP6EF.d.ts.map +1 -0
  21. package/dist/internals/{chunk-D1bD7otk.d.ts → chunk-DDkLpKXp.d.ts} +3 -3
  22. package/dist/internals/chunk-DDkLpKXp.d.ts.map +1 -0
  23. package/dist/internals/{index-al1N-qi7.mjs → index-BzDsTt4R.mjs} +2 -2
  24. package/dist/internals/{index-al1N-qi7.mjs.map → index-BzDsTt4R.mjs.map} +1 -1
  25. package/dist/internals/{index-SSEdPyhp.d.ts → index-CYhA8SU8.d.ts} +2 -2
  26. package/dist/internals/index-CYhA8SU8.d.ts.map +1 -0
  27. package/dist/internals/{index-ZPbBr9Db.mjs → index-DF2AfSGK.mjs} +2 -2
  28. package/dist/internals/index-DF2AfSGK.mjs.map +1 -0
  29. package/dist/internals/index-DfnPpl1I.js.map +1 -1
  30. package/dist/internals/{index-CbjY-gJ7.d.ts → index-HdMgLYtD.d.ts} +14 -5
  31. package/dist/internals/index-HdMgLYtD.d.ts.map +1 -0
  32. package/dist/internals/{validator-DoRPoIs2.js → validator-BBjOdLiT.js} +42 -3
  33. package/dist/internals/validator-BBjOdLiT.js.map +1 -0
  34. package/dist/internals/{validator-Dxmbwa3P.mjs → validator-LNgZGT_l.mjs} +42 -5
  35. package/dist/internals/validator-LNgZGT_l.mjs.map +1 -0
  36. package/package.json +1 -1
  37. package/dist/internals/base-E1b8J-Fs.d.ts.map +0 -1
  38. package/dist/internals/chunk-D1bD7otk.d.ts.map +0 -1
  39. package/dist/internals/index-CbjY-gJ7.d.ts.map +0 -1
  40. package/dist/internals/index-SSEdPyhp.d.ts.map +0 -1
  41. package/dist/internals/index-ZPbBr9Db.mjs.map +0 -1
  42. package/dist/internals/validator-DoRPoIs2.js.map +0 -1
  43. package/dist/internals/validator-Dxmbwa3P.mjs.map +0 -1
@@ -1,11 +1,11 @@
1
1
  import _ from 'lodash';
2
- import { a as isRelation, i as isPointer, b as isShape, e as decodeUpdateOp, s as shapePaths, c as isPrimitive, T as TObject, _ as _isTypeof, f as isVector, g as _encodeValue, h as _decodeValue, j as dimensionOf, k as _typeof } from '../../internals/index-al1N-qi7.mjs';
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, a as resolveDataType$1, c as QueryCoditionalSelector, d as QueryFieldSelector, e as QueryExpressionSelector, Q as QueryValidator, f as QueryDistanceExpression, g as QueryKeyExpression, h as QueryValueExpression, i as QueryCoditionalExpression, j as QueryComparisonExpression, k as QueryNotExpression, l as QueryArrayExpression, m as QueryNoParamAccumulator, n as QueryExprAccumulator, b as QuerySelector, F as FieldSelectorExpression } from '../../internals/validator-Dxmbwa3P.mjs';
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 = (opts) => {
951
- return opts.weight ? sql `-ln(random()) / ${{ identifier: opts.weight }}` : sql `random()`;
1347
+ const random = (weight) => {
1348
+ return weight ? sql `-ln(random()) / ${weight}` : sql `random()`;
952
1349
  };
953
1350
 
954
1351
  //
@@ -1186,180 +1583,13 @@ class PostgresDriver extends PostgresClientDriver {
1186
1583
  if (_channel === channel)
1187
1584
  callback(payload);
1188
1585
  });
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;
1586
+ return () => {
1587
+ release();
1588
+ if (this.pubsub?.isEmpty())
1589
+ this._release_pubsub();
1590
+ };
1360
1591
  }
1361
- return null;
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,30 +1993,67 @@ 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;
2000
+ if (!value)
2001
+ throw Error('Invalid expression');
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
+ }
2034
+ }
2035
+ else if (expr instanceof QueryPercentileAccumulator) {
2036
+ const op = {
2037
+ 'discrete': 'PERCENTILE_DISC',
2038
+ 'continuous': 'PERCENTILE_CONT',
2039
+ }[expr.mode];
2040
+ if (!expr.input)
2041
+ throw Error('Invalid expression');
2042
+ const exprs = encodeTypedQueryExpression(compiler, populate, expr.input);
2043
+ const value = _.first(exprs)?.sql;
2006
2044
  if (!value)
2007
2045
  throw Error('Invalid expression');
2008
2046
  columns.push(sql `
2009
2047
  (
2010
- SELECT ${{ literal: op }}(${value}) FROM (
2048
+ SELECT ${{ literal: op }}(${{ value: expr.p }}) WITHIN GROUP (ORDER BY (${value})) FROM (
2011
2049
  ${_selectRelationPopulate(compiler, parent, populate, field, false)}
2012
2050
  ) ${{ identifier: populate.name }}
2013
2051
  ) AS ${{ identifier: `${field}.${key}` }}
2014
2052
  `);
2015
2053
  }
2054
+ else {
2055
+ throw Error('Invalid expression');
2056
+ }
2016
2057
  }
2017
2058
  }
2018
2059
  else {