Skip to content

feat: add null-safe nullable-argument extensions for querydsl-kotlin#1846

Open
HarryJhin wants to merge 1 commit into
OpenFeign:masterfrom
HarryJhin:feat/null-safe-kotlin-extensions
Open

feat: add null-safe nullable-argument extensions for querydsl-kotlin#1846
HarryJhin wants to merge 1 commit into
OpenFeign:masterfrom
HarryJhin:feat/null-safe-kotlin-extensions

Conversation

@HarryJhin

Copy link
Copy Markdown

Fixes #1845

Problem

Building dynamic queries in Kotlin is more verbose than in Java. where(...) already ignores
null predicates (DefaultQueryMetadata.addWhere returns early on null), but Kotlin has no
ternary operator, so an optional filter forces if/else, ?.let, a BooleanBuilder, or a
per-field helper. The existing ExpressionExtensions.kt provides only non-null operator overloads,
which do not cover the "skip this condition when the argument is null" case that dominates
dynamic-query code.

Approach

Null-safe infix extensions that return null (dropped by where(...)) when the receiver or the
argument is null:

where(
    member.name eq name,   // name: String? -> skipped when null
    member.age gt age,      // age: Int?      -> skipped when null
)
  • New per-type files: SimpleExpressionExtensions, ComparableExpressionExtensions,
    NumberExpressionExtensions, StringExpressionExtensions.
  • Each comparison/equality helper has a value form and an expression form
    (gt(value) and gt(expr)), covering parameter and column-to-column comparisons.
  • between / notBetween support partial ranges: both bounds yields BETWEEN, one bound yields
    a one-sided comparison, neither is skipped. A ClosedRange overload (age between (20..60)) is
    also provided.
  • The existing top-level and / or / xor / xnor / not in ExpressionExtensions.kt are
    folded into null-safe variants (and / or with partial application, the others skip on null).

Compatibility

  • Additive: eq / ne / in / gt / goe / lt / loe / between / like / contains
    / startsWith / endsWith reuse member-backed names, so member-over-extension keeps existing
    non-null calls intact.
  • Source-breaking: folding and / or / xor / xnor / not changes their return type to
    BooleanExpression?. In practice BooleanExpression.and/or/not stay non-null via their member
    methods, so only raw Expression<Boolean> callers are affected. I'll leave the release and
    version placement entirely to your judgement.

Tests

  • The four null combinations (this-null / arg-null / both-null / both-non-null) per function, for
    both the value and expression overloads; partial application for between / notBetween /
    and / or.
  • Non-null cases are asserted equal to the member-method result (member-over-extension check).
  • Existing CollectionTest still passes. 39 tests total, green with the JDK 17 compile target and
    the JDK 25 build toolchain.

Nullable-receiver / nullable-argument infix extensions that return null so
where(...) drops them, letting optional filters skip the if-else /
BooleanBuilder / per-field helper boilerplate:

    where(member.name eq name, member.age gt age)  // null args skipped

Comparison / equality / string / collection helpers, each null-safe against
both a value and another expression (member-over-extension keeps existing
non-null calls intact): eq, ne, in, gt, goe, lt, loe,
between (partial range: both/lower-only/upper-only/skip), notBetween,
between(ClosedRange), like, contains, startsWith, endsWith.

Boolean operators are folded into null-safe variants (and/or with partial
application, xor/xnor/not skip on null). This changes their return type to
BooleanExpression?, a source-breaking change for raw Expression<Boolean>
callers; BooleanExpression.and/or/not stay non-null via member methods.

Each function covers the four null combinations (value and expression
overloads, plus partial application for between/and/or); existing
CollectionTest still passes.
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.

Provide null-safe (nullable-argument) extensions for querydsl-kotlin dynamic queries

1 participant