Skip to content

fix(filter): treat every IEnumerable<T> property as a collection - #203

Merged
pdevito3 merged 1 commit into
mainfrom
fix/readonly-collection-filters
Oct 18, 2026
Merged

pdevito3 merged 1 commit into
mainfrom
fix/readonly-collection-filters

Conversation

@pdevito3

Copy link
Copy Markdown
Owner

Fixes #201

Problem

A filter on a collection property failed for some collection types:

  • ^$ and !^$ failed on IReadOnlyList<T>, IReadOnlyCollection<T>, HashSet<T>, arrays, and classes that inherit from List<T>.
  • == and the other comparison operators failed on a direct collection property of every type, List<T> included. Only a nested path such as Ingredients.Name == "salt" worked.

Change

  • IsCollection() and GetCollectionElementType() in QueryKit/Extensions.cs read IEnumerable<T> from the interfaces of the type. They replace two copies of the check in FilterParser and PropertyResolver, and the checks in each operator.
  • A comparison on a direct collection property matches when any element matches. % makes it match when all elements match. This is the same as a nested path.
  • Tags == null and Tags != null check the property for null. Ingredients.Name == null keeps its old meaning: any element is null.
  • != on a collection emits Any(z => !(z == value)). EF Core 10 with Npgsql can not translate Any(z => z != value) on a primitive collection. Both forms give the same SQL: t.value <> 'dinner'.

Behavior change: the expression text for != and !=* on a nested path changed from z != "flour" to Not((z == "flour")). The results do not change.

Tests

  • QueryKit.UnitTests/CollectionTypeFilterTests.cs: 8 collection types, each with the same operators. Also number collections, null checks, and nested paths. Before the fix, 89 cases failed.
  • QueryKit.IntegrationTests/Tests/CollectionTypeFilterTests.cs: a projection from an EF entity to a DTO with IReadOnlyList<string> and IReadOnlyCollection<string>, filtered on Postgres. This test found the != translation fault.
  • The verify-querykit harness model has a new string[] Labels column (text[]). Ten filters on Tags and on Labels gave the same rows in memory and on Postgres.

Out of scope

A filter on a collection property failed for IReadOnlyList<T>,
IReadOnlyCollection<T>, HashSet<T>, arrays, and classes that inherit
from List<T>. The == operator and the other comparison operators failed
on a direct collection property of every type.

- Detect a collection and its element type from IEnumerable<T> with one
  pair of helpers in Extensions.cs.
- A comparison on a direct collection property matches when any element
  matches, the same as a nested path.
- == null and != null on a collection property check the property for
  null. A nested path keeps the element comparison.
- != on a collection emits !(z == value), because EF Core 10 with
  Npgsql can not translate z != value on a primitive collection.

Fixes #201
@pdevito3
pdevito3 merged commit 16ec779 into main Oct 18, 2026
2 checks passed
@pdevito3
pdevito3 deleted the fix/readonly-collection-filters branch October 18, 2026 20:21
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.

Collection filters fail on IReadOnlyList<T>, HashSet<T>, arrays, and on == against a direct collection property

1 participant