fix: server-side content list search + locale-aware list indexes#1226
fix: server-side content list search + locale-aware list indexes#1226scottbuscemi wants to merge 1 commit into
Conversation
The admin content list filtered only the rows already loaded on the current page, so entries far back in a large collection were unfindable until you navigated near them. Add a `q` query param to the content list endpoint that performs a case-insensitive substring search across the collection's title/name/slug columns (LIKE wildcards escaped), and wire the admin search box to drive it (debounced) instead of filtering in memory. Add locale-aware composite indexes so i18n-filtered lists stay index-served on large tables. Closes #1219.
🦋 Changeset detectedLatest commit: 7545b03 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
PR template validation failedPlease fix the following issues by editing your PR description:
See CONTRIBUTING.md for the full contribution policy. |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | 7545b03 | May 29 2026, 11:03 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 7545b03 | May 29 2026, 11:03 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 7545b03 | May 29 2026, 11:05 PM |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
Addresses #1219.
Problem
On collections with 1000+ entries:
ContentListfiltereddata.pages.flatMap(...).filter(byTitle)client-side; the API client never sent a search term; the list query/handler/repository had no search filter.)Fix
Server-side search
contentListQueryschema gains an optionalq(trimmed, 1–200 chars).ContentRepository.findMany/countapply a case-insensitive substring filter across handler-resolvedsearchColumns, OR'd.lower()on both sides for SQLite/Postgres parity; LIKE wildcards (% _ \) in the query are escaped withESCAPE '\'.handleContentListresolves the searchable columns from the collection's fields — alwaysslug, plustitle/namewhen defined (mirrors the admin's title resolution) — so collections without those columns don't error.fetchContentListsendsq;ContentListdebounces the search box (300ms) and reports it up via a newonSearchChangeprop; the route feeds it into the infinite query key + fetch. Client-side filtering is retained only as legacy behavior whenonSearchChangeisn't supplied.Navigation
041_content_locale_list_indexaddsidx_{table}_deleted_locale_updated_idand…_created_idacross allec_*tables (idempotent, forward-only), mirrored inSchemaRegistryfor new collections.Deliberately out of scope
The research flagged the UI's page-number pagination over forward-only cursors as the bigger driver of deep-navigation latency; that's a larger UX refactor and is deferred. I also did not skip the per-page COUNT — there's an explicit regression guard requiring
totalon every page, so that optimization would break documented behavior.Testing
packages/core/tests/integration/content/content-list-search.test.ts(describeEachDialect): finds a deep entry, case-insensitive, slug match, wildcard-escape ("50%"matches only the literal), and unfiltered fallback. Passing on SQLite locally; Postgres in CI.ContentList.test.tsx: server mode reports the debounced query and does not client-filter.pnpm lint:jsonclean;emdash+@emdash-cms/admintypecheck pass. Admin browser tests run in CI (couldn't launch a local browser here).Manual verification
Try this PR
Open a fresh playground →
A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.
Tracks
fix/1219-content-list-search. Updated automatically when the playground redeploys.