Skip to content

fix(aggregation): filters accept IS NULL, IS NOT NULL and [isnull], as the list does - #4680

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/aggregation-isnull-filter
Oct 17, 2026
Merged

rubenvdlinde merged 1 commit into
developmentfrom
fix/aggregation-isnull-filter

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What

The aggregation endpoints (/api/objects/aggregations/{register}/{schema}, .../grouped, named aggregations) accept the list endpoint's "empty" / "filled" filter syntax:

  • assignee=IS NULL and assignee=IS NOT NULL;
  • assignee[isnull]=true|false (true, "true", "1", "on", "yes" ask for empty; anything else for filled).

"Empty" means a missing or SQL NULL value, exactly as on the list endpoint. Both the native SQL path (field IS NULL / IS NOT NULL; isnull no longer makes it bail) and the PHP fallback apply it.

One reader, not a second parser

The meaning lives in lib/Support/NullFilter.php (nullCheck(), isNullOperatorAsksForNull(), the IS NULL / IS NOT NULL / isnull constants). The list search (MagicSearchHandler::isNullOperatorAsksForNull()), nested-path filters (NestedPathFilter) and the aggregation runner all read it, so the endpoint that lists a page and the one that counts its chips cannot disagree about what "empty" is.

Authorization

Unchanged: the native path keeps the reader's row predicate (rbacRowPredicateSql) and organisation scope in the same WHERE; the PHP fallback filters the rows it already hydrated under the same rules.

Why

nextcloud-vue's quickFiltersFrom and chip counts count through the grouped aggregation with the page filter. On dossiq's Queue (assignee: IS NULL) IS NULL was compared as literal text (empty groups) and [isnull]=true was refused ("Unsupported aggregation filter operator isnull"), so the chips drew nothing while the list showed rows.

Also in this PR

Dutch for the OpenRegister flow step names and descriptions on the palette (53 strings in l10n/nl.json and l10n/nl.js; "When someone runs it" is "Als iemand hem start"), written without em-dashes.

Verified locally

  • NullFilterTest (3, new), AggregationRunnerMultiValueFilterTest (17, 3 new: PHP fallback in every spelling, native grouped IS NULL and [isnull]=false SQL and counts), all of tests/Unit/Service/Aggregation/ (276), FilterParamsTest, MagicSearchHandlerIsNullOperatorTest, MagicSearchHandlerTest, MagicSearchHandlerNestedPathFilterTest: all OK. The new tests fail on development.
  • phpcs and phpmd on the changed files: no findings on changed lines.
  • composer check:strict once: lint, migration-version, phpcs, phpmd, psalm ("No errors found"), phpstan ("No errors") green; test:all 26,311 tests, 15 errors, the same inherited AuditTrail* / DraftRemovalAuditRow DB tests that need the server's OC\DB\Schema\Table (red on development too).

AI-assisted: written with Claude Code.

…s the list does

The grouped and plain aggregations compared IS NULL as literal text and
refused the isnull operator, so chip counts on a page filtered on an empty
field (dossiq's Queue, assignee IS NULL) drew nothing. Emptiness is now read
by one NullFilter, shared with the list search and nested-path filters, on
both the native SQL path and the PHP fallback. Also adds Dutch for the flow
step names and descriptions on the palette.

Assisted-by: ClaudeCode:claude-opus-5-5
@rubenvdlinde
rubenvdlinde merged commit 395eeb9 into development Oct 17, 2026
46 of 48 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/aggregation-isnull-filter branch October 17, 2026 12:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant