Repository navigation
feat(aggregate): type, name and decode rows as a read returns them - #1024
Merged
Merged
Conversation
aggregate() handed back each row as the driver fetched it: numbers as text under MariaDB, MySQL and PostgreSQL, a sum of integers as a DECIMAL string such as "7.0000", booleans as 0/1, datetimes in the engine's format, arrays as JSON, filtered attributes still encoded, internal groups under their storage column (`_uid`) and joined groups bare, so a consumer had to rebuild each row against the schema. Appwrite does that in Appwrite\Utopia\Database\Aggregation; this moves it into the library. Database\Aggregation shapes each row from the queries and the read collections: counts and bitwise aggregates are integers, averages, deviations and variances floats, a sum an integer over an integer attribute and a float otherwise, and min()/max() take their attribute's type. Each group is named as a read names its attribute (`$id`, and a joined one qualified by its join alias, also when the queries name it bare), cast as a read casts it and passed through its attribute's filters. The tenant group is returned as read, as find() returns it. fetch() hands back the joined collections it resolved, so no join is resolved twice, and the default alias is built without a regex. Mongo, Memory and Redis still refuse aggregates. Tests that asserted the old storage-column and bare joined names now assert the read names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 50 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
abnegate
added a commit
to appwrite/appwrite
that referenced
this pull request
Oct 15, 2026
…rns them utopia-php/database#1024 types counts and bitwise aggregates as int, statistics as float, an integer sum as int and min/max as the attribute, and names a joined group by its alias. The assertions that cast numeric strings or fell back to the bare joined column now require the typed value and the qualified name, so they fail on a library that returns the raw row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abnegate
added a commit
to appwrite/appwrite
that referenced
this pull request
Oct 15, 2026
utopia-php/database#1024 (8.3.0) types, names and decodes aggregate rows the way find() returns documents, and owns the tests for it, so Aggregation and its unit tests go. The list wraps each row in an Aggregate, which the Document and Row response models use to leave out the stored attributes an aggregate row does not group. The AggregateShape e2e tests now cover the library's shaping end to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
aggregate()returned each row exactly as the driver fetched it:"7.0000".0/1, datetimes in the engine's format, and arrays and objects as JSON._uid,_createdAt,_tenant), and joined groups under their bare name.Every consumer had to rebuild the row against the schema. appwrite/appwrite#14537 does that in
Appwrite\Utopia\Database\Aggregation(416 lines). This moves that behaviour into the library so the class can be deleted.Change
A new
Utopia\Database\Aggregationshapes each row from the queries and the collections read.Aggregates
count,countDistinct,bitAnd,bitOr,bitXoravg,stddev*,variance,varPop,varSampsum"7.0000"becomes7)min,maxAn aggregate over no rows stays
null.Groups
$id,$createdAt,$permissions, and so on.o.label,o.$id), including when the queries name it bare and only the join declares it.find()casts it and passed through its attribute's filters (Database::decode), from the joined collection's attribute for a joined group.find()returns it.Other
<method>_<attribute>) is built without a regex.fetch()hands back the joined collections it already resolved, so nothing is resolved twice.JoinResolutionTestholds that.MongoDB, Memory and Redis still refuse aggregates, as before.
Release
This changes what
aggregate()returns: value types, and group keys for internal and joined attributes.aggregate()is new in 8.x, and Appwrite applies exactly this shape itself today. The commit is afeatand the change is additive for the API surface, so 8.3.0 (minor) rather than a patch. Anyone reading_uidor a bare joined key fromaggregate()rows should switch to$id/alias.key.Tests
tests/unit/AggregationTest.phpports Appwrite'sAggregationTestand runs every case on SQLite, with stringified fetches (as MariaDB, MySQL and PostgreSQL return them) and native fetches. It covers aggregate typing, statistics as floats, a whole DECIMAL sum as an int,nullover no rows, group typing (bool, array, datetime, int, float), internal groups under public names, joined groups qualified (alone, beside a main group of the same name, internal, named bare), joined aggregates typed by the joined attribute, unaliased default names, filter decoding for main and joined groups, and rows carrying no ungrouped internal attribute. 18 of the 22 cases fail againstmain'saggregate().AggregationTests::testAggregateRowsAreTypedNamedAndDecodedAsAReadReturnsThemchecks typing and naming on every adapter. It fails onmainon MySQL._uid/_tenantkeys or bare joined group names now assert the read names ($id,note.$id,it.name,purchase.status, ...).Verified locally:
testListCollectionsReturnsOnlyReadableDefinitions, came from running three adapters in parallel against the same Mongo instance and passes when run on its own.mainthere.🤖 Generated with Claude Code