Skip to content

feat(sources): B2.1 — enrichment source (batched per-page lookup) - #72

Merged
thiagoluga merged 3 commits into
masterfrom
feat/b2-1-enrichment
Jul 1, 2026
Merged

feat(sources): B2.1 — enrichment source (batched per-page lookup)#72
thiagoluga merged 3 commits into
masterfrom
feat/b2-1-enrichment

Conversation

@thiagoluga

Copy link
Copy Markdown
Owner

First step of Epic B2 (multi-source) — the enrichment strategy.

A primary source is enriched with data looked up from a secondary, once per page:

.From(Source.Sql(conn, sqlCustomers).Keyset<Customer, long>(c => c.Id)
    .Enrich(
        key:    c => c.Id,
        lookup: (keys, ct) => LoadOrderCountsAsync(keys, ct),   // ONE batched call per page
        map:    (c, orderCount) => new CustomerSummary(c, orderCount)))

Changes

  • New package NeoReports.Sources.JoinEnrichingBatchSource<TPrimary,TKey,TLookup,TResult>: per page it collects the distinct keys, makes one batched lookup call (never one per row), and maps each row with its value (missing → default). O(pageSize), structurally no N+1. Fluent entry .Enrich(key, lookup, map).
  • It's a standard IBatchSource<TResult>, so the existing pipeline consumes it unchanged.
  • IsPackable=false — packaging & license (Pro vs free) are the open B2 decision (D29), settled in B2.3; not auto-published yet.

Tests (2 green)

  • Batched per page: two pages → exactly two lookup calls, each with that page's distinct keys (not one call per row).
  • Missing keys map to the default lookup value.

Next

B2.2 — the keyset merge-join (streaming, two same-key-ordered sources, inner + left-outer).

Still pending from you (before B2.3 packaging): Pro vs free for the join package (I recommend Pro), join types, dynamic-config, validation gate — see docs/epic-b2-multisource.md.

First step of Epic B2 (multi-source). A primary source can be enriched with data looked up
from a secondary, once per page.

- New package NeoReports.Sources.Join (IsPackable=false; packaging/license are the open
  B2 decision D29, settled in B2.3). EnrichingBatchSource<TPrimary,TKey,TLookup,TResult>
  wraps a primary IBatchSource: per page it collects distinct keys, makes ONE batched
  lookup call, and maps each row with its value (missing -> default). O(pageSize), no N+1.
  Fluent entry .Enrich(key, lookup, map). It is a standard IBatchSource<TResult>, so the
  existing pipeline (batch/retry/writer/destination) consumes it unchanged.

Tests (2 green): one batched call per page with that page's distinct keys; missing keys
map to the default lookup value. PLAN B2.1 done.
Comment thread src/Sources/NeoReports.Sources.Join/EnrichingBatchSource.cs Fixed
…(S2436/CodeQL)

- The class now takes a page-transform delegate (2 type params, clears S2436). The
  four-type ergonomic API stays on the Enrich extension, where S2436 is suppressed with a
  justification (a batched join needs 4 types, like BCL Enumerable.GroupJoin).
- Build the enriched page with Select/Distinct (CodeQL use-Select). No behavior change; 2 green.
@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@thiagoluga
thiagoluga merged commit e4c2c3c into master Jul 1, 2026
5 checks passed
@thiagoluga
thiagoluga deleted the feat/b2-1-enrichment branch July 1, 2026 19:20
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