Skip to content

feat: [GH-139] Add findFirst and findFirstAny ordered single-match reads#141

Open
javierlores wants to merge 2 commits into
developfrom
feature/GH-139
Open

feat: [GH-139] Add findFirst and findFirstAny ordered single-match reads#141
javierlores wants to merge 2 commits into
developfrom
feature/GH-139

Conversation

@javierlores

Copy link
Copy Markdown
Contributor

Closes #139.

What

Adds findFirst and its hierarchy-inclusive sibling findFirstAny as default methods on DatabaseInterface. Each returns the single first Record matching a Criteria under a caller-supplied Order, or null when nothing matches — pushing the Order plus a one-row Page to the server so a single sorted row comes back rather than the full match set.

Connector next = runway.findFirst(Connector.class, claimable,
        Order.by("lastSyncedAt").ascending());

Surface — 8 default methods

Four overloads each (core, +realms, +filter, +filter+realms), for both findFirst and findFirstAny, built inline from the Selection fluent API and routed through fetch(Selection) — matching the surrounding find/findAny family.

Contract

  • null on no match (consistent with findUnique/load); never an empty collection.
  • No duplicate detection — strictly cheaper than findUnique (requests 1 row, not 2) and never throws DuplicateEntryException when more than one record matches.
  • Order is required — "first" is meaningless without it.
  • Filter caveat handled — the filtered overloads apply the one-row limit after the client-side predicate (via the existing filter-plus-page path), so a rejected head row doesn't mask a later record that matches the criteria and passes the filter. No-filter overloads omit .filter() to preserve the native order+limit-1 pushdown.

Tests

Parameterized FindFirstTest over the bulkCommands={true,false} matrix (mirrors PreventStaleWriteTest), four-section Javadoc on every @Test: ascending/descending first, null-on-no-match, sole match, no-throw-on-multiple (the findUnique contrast), findFirstAny includes descendants while findFirst excludes them, realms scoping, the filter-skips-head-row caveat, and the connector stale-lock claim.

Per repo policy, tests are written but not run (the suite needs a live Concourse server). Locally verified: spotlessApply clean and compileJava/compileTestJava succeed (no fabricated APIs).

Notes

  • A design choice worth a glance: the ticket suggested delegating to a most-parameterized overload with a no-op-filter default; I built each overload inline instead, matching the 30+ sibling find/findAny/findUnique methods and avoiding an unchecked NO_FILTER cast. Behavior and API are identical.

Scope / follow-ups

Part of the connector data-sync locking initiative.

Add findFirst and its hierarchy-inclusive sibling findFirstAny to the
read API as default methods on DatabaseInterface. Each returns the first
Record matching a Criteria under a caller-supplied Order, or null when
nothing matches, by pushing the Order plus a one-row page to the server
so a single sorted row comes back rather than the full match set.

- Four overloads each (core, +realms, +filter, +filter+realms), built
  inline from the Selection fluent API and routed through fetch(Selection),
  matching the surrounding find/findAny family.
- null-on-no-match (consistent with findUnique/load); no duplicate
  detection, so strictly cheaper than findUnique and never throws
  DuplicateEntryException when more than one record matches.
- Order is required. The filtered overloads apply the one-row limit after
  the client-side predicate (via the existing filter-plus-page path) so a
  rejected head row does not mask a later matching+passing record.
- No-filter overloads omit .filter() to preserve the native order+limit-1
  pushdown on capable servers; legacy servers fall back to the existing
  client-side sort/skip path.

Foundation for the findFirstAndEdit family (GH-140), the atomic
claim-and-update used by the cinchapi-server connector data-sync lock
loop.

Tests: parameterized FindFirstTest (bulkCommands matrix) covering
ascending/descending first, null-on-no-match, sole match,
no-throw-on-multiple, findFirstAny-includes-descendants, realms scoping,
the filter-skips-head-row caveat, and the connector stale-lock claim.
Tests written but not run (suite requires a live server).
- Add multi-page filter-ordering test exercising applyFilterAndPage's
  page-widening loop across page boundaries
- Correct test-class Javadoc to describe the bulk vs incremental
  command-read paths (not the sorting/pagination branch)
- Clarify rankPositive() helper Javadoc
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.

Add findFirst and findFirstAny: return the first ordered match, pushing Order + limit-1 to Concourse

1 participant