@budibase/backend-core 2.32.11 → 2.32.12

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/index.js CHANGED
@@ -61911,6 +61911,7 @@ __export(filters_exports, {
61911
61911
  ColumnSplitter: () => ColumnSplitter,
61912
61912
  NoEmptyFilterStrings: () => NoEmptyFilterStrings,
61913
61913
  buildQuery: () => buildQuery,
61914
+ buildQueryLegacy: () => buildQueryLegacy,
61914
61915
  cleanupQuery: () => cleanupQuery,
61915
61916
  fixupFilterArrays: () => fixupFilterArrays,
61916
61917
  getKeyNumbering: () => getKeyNumbering,
@@ -61926,6 +61927,156 @@ __export(filters_exports, {
61926
61927
  });
61927
61928
  var import_dayjs = __toESM(require_dayjs_min());
61928
61929
 
61930
+ // ../shared-core/src/utils.ts
61931
+ var utils_exports = {};
61932
+ __export(utils_exports, {
61933
+ filterValueToLabel: () => filterValueToLabel,
61934
+ hasSchema: () => hasSchema,
61935
+ isSupportedUserSearch: () => isSupportedUserSearch,
61936
+ parallelForeach: () => parallelForeach,
61937
+ processSearchFilters: () => processSearchFilters,
61938
+ trimOtherProps: () => trimOtherProps,
61939
+ unreachable: () => unreachable
61940
+ });
61941
+ function unreachable(value, message = `No such case in exhaustive switch: ${value}`) {
61942
+ throw new Error(message);
61943
+ }
61944
+ async function parallelForeach(items, task, maxConcurrency) {
61945
+ const promises = [];
61946
+ let index2 = 0;
61947
+ const processItem = async (item) => {
61948
+ try {
61949
+ await task(item);
61950
+ } finally {
61951
+ processNext();
61952
+ }
61953
+ };
61954
+ const processNext = () => {
61955
+ if (index2 >= items.length) {
61956
+ return;
61957
+ }
61958
+ const item = items[index2];
61959
+ index2++;
61960
+ const promise = processItem(item);
61961
+ promises.push(promise);
61962
+ if (promises.length >= maxConcurrency) {
61963
+ Promise.race(promises).then(processNext);
61964
+ } else {
61965
+ processNext();
61966
+ }
61967
+ };
61968
+ processNext();
61969
+ await Promise.all(promises);
61970
+ }
61971
+ function filterValueToLabel() {
61972
+ return Object.keys(OperatorOptions).reduce(
61973
+ (acc, key) => {
61974
+ const ops = OperatorOptions;
61975
+ const op = ops[key];
61976
+ acc[op["value"]] = op.label;
61977
+ return acc;
61978
+ },
61979
+ {}
61980
+ );
61981
+ }
61982
+ function hasSchema(test) {
61983
+ return typeof test === "object" && !Array.isArray(test) && test !== null && !(test instanceof Date) && Object.keys(test).length > 0;
61984
+ }
61985
+ function trimOtherProps(object, allowedProps) {
61986
+ const result = Object.keys(object).filter((key) => allowedProps.includes(key)).reduce(
61987
+ (acc, key) => ({ ...acc, [key]: object[key] }),
61988
+ {}
61989
+ );
61990
+ return result;
61991
+ }
61992
+ function isSupportedUserSearch(query) {
61993
+ const allowed = [
61994
+ { op: "string" /* STRING */, key: "email" },
61995
+ { op: "equal" /* EQUAL */, key: "_id" },
61996
+ { op: "oneOf" /* ONE_OF */, key: "_id" }
61997
+ ];
61998
+ for (let [key, operation] of Object.entries(query)) {
61999
+ if (typeof operation !== "object") {
62000
+ return false;
62001
+ }
62002
+ const fields = Object.keys(operation || {});
62003
+ if (fields.length === 0) {
62004
+ continue;
62005
+ }
62006
+ const allowedOperation = allowed.find(
62007
+ (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
62008
+ );
62009
+ if (!allowedOperation) {
62010
+ return false;
62011
+ }
62012
+ }
62013
+ return true;
62014
+ }
62015
+ var processSearchFilters = (filters) => {
62016
+ if (!filters) {
62017
+ return;
62018
+ }
62019
+ const defaultCfg = {
62020
+ logicalOperator: "all" /* ALL */,
62021
+ groups: []
62022
+ };
62023
+ const filterAllowedKeys = [
62024
+ "field",
62025
+ "operator",
62026
+ "value",
62027
+ "type",
62028
+ "externalType",
62029
+ "valueType",
62030
+ "noValue",
62031
+ "formulaType"
62032
+ ];
62033
+ if (Array.isArray(filters)) {
62034
+ let baseGroup = {
62035
+ filters: [],
62036
+ logicalOperator: "all" /* ALL */
62037
+ };
62038
+ return filters.reduce((acc, filter) => {
62039
+ const filterPropertyKeys = Object.keys(filter).sort((a, b) => {
62040
+ return a.localeCompare(b);
62041
+ }).filter((key) => key in filter);
62042
+ if (filterPropertyKeys.length == 1) {
62043
+ const key = filterPropertyKeys[0], value = filter[key];
62044
+ if (key === "onEmptyFilter") {
62045
+ acc.onEmptyFilter = value;
62046
+ } else if (key === "operator" && value === "allOr") {
62047
+ baseGroup.logicalOperator = "any" /* ANY */;
62048
+ }
62049
+ return acc;
62050
+ }
62051
+ const allowedFilterSettings = filterPropertyKeys.reduce(
62052
+ (acc2, key) => {
62053
+ const value = filter[key];
62054
+ if (filterAllowedKeys.includes(key)) {
62055
+ if (key === "field") {
62056
+ acc2.push([key, removeKeyNumbering(value)]);
62057
+ } else {
62058
+ acc2.push([key, value]);
62059
+ }
62060
+ }
62061
+ return acc2;
62062
+ },
62063
+ []
62064
+ );
62065
+ const migratedFilter = Object.fromEntries(
62066
+ allowedFilterSettings
62067
+ );
62068
+ baseGroup.filters.push(migratedFilter);
62069
+ if (!acc.groups || !acc.groups.length) {
62070
+ acc.groups = [baseGroup];
62071
+ }
62072
+ return acc;
62073
+ }, defaultCfg);
62074
+ } else if (!filters?.groups) {
62075
+ return;
62076
+ }
62077
+ return filters;
62078
+ };
62079
+
61929
62080
  // ../shared-core/src/helpers/index.ts
61930
62081
  var helpers_exports = {};
61931
62082
  __export(helpers_exports, {
@@ -62221,7 +62372,7 @@ var NoEmptyFilterStrings = [
62221
62372
  ];
62222
62373
  function recurseLogicalOperators(filters, fn) {
62223
62374
  for (const logical of LOGICAL_OPERATORS) {
62224
- if (filters?.[logical]) {
62375
+ if (filters[logical]) {
62225
62376
  filters[logical].conditions = filters[logical].conditions.map(
62226
62377
  (condition) => fn(condition)
62227
62378
  );
@@ -62241,9 +62392,6 @@ function recurseSearchFilters(filters, processFn) {
62241
62392
  return filters;
62242
62393
  }
62243
62394
  var cleanupQuery = (query) => {
62244
- if (!query) {
62245
- return query;
62246
- }
62247
62395
  for (let filterField of NoEmptyFilterStrings) {
62248
62396
  if (!query[filterField]) {
62249
62397
  continue;
@@ -62334,7 +62482,106 @@ var ColumnSplitter = class {
62334
62482
  };
62335
62483
  }
62336
62484
  };
62337
- var buildQuery = (filter) => {
62485
+ var buildCondition = (expression) => {
62486
+ let query = {
62487
+ string: {},
62488
+ fuzzy: {},
62489
+ range: {},
62490
+ equal: {},
62491
+ notEqual: {},
62492
+ empty: {},
62493
+ notEmpty: {},
62494
+ contains: {},
62495
+ notContains: {},
62496
+ oneOf: {},
62497
+ containsAny: {}
62498
+ };
62499
+ let { operator, field, type, value, externalType, onEmptyFilter } = expression;
62500
+ if (!operator || !field) {
62501
+ return;
62502
+ }
62503
+ const queryOperator = operator;
62504
+ const isHbs = typeof value === "string" && (value.match(HBS_REGEX) || []).length > 0;
62505
+ if (operator === "allOr") {
62506
+ query.allOr = true;
62507
+ return;
62508
+ }
62509
+ if (onEmptyFilter) {
62510
+ query.onEmptyFilter = onEmptyFilter;
62511
+ return;
62512
+ }
62513
+ if (queryOperator === "empty" || queryOperator === "notEmpty") {
62514
+ value = null;
62515
+ }
62516
+ if (type === "datetime" && !isHbs && queryOperator !== "empty" && queryOperator !== "notEmpty") {
62517
+ if (!value) {
62518
+ return;
62519
+ }
62520
+ try {
62521
+ value = new Date(value).toISOString();
62522
+ } catch (error) {
62523
+ return;
62524
+ }
62525
+ }
62526
+ if (type === "number" && typeof value === "string" && !isHbs) {
62527
+ if (queryOperator === "oneOf") {
62528
+ value = value.split(",").map((item) => parseFloat(item));
62529
+ } else {
62530
+ value = parseFloat(value);
62531
+ }
62532
+ }
62533
+ if (type === "boolean") {
62534
+ value = `${value}`?.toLowerCase() === "true";
62535
+ }
62536
+ if (["contains", "notContains", "containsAny"].includes(
62537
+ operator.toLocaleString()
62538
+ ) && type === "array" && typeof value === "string") {
62539
+ value = value.split(",");
62540
+ }
62541
+ if (operator.toLocaleString().startsWith("range") && query.range) {
62542
+ const minint = SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER;
62543
+ const maxint = SqlNumberTypeRangeMap[externalType]?.max || Number.MAX_SAFE_INTEGER;
62544
+ if (!query.range[field]) {
62545
+ query.range[field] = {
62546
+ low: type === "number" ? minint : "0000-00-00T00:00:00.000Z",
62547
+ high: type === "number" ? maxint : "9999-00-00T00:00:00.000Z"
62548
+ };
62549
+ }
62550
+ if (operator === "rangeLow" && value != null && value !== "") {
62551
+ query.range[field] = {
62552
+ ...query.range[field],
62553
+ low: value
62554
+ };
62555
+ } else if (operator === "rangeHigh" && value != null && value !== "") {
62556
+ query.range[field] = {
62557
+ ...query.range[field],
62558
+ high: value
62559
+ };
62560
+ }
62561
+ } else if (isLogicalSearchOperator(queryOperator)) {
62562
+ } else if (query[queryOperator] && operator !== "onEmptyFilter") {
62563
+ if (type === "boolean") {
62564
+ if (queryOperator === "equal" && value === false) {
62565
+ query.notEqual = query.notEqual || {};
62566
+ query.notEqual[field] = true;
62567
+ } else if (queryOperator === "notEqual" && value === false) {
62568
+ query.equal = query.equal || {};
62569
+ query.equal[field] = true;
62570
+ } else {
62571
+ query[queryOperator] ??= {};
62572
+ query[queryOperator][field] = value;
62573
+ }
62574
+ } else {
62575
+ query[queryOperator] ??= {};
62576
+ query[queryOperator][field] = value;
62577
+ }
62578
+ }
62579
+ return query;
62580
+ };
62581
+ var buildQueryLegacy = (filter) => {
62582
+ if (!Array.isArray(filter)) {
62583
+ return filter;
62584
+ }
62338
62585
  let query = {
62339
62586
  string: {},
62340
62587
  fuzzy: {},
@@ -62383,10 +62630,12 @@ var buildQuery = (filter) => {
62383
62630
  if (type === "boolean") {
62384
62631
  value = `${value}`?.toLowerCase() === "true";
62385
62632
  }
62386
- if (["contains", "notContains", "containsAny"].includes(operator) && type === "array" && typeof value === "string") {
62633
+ if (["contains", "notContains", "containsAny"].includes(
62634
+ operator.toLocaleString()
62635
+ ) && type === "array" && typeof value === "string") {
62387
62636
  value = value.split(",");
62388
62637
  }
62389
- if (operator.startsWith("range") && query.range) {
62638
+ if (operator.toLocaleString().startsWith("range") && query.range) {
62390
62639
  const minint = SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER;
62391
62640
  const maxint = SqlNumberTypeRangeMap[externalType]?.max || Number.MAX_SAFE_INTEGER;
62392
62641
  if (!query.range[field]) {
@@ -62427,6 +62676,30 @@ var buildQuery = (filter) => {
62427
62676
  });
62428
62677
  return query;
62429
62678
  };
62679
+ var buildQuery = (filter) => {
62680
+ const parsedFilter = processSearchFilters(filter);
62681
+ if (!parsedFilter) {
62682
+ return;
62683
+ }
62684
+ const operatorMap = {
62685
+ ["all" /* ALL */]: "$and" /* AND */,
62686
+ ["any" /* ANY */]: "$or" /* OR */
62687
+ };
62688
+ const globalOnEmpty = parsedFilter.onEmptyFilter ? parsedFilter.onEmptyFilter : null;
62689
+ const globalOperator = operatorMap[parsedFilter.logicalOperator];
62690
+ return {
62691
+ ...globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {},
62692
+ [globalOperator]: {
62693
+ conditions: parsedFilter.groups?.map((group) => {
62694
+ return {
62695
+ [operatorMap[group.logicalOperator]]: {
62696
+ conditions: group.filters?.map((x) => buildCondition(x)).filter((filter2) => filter2)
62697
+ }
62698
+ };
62699
+ })
62700
+ }
62701
+ };
62702
+ };
62430
62703
  function fixupFilterArrays(filters) {
62431
62704
  for (const searchField of Object.values(ArrayOperator)) {
62432
62705
  const field = filters[searchField];
@@ -62767,91 +63040,6 @@ var hasFilters = (query) => {
62767
63040
  return check(query);
62768
63041
  };
62769
63042
 
62770
- // ../shared-core/src/utils.ts
62771
- var utils_exports = {};
62772
- __export(utils_exports, {
62773
- filterValueToLabel: () => filterValueToLabel,
62774
- hasSchema: () => hasSchema,
62775
- isSupportedUserSearch: () => isSupportedUserSearch,
62776
- parallelForeach: () => parallelForeach,
62777
- trimOtherProps: () => trimOtherProps,
62778
- unreachable: () => unreachable
62779
- });
62780
- function unreachable(value, message = `No such case in exhaustive switch: ${value}`) {
62781
- throw new Error(message);
62782
- }
62783
- async function parallelForeach(items, task, maxConcurrency) {
62784
- const promises = [];
62785
- let index2 = 0;
62786
- const processItem = async (item) => {
62787
- try {
62788
- await task(item);
62789
- } finally {
62790
- processNext();
62791
- }
62792
- };
62793
- const processNext = () => {
62794
- if (index2 >= items.length) {
62795
- return;
62796
- }
62797
- const item = items[index2];
62798
- index2++;
62799
- const promise = processItem(item);
62800
- promises.push(promise);
62801
- if (promises.length >= maxConcurrency) {
62802
- Promise.race(promises).then(processNext);
62803
- } else {
62804
- processNext();
62805
- }
62806
- };
62807
- processNext();
62808
- await Promise.all(promises);
62809
- }
62810
- function filterValueToLabel() {
62811
- return Object.keys(OperatorOptions).reduce(
62812
- (acc, key) => {
62813
- const ops = OperatorOptions;
62814
- const op = ops[key];
62815
- acc[op["value"]] = op.label;
62816
- return acc;
62817
- },
62818
- {}
62819
- );
62820
- }
62821
- function hasSchema(test) {
62822
- return typeof test === "object" && !Array.isArray(test) && test !== null && !(test instanceof Date) && Object.keys(test).length > 0;
62823
- }
62824
- function trimOtherProps(object, allowedProps) {
62825
- const result = Object.keys(object).filter((key) => allowedProps.includes(key)).reduce(
62826
- (acc, key) => ({ ...acc, [key]: object[key] }),
62827
- {}
62828
- );
62829
- return result;
62830
- }
62831
- function isSupportedUserSearch(query) {
62832
- const allowed = [
62833
- { op: "string" /* STRING */, key: "email" },
62834
- { op: "equal" /* EQUAL */, key: "_id" },
62835
- { op: "oneOf" /* ONE_OF */, key: "_id" }
62836
- ];
62837
- for (let [key, operation] of Object.entries(query)) {
62838
- if (typeof operation !== "object") {
62839
- return false;
62840
- }
62841
- const fields = Object.keys(operation || {});
62842
- if (fields.length === 0) {
62843
- continue;
62844
- }
62845
- const allowedOperation = allowed.find(
62846
- (allow) => allow.op === key && fields.length === 1 && fields[0] === allow.key
62847
- );
62848
- if (!allowedOperation) {
62849
- return false;
62850
- }
62851
- }
62852
- return true;
62853
- }
62854
-
62855
63043
  // ../shared-core/src/sdk/index.ts
62856
63044
  var sdk_exports = {};
62857
63045
  __export(sdk_exports, {
@@ -75270,25 +75458,56 @@ var InternalBuilder = class {
75270
75458
  const { column } = this.splitter.run(key);
75271
75459
  return this.table.schema[column];
75272
75460
  }
75273
- // Takes a string like foo and returns a quoted string like [foo] for SQL Server
75274
- // and "foo" for Postgres.
75275
- quote(str) {
75461
+ quoteChars() {
75276
75462
  switch (this.client) {
75277
- case "sqlite3" /* SQL_LITE */:
75278
75463
  case "oracledb" /* ORACLE */:
75279
75464
  case "pg" /* POSTGRES */:
75280
- return `"${str}"`;
75465
+ return ['"', '"'];
75281
75466
  case "mssql" /* MS_SQL */:
75282
- return `[${str}]`;
75467
+ return ["[", "]"];
75283
75468
  case "mariadb" /* MARIADB */:
75284
75469
  case "mysql2" /* MY_SQL */:
75285
- return `\`${str}\``;
75470
+ case "sqlite3" /* SQL_LITE */:
75471
+ return ["`", "`"];
75286
75472
  }
75287
75473
  }
75288
- // Takes a string like a.b.c and returns a quoted identifier like [a].[b].[c]
75289
- // for SQL Server and `a`.`b`.`c` for MySQL.
75474
+ // Takes a string like foo and returns a quoted string like [foo] for SQL Server
75475
+ // and "foo" for Postgres.
75476
+ quote(str) {
75477
+ const [start2, end2] = this.quoteChars();
75478
+ return `${start2}${str}${end2}`;
75479
+ }
75480
+ isQuoted(key) {
75481
+ const [start2, end2] = this.quoteChars();
75482
+ return key.startsWith(start2) && key.endsWith(end2);
75483
+ }
75484
+ // Takes a string like a.b.c or an array like ["a", "b", "c"] and returns a
75485
+ // quoted identifier like [a].[b].[c] for SQL Server and `a`.`b`.`c` for
75486
+ // MySQL.
75290
75487
  quotedIdentifier(key) {
75291
- return key.split(".").map((part) => this.quote(part)).join(".");
75488
+ if (!Array.isArray(key)) {
75489
+ key = this.splitIdentifier(key);
75490
+ }
75491
+ return key.map((part) => this.quote(part)).join(".");
75492
+ }
75493
+ // Turns an identifier like a.b.c or `a`.`b`.`c` into ["a", "b", "c"]
75494
+ splitIdentifier(key) {
75495
+ const [start2, end2] = this.quoteChars();
75496
+ if (this.isQuoted(key)) {
75497
+ return key.slice(1, -1).split(`${end2}.${start2}`);
75498
+ }
75499
+ return key.split(".");
75500
+ }
75501
+ qualifyIdentifier(key) {
75502
+ const tableName = this.getTableName();
75503
+ const parts = this.splitIdentifier(key);
75504
+ if (parts[0] !== tableName) {
75505
+ parts.unshift(tableName);
75506
+ }
75507
+ if (this.isQuoted(key)) {
75508
+ return this.quotedIdentifier(parts);
75509
+ }
75510
+ return parts.join(".");
75292
75511
  }
75293
75512
  isFullSelectStatementRequired() {
75294
75513
  const { meta } = this.query;
@@ -75339,13 +75558,22 @@ var InternalBuilder = class {
75339
75558
  // OracleDB can't use character-large-objects (CLOBs) in WHERE clauses,
75340
75559
  // so when we use them we need to wrap them in to_char(). This function
75341
75560
  // converts a field name to the appropriate identifier.
75342
- convertClobs(field) {
75343
- const parts = field.split(".");
75561
+ convertClobs(field, opts) {
75562
+ if (this.client !== "oracledb" /* ORACLE */) {
75563
+ throw new Error(
75564
+ "you've called convertClobs on a DB that's not Oracle, this is a mistake"
75565
+ );
75566
+ }
75567
+ const parts = this.splitIdentifier(field);
75344
75568
  const col = parts.pop();
75345
75569
  const schema = this.table.schema[col];
75346
75570
  let identifier = this.quotedIdentifier(field);
75347
75571
  if (schema.type === "string" /* STRING */ || schema.type === "longform" /* LONGFORM */ || schema.type === "bb_reference_single" /* BB_REFERENCE_SINGLE */ || schema.type === "bb_reference" /* BB_REFERENCE */ || schema.type === "options" /* OPTIONS */ || schema.type === "barcodeqr" /* BARCODEQR */) {
75348
- identifier = `to_char(${identifier})`;
75572
+ if (opts?.forSelect) {
75573
+ identifier = `to_char(${identifier}) as ${this.quotedIdentifier(col)}`;
75574
+ } else {
75575
+ identifier = `to_char(${identifier})`;
75576
+ }
75349
75577
  }
75350
75578
  return identifier;
75351
75579
  }
@@ -75842,28 +76070,56 @@ var InternalBuilder = class {
75842
76070
  const fields = this.query.resource?.fields || [];
75843
76071
  const tableName = this.getTableName();
75844
76072
  if (fields.length > 0) {
75845
- query = query.groupBy(fields.map((field) => `${tableName}.${field}`));
75846
- query = query.select(fields.map((field) => `${tableName}.${field}`));
76073
+ const qualifiedFields = fields.map((field) => this.qualifyIdentifier(field));
76074
+ if (this.client === "oracledb" /* ORACLE */) {
76075
+ const groupByFields = qualifiedFields.map(
76076
+ (field) => this.convertClobs(field)
76077
+ );
76078
+ const selectFields = qualifiedFields.map(
76079
+ (field) => this.convertClobs(field, { forSelect: true })
76080
+ );
76081
+ query = query.groupByRaw(groupByFields.join(", ")).select(this.knex.raw(selectFields.join(", ")));
76082
+ } else {
76083
+ query = query.groupBy(qualifiedFields).select(qualifiedFields);
76084
+ }
75847
76085
  }
75848
76086
  for (const aggregation of aggregations) {
75849
76087
  const op = aggregation.calculationType;
75850
- const field = `${tableName}.${aggregation.field} as ${aggregation.name}`;
75851
- switch (op) {
75852
- case "count" /* COUNT */:
75853
- query = query.count(field);
75854
- break;
75855
- case "sum" /* SUM */:
75856
- query = query.sum(field);
75857
- break;
75858
- case "avg" /* AVG */:
75859
- query = query.avg(field);
75860
- break;
75861
- case "min" /* MIN */:
75862
- query = query.min(field);
75863
- break;
75864
- case "max" /* MAX */:
75865
- query = query.max(field);
75866
- break;
76088
+ if (op === "count" /* COUNT */) {
76089
+ if ("distinct" in aggregation && aggregation.distinct) {
76090
+ if (this.client === "oracledb" /* ORACLE */) {
76091
+ const field = this.convertClobs(`${tableName}.${aggregation.field}`);
76092
+ query = query.select(
76093
+ this.knex.raw(
76094
+ `COUNT(DISTINCT ${field}) as ${this.quotedIdentifier(
76095
+ aggregation.name
76096
+ )}`
76097
+ )
76098
+ );
76099
+ } else {
76100
+ query = query.countDistinct(
76101
+ `${tableName}.${aggregation.field} as ${aggregation.name}`
76102
+ );
76103
+ }
76104
+ } else {
76105
+ query = query.count(`* as ${aggregation.name}`);
76106
+ }
76107
+ } else {
76108
+ const field = `${tableName}.${aggregation.field} as ${aggregation.name}`;
76109
+ switch (op) {
76110
+ case "sum" /* SUM */:
76111
+ query = query.sum(field);
76112
+ break;
76113
+ case "avg" /* AVG */:
76114
+ query = query.avg(field);
76115
+ break;
76116
+ case "min" /* MIN */:
76117
+ query = query.min(field);
76118
+ break;
76119
+ case "max" /* MAX */:
76120
+ query = query.max(field);
76121
+ break;
76122
+ }
75867
76123
  }
75868
76124
  }
75869
76125
  return query;