Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
66221d4
feat(records): add aggregate endpoint
andersfylling Jun 25, 2026
dd98ac8
feat(records): add aggregate helpers
andersfylling Jun 25, 2026
6653552
Merge origin/master into aggregate branch
andersfylling Jun 30, 2026
389eee2
fix: import ordering and docstring example for aggregate
andersfylling Jun 30, 2026
119f28d
fix: import ordering in API file and exclude aggregate classes from g…
andersfylling Jun 30, 2026
b353db4
Merge branch 'master' into andersfylling/records/aggregate-pr1
andersfylling Jun 30, 2026
ce4149c
feat(records): add typed aggregate examples to docstring and exclude …
andersfylling Jun 30, 2026
fb9c35e
refactor(records): expose bucket aggregate buckets as a read-only pro…
andersfylling Jul 6, 2026
d46279c
Merge branch 'master' into andersfylling/records/aggregate-pr1
andersfylling Jul 12, 2026
a607d83
Update cognite/client/data_classes/data_modeling/records.py
andersfylling Jul 20, 2026
12524ec
update code example
andersfylling Jul 20, 2026
6c37d01
dont use sequence[str]
andersfylling Jul 20, 2026
100c3c1
unroll loops for aggregate results
andersfylling Jul 21, 2026
23d7777
split up responsibilities of init/load/dump
andersfylling Jul 21, 2026
bed39fb
remove redundant load/dump test
andersfylling Jul 21, 2026
4b3d786
commit to using proper types for aggs
andersfylling Jul 21, 2026
4b4bf0a
rewrite aggs
andersfylling Jul 21, 2026
a209fb3
cleanup the aggs examples
andersfylling Jul 21, 2026
aedf329
prefix records agg result types with Records
andersfylling Jul 21, 2026
9935753
Merge branch 'master' into andersfylling/records/aggregate-pr1
andersfylling Jul 21, 2026
04309fe
whops, re-add load support to the agg request types
andersfylling Jul 21, 2026
a0f7ab7
add unknown aggregate type
andersfylling Jul 21, 2026
26cb596
stricter types
andersfylling Jul 21, 2026
6385ad3
rename Avg => Average
andersfylling Jul 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 103 additions & 1 deletion cognite/client/_api/data_modeling/records.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import asyncio
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
from typing import TYPE_CHECKING, Any, Literal

from cognite.client._api_client import APIClient
Expand All @@ -11,13 +11,15 @@
RecordId,
RecordIdSequence,
RecordList,
RecordsAggregation,
RecordSourceSelector,
RecordTargetUnit,
RecordTargetUnits,
RecordWrite,
SyncRecord,
SyncRecordList,
TimeRange,
_dump_aggregate_value,
)
from cognite.client.data_classes.filters import Filter
from cognite.client.utils._experimental import FeaturePreviewWarning
Expand Down Expand Up @@ -235,6 +237,106 @@ async def upsert(
no_response=True,
)

async def aggregate(
self,
aggregates: Mapping[str, Any],
Comment thread
andersfylling marked this conversation as resolved.
Outdated
*,
stream_id: str,
last_updated_time: TimeRange | None = None,
filter: Filter | dict[str, Any] | None = None,
target_units: RecordTargetUnits | Sequence[RecordTargetUnit] | None = None,
include_typing: bool = False,
) -> RecordsAggregation:
"""`Aggregate records from a stream <https://api-docs.cognite.com/20230101/tag/Records/operation/aggregateRecords>`_.

Args:
aggregates (Mapping[str, Any]): Aggregate request tree keyed by client-defined aggregate IDs.
stream_id (str): External ID of the stream to aggregate from.
last_updated_time (TimeRange | None): Filter records by last-updated time.
**Required** for immutable streams (must include a lower bound).
filter (Filter | dict[str, Any] | None): Filter expression.
target_units (RecordTargetUnits | Sequence[RecordTargetUnit] | None): Unit conversion specification.
include_typing (bool): Include property type metadata in the response.

Returns:
RecordsAggregation: Aggregate results keyed by the requested aggregate IDs.

Examples:

Aggregate average temperature using typed helpers:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder a bit about these examples - they feel like examples of what kind of data that belongs in the Datapoints service, not the Records service. Could you ask Claude to come up with different examples? 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-used the examples from the cognite api docs


>>> from cognite.client import CogniteClient
>>> from cognite.client.data_classes.data_modeling.records import Avg
>>> client = CogniteClient()
>>> res = client.data_modeling.records.aggregate(
... stream_id="my-stream",
... aggregates={
... "avg_temp": Avg(["my-space", "sensor", "temperature"]),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make it more clear that this is a property reference.

Ref docs:

the first segment is the space that the container belongs to,
the second segment is the container external id,
the third segment is the property id inside the container.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

specified the property key in the examples

... },
... )

Count records with a time range filter:

>>> from cognite.client.data_classes.data_modeling.records import Count, TimeRange
>>> res = client.data_modeling.records.aggregate(
... stream_id="my-stream",
... aggregates={"total": Count()},
... last_updated_time=TimeRange(gte=1705341600000),
... )

Daily time histogram with nested metric aggregates:

>>> from cognite.client.data_classes.data_modeling.records import (
... Max,
... Min,
... TimeHistogram,
... )
>>> res = client.data_modeling.records.aggregate(
... stream_id="my-stream",
... aggregates={
... "by_day": TimeHistogram(
... ["my-space", "sensor", "timestamp"],
... calendar_interval="1d",
... aggregates={
... "min_temp": Min(["my-space", "sensor", "temperature"]),
... "max_temp": Max(["my-space", "sensor", "temperature"]),
... },
... ),
... },
... )

Unique values with sub-aggregation:

>>> from cognite.client.data_classes.data_modeling.records import Sum, UniqueValues
>>> res = client.data_modeling.records.aggregate(
... stream_id="my-stream",
... aggregates={
... "by_region": UniqueValues(
... ["my-space", "sensor", "region"],
... aggregates={"total_output": Sum(["my-space", "sensor", "output"])},
... size=10,
... ),
... },
... )
"""
self._warning.warn()
body: dict[str, Any] = {"aggregates": _dump_aggregate_value(aggregates)}
if last_updated_time is not None:
body["lastUpdatedTime"] = last_updated_time.dump()
if filter is not None:
body["filter"] = filter.dump() if isinstance(filter, Filter) else filter
if target_units is not None:
body["targetUnits"] = self._dump_target_units(target_units)
if include_typing:
body["includeTyping"] = True

