Skip to content

feat(content): support indexed custom field sorting - #2212

Open
logelog wants to merge 9 commits into
emdash-cms:mainfrom
logelog:feat/indexed-custom-field-sorting
Open

feat(content): support indexed custom field sorting#2212
logelog wants to merge 9 commits into
emdash-cms:mainfrom
logelog:feat/indexed-custom-field-sorting

Conversation

@logelog

@logelog logelog commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds server-backed sorting for custom scalar fields that are explicitly marked indexed.

Collection fields gain an opt-in indexed flag. EmDash validates that flag against supported scalar field types, maintains a normalized index table when content changes, and allows the existing orderBy contract to sort by those field slugs. Sorting remains database-backed and cursor-safe, so it applies to the complete result set instead of only the currently loaded admin page.

This enables uses such as SEO score, ticket priority, workflow rank, or other numeric and textual metadata without introducing full-table scans.

This PR is stacked on #2194 to keep the five related content-list contributions conflict-free and reviewable at the same time. #2194 should merge first; GitHub will then narrow this PR to its unique sorting commit automatically.

Addresses the structured sorting portion of #2179.
Discussion: #1717

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

AI-generated code disclosure

  • This PR includes AI generated code: Claude Opus 5, GPT-5.6 (via private dev orchestra)

Screenshots / test output

Validated on EmDash 0.31 with Node 24 in WSL2:

  • focused core sorting, migration, repository, and schema tests: 163 passed
  • focused admin field configuration and content-list browser tests: 69 passed
  • combined five-PR integration suite: 234 core and 120 admin tests passed
  • pnpm typecheck
  • pnpm lint:quick
  • pnpm format:check
  • pnpm --filter emdash --filter @emdash-cms/admin build

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4e682e7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Minor
@emdash-cms/admin Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Major
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 920 lines across 35 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@pkg-pr-new

pkg-pr-new Bot commented Jul 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2212

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2212

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2212

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2212

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2212

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2212

emdash

npm i https://pkg.pr.new/emdash@2212

create-emdash

npm i https://pkg.pr.new/create-emdash@2212

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2212

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2212

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2212

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2212

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2212

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2212

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2212

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2212

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2212

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2212

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2212

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2212

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2212

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2212

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2212

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2212

commit: 4e682e7

@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@logelog
logelog force-pushed the feat/indexed-custom-field-sorting branch from 138f0cd to 33ed15e Compare August 3, 2026 12:23
@MA2153 MA2153 added the bot:review Trigger an emdashbot code review on this PR label Aug 3, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server-side sorting approach is sound: it adds physical SQLite indexes on real ec_* columns for scalar field types, keeps pagination cursor-stable with explicit null ordering, and validates the indexed flag against a whitelist of indexable types. That fits EmDash’s schema-in-the-database architecture.

Two things block a clean sign-off:

  1. AGENTS.md Discussion requirement. The PR introduces a new user-facing feature (indexed custom-field sorting) but the description explicitly states that the linked Discussion #1717 only covers admin.listColumns (#2194) and “no Discussion has been opened for indexed sorting yet.” EmDash requires a maintainer-approved Discussion before merging a feature. This needs to be opened/approved before merge.

  2. FieldEditor bug when changing an indexed field to a non-indexable type. The editor sends indexed: undefined when the selected type is not indexable. The backend interprets undefined as “preserve existing value,” so an already-indexed field that is changed to a non-indexable type (e.g. stringtext) fails with FIELD_NOT_INDEXABLE instead of dropping the index. Fix by explicitly sending false for non-indexable types.

Other notes:

  • The diff still includes the stacked #2194 admin.listColumns commits/changeset. Review comments here focus on the indexed-sorting additions; the list-columns pieces should land via #2194.
  • Server support for custom-field sorting is complete, but the admin content list does not yet expose sort controls on custom columns (the new columns are display-only). If admin UI for selecting a custom sort is intended for this PR, it is missing.
  • I did not run tests/lint/typecheck (no shell/tooling). Test coverage looks reasonable for null ordering, equal-value stability, unindexed-field rejection, and index lifecycle; the query-plan assertions are brittle but directly verify the stated performance goal.

required,
unique,
searchable: isSearchableType ? searchable : undefined,
indexed: isIndexableType ? indexed : undefined,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] When the selected type is not indexable, this sends indexed: undefined. In updateField the backend computes nextIndexed = input.indexed ?? field.indexed, so an already-indexed field that is changed to a non-indexable type (e.g. stringtext, same column type) keeps indexed = true and assertIndexableField throws FIELD_NOT_INDEXABLE. The UI gives the user no way to recover because the Indexed switch has disappeared.

Send false explicitly for non-indexable types so the index is dropped and the type change can succeed:

Suggested change
indexed: isIndexableType ? indexed : undefined,
indexed: isIndexableType ? indexed : false,

"@emdash-cms/admin": minor
---

Adds opt-in database indexes for scalar custom fields and stable cursor pagination when ordering content lists by those fields.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This changeset describes a new user-facing feature, but AGENTS.md requires a prior maintainer-approved Discussion for features. The PR description acknowledges that Discussion #1717 covers admin.listColumns (#2194) only and that no Discussion has been opened for indexed custom-field sorting yet. Please open and get approval for a Discussion covering this feature before merging.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 3, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/needs-review No maintainer or bot review yet labels Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants