Skip to content

Return the total match count alongside files query results — Closes #76#78

Draft
conradbzura wants to merge 2 commits into
masterfrom
76-files-total-count
Draft

Return the total match count alongside files query results — Closes #76#78
conradbzura wants to merge 2 commits into
masterfrom
76-files-total-count

Conversation

@conradbzura

Copy link
Copy Markdown
Collaborator

Summary

Change the files GraphQL query to return a FileList envelope — { totalCount, items } — instead of a bare list. items carries the requested page and totalCount reports the full number of documents matching the input filter, before page/pageSize are applied, so clients can size pagination controls instead of guessing from a short final page.

totalCount is served by count_documents reusing the same filter already built for the page query, issued concurrently with the page fetch via asyncio.gather so latency is the max of the two round trips rather than their sum. This was chosen over a single $facet aggregation because the in-memory test double implements count_documents but not aggregate, and over a separate sibling fileCount query because the count should travel with the results in one round trip.

This is a deliberate breaking change to the GraphQL contract: every caller of files must move its field selection under items. The blast radius is contained — tests/test_schema.py is the only in-repo consumer; the CLI, REST routers, sync service, and Rust materializer all read MongoDB directly.

Closes #76

Proposed changes

  • src/cfdb/api/gql/types.py — Add a FileList Strawberry type with total_count: int and items: List[FileMetadataType], exposed in the schema as totalCount/items.
  • src/cfdb/api/gql/schema.py — Change the files resolver to return FileList, fetching the count and the page concurrently. The wait_for_cutover gate and filter translation are unchanged; file and distinctValues are untouched.
  • schema.graphql — Regenerate the committed schema snapshot: files now returns FileList!, plus the new type FileList block.
  • README.md — Update the GraphQL examples to the envelope shape, and correct two pre-existing inaccuracies in the same section: the API exposes three queries (not two), and the pageSize default is 25 (not 100).
  • tests/test_schema.py — Migrate the existing files-query tests to the envelope shape and add example plus property-based coverage for totalCount.

Test cases

# Test Suite Given When Then Coverage Target
1 TestFilesQuery Three files and a pageSize of 2 The files query runs Returns exactly 2 items Pagination cap under the envelope
2 TestFilesQuery Three files and a pageSize of 2 The files query runs totalCount is 3 while items holds 2 totalCount independent of page size
3 TestFilesQuery Three files The query runs for page 0 then page 1 totalCount is 3 on both pages totalCount stable across pages
4 TestFilesQuery Three files and a page past the end The files query runs totalCount is 3 with empty items True count beyond the last page
5 TestFilesQuery Files, none matching the filter The files query filters on an absent filename Returns totalCount 0 and empty items, not null Empty-result envelope
6 TestFilesQuery Five files, two in the 4DN DCC The files query filters on 4DN totalCount is 2, not the collection total totalCount reflects the filter
7 TestFilesQuery Twelve files and arbitrary page/pageSize The files query runs across generated pagination windows totalCount is always 12 and items matches the page slice Pagination invariant (property-based)
8 TestFilesQuery 4DN files with CV-object extra-file formats and float protocol fields The files query selects those nested fields Serializes without error under the envelope Item content unchanged by the envelope

https://claude.ai/code/session_01654cZBZnxJUcvPwahVGmeb

The files GraphQL query returned a bare list, so a client had no way to
learn how many documents matched its filter and could not size
pagination controls; a short page was the only end-of-results signal,
which is wrong whenever the total is an exact multiple of pageSize.

files now returns a FileList envelope: items carries the requested page
and totalCount reports the full match count for the input filter, before
page and pageSize are applied. The count is issued concurrently with the
page fetch via asyncio.gather, reusing the filter already built for the
query, so latency is the max of the two round trips rather than the sum.

Tests migrate the existing files-query cases to the envelope shape and
add example and property-based coverage for totalCount, including that it
stays invariant across pagination windows and empty results.

BREAKING CHANGE: files returns FileList { totalCount, items } instead of
a list of files. Callers must move their field selection under items.

Claude-Session: https://claude.ai/code/session_01654cZBZnxJUcvPwahVGmeb
Update the GraphQL query examples to the new FileList envelope, selecting
totalCount alongside items. Also fix two pre-existing inaccuracies in the
same section: the API exposes three queries, not two (distinctValues was
added later), and the pageSize default is 25, not 100.

Claude-Session: https://claude.ai/code/session_01654cZBZnxJUcvPwahVGmeb
@conradbzura conradbzura self-assigned this Jul 18, 2026
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.

Return the total match count alongside files query results

1 participant