Skip to content

Add support for filtering when listing objects in store.Store#56

Open
gonzolino wants to merge 4 commits into
ironcore-dev:mainfrom
gonzolino:enh/extend-store
Open

Add support for filtering when listing objects in store.Store#56
gonzolino wants to merge 4 commits into
ironcore-dev:mainfrom
gonzolino:enh/extend-store

Conversation

@gonzolino

@gonzolino gonzolino commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Extend the List method of the store.Store interface to allow filtering of
objects. This allows for native filtering, instead of requiring clients to list
all objects in the store and then filter on the cleint side.

Also extend host.Store for the new interface.

Proposed Changes

  • Add ListOptions to store.Store
  • Implement HasLabels, MatchingLabels and MatchingFields ListOptions
  • Extend host.Store to support ListOptions

Fixes #55

Summary by CodeRabbit

Summary

  • New Features
    • Listing operations now accept optional selector filtering via variadic list options.
    • Added label-based filtering (exact matches and key existence) and field-based filtering.
    • When both label and field selectors are provided, results match both (AND).
    • Field filtering is enabled by registering field indexers during store setup.
  • Bug Fixes
    • Ensures combined label + field constraints are applied consistently.
  • Tests
    • Added coverage for label-only, field-only, combined filtering, and no-match scenarios; improved test cleanup.
  • Documentation
    • Updated list/watch interfaces to accept the new list options.

@gonzolino gonzolino requested a review from a team as a code owner June 2, 2026 11:02
@github-actions github-actions Bot added size/L enhancement New feature or request labels Jun 2, 2026
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d126e75c-e417-4234-9ccb-a63de549e2e1

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab4eff and 1e4b523.

📒 Files selected for processing (6)
  • eventutils/event/event.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
  • storeutils/store/store.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • storeutils/host/host_suite_test.go
  • storeutils/host/store_test.go
  • storeutils/store/store.go
  • eventutils/event/event.go
  • storeutils/host/store.go
  • storeutils/store/listoptions.go

📝 Walkthrough

Walkthrough

This PR adds variadic list options, label and field selector primitives, host-store field indexing, event wiring updates, and tests covering label, field, and combined selector filtering.

Changes

Store Selector Filtering

Layer / File(s) Summary
List filtering contract and selectors
storeutils/store/store.go, storeutils/store/listoptions.go, eventutils/event/event.go
Store.List now accepts variadic ListOption arguments. ListOptions adds label and field selectors, selector helpers build matching and existence filters, and NewListWatchSource now accepts the updated list function signature.
Host store selector filtering
storeutils/host/store.go
The host store registers field indexers, accepts list options in List, and filters listed objects by label and field selector matches during iteration.
Selector filtering tests
storeutils/host/host_suite_test.go, storeutils/host/store_test.go
The host test setup adds a field-backed dummy store, and tests cover label matching, field matching, combined selector behavior, and cleanup of the created object.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding filtering support to store.Store listings.
Description check ✅ Passed The description matches the template with a summary, proposed changes, and fix reference filled in.
Linked Issues check ✅ Passed The PR implements variadic ListOption filtering on store.Store.List and supports label-based selection as requested in #55.
Out of Scope Changes check ✅ Passed The extra eventutils and test updates are directly related to propagating and validating the new ListOption-based API.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
storeutils/host/store_test.go (1)

72-104: ⚡ Quick win

Exact HaveLen assertions rely on shared store state.

