31. Search orders results by section on the server#
Date: 2026-10-26
Status#
Accepted
Context#
The PWA groups a list in sections (Later by type, Todos by Now or Due, any list A to Z or by tag). It grouped the hits it had fetched, 100 at a time. Pages came back in the search’s order, so a section could start on one page and continue on the next, and “Show more” re-sorted rows the reader had already seen.
Decision#
group:G(letter,tag,type,now,due) is a search term. The server puts a page in a section by the same rules as the app and orders by section first, then by the search’s own order (or A to Z forgroup:letter). Paging byoffsetthen never splits a section. The section keys are SQL functions registered on the connection (store/grouping.py); done and cancelled pages sort last, as the Done fold.Later has a medium,
media_type(book, article, film, tv, video, podcast, music, game), from which its subtype (read, watch, listen, play) follows.media:andlater:filter by them. A saved link gets its medium from the site’s address (YouTube: video, Goodreads: book, Spotify by its path …) and, failing that, from the model’s reading of the page; the user’s choice in the editor or over MCP (edit_metafields) wins.edit_metacan setmedia_typeandsubtype. A field edit is a metadata revision like a kind or tag edit, sorevertundoes it. Migration 15 addspage_meta_rev.fields_was(the values a field had before), because a revert of a changed field needs them;fields_addedalone could only say a field was new.due:takes a time ahead asafter:takes one back:due:<=7d,due:>=2w.Search hits carry
tags, forgroup:tag.
Consequences#
The client sends the group in force with the query and draws the sections it is given. A new
group is a key function in grouping.py and a section list in groups.ts, which must agree
(the tests assert the same order for both). Revisions made before migration 15 have an empty
fields_was, which is correct: they never changed a field.