industrial-model 0.1.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/LICENSE +201 -0
- package/README.md +693 -0
- package/dist/index.cjs +740 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +738 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
# industrial-model
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for querying [Cognite Flexible Data Models (FDM)](https://docs.cognite.com/cdf/data_modeling/) with a type-safe, graph-aware API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-safe queries** — define your data model types once, get compile-time validation on filters, selects, and sorts
|
|
8
|
+
- **Relation traversal** — query nested relations (edges/nodes) up to 3 levels deep with automatic pagination
|
|
9
|
+
- **Dual CJS/ESM** — works in Node.js and bundlers out of the box
|
|
10
|
+
- **Cursor-based pagination** — built-in support for iterating large result sets
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install industrial-model
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`@cognite/sdk` is a peer dependency and must be installed separately:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @cognite/sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { CogniteClient } from "@cognite/sdk";
|
|
28
|
+
import { IndustrialModel } from "industrial-model";
|
|
29
|
+
|
|
30
|
+
const client = new CogniteClient({
|
|
31
|
+
appId: "my-app",
|
|
32
|
+
project: "my-project",
|
|
33
|
+
baseUrl: "https://az-eastus-1.cognitedata.com",
|
|
34
|
+
oidcTokenProvider: async () => getAccessToken(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const model = new IndustrialModel(client, {
|
|
38
|
+
space: "cdf_cdm",
|
|
39
|
+
externalId: "CogniteCore",
|
|
40
|
+
version: "v1",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const { items } = await model.query({
|
|
44
|
+
viewExternalId: "CogniteAsset",
|
|
45
|
+
select: { name: true, description: true },
|
|
46
|
+
filters: { name: { prefix: "Pump" } },
|
|
47
|
+
limit: 10,
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
| Topic | Section |
|
|
54
|
+
|-------|---------|
|
|
55
|
+
| Setup & types | [Shared type definitions](#shared-type-definitions) |
|
|
56
|
+
| Basic queries | [Query assets](#query-assets), [Single asset](#query-a-single-asset-by-externalid) |
|
|
57
|
+
| Relations | [Parent/root](#query-assets-with-parent-and-root-relations), [Path](#query-assets-with-their-full-path), [Children](#query-child-assets-reverse-relation), [Edges](#traverse-edge-relations-360-images-on-3d-objects) |
|
|
58
|
+
| 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) |
|
|
59
|
+
| Select & sort | [Select all scalars](#select-all-scalar-fields), [Multi-field sort](#sort-by-multiple-fields) |
|
|
60
|
+
| Pagination | [Manual cursor loop](#paginate-through-all-assets), [Fetch all pages](#fetch-all-pages-in-one-call) |
|
|
61
|
+
| Advanced | [Custom data model](#use-a-custom-data-model), [Full query](#full-example-assets-equipment-and-filters) |
|
|
62
|
+
|
|
63
|
+
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`.
|
|
64
|
+
|
|
65
|
+
### Shared type definitions
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import type { NodeId } from "industrial-model";
|
|
69
|
+
|
|
70
|
+
type CogniteAsset = {
|
|
71
|
+
name: string;
|
|
72
|
+
description: string;
|
|
73
|
+
tags: string[];
|
|
74
|
+
aliases: string[];
|
|
75
|
+
sourceId: string;
|
|
76
|
+
sourceCreatedTime: string;
|
|
77
|
+
sourceUpdatedTime: string;
|
|
78
|
+
parent?: NodeId;
|
|
79
|
+
root?: NodeId;
|
|
80
|
+
path: NodeId[];
|
|
81
|
+
assetClass?: NodeId;
|
|
82
|
+
type?: NodeId;
|
|
83
|
+
source?: NodeId;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
type CogniteAssetRelations = {
|
|
87
|
+
parent: CogniteAsset;
|
|
88
|
+
root: CogniteAsset;
|
|
89
|
+
path: CogniteAsset[];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
type CogniteEquipment = {
|
|
93
|
+
name: string;
|
|
94
|
+
description: string;
|
|
95
|
+
manufacturer: string;
|
|
96
|
+
serialNumber: string;
|
|
97
|
+
tags: string[];
|
|
98
|
+
asset?: NodeId;
|
|
99
|
+
equipmentType?: NodeId;
|
|
100
|
+
source?: NodeId;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type CogniteTimeSeries = {
|
|
104
|
+
name: string;
|
|
105
|
+
description: string;
|
|
106
|
+
isStep: boolean;
|
|
107
|
+
sourceUnit: string;
|
|
108
|
+
unit?: NodeId;
|
|
109
|
+
assets: NodeId[];
|
|
110
|
+
equipment: NodeId[];
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type CogniteActivity = {
|
|
114
|
+
name: string;
|
|
115
|
+
description: string;
|
|
116
|
+
startTime: string;
|
|
117
|
+
endTime: string;
|
|
118
|
+
scheduledStartTime: string;
|
|
119
|
+
scheduledEndTime: string;
|
|
120
|
+
assets: NodeId[];
|
|
121
|
+
equipment: NodeId[];
|
|
122
|
+
timeSeries: NodeId[];
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
type CogniteUnit = {
|
|
126
|
+
name: string;
|
|
127
|
+
symbol: string;
|
|
128
|
+
quantity: string;
|
|
129
|
+
source: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
type CogniteUnitRelations = {
|
|
133
|
+
unit: CogniteUnit;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
type Cognite3DObject = {
|
|
137
|
+
name: string;
|
|
138
|
+
description: string;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
type Cognite360ImageRelations = {
|
|
142
|
+
images360: { takenAt: string };
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Query assets
|
|
149
|
+
|
|
150
|
+
Fetch the first 100 assets whose name starts with `"Pump"`, sorted alphabetically.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const { items, cursor } = await model.query<CogniteAsset>({
|
|
154
|
+
viewExternalId: "CogniteAsset",
|
|
155
|
+
select: {
|
|
156
|
+
name: true,
|
|
157
|
+
description: true,
|
|
158
|
+
tags: true,
|
|
159
|
+
sourceId: true,
|
|
160
|
+
},
|
|
161
|
+
filters: {
|
|
162
|
+
name: { prefix: "Pump" },
|
|
163
|
+
},
|
|
164
|
+
sortClauses: { name: "ascending" },
|
|
165
|
+
limit: 100,
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Query a single asset by externalId
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const { items } = await model.query<CogniteAsset>({
|
|
175
|
+
viewExternalId: "CogniteAsset",
|
|
176
|
+
select: { name: true, description: true, tags: true },
|
|
177
|
+
filters: {
|
|
178
|
+
externalId: { eq: "WMT:VAL" },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const asset = items[0];
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### Query assets with parent and root relations
|
|
188
|
+
|
|
189
|
+
Traverse up the asset hierarchy — fetch each asset alongside its direct parent and the root of the tree.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const { items } = await model.query<CogniteAsset, CogniteAssetRelations>({
|
|
193
|
+
viewExternalId: "CogniteAsset",
|
|
194
|
+
select: {
|
|
195
|
+
name: true,
|
|
196
|
+
description: true,
|
|
197
|
+
parent: {
|
|
198
|
+
name: true,
|
|
199
|
+
description: true,
|
|
200
|
+
parent: {
|
|
201
|
+
name: true,
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
root: {
|
|
205
|
+
name: true,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
filters: {
|
|
209
|
+
name: { prefix: "Pump" },
|
|
210
|
+
},
|
|
211
|
+
limit: 50,
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### Query assets with their full path
|
|
218
|
+
|
|
219
|
+
The `path` property is a list of `NodeId` references representing the ancestor chain. Use it to reconstruct breadcrumbs.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const { items } = await model.query<CogniteAsset, CogniteAssetRelations>({
|
|
223
|
+
viewExternalId: "CogniteAsset",
|
|
224
|
+
select: {
|
|
225
|
+
name: true,
|
|
226
|
+
path: {
|
|
227
|
+
name: true,
|
|
228
|
+
description: true,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
filters: {
|
|
232
|
+
externalId: { eq: "WMT:VAL" },
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### Query equipment linked to an asset
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const { items } = await model.query<CogniteEquipment>({
|
|
243
|
+
viewExternalId: "CogniteEquipment",
|
|
244
|
+
select: {
|
|
245
|
+
name: true,
|
|
246
|
+
manufacturer: true,
|
|
247
|
+
serialNumber: true,
|
|
248
|
+
tags: true,
|
|
249
|
+
asset: true,
|
|
250
|
+
},
|
|
251
|
+
filters: {
|
|
252
|
+
asset: { eq: { space: "my-space", externalId: "WMT:VAL" } },
|
|
253
|
+
manufacturer: { exists: true },
|
|
254
|
+
},
|
|
255
|
+
sortClauses: { name: "ascending" },
|
|
256
|
+
limit: 50,
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### Query time series with their unit
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const { items } = await model.query<CogniteTimeSeries, CogniteUnitRelations>({
|
|
266
|
+
viewExternalId: "CogniteTimeSeries",
|
|
267
|
+
select: {
|
|
268
|
+
name: true,
|
|
269
|
+
description: true,
|
|
270
|
+
isStep: true,
|
|
271
|
+
sourceUnit: true,
|
|
272
|
+
unit: {
|
|
273
|
+
name: true,
|
|
274
|
+
symbol: true,
|
|
275
|
+
quantity: true,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
filters: {
|
|
279
|
+
isStep: { eq: false },
|
|
280
|
+
sourceUnit: { exists: true },
|
|
281
|
+
},
|
|
282
|
+
limit: 200,
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### Query activities in a time window
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
const { items } = await model.query<CogniteActivity>({
|
|
292
|
+
viewExternalId: "CogniteActivity",
|
|
293
|
+
select: {
|
|
294
|
+
name: true,
|
|
295
|
+
description: true,
|
|
296
|
+
startTime: true,
|
|
297
|
+
endTime: true,
|
|
298
|
+
scheduledStartTime: true,
|
|
299
|
+
scheduledEndTime: true,
|
|
300
|
+
},
|
|
301
|
+
filters: {
|
|
302
|
+
startTime: { gte: "2024-01-01T00:00:00Z", lte: "2024-12-31T23:59:59Z" },
|
|
303
|
+
},
|
|
304
|
+
sortClauses: { startTime: "ascending" },
|
|
305
|
+
limit: 500,
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### Combine filters with AND / OR / NOT
|
|
312
|
+
|
|
313
|
+
Fetch assets that are either tagged `"critical"` or have a name starting with `"Compressor"`, but exclude those from source `"legacy-system"`.
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
const { items } = await model.query<CogniteAsset>({
|
|
317
|
+
viewExternalId: "CogniteAsset",
|
|
318
|
+
select: { name: true, tags: true, sourceId: true },
|
|
319
|
+
filters: {
|
|
320
|
+
OR: [
|
|
321
|
+
{ tags: { containsAny: ["critical"] } },
|
|
322
|
+
{ name: { prefix: "Compressor" } },
|
|
323
|
+
],
|
|
324
|
+
NOT: { sourceId: { eq: "legacy-system" } },
|
|
325
|
+
},
|
|
326
|
+
limit: 100,
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### Paginate through all assets
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
let cursor: string | null = null;
|
|
336
|
+
const allAssets: Record<string, unknown>[] = [];
|
|
337
|
+
|
|
338
|
+
do {
|
|
339
|
+
const result = await model.query<CogniteAsset>({
|
|
340
|
+
viewExternalId: "CogniteAsset",
|
|
341
|
+
select: { name: true, description: true },
|
|
342
|
+
limit: 1000,
|
|
343
|
+
cursor,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
allAssets.push(...result.items);
|
|
347
|
+
cursor = result.cursor;
|
|
348
|
+
} while (cursor !== null);
|
|
349
|
+
|
|
350
|
+
console.log(`Total assets: ${allAssets.length}`);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
### Fetch all pages in one call
|
|
356
|
+
|
|
357
|
+
Pass `limit: -1` to automatically follow cursors until every page is loaded. The returned `cursor` is always `null`.
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
const { items } = await model.query<CogniteAsset>({
|
|
361
|
+
viewExternalId: "CogniteAsset",
|
|
362
|
+
select: { name: true, description: true },
|
|
363
|
+
filters: { tags: { containsAny: ["production"] } },
|
|
364
|
+
limit: -1,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
console.log(`Loaded ${items.length} assets in one request chain`);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
### Select all scalar fields
|
|
373
|
+
|
|
374
|
+
Use `_all` to include every scalar property on the view without listing them individually. Relation fields are returned as `NodeId` references but are not expanded — add nested `select` blocks when you need related data.
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
const { items } = await model.query<CogniteAsset>({
|
|
378
|
+
viewExternalId: "CogniteAsset",
|
|
379
|
+
select: { _all: true },
|
|
380
|
+
limit: 50,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// items[0] includes name, description, tags, parent (as NodeId), etc.
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Combine `_all` with explicit relation expansion:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
const { items } = await model.query<CogniteAsset, CogniteAssetRelations>({
|
|
390
|
+
viewExternalId: "CogniteAsset",
|
|
391
|
+
select: {
|
|
392
|
+
_all: true,
|
|
393
|
+
parent: { name: true },
|
|
394
|
+
},
|
|
395
|
+
limit: 25,
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
### Filter by multiple external IDs
|
|
402
|
+
|
|
403
|
+
```ts
|
|
404
|
+
const { items } = await model.query<CogniteAsset>({
|
|
405
|
+
viewExternalId: "CogniteAsset",
|
|
406
|
+
select: { name: true, description: true },
|
|
407
|
+
filters: {
|
|
408
|
+
externalId: {
|
|
409
|
+
in: ["WMT:VAL", "WMT:PUMP-01", "WMT:PUMP-02"],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
### Filter assets by tags
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
// Match assets that have at least one of these tags
|
|
421
|
+
const critical = await model.query<CogniteAsset>({
|
|
422
|
+
viewExternalId: "CogniteAsset",
|
|
423
|
+
select: { name: true, tags: true },
|
|
424
|
+
filters: { tags: { containsAny: ["critical", "safety"] } },
|
|
425
|
+
limit: 100,
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Match assets that must have every tag
|
|
429
|
+
const fullyTagged = await model.query<CogniteAsset>({
|
|
430
|
+
viewExternalId: "CogniteAsset",
|
|
431
|
+
select: { name: true, tags: true },
|
|
432
|
+
filters: { tags: { containsAll: ["production", "verified"] } },
|
|
433
|
+
limit: 100,
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
### Filter on related nodes
|
|
440
|
+
|
|
441
|
+
Filter the root view based on properties of a direct or nested relation. This uses Cognite nested filters under the hood.
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
// Assets whose parent is named "Site Root"
|
|
445
|
+
const { items } = await model.query<CogniteAsset>({
|
|
446
|
+
viewExternalId: "CogniteAsset",
|
|
447
|
+
select: { name: true, parent: { name: true } },
|
|
448
|
+
filters: {
|
|
449
|
+
parent: { name: { eq: "Site Root" } },
|
|
450
|
+
},
|
|
451
|
+
limit: 50,
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Assets whose parent's asset class code is "PUMP"
|
|
455
|
+
const pumpsByClass = await model.query<CogniteAsset>({
|
|
456
|
+
viewExternalId: "CogniteAsset",
|
|
457
|
+
select: {
|
|
458
|
+
name: true,
|
|
459
|
+
parent: { assetClass: { name: true, code: true } },
|
|
460
|
+
},
|
|
461
|
+
filters: {
|
|
462
|
+
parent: { assetClass: { code: { eq: "PUMP" } } },
|
|
463
|
+
},
|
|
464
|
+
limit: 50,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Combine root and nested conditions
|
|
468
|
+
const filtered = await model.query<CogniteAsset>({
|
|
469
|
+
viewExternalId: "CogniteAsset",
|
|
470
|
+
select: { name: true, parent: { name: true } },
|
|
471
|
+
filters: {
|
|
472
|
+
AND: [
|
|
473
|
+
{ name: { prefix: "Pump" } },
|
|
474
|
+
{ parent: { name: { exists: true } } },
|
|
475
|
+
],
|
|
476
|
+
},
|
|
477
|
+
limit: 100,
|
|
478
|
+
});
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
### Query child assets (reverse relation)
|
|
484
|
+
|
|
485
|
+
Declare reverse relations in the second generic parameter (`TRelation`). The library resolves the correct traversal direction from your data model.
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
type AssetWithChildren = CogniteAsset & {
|
|
489
|
+
children?: CogniteAsset[];
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
const { items } = await model.query<AssetWithChildren, { children: CogniteAsset }>({
|
|
493
|
+
viewExternalId: "CogniteAsset",
|
|
494
|
+
select: {
|
|
495
|
+
name: true,
|
|
496
|
+
children: {
|
|
497
|
+
name: true,
|
|
498
|
+
description: true,
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
filters: {
|
|
502
|
+
externalId: { eq: "WMT:VAL" },
|
|
503
|
+
},
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
### Traverse edge relations (360 images on 3D objects)
|
|
510
|
+
|
|
511
|
+
Some relations are modeled as edges rather than direct node links. Select them the same way — the SDK builds the edge hop automatically.
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
const { items } = await model.query<Cognite3DObject, Cognite360ImageRelations>({
|
|
515
|
+
viewExternalId: "Cognite3DObject",
|
|
516
|
+
select: {
|
|
517
|
+
name: true,
|
|
518
|
+
images360: { takenAt: true },
|
|
519
|
+
},
|
|
520
|
+
filters: {
|
|
521
|
+
name: { prefix: "Tank" },
|
|
522
|
+
},
|
|
523
|
+
limit: 20,
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### Sort by multiple fields
|
|
530
|
+
|
|
531
|
+
Sort clauses apply to primitive fields on the root view, including node-level properties like `externalId`.
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
const { items } = await model.query<CogniteAsset>({
|
|
535
|
+
viewExternalId: "CogniteAsset",
|
|
536
|
+
select: { name: true, sourceId: true },
|
|
537
|
+
sortClauses: {
|
|
538
|
+
name: "ascending",
|
|
539
|
+
externalId: "descending",
|
|
540
|
+
},
|
|
541
|
+
limit: 100,
|
|
542
|
+
});
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
### Use a custom data model
|
|
548
|
+
|
|
549
|
+
Point the client at any FDM in your project — not only Cognite Core. Views and filters work the same way once your TypeScript types match the model.
|
|
550
|
+
|
|
551
|
+
```ts
|
|
552
|
+
const model = new IndustrialModel(client, {
|
|
553
|
+
space: "my-custom-space",
|
|
554
|
+
externalId: "MyPlantModel",
|
|
555
|
+
version: "1",
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
type PlantArea = {
|
|
559
|
+
name: string;
|
|
560
|
+
code: string;
|
|
561
|
+
site?: NodeId;
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const { items } = await model.query<PlantArea>({
|
|
565
|
+
viewExternalId: "PlantArea",
|
|
566
|
+
select: { name: true, code: true, site: true },
|
|
567
|
+
filters: { code: { prefix: "AREA-" } },
|
|
568
|
+
limit: 200,
|
|
569
|
+
});
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
### Full example: assets, equipment, and filters
|
|
575
|
+
|
|
576
|
+
A single query combining nested selects, nested filters, sorting, and pagination.
|
|
577
|
+
|
|
578
|
+
```ts
|
|
579
|
+
type AssetWithRelations = CogniteAsset & {
|
|
580
|
+
parent?: CogniteAsset & { assetClass?: { name: string; code: string } };
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const { items, cursor } = await model.query<AssetWithRelations, CogniteAssetRelations>({
|
|
584
|
+
viewExternalId: "CogniteAsset",
|
|
585
|
+
select: {
|
|
586
|
+
name: true,
|
|
587
|
+
description: true,
|
|
588
|
+
tags: true,
|
|
589
|
+
parent: {
|
|
590
|
+
name: true,
|
|
591
|
+
assetClass: { name: true, code: true },
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
filters: {
|
|
595
|
+
name: { prefix: "WMT" },
|
|
596
|
+
parent: { name: { exists: true } },
|
|
597
|
+
OR: [
|
|
598
|
+
{ tags: { containsAny: ["critical"] } },
|
|
599
|
+
{ sourceId: { eq: "sap" } },
|
|
600
|
+
],
|
|
601
|
+
},
|
|
602
|
+
sortClauses: { name: "ascending" },
|
|
603
|
+
limit: 25,
|
|
604
|
+
cursor: null,
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// Follow-up page
|
|
608
|
+
if (cursor) {
|
|
609
|
+
const next = await model.query<AssetWithRelations, CogniteAssetRelations>({
|
|
610
|
+
viewExternalId: "CogniteAsset",
|
|
611
|
+
select: {
|
|
612
|
+
name: true,
|
|
613
|
+
description: true,
|
|
614
|
+
tags: true,
|
|
615
|
+
parent: { name: true, assetClass: { name: true, code: true } },
|
|
616
|
+
},
|
|
617
|
+
filters: {
|
|
618
|
+
name: { prefix: "WMT" },
|
|
619
|
+
parent: { name: { exists: true } },
|
|
620
|
+
},
|
|
621
|
+
sortClauses: { name: "ascending" },
|
|
622
|
+
limit: 25,
|
|
623
|
+
cursor,
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## API
|
|
631
|
+
|
|
632
|
+
### `new IndustrialModel(client, dataModelId)`
|
|
633
|
+
|
|
634
|
+
| Parameter | Type | Description |
|
|
635
|
+
|-----------|------|-------------|
|
|
636
|
+
| `client` | `CogniteClient` | Authenticated Cognite SDK client |
|
|
637
|
+
| `dataModelId` | `DataModelId` | Space, externalId, and version of the data model |
|
|
638
|
+
|
|
639
|
+
### `model.query<T, TRelation?>(options)`
|
|
640
|
+
|
|
641
|
+
| Option | Type | Description |
|
|
642
|
+
|--------|------|-------------|
|
|
643
|
+
| `viewExternalId` | `string` | The view to query |
|
|
644
|
+
| `select` | `QuerySelect<T, TRelation>` | Fields to include; use `_all: true` for all scalars |
|
|
645
|
+
| `filters` | `WhereInput<T, TRelation>` | Filter conditions (supports nested relation filters) |
|
|
646
|
+
| `sortClauses` | `SortInput<T>` | Sort by primitive fields |
|
|
647
|
+
| `limit` | `number` | Max items per page (default 1000, max 10000). Use `-1` to fetch all pages |
|
|
648
|
+
| `cursor` | `string \| null` | Pagination cursor from a previous response |
|
|
649
|
+
|
|
650
|
+
Returns `Promise<QueryResult>`:
|
|
651
|
+
|
|
652
|
+
```ts
|
|
653
|
+
type QueryResult = {
|
|
654
|
+
items: Record<string, unknown>[];
|
|
655
|
+
cursor: string | null; // null when no more pages
|
|
656
|
+
};
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### Filter operators
|
|
660
|
+
|
|
661
|
+
| Type | Operators |
|
|
662
|
+
|------|-----------|
|
|
663
|
+
| `string` | `eq`, `in`, `prefix`, `exists` |
|
|
664
|
+
| `number` | `eq`, `in`, `gt`, `gte`, `lt`, `lte`, `exists` |
|
|
665
|
+
| `boolean` | `eq`, `exists` |
|
|
666
|
+
| `Date` | `eq`, `in`, `gt`, `gte`, `lt`, `lte`, `exists` |
|
|
667
|
+
| `NodeId` | `eq`, `in`, `exists` |
|
|
668
|
+
| `T[]` | `containsAny`, `containsAll`, `exists` |
|
|
669
|
+
|
|
670
|
+
Logical combinators `AND`, `OR`, and `NOT` are supported at any nesting level, including inside nested relation filters (e.g. `parent: { OR: [...] }`).
|
|
671
|
+
|
|
672
|
+
### Relation traversal
|
|
673
|
+
|
|
674
|
+
- **Direct relations** — `parent`, `asset`, `unit` (outwards from the current node)
|
|
675
|
+
- **Reverse relations** — declare in `TRelation` (e.g. `children` on `CogniteAsset`)
|
|
676
|
+
- **Edge relations** — declare in `TRelation` (e.g. `images360` on `Cognite3DObject`)
|
|
677
|
+
- **Depth** — nested selects and filters up to 3 levels; dependency pages are fetched automatically
|
|
678
|
+
|
|
679
|
+
## Releasing
|
|
680
|
+
|
|
681
|
+
This project uses [Changesets](https://github.com/changesets/changesets) to manage versions and changelogs.
|
|
682
|
+
|
|
683
|
+
When you change something user-facing, add a changeset:
|
|
684
|
+
|
|
685
|
+
```bash
|
|
686
|
+
npx changeset
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Commit the generated file under `.changeset/` with your PR. After merge to `main`, the release workflow opens a "Version packages" PR. Merging that PR publishes to npm.
|
|
690
|
+
|
|
691
|
+
## License
|
|
692
|
+
|
|
693
|
+
MIT
|