List without a filter uses BeNumerically(">=", 3) to tolerate objects created by other specs, but the filtered assertions (HaveLen(2/1/3) and BeEmpty()) assume no other spec ever creates objects carrying app/env labels. This holds today but is fragile under spec randomization or future tests. Consider adding DeferCleanup to delete the created objects, or scope this test to a dedicated store instance to keep counts deterministic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storeutils/host/store_test.go` around lines 72 - 104, The test relies on
exact counts against shared dummyStore state (calls to dummyStore.List with
store.MatchingLabels and store.HasLabels), which makes HaveLen assertions
fragile; modify the test to either create and use a dedicated store instance for
this spec or ensure created objects are removed after the test using
t.DeferCleanup (or Ginkgo’s DeferCleanup) to delete the specific objects you
inserted, so that filtered assertions (Expect(filtered).To(HaveLen(...)) and
Expect(filtered).To(BeEmpty())) are deterministic; update the setup/teardown
around dummyStore, the object creation helpers, and the List calls accordingly
to scope state cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@storeutils/host/store_test.go`:
- Around line 72-104: The test relies on exact counts against shared dummyStore
state (calls to dummyStore.List with store.MatchingLabels and store.HasLabels),
which makes HaveLen assertions fragile; modify the test to either create and use
a dedicated store instance for this spec or ensure created objects are removed
after the test using t.DeferCleanup (or Ginkgo’s DeferCleanup) to delete the
specific objects you inserted, so that filtered assertions
(Expect(filtered).To(HaveLen(...)) and Expect(filtered).To(BeEmpty())) are
deterministic; update the setup/teardown around dummyStore, the object creation
helpers, and the List calls accordingly to scope state cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 296e2d3d-9e56-444b-ac8d-061bf204e07e

📥 Commits

Reviewing files that changed from the base of the PR and between fb34fe8 and a085a13.

📒 Files selected for processing (4)
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
  • storeutils/store/store.go

@gonzolino gonzolino force-pushed the enh/extend-store branch 2 times, most recently from 9cad0d5 to 72c44ab Compare June 2, 2026 13:08
adracus
adracus previously approved these changes Jun 3, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
storeutils/store/listoptions.go (1)

30-37: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Guard labels.NewRequirement errors in HasLabels.ApplyToList
labels.NewRequirement(key, selection.Exists, nil) can return nil, error for malformed keys, and *req will panic when that happens. Handle the error instead of dereferencing unconditionally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storeutils/store/listoptions.go` around lines 30 - 37,
`HasLabels.ApplyToList` currently ignores the error from `labels.NewRequirement`
and blindly dereferences the returned requirement, which can panic on malformed
keys. Update the loop in `HasLabels.ApplyToList` to handle the
`labels.NewRequirement` error explicitly before calling `sel.Add`, and only
append valid requirements to the selector; keep `andSelectors` and
`ListOptions.LabelSelector` behavior unchanged for valid labels.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@storeutils/store/listoptions.go`:
- Around line 47-51: The MatchingFields.ApplyToList method currently overwrites
any existing field selector on ListOptions, which discards earlier filters
applied by other options. Update ApplyToList to merge the new selector with
o.FieldSelector using fields.AndSelectors, matching the composition behavior
used by MatchingLabels and HasLabels, so existing selectors are preserved
instead of replaced.

---

Outside diff comments:
In `@storeutils/store/listoptions.go`:
- Around line 30-37: `HasLabels.ApplyToList` currently ignores the error from
`labels.NewRequirement` and blindly dereferences the returned requirement, which
can panic on malformed keys. Update the loop in `HasLabels.ApplyToList` to
handle the `labels.NewRequirement` error explicitly before calling `sel.Add`,
and only append valid requirements to the selector; keep `andSelectors` and
`ListOptions.LabelSelector` behavior unchanged for valid labels.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b23350ed-c705-47ac-97ed-5b7159b564ff

📥 Commits

Reviewing files that changed from the base of the PR and between 72c44ab and 088269a.

📒 Files selected for processing (4)
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • storeutils/host/store.go

Comment thread storeutils/store/listoptions.go
@gonzolino gonzolino force-pushed the enh/extend-store branch 3 times, most recently from 9059c5b to d9bacb1 Compare July 3, 2026 11:09

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
storeutils/store/store.go (1)

47-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider splitting IndexField into a separate interface.

Bundling IndexField into Store[E] forces every implementation (including future ones like the ceph-provider store mentioned in the linked issue) to support field indexing even if they only need label-based filtering. Mirroring controller-runtime's separation of client.Reader and client.FieldIndexer would keep the core Store contract minimal and let implementations opt into field indexing.

♻️ Possible interface split
 type Store[E api.Object] interface {
 	Create(ctx context.Context, obj E) (E, error)
 	Get(ctx context.Context, id string) (E, error)
 	Update(ctx context.Context, obj E) (E, error)
 	Delete(ctx context.Context, id string) error
 	List(ctx context.Context, opts ...ListOption) ([]E, error)

 	Watch(ctx context.Context) (Watch[E], error)
+}
 
