Skip to content

feat(data-fabric): add havingFilter to entity record queries [DS-9078] - #647

Open
aayushuipath wants to merge 5 commits into
mainfrom
feat/entities-having-filter
Open

feat(data-fabric): add havingFilter to entity record queries [DS-9078]#647
aayushuipath wants to merge 5 commits into
mainfrom
feat/entities-having-filter

Conversation

@aayushuipath

Copy link
Copy Markdown
Contributor

What

Adds havingFilter to EntityQueryRecordsOptions: a post-aggregation filter (SQL HAVING) over grouped aggregate results. Conditions reference declared aggregate aliases ({ aggregateAlias, operator, value }), combined with AND/OR.

How

  • New types: EntityHavingFilter, EntityHavingCondition, EntityHavingOperator (wire-shape 1:1 with the Data Service contract from CommonEntityPlatform#5832)
  • queryRecordsById: client-side ValidationError when havingFilter is supplied without aggregates + groupBy (mirrors the join validations)
  • havingFilter added to excludeFromPrefix - without this the pagination helper OData-prefixes the key to $havingFilter and it silently never reaches the server
  • Works on both the ID-based route and the name-based multi-entity (joins) route

Server contract notes

  • Aggregates-only: conditions must reference declared aggregate aliases; row-level conditions stay in filterGroup
  • Native (LDO) entities only; federated entities and FQS-routed queries return 400
  • Tenant must have the enable-having-on-query feature flag (rolling out ring-by-ring, alpha first)

Tests

3 new unit tests (verbatim forwarding + excludeFromPrefix, missing-aggregates rejection, missing-groupBy rejection). Full suite: 2153 passed, lint and build clean.

Consumer-rollout ticket: DS-9078. The uip CLI's df records query pass-through depends on this change + a version bump (its body keys are gated by this SDK's excludeFromPrefix allowlist).

🤖 Generated with Claude Code

@aayushuipath
aayushuipath requested a review from a team August 6, 2026 07:15
Comment thread src/models/data-fabric/entities.types.ts Outdated
@claude

claude Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review findings

Inline comment posted

src/models/data-fabric/entities.types.ts:241EntityHavingOperator should be an enum (see inline). Also drop the duplicate "<>" operator.


entities.models.ts JSDoc not updated (file not in diff)

Both queryRecordsById (line 432) and queryRecords (line 923) have @param options that enumerate the accepted keys — but havingFilter is missing from both. CLAUDE.md: "When a new option or feature is added to one method, update related methods' @example blocks too."

queryRecordsById @param (line 432):

@param options - Query options including filterGroup, selectedFields, sortOptions, aggregates, groupBy, joins, and pagination

→ append , havingFilter to the list.

queryRecords @param (line 923): Same fix.

Both methods also need a new @example block demonstrating havingFilter — e.g.:

// Post-aggregation filter: only groups with count > 5
await entities.queryRecordsById(<id>, {
  selectedFields: ["region"],
  groupBy: ["region"],
  aggregates: [{ function: EntityAggregateFunction.Count, field: "Id", alias: "cnt" }],
  havingFilter: {
    logicalOperator: LogicalOperator.And,
    aggregateFilters: [{ aggregateAlias: "cnt", operator: EntityHavingOperator.GreaterThan, value: "5" }],
  },
});

@aayushuipath

Copy link
Copy Markdown
Contributor Author

Addressed both findings in 814bad9: EntityHavingOperator is now an enum (QueryFilterOperator pattern, '<>' dropped), and both queryRecordsById/queryRecords JSDoc @param lists mention havingFilter with a HAVING example added to each (including EntityHavingOperator in the example imports).

Comment thread src/services/data-fabric/entities.ts
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review findings

Inline comment posted

src/services/data-fabric/entities.ts:312 — Missing integration test for havingFilter. CLAUDE.md requires updating the integration test when a new parameter is added to an existing method. See inline for a suggested test structure that probes feature-flag availability in beforeAll and throws loudly if the flag is not enabled, rather than leaving the parameter silently untested in CI.

@aayushuipath
aayushuipath force-pushed the feat/entities-having-filter branch from 814bad9 to dc1993c Compare August 7, 2026 05:32
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

Comment thread tests/integration/shared/data-fabric/entities.integration.test.ts Outdated
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review findingtests/integration/shared/data-fabric/entities.integration.test.ts:847-858: the havingFilter integration test gates on DATA_FABRIC_TEST_JOIN_FIELD_NAME, a config variable whose purpose is cross-entity join tests. Environments that have enable-having-on-query enabled but no join config will fail with a misleading error instead of exercising the feature. Id is always present on every DF entity and produces correct assertions without any config coupling — see inline suggestion.

aayushuipath and others added 3 commits August 7, 2026 11:17
Post-aggregation filter (SQL HAVING) on grouped aggregate results: conditions
reference declared aggregate aliases (aggregates-only server contract,
DS-9077/DS-9078). Requires aggregates + groupBy - enforced client-side with an
actionable ValidationError. havingFilter is registered in excludeFromPrefix so
the key reaches the wire un-prefixed; without this the pagination helper would
send it as $havingFilter and the server would never see it.

Server-side: native (LDO) entities only, gated by the enable-having-on-query
feature flag (400 otherwise).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ingFilter

Review follow-ups: enum matching the QueryFilterOperator convention (drop the
redundant <> - the server normalizes !=), tests on enum members, and
havingFilter added to both query methods @param lists with HAVING examples.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Data-independent HAVING assertions on the fixture entity: a satisfiable
threshold (cnt >= 1) must return every group and an unsatisfiable one must
return none - a backend that ignores havingFilter returns every group there,
which is the failing signal. Requires the enable-having-on-query tenant flag;
without it the server 400s naming the flag, failing loud as requested.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aayushuipath
aayushuipath force-pushed the feat/entities-having-filter branch from af80296 to 65ecdfc Compare August 7, 2026 05:47
Comment thread src/models/data-fabric/entities.types.ts Outdated
Comment thread tests/integration/shared/data-fabric/entities.integration.test.ts Outdated
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review findings

New inline comments posted this run

src/models/data-fabric/entities.types.ts:273–274(LDO) is an unexplained acronym in the EntityHavingFilter JSDoc. CLAUDE.md requires spelling out the full form or dropping it. Suggestion: remove the parenthetical and keep the plain-English native entities only.

tests/integration/shared/data-fabric/entities.integration.test.ts:870Record<string, any> violates CLAUDE.md's no-any rule. Record<string, unknown> works here since Vitest's expect() accepts unknown.

Group by Id (always present) instead of the join fixture field, drop the
unexplained LDO acronym from the JSDoc, and use unknown over any in the
integration assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

Sonar S5906: toHaveLength reports the actual array contents on failure.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
*
* Requires `aggregates` and `groupBy` on the query. Supported for native entities
* only; gated by the tenant's `enable-having-on-query` feature
* flag — the server responds 400 when either doesn't hold. Maximum 5 conditions.

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.

who enforces "max 5 conditions"?

@aayushuipath aayushuipath Aug 10, 2026

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.

The server does - the backend's HavingFilterValidator caps it (MAX_HAVING_FILTERS = 5) and returns 400 'Maximum 5 havingFilter conditions allowed per query'.

@claude

claude Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

@sonarqubecloud

Copy link
Copy Markdown

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.

2 participants