industrial-model 0.2.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/README.md CHANGED
@@ -63,6 +63,7 @@ const { items } = await model.query<{ name: string; description: string }>()({
63
63
  | Filters | [AND/OR/NOT](#combine-filters-with-and--or--not), [Nested](#filter-on-related-nodes), [Tags](#filter-assets-by-tags), [Batch IDs](#filter-by-multiple-external-ids) |
64
64
  | Select & sort | [Select all scalars](#select-all-scalar-fields), [Multi-field sort](#sort-by-multiple-fields) |
65
65
  | Pagination | [Manual cursor loop](#paginate-through-all-assets), [Fetch all pages](#fetch-all-pages-automatically) |
66
+ | Aggregation | [Count by group](#count-assets-by-source-id), [Distinct values](#list-distinct-source-ids), [Numeric aggregates](#average-volume-by-type), [Global count](#count-all-matching-assets) |
66
67
  | Advanced | [Custom data model](#use-a-custom-data-model), [Full query](#full-example-assets-equipment-and-filters) |
67
68
 
68
69
  All examples below use the [Cognite Core Data Model](https://docs.cognite.com/cdf/data_modeling/reference_data_models/cognite_core/), space `cdf_cdm`, version `v1`.
@@ -661,6 +662,135 @@ if (cursor) {
661
662
 
662
663
  ---
663
664
 
665
+ ### Count assets by source ID
666
+
667
+ Group assets and count how many share each `sourceId`. Uses the same `filters` syntax as `query`.
668
+
669
+ ```ts
670
+ const { items } = await model.aggregate<CogniteAsset>()({
671
+ viewExternalId: "CogniteAsset",
672
+ groupBy: { sourceId: true },
673
+ aggregate: { count: {} },
674
+ filters: { name: { prefix: "WMT" } },
675
+ });
676
+
677
+ for (const row of items) {
678
+ console.log(row.group?.sourceId, row.aggregate?.value);
679
+ }
680
+ ```
681
+
682
+ ---
683
+
684
+ ### List distinct source IDs
685
+
686
+ Omit `aggregate` to return unique combinations of the `groupBy` fields (up to 1000 groups).
687
+
688
+ ```ts
689
+ const { items } = await model.aggregate<CogniteAsset>()({
690
+ viewExternalId: "CogniteAsset",
691
+ groupBy: { sourceId: true },
692
+ });
693
+
694
+ const sourceIds = items.map((row) => row.group?.sourceId);
695
+ ```
696
+
697
+ ---
698
+
699
+ ### Average volume by type
700
+
701
+ Use `avg`, `min`, `max`, or `sum` on numeric view properties. Only one aggregate operation per call.
702
+
703
+ ```ts
704
+ type PointCloudVolume = IndustrialModel<{
705
+ volume: number;
706
+ volumeType: string;
707
+ }>;
708
+
709
+ const { items } = await model.aggregate<PointCloudVolume>()({
710
+ viewExternalId: "CognitePointCloudVolume",
711
+ groupBy: { volumeType: true },
712
+ aggregate: { avg: "volume" },
713
+ });
714
+
715
+ items[0]?.group?.volumeType;
716
+ items[0]?.aggregate?.property; // "volume"
717
+ items[0]?.aggregate?.value;
718
+ ```
719
+
720
+ Other numeric aggregates:
721
+
722
+ ```ts
723
+ await model.aggregate<PointCloudVolume>()({
724
+ viewExternalId: "CognitePointCloudVolume",
725
+ aggregate: { min: "volume" },
726
+ });
727
+
728
+ await model.aggregate<PointCloudVolume>()({
729
+ viewExternalId: "CognitePointCloudVolume",
730
+ aggregate: { max: "volume" },
731
+ });
732
+
733
+ await model.aggregate<PointCloudVolume>()({
734
+ viewExternalId: "CognitePointCloudVolume",
735
+ aggregate: { sum: "volume" },
736
+ });
737
+ ```
738
+
739
+ ---
740
+
741
+ ### Count non-null values for a property
742
+
743
+ ```ts
744
+ const { items } = await model.aggregate<CogniteAsset>()({
745
+ viewExternalId: "CogniteAsset",
746
+ aggregate: { count: "name" },
747
+ });
748
+
749
+ items[0]?.aggregate?.property; // "name"
750
+ items[0]?.aggregate?.value;
751
+ ```
752
+
753
+ ---
754
+
755
+ ### Count all matching assets
756
+
757
+ A global count with no `groupBy`:
758
+
759
+ ```ts
760
+ const { items } = await model.aggregate<CogniteAsset>()({
761
+ viewExternalId: "CogniteAsset",
762
+ aggregate: { count: {} },
763
+ filters: {
764
+ OR: [{ tags: { containsAny: ["critical"] } }, { sourceId: { eq: "sap" } }],
765
+ },
766
+ });
767
+
768
+ items[0]?.aggregate?.value;
769
+ ```
770
+
771
+ ---
772
+
773
+ ### Group by a direct relation
774
+
775
+ `groupBy` supports direct relations; results are returned as `NodeId` objects.
776
+
777
+ ```ts
778
+ type PointCloudVolume = IndustrialModel<{
779
+ volume: number;
780
+ object3D?: NodeId;
781
+ }>;
782
+
783
+ const { items } = await model.aggregate<PointCloudVolume>()({
784
+ viewExternalId: "CognitePointCloudVolume",
785
+ groupBy: { object3D: true },
786
+ aggregate: { sum: "volume" },
787
+ });
788
+
789
+ items[0]?.group?.object3D?.externalId;
790
+ ```
791
+
792
+ ---
793
+
664
794
  ## API
665
795
 
666
796
  ### Exports
@@ -672,17 +802,31 @@ if (cursor) {
672
802
  | `NodeId`, `DataModelId` | Instance and data-model identifiers |
673
803
  | `QueryOptions`, `QuerySelect`, `WhereInput`, `SortInput` | Query input types |
674
804
  | `QueryResult`, `QueryResultItem`, `QueryResultMetadata` | Query output types |
805
+ | `AggregateOptions`, `AggregateGroupBy`, `AggregateDefinition` | Aggregate input types |
806
+ | `AggregateResult`, `AggregateResultItem`, `GroupByKey` | Aggregate output types |
807
+ | `buildViewSchema`, `nodeIdSchema` | Zod schemas built from Cognite view metadata |
675
808
  | `SortDirection` | `"ascending"` \| `"descending"` |
676
809
 
677
- ### `new IndustrialModelClient(client, dataModelId)`
810
+ ### `new IndustrialModelClient(client, dataModelId, options?)`
678
811
 
679
812
  | Parameter | Type | Description |
680
813
  |-----------|------|-------------|
681
814
  | `client` | `CogniteClient` | Authenticated Cognite SDK client |
682
815
  | `dataModelId` | `DataModelId` | Space, externalId, and version of the data model |
816
+ | `options.validateResults` | `boolean` | Optional. Validate and parse query results with Zod schemas derived from Cognite view metadata |
683
817
 
684
818
  On the first query, view definitions are loaded from CDF and cached for the lifetime of the client instance.
685
819
 
820
+ Query inputs are validated against the loaded view metadata before the Cognite request is built. Result validation is opt-in because it parses every returned item:
821
+
822
+ ```ts
823
+ const model = new IndustrialModelClient(client, dataModelId, {
824
+ validateResults: true,
825
+ });
826
+ ```
827
+
828
+ When `validateResults` is enabled, Cognite `date` and `timestamp` view properties are converted to JavaScript `Date` objects. Without this option, result values are returned as Cognite provides them, usually ISO strings for timestamps.
829
+
686
830
  ### `model.query<TModel>()(options)`
687
831
 
688
832
  | Option | Type | Description |
@@ -725,6 +869,39 @@ items[0]?.parent?.name;
725
869
  items[0]?.externalId;
726
870
  ```
727
871
 
872
+ ### `model.aggregate<TModel>()(options)`
873
+
874
+ | Option | Type | Description |
875
+ |--------|------|-------------|
876
+ | `viewExternalId` | `string` | The view to aggregate |
877
+ | `groupBy` | `AggregateGroupBy<TModel>` | Optional. Object of groupable properties set to `true` (max 5) |
878
+ | `filters` | `WhereInput<TModel>` | Same filter syntax as `query` |
879
+ | `aggregate` | `AggregateDefinition<TModel>` | Optional. One of `avg`, `min`, `max`, `sum`, or `count` per call |
880
+
881
+ Provide at least one of `groupBy` or `aggregate`. Omit `aggregate` to fetch distinct values for the grouped fields. The client always requests nodes with `limit: 1000`.
882
+
883
+ `aggregate()` uses the same curried form as `query`. Each result item has the shape:
884
+
885
+ ```ts
886
+ type AggregateResultItem = {
887
+ group?: { /* keys from groupBy */ };
888
+ aggregate?: { property?: string; value: number };
889
+ };
890
+ ```
891
+
892
+ When counting all rows, `aggregate` has only `value` (no `property`). When aggregating a field, `property` matches the name you passed in the request.
893
+
894
+ | Aggregate | Input | Use case |
895
+ |-----------|-------|----------|
896
+ | `count` | `{ count: {} }` | Row count (optionally filtered) |
897
+ | `count` | `{ count: "name" }` | Count non-null values for a property |
898
+ | `avg` | `{ avg: "volume" }` | Average of a numeric property |
899
+ | `min` | `{ min: "volume" }` | Minimum numeric value |
900
+ | `max` | `{ max: "volume" }` | Maximum numeric value |
901
+ | `sum` | `{ sum: "volume" }` | Sum of a numeric property |
902
+
903
+ See [Count assets by source ID](#count-assets-by-source-id), [List distinct source IDs](#list-distinct-source-ids), and [Average volume by type](#average-volume-by-type) for full examples.
904
+
728
905
  ### Automatic query behavior
729
906
 
730
907
  - **`hasData` filter** — every root query includes a `hasData` constraint for the target view.