-	IndexField(field string, fn FieldIndexerFunc[E])
+type FieldIndexer[E api.Object] interface {
+	IndexField(field string, fn FieldIndexerFunc[E])
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storeutils/store/store.go` around lines 47 - 56, The Store[E] contract
currently includes IndexField, which forces every implementation to support
field indexing even when it is unnecessary. Split this out of Store[E] in
storeutils/store/store.go by moving IndexField(field string, fn
FieldIndexerFunc[E]) into a separate opt-in interface, and update the affected
Store-related types/usages to depend only on the minimal Store methods unless
field indexing is explicitly required. Use the Store[E] interface and IndexField
method as the key symbols when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@storeutils/host/store.go`:
- Line 70: The Store indexers map is accessed concurrently without
synchronization, since IndexField mutates indexers while List iterates over it.
Add the same mutex protection used for watches around all indexers reads and
writes in the Store methods, and update IndexField and List to lock/unlock
consistently when touching indexers.
- Around line 205-214: The Store.List field-selector check only rejects
selectors when no indexers exist, so invalid selectors on partially indexed
stores still fall through and behave like no-op filters. Update Store.List to
validate the requested FieldSelector against the registered indexers before
calling Matches(merged), and return a clear error when the selector references
any unindexed field; use the existing List and FieldSelector handling in
Store[E] to keep the validation alongside the current list option merging.

---

Nitpick comments:
In `@storeutils/store/store.go`:
- Around line 47-56: The Store[E] contract currently includes IndexField, which
forces every implementation to support field indexing even when it is
unnecessary. Split this out of Store[E] in storeutils/store/store.go by moving
IndexField(field string, fn FieldIndexerFunc[E]) into a separate opt-in
interface, and update the affected Store-related types/usages to depend only on
the minimal Store methods unless field indexing is explicitly required. Use the
Store[E] interface and IndexField method as the key symbols when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f95517af-8cd5-4096-b7ae-ed15a72555ec

📥 Commits

Reviewing files that changed from the base of the PR and between 9059c5b and d9bacb1.

📒 Files selected for processing (6)
  • eventutils/event/event.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
  • storeutils/store/store.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • eventutils/event/event.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go

Comment thread storeutils/host/store.go Outdated
Comment thread storeutils/host/store.go
@gonzolino gonzolino force-pushed the enh/extend-store branch 2 times, most recently from a036686 to 3062587 Compare July 3, 2026 12:28
@gonzolino

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@gonzolino gonzolino dismissed adracus’s stale review July 3, 2026 12:43

Included additional feature in this PR (MatchingFields filters) that invalidate the former review.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
storeutils/store/store.go (1)

45-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider doc comments for new exported API.

IndexerFunc, the updated Store.List signature, and FieldIndexer are all new/changed public API surface in a shared library module, but none carry GoDoc comments explaining semantics (e.g., what IndexerFunc should return, that List with no options returns everything, or that IndexField must be called before referencing a field in a FieldSelector). This last point is a hard runtime contract enforced in storeutils/host/store.go (field selector references unindexed field %q error) that isn't otherwise documented at the interface level.

📝 Suggested doc comments
+// IndexerFunc extracts an indexable value from an object for a given field.
 type IndexerFunc[E api.Object] func(E) string

 type Store[E api.Object] interface {
 	Create(ctx context.Context, obj E) (E, error)
 	Get(ctx context.Context, id string) (E, error)
 	Update(ctx context.Context, obj E) (E, error)
 	Delete(ctx context.Context, id string) error
+	// List returns objects matching all provided ListOptions. Implementations
+	// may return an error if a FieldSelector references a field that has not
+	// been registered via FieldIndexer.IndexField.
 	List(ctx context.Context, opts ...ListOption) ([]E, error)

 	Watch(ctx context.Context) (Watch[E], error)
 }

+// FieldIndexer allows registering additional indexed fields that can later
+// be used in ListOptions.FieldSelector.
 type FieldIndexer[E api.Object] interface {
 	IndexField(field string, fn IndexerFunc[E])
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storeutils/store/store.go` around lines 45 - 59, Add GoDoc comments for the
exported API in Store and related types: document IndexerFunc, Store.List, and
FieldIndexer so callers understand their behavior and contracts. In particular,
explain what IndexerFunc should return for a field value, that List with no
options returns all objects, and that IndexField must be registered before any
FieldSelector can reference that field. Use the symbols IndexerFunc, Store.List,
and FieldIndexer/IndexField to place the comments near the interface
definitions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@storeutils/store/store.go`:
- Around line 45-59: Add GoDoc comments for the exported API in Store and
related types: document IndexerFunc, Store.List, and FieldIndexer so callers
understand their behavior and contracts. In particular, explain what IndexerFunc
should return for a field value, that List with no options returns all objects,
and that IndexField must be registered before any FieldSelector can reference
that field. Use the symbols IndexerFunc, Store.List, and FieldIndexer/IndexField
to place the comments near the interface definitions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 349d2c8e-c597-44c4-9106-75f2268d3128

📥 Commits

Reviewing files that changed from the base of the PR and between 9059c5b and 3062587.

📒 Files selected for processing (6)
  • eventutils/event/event.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
  • storeutils/store/store.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • storeutils/host/store_test.go
  • eventutils/event/event.go
  • storeutils/store/listoptions.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go

@gonzolino

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (1)
storeutils/host/store.go (1)

236-249: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Minor: recomputes all indexers per object regardless of selector fields.

merged is built by invoking every registered IndexerFunc per object even when the FieldSelector only references a subset of fields. With few indexers this is negligible, but for stores with many field indexers this adds unnecessary per-object work on every List call.

♻️ Optional: only compute fields referenced by the selector
 		if listOpts.FieldSelector != nil {
 			merged := fields.Set{}
-			for field, fn := range s.indexers {
-				merged[field] = fn(object)
+			for _, req := range listOpts.FieldSelector.Requirements() {
+				if fn, ok := s.indexers[req.Field]; ok {
+					merged[req.Field] = fn(object)
+				}
 			}
 			if !listOpts.FieldSelector.Matches(merged) {
 				continue
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@storeutils/host/store.go` around lines 236 - 249, In
storeutils/host/store.go, the List path in the field-selector check builds
merged by calling every indexer for every object even when only a few fields are
actually queried. Update the List logic around the FieldSelector handling to
compute only the fields referenced by listOpts.FieldSelector instead of
iterating all entries in s.indexers, while preserving the existing matches
behavior for object.GetLabels() and field matching.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@storeutils/host/store.go`:
- Around line 236-249: In storeutils/host/store.go, the List path in the
field-selector check builds merged by calling every indexer for every object
even when only a few fields are actually queried. Update the List logic around
the FieldSelector handling to compute only the fields referenced by
listOpts.FieldSelector instead of iterating all entries in s.indexers, while
preserving the existing matches behavior for object.GetLabels() and field
matching.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 549995bc-00f2-47db-b70b-b9d0d11fd513

📥 Commits

Reviewing files that changed from the base of the PR and between 3062587 and 0ab4eff.

📒 Files selected for processing (6)
  • eventutils/event/event.go
  • storeutils/host/host_suite_test.go
  • storeutils/host/store.go
  • storeutils/host/store_test.go
  • storeutils/store/listoptions.go
  • storeutils/store/store.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • storeutils/host/store_test.go
  • eventutils/event/event.go
  • storeutils/store/listoptions.go
  • storeutils/host/host_suite_test.go

gonzolino added 4 commits July 5, 2026 17:23
Allow filtering results in the `store.Store` `List(ctx)` method by
adding a new `ListOption` parameter.

Signed-off-by: Daniel Gonzalez Nothnagel <daniel.gonzalez.nothnagel@sap.com>
These ListOptions allow to filter objects based on their labels.

Signed-off-by: Daniel Gonzalez Nothnagel <daniel.gonzalez.nothnagel@sap.com>
This ListOptions allows to filter objects based on their fields.

Signed-off-by: Daniel Gonzalez Nothnagel <daniel.gonzalez.nothnagel@sap.com>
Signed-off-by: Daniel Gonzalez Nothnagel <daniel.gonzalez.nothnagel@sap.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support filtering in store.Store.List(ctx)

2 participants