Description
User group membership is stored as a single serialized column on the user row: groups holds a pipe-delimited list of uuid:name pairs, mapped through a value converter in UserConfiguration.
Because the membership lives inside one string column, a group filter cannot be expressed in SQL. IUserRepository.GetGroupMembersAsync therefore reads every user row and filters in memory before the service pages the result. That is correct — it replaced an earlier version that filtered a single already-paged page and then reported that page's count as the total — but it reads the whole table on every filtered request, and no index can help.
Proposal: give groups their own table plus a user-group join table, so filtering, sorting and paging all happen in the query. This needs a migration and a backfill from the serialized column.
The same change should settle the semantics of an absent versus an empty group list. Rows written before the current fix hold NULL and read back as a null list; rows written since hold an empty string and read back as an empty list. UserMapper coalesces both to an empty list, so no response differs today, but the entity exposes two shapes for "no groups".
Related: #139 covers a separate paging gap in the same service, where the roles endpoint accepts no paging parameters at all.
Definition of Done
Description
User group membership is stored as a single serialized column on the user row:
groupsholds a pipe-delimited list ofuuid:namepairs, mapped through a value converter inUserConfiguration.Because the membership lives inside one string column, a group filter cannot be expressed in SQL.
IUserRepository.GetGroupMembersAsynctherefore reads every user row and filters in memory before the service pages the result. That is correct — it replaced an earlier version that filtered a single already-paged page and then reported that page's count as the total — but it reads the whole table on every filtered request, and no index can help.Proposal: give groups their own table plus a user-group join table, so filtering, sorting and paging all happen in the query. This needs a migration and a backfill from the serialized column.
The same change should settle the semantics of an absent versus an empty group list. Rows written before the current fix hold NULL and read back as a null list; rows written since hold an empty string and read back as an empty list.
UserMappercoalesces both to an empty list, so no response differs today, but the entity exposes two shapes for "no groups".Related: #139 covers a separate paging gap in the same service, where the roles endpoint accepts no paging parameters at all.
Definition of Done
User.Groupshas a single representation for "no groups"GET /auth/users?group=returns the same results it does today