Replies: 2 comments
|
Sounds like a good addition |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Problem
There is no way for a frontend to enumerate bylines. The public API offers only identifier-keyed lookups —
getByline(id),getBylineBySlug(slug)— plusgetEntriesByByline(), all of which require a byline you already have.The only site-wide enumeration in the codebase is
BylineRepository.findMany()(byline.ts:549), reachable solely viaGET /_emdash/api/admin/bylines, which requiresbylines:read, a session, and the CSRF header.BylineRepositoryis not exported from the package root or any subpath.So the only thing site code can do today is query a content collection and dedupe
entry.data.bylines. That drives the read from_emdash_content_bylines— and from a content-table query before it — to reach data that lives in_emdash_bylines. Splitting the repository's reads by driving table makes the shape of the gap clear:getContentBylines:938,getContentBylinesMany:1077,hasContentBylines(Many):1018/:1038,copyContentBylines:1255findById:506,findBySlug:519,findByUserId:532,findByTranslationGroup:620,findByUserIds:1187— all identifier-keyed — andfindMany:549, which is admin-gatedEvery byline read available to a frontend that returns more than one row starts from the credit table.
Motivation
Author index pages, contributor lists, author pickers in site-side search, and sitemaps all want "the bylines on this site". Deriving that from content is wrong on three counts:
_emdash_bylinesthrough a join on_emdash_content_bylines(itself reached through a content query) to read columns that sit on the byline row.limit/cursor, so the byline list is incomplete by construction and changes as you paginate.bylines/index.ts:12).Bylines are also the only content-adjacent system without a list function. Taxonomies have
getTaxonomyTerms, menusgetMenus, sectionsgetSections, widgetsgetWidgetAreas.Proposed solution
Export one function from
emdash:Backed by a new byline-table-driven repository read:
Design points:
avatar_media_idis an ID, not a URL, andavatarStorageKeyis currently populated only on the content-credit path (documented atrepositories/types.ts:62: "The plain byline finders … leave it null"). Without the join, every caller rendering avatars does aMediaRepository.findByIdper byline — an N+1 across exactly the page being built. This is the sameLEFT JOIN mediagetContentBylinesManyalready performs at:1079.getTaxonomyTerms. Rows are per-locale, so filtering on the resolved locale yields one row per person and needs no translation-group dedupe. A per-row fallback chain would multiply queries and return mixed-locale output.findManycallswithCustomFieldsunconditionally (:301), costing 2–4 extra queries against the EAV tables from Discussion Custom fields on bylines #1174. A list of names and avatars does not need them.customFieldscomes back{}, matching the existingskipHydrationbehaviour. AddingincludeCustomFields: truelater is additive.findManyorders bycreated_at DESC, which is wrong for an author index. Cursor keyed on(display_name, id)via the existingencodeCursor/decodeCursor. Returns{ items, nextCursor? }per the pagination convention.requestCached, keyed on every argument.Cost: one query. Opt-in, so no existing route's query count moves and the snapshots are unaffected.
Index behaviour
_emdash_bylineshas single-column indexes ondisplay_nameandlocale(migration040), but no composite.EXPLAIN QUERY PLANfor the query above:The locale filter is indexed; the ordering is not, so each page sorts the whole locale partition before
LIMITapplies and the cursor is not a true seek. This is index-availability only — it does not depend onsqlite_stat1.No migration proposed. At the byline counts real sites carry, sorting the partition is negligible, and a forward-only index migration is easy to add later if that stops being true. A
(locale, display_name)composite removes the temp B-tree and makes the cursor seek-based; that belongs with #1532, which covers the same full-scan concern across taxonomy terms, bylines, users, and media.Out of scope
No migration, no schema change, no change to existing behaviour or exports. Not a breaking change.
Alternatives considered
BylineRepository. Exposescreate/update/deleteto site code, andfindMany's unconditional custom-field hydration plus missing media join make it the wrong shape regardless.getDb()escape hatch fromemdash/runtime. Pushes locale resolution, translation-group semantics, and the media join onto every site, and puts internal table shapes into site code.Deliberately not included
searchandisGuestfilters, anincludeCustomFieldsflag, and adding the media join tofindManyso the admin list stops N+1-ing avatars. Each is additive and none is needed by the use case that motivates this; the last is a separate change to an admin path and shouldn't ride along.All reactions