res = await self._post(
url_path=self._records_url(stream_id, "/aggregate"),
json=body,
semaphore=self._get_semaphore("read"),
)
return RecordsAggregation._load(res.json())

async def filter(
self,
stream_id: str,
Expand Down
101 changes: 98 additions & 3 deletions cognite/client/_sync_api/data_modeling/records.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions cognite/client/data_classes/data_modeling/__init__.py

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep these out of cognite/client/data_classes/data_modeling/__init__.py and only in ...records.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a heads up it will be inconsistent with the other data classes that are accessible there. Do you want purely aggregate related classes, or all records/streams classes to be in their own module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've prefixed the response agg types with records, and then i moved the builder types into the records module. Do you want me to move the response types into records as well?

Original file line number Diff line number Diff line change
Expand Up @@ -120,19 +120,39 @@
UnionAll,
)
from cognite.client.data_classes.data_modeling.records import (
Avg,
Count,
FilterAggregateResult,
Filters,
Max,
MetricAggregateResult,
Min,
MovingFunction,
MovingFunctionAggregateResult,
NumberHistogram,
NumberHistogramAggregateResult,
Record,
RecordContainerId,
RecordId,
RecordList,
RecordsAggregate,
RecordsAggregateResult,
RecordsAggregation,
RecordsBucket,
RecordSource,
RecordSourceSelector,
RecordTargetUnit,
RecordTargetUnits,
RecordWrite,
RecordWriteList,
Sum,
SyncRecord,
SyncRecordList,
TimeHistogram,
TimeHistogramAggregateResult,
TimeRange,
UniqueValues,
UniqueValuesAggregateResult,
)
from cognite.client.data_classes.data_modeling.spaces import Space, SpaceApply, SpaceApplyList, SpaceList
from cognite.client.data_classes.data_modeling.streams import (
Expand Down Expand Up @@ -169,6 +189,7 @@
__all__ = [
"AggregatedValue",
"Aggregation",
"Avg",
"BTreeIndex",
"BTreeIndexApply",
"Boolean",
Expand All @@ -185,6 +206,7 @@
"ContainerProperty",
"ContainerPropertyApply",
"ContainerUsedFor",
"Count",
"DataModel",
"DataModelApply",
"DataModelApplyList",
Expand Down Expand Up @@ -215,6 +237,8 @@
"ExecutionPlan",
"FileReference",
"Filter",
"FilterAggregateResult",
"Filters",
"Float32",
"Float64",
"Index",
Expand All @@ -235,6 +259,11 @@
"Json",
"MappedProperty",
"MappedPropertyApply",
"Max",
"MetricAggregateResult",
"Min",
"MovingFunction",
"MovingFunctionAggregateResult",
"MultiEdgeConnection",
"MultiEdgeConnectionApply",
"MultiReverseDirectRelation",
Expand All @@ -251,6 +280,8 @@
"NodeOrEdgeResultSetExpression",
"NodeResultSetExpression",
"NodeResultSetExpressionSync",
"NumberHistogram",
"NumberHistogramAggregateResult",
"PropertyId",
"PropertyOptions",
"PropertyType",
Expand All @@ -267,6 +298,10 @@
"RecordTargetUnits",
"RecordWrite",
"RecordWriteList",
"RecordsAggregate",
"RecordsAggregateResult",
"RecordsAggregation",
"RecordsBucket",
"RequiresConstraint",
"RequiresConstraintApply",
"ResultSetExpression",
Expand All @@ -291,9 +326,12 @@
"StreamTemplateWriteSettings",
"StreamWrite",
"SubscriptionContext",
"Sum",
"SyncRecord",
"SyncRecordList",
"Text",
"TimeHistogram",
"TimeHistogramAggregateResult",
"TimeRange",
"TimeSeriesReference",
"Timestamp",
Expand All @@ -304,6 +342,8 @@
"TypedNodeApply",
"Union",
"UnionAll",
"UniqueValues",
"UniqueValuesAggregateResult",
"UniquenessConstraint",
"UniquenessConstraintApply",
"VersionedDataModelingId",
Expand Down
Loading
Loading