industrial-model 0.3.0 → 0.4.0

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
@@ -26,6 +26,14 @@ var CogniteSdkAdapter = class {
26
26
  nextCursor: response.nextCursor
27
27
  };
28
28
  }
29
+ async aggregateInstances(request) {
30
+ const response = await this.client.instances.aggregate(
31
+ request
32
+ );
33
+ return {
34
+ items: response.items
35
+ };
36
+ }
29
37
  };
30
38
 
31
39
  // src/constants.ts
@@ -34,6 +42,8 @@ var EDGE_MARKER = "<EdgeMarker>";
34
42
  var MAX_LIMIT = 1e4;
35
43
  var DEFAULT_LIMIT = 1e3;
36
44
  var MAX_DEPENDENCY_DEPTH = 3;
45
+ var AGGREGATE_LIMIT = 1e3;
46
+ var MAX_GROUP_BY = 5;
37
47
 
38
48
  // src/mappers/utils.ts
39
49
  var NODE_PROPERTIES = /* @__PURE__ */ new Set([
@@ -75,135 +85,32 @@ function buildSelect(source, properties) {
75
85
  if (properties.length === 0) return {};
76
86
  return { sources: [{ source, properties }] };
77
87
  }
78
-
79
- // src/mappers/filter-mapper.ts
80
- var LEAF_OPS = /* @__PURE__ */ new Set([
81
- "eq",
82
- "in",
83
- "gt",
84
- "gte",
85
- "lt",
86
- "lte",
87
- "exists",
88
- "prefix",
89
- "containsAny",
90
- "containsAll"
88
+ var GROUPABLE_PROPERTY_TYPES = /* @__PURE__ */ new Set([
89
+ "text",
90
+ "direct",
91
+ "int32",
92
+ "int64",
93
+ "float32",
94
+ "float64",
95
+ "boolean",
96
+ "enum"
91
97
  ]);
92
- function isLeafFilter(value) {
93
- return Object.keys(value).some((k) => LEAF_OPS.has(k));
98
+ var NUMERIC_PROPERTY_TYPES = /* @__PURE__ */ new Set(["int32", "int64", "float32", "float64"]);
99
+ function isGroupableProperty(property) {
100
+ if (!isViewPropertyDefinition(property)) return false;
101
+ if (property.type.list === true) return false;
102
+ const type = property.type.type;
103
+ return type != null && GROUPABLE_PROPERTY_TYPES.has(type);
94
104
  }
95
- var FilterMapper = class {
96
- constructor(viewMapper) {
97
- this.viewMapper = viewMapper;
98
- }
99
- async map(input, rootView) {
100
- const result = [];
101
- for (const [key, value] of Object.entries(input)) {
102
- if (value == null) continue;
103
- if (key === "AND") {
104
- const clauses = Array.isArray(value) ? value : [value];
105
- const inner = await Promise.all(clauses.map((c) => this.whereInputToSingle(c, rootView)));
106
- result.push({ and: inner });
107
- } else if (key === "OR") {
108
- const clauses = value;
109
- const branches = await Promise.all(
110
- clauses.map((c) => this.whereInputToSingle(c, rootView))
111
- );
112
- result.push({ or: branches });
113
- } else if (key === "NOT") {
114
- const clauses = Array.isArray(value) ? value : [value];
115
- const [firstClause, ...restClauses] = clauses;
116
- const combined = restClauses.length === 0 && firstClause !== void 0 ? firstClause : { AND: clauses };
117
- result.push({ not: await this.whereInputToSingle(combined, rootView) });
118
- } else {
119
- const filterValue = value;
120
- const property = getPropertyRef(key, rootView);
121
- if (isLeafFilter(filterValue)) {
122
- result.push(...this.leafToFilterDefs(property, filterValue));
123
- } else {
124
- const targetView = await this.getNestedTargetView(key, rootView);
125
- const innerFilter = await this.whereInputToSingle(filterValue, targetView);
126
- result.push({ nested: { scope: property, filter: innerFilter } });
127
- }
128
- }
129
- }
130
- return result;
131
- }
132
- async whereInputToSingle(input, rootView) {
133
- const filters = await this.map(input, rootView);
134
- const [firstFilter, ...restFilters] = filters;
135
- if (restFilters.length === 0 && firstFilter !== void 0) {
136
- return firstFilter;
137
- }
138
- return { and: filters };
139
- }
140
- leafToFilterDefs(property, filter) {
141
- const result = [];
142
- if ("eq" in filter && filter.eq !== void 0) {
143
- result.push({
144
- equals: { property, value: this.coerceValue(filter.eq) }
145
- });
146
- }
147
- if ("in" in filter && filter.in !== void 0) {
148
- result.push({
149
- in: { property, values: this.coerceValue(filter.in) }
150
- });
151
- }
152
- if ("gt" in filter && filter.gt !== void 0) {
153
- result.push({ range: { property, gt: this.coerceValue(filter.gt) } });
154
- }
155
- if ("gte" in filter && filter.gte !== void 0) {
156
- result.push({ range: { property, gte: this.coerceValue(filter.gte) } });
157
- }
158
- if ("lt" in filter && filter.lt !== void 0) {
159
- result.push({ range: { property, lt: this.coerceValue(filter.lt) } });
160
- }
161
- if ("lte" in filter && filter.lte !== void 0) {
162
- result.push({ range: { property, lte: this.coerceValue(filter.lte) } });
163
- }
164
- if ("exists" in filter) {
165
- if (filter.exists === true) {
166
- result.push({ exists: { property } });
167
- } else if (filter.exists === false) {
168
- result.push({ not: { exists: { property } } });
169
- }
170
- }
171
- if ("prefix" in filter && filter.prefix !== void 0) {
172
- result.push({ prefix: { property, value: this.coerceValue(filter.prefix) } });
173
- }
174
- if ("containsAll" in filter && filter.containsAll !== void 0) {
175
- result.push({
176
- containsAll: {
177
- property,
178
- values: this.coerceValue(filter.containsAll)
179
- }
180
- });
181
- }
182
- if ("containsAny" in filter && filter.containsAny !== void 0) {
183
- result.push({
184
- containsAny: {
185
- property,
186
- values: this.coerceValue(filter.containsAny)
187
- }
188
- });
189
- }
190
- return result;
191
- }
192
- async getNestedTargetView(property, rootView) {
193
- const viewProp = rootView.properties[property];
194
- if (!viewProp || !isViewPropertyDefinition(viewProp)) {
195
- throw new Error(`Property "${property}" is not a mapped property`);
196
- }
197
- const source = getDirectRelationSource(viewProp);
198
- if (!source) throw new Error(`Property "${property}" has no relation source`);
199
- return this.viewMapper.getView(source.externalId);
200
- }
201
- coerceValue(value) {
202
- if (value instanceof Date) return value.toISOString();
203
- if (Array.isArray(value)) return value.map((v) => this.coerceValue(v));
204
- return value;
205
- }
206
- };
105
+ function isNumericProperty(property) {
106
+ const type = property.type.type;
107
+ return type != null && NUMERIC_PROPERTY_TYPES.has(type);
108
+ }
109
+ function getSelectedGroupByKeys(groupBy) {
110
+ return Object.entries(groupBy).filter((entry) => entry[1] === true).map(([key]) => key);
111
+ }
112
+
113
+ // src/validation.ts
207
114
  var nodeIdSchema = z.object({
208
115
  space: z.string().min(1),
209
116
  externalId: z.string().min(1)
@@ -283,7 +190,7 @@ var leafOps = /* @__PURE__ */ new Set([
283
190
  function isRecord(value) {
284
191
  return value != null && typeof value === "object" && !Array.isArray(value);
285
192
  }
286
- function isLeafFilter2(value) {
193
+ function isLeafFilter(value) {
287
194
  return Object.keys(value).some((key) => leafOps.has(key));
288
195
  }
289
196
  function issuePath(path) {
@@ -403,7 +310,7 @@ var QueryValidator = class {
403
310
  errors.push(...await this.validateSelect(options.select, rootView, ["select"]));
404
311
  }
405
312
  if (options.filters !== void 0) {
406
- errors.push(...await this.validateFilters(options.filters, rootView, ["filters"]));
313
+ errors.push(...await this.validateWhereInput(options.filters, rootView, ["filters"]));
407
314
  }
408
315
  if (options.sort !== void 0) {
409
316
  errors.push(...this.validateSort(options.sort, rootView, ["sort"]));
@@ -463,6 +370,9 @@ ${errors.map((error) => `- ${error}`).join("\n")}`);
463
370
  }
464
371
  return errors;
465
372
  }
373
+ async validateWhereInput(filters, view, path) {
374
+ return this.validateFilters(filters, view, path);
375
+ }
466
376
  async validateFilters(filters, view, path) {
467
377
  const shape = {
468
378
  AND: z.union([recordSchema, z.array(recordSchema)]).optional(),
@@ -506,7 +416,7 @@ ${errors.map((error) => `- ${error}`).join("\n")}`);
506
416
  if (!property) continue;
507
417
  if (isViewPropertyDefinition(property)) {
508
418
  const target2 = getDirectRelationSource(property);
509
- if (target2 != null && !isLeafFilter2(value)) {
419
+ if (target2 != null && !isLeafFilter(value)) {
510
420
  const targetView = await this.viewMapper.getView(target2.externalId);
511
421
  errors.push(...await this.validateFilters(value, targetView, [...path, name]));
512
422
  } else {
@@ -544,6 +454,351 @@ ${errors.map((error) => `- ${error}`).join("\n")}`);
544
454
  }
545
455
  };
546
456
 
457
+ // src/mappers/aggregate-validator.ts
458
+ var NODE_COUNT_PROPERTIES = /* @__PURE__ */ new Set(["externalId", "space"]);
459
+ function issuePath2(path) {
460
+ return path.length === 0 ? "aggregate" : path.map(String).join(".");
461
+ }
462
+ function formatZodIssues2(error, path) {
463
+ return error.issues.map((issue) => `${issuePath2([...path, ...issue.path])}: ${issue.message}`);
464
+ }
465
+ function isEmptyObject(value) {
466
+ return value != null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
467
+ }
468
+ var AggregateValidator = class {
469
+ constructor(viewMapper) {
470
+ this.queryValidator = new QueryValidator(viewMapper);
471
+ }
472
+ async validate(options, rootView) {
473
+ const errors = [];
474
+ errors.push(...this.validateOptionsShape(options, rootView));
475
+ const selectedGroupBy = options.groupBy ? getSelectedGroupByKeys(options.groupBy) : [];
476
+ if (selectedGroupBy.length === 0 && options.aggregate === void 0) {
477
+ errors.push("aggregate: either groupBy or aggregate must be provided");
478
+ }
479
+ if (options.filters !== void 0) {
480
+ errors.push(
481
+ ...await this.queryValidator.validateWhereInput(options.filters, rootView, ["filters"])
482
+ );
483
+ }
484
+ if (options.groupBy !== void 0) {
485
+ errors.push(...this.validateGroupBy(options.groupBy, rootView, ["groupBy"]));
486
+ }
487
+ if (options.aggregate !== void 0) {
488
+ errors.push(
489
+ ...this.validateAggregate(
490
+ options.aggregate,
491
+ rootView,
492
+ ["aggregate"]
493
+ )
494
+ );
495
+ }
496
+ if (errors.length > 0) {
497
+ throw new Error(
498
+ `Invalid aggregate options:
499
+ ${errors.map((error) => `- ${error}`).join("\n")}`
500
+ );
501
+ }
502
+ }
503
+ validateOptionsShape(options, rootView) {
504
+ const schema = z.object({
505
+ viewExternalId: z.literal(rootView.externalId),
506
+ filters: z.unknown().optional(),
507
+ groupBy: z.unknown().optional(),
508
+ aggregate: z.unknown().optional()
509
+ }).strict();
510
+ const result = schema.safeParse(options);
511
+ return result.success ? [] : formatZodIssues2(result.error, []);
512
+ }
513
+ validateGroupBy(groupBy, view, path) {
514
+ const shape = {};
515
+ for (const [name, property] of Object.entries(view.properties)) {
516
+ if (isGroupableProperty(property)) {
517
+ shape[name] = z.literal(true).optional();
518
+ }
519
+ }
520
+ const result = z.object(shape).strict().safeParse(groupBy);
521
+ if (!result.success) {
522
+ return formatZodIssues2(result.error, path);
523
+ }
524
+ const selected = getSelectedGroupByKeys(groupBy);
525
+ const errors = [];
526
+ if (selected.length === 0) {
527
+ errors.push(`${issuePath2(path)}: at least one property must be set to true`);
528
+ }
529
+ if (selected.length > MAX_GROUP_BY) {
530
+ errors.push(`${issuePath2(path)}: at most ${MAX_GROUP_BY} properties can be grouped`);
531
+ }
532
+ for (const name of selected) {
533
+ const property = view.properties[name];
534
+ if (!property || !isGroupableProperty(property)) {
535
+ errors.push(`${issuePath2([...path, name])}: property "${name}" cannot be used in groupBy`);
536
+ }
537
+ }
538
+ return errors;
539
+ }
540
+ validateAggregate(aggregate, view, path) {
541
+ if ("count" in aggregate) {
542
+ const property2 = aggregate.count;
543
+ if (isEmptyObject(property2)) {
544
+ return [];
545
+ }
546
+ if (typeof property2 === "string") {
547
+ if (NODE_COUNT_PROPERTIES.has(property2)) {
548
+ return [];
549
+ }
550
+ const viewProperty = view.properties[property2];
551
+ if (!viewProperty || !isGroupableProperty(viewProperty)) {
552
+ return [`${issuePath2([...path, "count"])}: property "${property2}" cannot be counted`];
553
+ }
554
+ return [];
555
+ }
556
+ return [`${issuePath2([...path, "count"])}: invalid count property`];
557
+ }
558
+ let propertyName;
559
+ let numericOp = null;
560
+ if ("avg" in aggregate) {
561
+ numericOp = "avg";
562
+ propertyName = aggregate.avg;
563
+ } else if ("min" in aggregate) {
564
+ numericOp = "min";
565
+ propertyName = aggregate.min;
566
+ } else if ("max" in aggregate) {
567
+ numericOp = "max";
568
+ propertyName = aggregate.max;
569
+ } else if ("sum" in aggregate) {
570
+ numericOp = "sum";
571
+ propertyName = aggregate.sum;
572
+ }
573
+ if (numericOp == null) {
574
+ return [`${issuePath2(path)}: unknown aggregate operation`];
575
+ }
576
+ if (typeof propertyName !== "string") {
577
+ return [`${issuePath2(path)}: aggregate property must be a string`];
578
+ }
579
+ const property = view.properties[propertyName];
580
+ if (!property || !isViewPropertyDefinition(property) || !isNumericProperty(property)) {
581
+ return [
582
+ `${issuePath2([...path, numericOp])}: property "${propertyName}" must be a numeric view property`
583
+ ];
584
+ }
585
+ if (getDirectRelationSource(property) != null) {
586
+ return [
587
+ `${issuePath2([...path, numericOp])}: property "${propertyName}" is a relation and cannot be aggregated`
588
+ ];
589
+ }
590
+ return [];
591
+ }
592
+ };
593
+
594
+ // src/mappers/filter-mapper.ts
595
+ var LEAF_OPS = /* @__PURE__ */ new Set([
596
+ "eq",
597
+ "in",
598
+ "gt",
599
+ "gte",
600
+ "lt",
601
+ "lte",
602
+ "exists",
603
+ "prefix",
604
+ "containsAny",
605
+ "containsAll"
606
+ ]);
607
+ function isLeafFilter2(value) {
608
+ return Object.keys(value).some((k) => LEAF_OPS.has(k));
609
+ }
610
+ var FilterMapper = class {
611
+ constructor(viewMapper) {
612
+ this.viewMapper = viewMapper;
613
+ }
614
+ async map(input, rootView) {
615
+ const result = [];
616
+ for (const [key, value] of Object.entries(input)) {
617
+ if (value == null) continue;
618
+ if (key === "AND") {
619
+ const clauses = Array.isArray(value) ? value : [value];
620
+ const inner = await Promise.all(clauses.map((c) => this.whereInputToSingle(c, rootView)));
621
+ result.push({ and: inner });
622
+ } else if (key === "OR") {
623
+ const clauses = value;
624
+ const branches = await Promise.all(
625
+ clauses.map((c) => this.whereInputToSingle(c, rootView))
626
+ );
627
+ result.push({ or: branches });
628
+ } else if (key === "NOT") {
629
+ const clauses = Array.isArray(value) ? value : [value];
630
+ const [firstClause, ...restClauses] = clauses;
631
+ const combined = restClauses.length === 0 && firstClause !== void 0 ? firstClause : { AND: clauses };
632
+ result.push({ not: await this.whereInputToSingle(combined, rootView) });
633
+ } else {
634
+ const filterValue = value;
635
+ const property = getPropertyRef(key, rootView);
636
+ if (isLeafFilter2(filterValue)) {
637
+ result.push(...this.leafToFilterDefs(property, filterValue));
638
+ } else {
639
+ const targetView = await this.getNestedTargetView(key, rootView);
640
+ const innerFilter = await this.whereInputToSingle(filterValue, targetView);
641
+ result.push({ nested: { scope: property, filter: innerFilter } });
642
+ }
643
+ }
644
+ }
645
+ return result;
646
+ }
647
+ async whereInputToSingle(input, rootView) {
648
+ const filters = await this.map(input, rootView);
649
+ const [firstFilter, ...restFilters] = filters;
650
+ if (restFilters.length === 0 && firstFilter !== void 0) {
651
+ return firstFilter;
652
+ }
653
+ return { and: filters };
654
+ }
655
+ leafToFilterDefs(property, filter) {
656
+ const result = [];
657
+ if ("eq" in filter && filter.eq !== void 0) {
658
+ result.push({
659
+ equals: { property, value: this.coerceValue(filter.eq) }
660
+ });
661
+ }
662
+ if ("in" in filter && filter.in !== void 0) {
663
+ result.push({
664
+ in: { property, values: this.coerceValue(filter.in) }
665
+ });
666
+ }
667
+ if ("gt" in filter && filter.gt !== void 0) {
668
+ result.push({ range: { property, gt: this.coerceValue(filter.gt) } });
669
+ }
670
+ if ("gte" in filter && filter.gte !== void 0) {
671
+ result.push({ range: { property, gte: this.coerceValue(filter.gte) } });
672
+ }
673
+ if ("lt" in filter && filter.lt !== void 0) {
674
+ result.push({ range: { property, lt: this.coerceValue(filter.lt) } });
675
+ }
676
+ if ("lte" in filter && filter.lte !== void 0) {
677
+ result.push({ range: { property, lte: this.coerceValue(filter.lte) } });
678
+ }
679
+ if ("exists" in filter) {
680
+ if (filter.exists === true) {
681
+ result.push({ exists: { property } });
682
+ } else if (filter.exists === false) {
683
+ result.push({ not: { exists: { property } } });
684
+ }
685
+ }
686
+ if ("prefix" in filter && filter.prefix !== void 0) {
687
+ result.push({ prefix: { property, value: this.coerceValue(filter.prefix) } });
688
+ }
689
+ if ("containsAll" in filter && filter.containsAll !== void 0) {
690
+ result.push({
691
+ containsAll: {
692
+ property,
693
+ values: this.coerceValue(filter.containsAll)
694
+ }
695
+ });
696
+ }
697
+ if ("containsAny" in filter && filter.containsAny !== void 0) {
698
+ result.push({
699
+ containsAny: {
700
+ property,
701
+ values: this.coerceValue(filter.containsAny)
702
+ }
703
+ });
704
+ }
705
+ return result;
706
+ }
707
+ async getNestedTargetView(property, rootView) {
708
+ const viewProp = rootView.properties[property];
709
+ if (!viewProp || !isViewPropertyDefinition(viewProp)) {
710
+ throw new Error(`Property "${property}" is not a mapped property`);
711
+ }
712
+ const source = getDirectRelationSource(viewProp);
713
+ if (!source) throw new Error(`Property "${property}" has no relation source`);
714
+ return this.viewMapper.getView(source.externalId);
715
+ }
716
+ coerceValue(value) {
717
+ if (value instanceof Date) return value.toISOString();
718
+ if (Array.isArray(value)) return value.map((v) => this.coerceValue(v));
719
+ return value;
720
+ }
721
+ };
722
+
723
+ // src/mappers/aggregate-mapper.ts
724
+ var AggregateMapper = class {
725
+ constructor(viewMapper) {
726
+ this.viewMapper = viewMapper;
727
+ this.filterMapper = new FilterMapper(viewMapper);
728
+ this.validator = new AggregateValidator(viewMapper);
729
+ }
730
+ async map(options) {
731
+ const { viewExternalId, filters, groupBy, aggregate } = options;
732
+ const rootView = await this.viewMapper.getView(viewExternalId);
733
+ await this.validator.validate(options, rootView);
734
+ const filterParts = filters ? await this.filterMapper.map(filters, rootView) : [];
735
+ const filter = filterParts.length === 0 ? void 0 : filterParts.length === 1 ? filterParts[0] : { and: filterParts };
736
+ return {
737
+ view: toViewReference(rootView),
738
+ instanceType: "node",
739
+ limit: AGGREGATE_LIMIT,
740
+ ...filter !== void 0 ? { filter } : {},
741
+ ...groupBy ? { groupBy: getSelectedGroupByKeys(groupBy) } : {},
742
+ ...aggregate ? { aggregates: [mapAggregateDefinition(aggregate)] } : {}
743
+ };
744
+ }
745
+ };
746
+ function mapAggregateDefinition(aggregate) {
747
+ if ("count" in aggregate) {
748
+ const property = aggregate.count;
749
+ if (property != null && typeof property === "object" && !Array.isArray(property) && Object.keys(property).length === 0) {
750
+ return { count: {} };
751
+ }
752
+ if (typeof property === "string") {
753
+ return { count: { property } };
754
+ }
755
+ return { count: {} };
756
+ }
757
+ if ("avg" in aggregate) {
758
+ return { avg: { property: aggregate.avg } };
759
+ }
760
+ if ("min" in aggregate) {
761
+ return { min: { property: aggregate.min } };
762
+ }
763
+ if ("max" in aggregate) {
764
+ return { max: { property: aggregate.max } };
765
+ }
766
+ if ("sum" in aggregate) {
767
+ return { sum: { property: aggregate.sum } };
768
+ }
769
+ throw new Error("Invalid aggregate definition");
770
+ }
771
+
772
+ // src/mappers/aggregate-result-mapper.ts
773
+ function isNodeId(value) {
774
+ return value != null && typeof value === "object" && "space" in value && "externalId" in value && typeof value.space === "string" && typeof value.externalId === "string";
775
+ }
776
+ var AggregateResultMapper = class {
777
+ map(response, options) {
778
+ const groupByKeys = options.groupBy ? getSelectedGroupByKeys(options.groupBy) : [];
779
+ return response.items.map((item) => {
780
+ let group;
781
+ if (item.group != null && groupByKeys.length > 0) {
782
+ group = {};
783
+ for (const key of groupByKeys) {
784
+ const value = item.group[key];
785
+ if (value === void 0) continue;
786
+ group[key] = isNodeId(value) ? { space: value.space, externalId: value.externalId } : value;
787
+ }
788
+ if (Object.keys(group).length === 0) {
789
+ group = void 0;
790
+ }
791
+ }
792
+ const aggregateValue = item.aggregates[0];
793
+ const aggregate = aggregateValue?.value !== void 0 ? aggregateValue.property != null ? { property: aggregateValue.property, value: aggregateValue.value } : { value: aggregateValue.value } : void 0;
794
+ return {
795
+ ...group !== void 0 ? { group } : {},
796
+ ...aggregate !== void 0 ? { aggregate } : {}
797
+ };
798
+ });
799
+ }
800
+ };
801
+
547
802
  // src/mappers/sort-mapper.ts
548
803
  var SortMapper = class {
549
804
  map(sort, rootView) {
@@ -1113,6 +1368,8 @@ var IndustrialModelClient = class {
1113
1368
  this.cognite = cognite;
1114
1369
  const viewMapper = new ViewMapper(cognite, dataModelId);
1115
1370
  this.queryMapper = new QueryMapper(viewMapper);
1371
+ this.aggregateMapper = new AggregateMapper(viewMapper);
1372
+ this.aggregateResultMapper = new AggregateResultMapper();
1116
1373
  this.resultMapper = new QueryResultMapper(viewMapper);
1117
1374
  this.resultValidator = new QueryResultValidator(viewMapper);
1118
1375
  this.validateResults = options.validateResults ?? false;
@@ -1121,6 +1378,19 @@ var IndustrialModelClient = class {
1121
1378
  const execute = (options) => this.queryInternal(options);
1122
1379
  return execute;
1123
1380
  }
1381
+ aggregate() {
1382
+ const execute = (options) => this.aggregateInternal(options);
1383
+ return execute;
1384
+ }
1385
+ async aggregateInternal(options) {
1386
+ const cogniteRequest = await this.aggregateMapper.map(options);
1387
+ const response = await this.cognite.aggregateInstances(cogniteRequest);
1388
+ const items = this.aggregateResultMapper.map(
1389
+ response,
1390
+ options
1391
+ );
1392
+ return { items };
1393
+ }
1124
1394
  async queryInternal(options) {
1125
1395
  const { viewExternalId, limit = DEFAULT_LIMIT } = options;
1126
1396
  const allPages = options.limit === -1;