Skip to content

Find aggregates by the contents of their unique keys - #536

Merged
erikrozendaal merged 5 commits into
masterfrom
feature/find-aggregates-by-unique-key-containing
Oct 6, 2026
Merged

erikrozendaal merged 5 commits into
masterfrom
feature/find-aggregates-by-unique-key-containing

Conversation

@erikrozendaal

@erikrozendaal erikrozendaal commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

find_aggregate_by_unique_key needs the whole key. Applications that keep a compound key, such as an employee and a period, also want every aggregate sharing part of it, and had to keep a view to find their ids.

find_aggregates_by_unique_key_containing loads the aggregates whose key in a scope contains the given part, using jsonb's @> operator. An optional block receives each matching key and decides which aggregates are loaded, for conditions a containment check cannot express, such as a period range.

find_aggregate_by_unique_key needs the whole key. Applications that keep a
compound key, such as an employee and a period, also want every aggregate
sharing part of it, and had to keep a view to find their ids.

find_aggregates_by_unique_key_containing loads the aggregates whose key in a
scope contains the given part, using jsonb's @>. An optional block receives
each matching key and decides which aggregates are loaded, for conditions a
containment check cannot express, such as a period range.
@erikrozendaal
erikrozendaal requested a review from lvonk September 25, 2026 12:45
The unique key lookups only searched the committed keys, so within a unit of
work an aggregate added moments earlier was not found by its key, and one whose
key had changed was still found by its old key. A command creating an aggregate
and then looking up its siblings by key would miss it.

Both lookups now match the aggregates in the repository on their current keys,
and leave out the committed keys of those aggregates, since the in-memory ones
are the more recent. The containment check the fake event store used moves to
UniqueKeys so the repository can use it as well.
find_aggregate_by_unique_key passed the event store the key normalized through
JSON, which turned a date into a string. The database compares JSON so it did not
mind, but the fake event store compares the key as given and no longer found an
aggregate whose key holds a date. Only the in-memory match needs the normalized
key.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

In-memory containment does not mirror PostgreSQL for scalar values contained directly in JSON arrays.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds partial unique-key containment lookup across persisted and in-memory aggregates.

Changes:

  • Adds repository and event-store containment APIs with optional filtering.
  • Keeps real and fake event-store behavior aligned.
  • Adds a concurrent GIN index migration and documentation.
File Description
spec/​lib/​sequent/​core/​aggregate_unique_keys_spec.rb Tests unique-key lookup behavior.
lib/​sequent/​test/​command_handler_helpers.rb Implements containment for the fake store.
lib/​sequent/​core/​helpers/​unique_keys.rb Adds key normalization and containment logic.
lib/​sequent/​core/​event_store.rb Queries contained JSONB keys.
lib/​sequent/​core/​aggregate_repository.rb Loads matching persisted and in-memory aggregates.
db/​structure.sql Records the extension and index.
db/​migrate/​20260925120000_sequent_index_aggregate_unique_keys_by_scope_and_key.rb Creates the concurrent GIN index.
CHANGELOG.md Documents the new API and migration.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/sequent/core/helpers/unique_keys.rb
PostgreSQL's @> makes one exception to structural matching: an array at the top
level contains a primitive that is one of its elements, so ['a', 'b'] is found
by 'b'. UniqueKeys.contains? did not, so the aggregates in the repository and the
fake event store missed array keys the database finds.
load_aggregates returns the aggregates already in the repository first, so the
order of the keys was lost anyway. Say so instead of sorting for nothing.
@erikrozendaal
erikrozendaal merged commit 11cc0b4 into master Oct 6, 2026
7 checks passed
@erikrozendaal
erikrozendaal deleted the feature/find-aggregates-by-unique-key-containing branch October 6, 2026 14:14
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