Skip to content

fix(core): match localized taxonomy term slugs in collection where filter (#1480)#1554

Open
marcusbellamyshaw-cell wants to merge 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/1480-taxonomy-where-translation-group
Open

fix(core): match localized taxonomy term slugs in collection where filter (#1480)#1554
marcusbellamyshaw-cell wants to merge 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/1480-taxonomy-where-translation-group

Conversation

@marcusbellamyshaw-cell

@marcusbellamyshaw-cell marcusbellamyshaw-cell commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

The emdashLoader collection where taxonomy filter joined taxonomies on t.id = content_taxonomies.taxonomy_id. Since migration 036, content_taxonomies.taxonomy_id stores the term's translation_group, not a row id — the two coincide only for the default-locale anchor row. As a result:

  • filtering a non-default locale by its localized term slug matched nothing (the join always landed on the default-locale row), and
  • the subquery had no t.locale predicate, so it ignored the query locale entirely even though the base row query already filters locale.

This changes the join to key on t.translation_group and adds a conditional t.locale = <query locale> filter (only when a locale is set), mirroring the canonical pattern already used in taxonomies/index.ts and repositories/taxonomy.ts. No migration needed; default-locale-only sites are unaffected (translation_group = id there).

Also fixes three loader test fixtures that seeded taxonomies rows without translation_group — an unrealistic shape the old t.id join tolerated, which masked the bug (migration 036 always backfills translation_group = id).

Closes #1480

Type of change

  • Bug fix

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (targeted: loader + taxonomy suites, both dialects via describeEachDialect)
  • pnpm format has been run
  • I have added/updated tests for my changes
  • User-visible strings in the admin UI are wrapped for translation (n/a — no admin UI strings)
  • I have added a changeset

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 ultracode

Screenshots / test output

New regression test asserts: FR-locale filter by the FR slug matches (0 before the fix), FR slug does not match the EN slug, and a locale-less query still resolves the default-locale slug. loader-taxonomy-filter + loader-field-filters + loader-byline-filter + taxonomy integration suites green.

…lter (emdash-cms#1480)

The loader's taxonomy EXISTS filter joined taxonomies on
`t.id = content_taxonomies.taxonomy_id`, but since migration 036 the
junction stores the term's translation_group, not a row id. The two
coincide only for the default-locale anchor row, so filtering a
non-default locale by its localized term slug matched nothing, and the
subquery ignored the query locale entirely.

Join on `t.translation_group` and scope `t.locale` to the query locale
when set, mirroring the canonical pattern in taxonomies/index.ts and the
base row query's localeFilter. Update three loader test fixtures that
seeded taxonomies rows without translation_group (an unrealistic shape
the old t.id join tolerated; migration 036 always backfills it = id).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6bb429f

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

This PR includes changesets to release 16 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@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/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@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

@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jun 23, 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.

Approach: This is the right fix for the right problem. Since migration 036, content_taxonomies.taxonomy_id stores a term's translation_group (not a row id), so the loader's old t.id = ct.taxonomy_id join only ever landed on the default-locale anchor row, and the subquery carried no t.locale predicate — so filtering a non-default locale by its localized term slug matched nothing and the locale was ignored entirely. The fix keys the join on t.translation_group and adds a conditional t.locale = <locale> filter, which exactly mirrors the canonical pattern already in taxonomies/index.ts (getEntryTerms) and repositories/taxonomy.ts (verified: same join column, same conditional locale scoping). No migration is needed because 036 backfills translation_group = id, and default-locale-only sites are genuinely unaffected. It's a scoped, backwards-compatible bug fix — no Discussion prerequisite applies.

Implementation (what I checked):

  • SQL safety: type, f.name, each slug, and locale are all bound via the sql tagged template; tableName goes through validated sql.ref (getTableName calls validateIdentifier). No interpolation.
  • Consistency: the conditional ${locale ? sql\AND t.locale = ${locale}` : sql``} uses the same idiom as the base query's localeFilter in the same function, so the taxonomy filter scopes locale exactly when the base row query does. The loader uses filter?.locale directly (not resolveLocale), but that's the pre-existing base-query behavior — the PR is consistent with it, not a new gap.
  • Indexes: join column taxonomies.translation_group is indexed (idx_taxonomies_translation_group), content_taxonomies.taxonomy_id has idx_content_taxonomies_term, and the (name, slug, locale) unique constraint covers the filter predicates. EXISTS is a semi-join with no row fan-out.
  • Edge cases: locale-less queries still resolve (predicate is conditional, matching the locale-less base query); a term whose group has no row in the query locale is correctly omitted (consistent with the documented "omitted" behavior); OR-within-taxonomy is locale-scoped; translation_group is never NULL for properly created terms (036 backfill + TaxonomyRepository.create), and orphaned pivots are purged on delete.
  • No other query path in the tree still uses the old t.id = ct.taxonomy_id join (the only remaining occurrence is inside migration 036's remap logic, correct in context). The single-entry loader path does no taxonomy filtering.

Tests: The new #1480 regression test reproduces the bug (FR slug match returned 0 before the fix, 1 after), asserts a FR-site/EN-slug query returns 0, and guards the locale-less path. It runs on both dialects via describeEachDialect. The three fixture edits (seeding translation_group = id) are required for the new join to match — the old fixtures seeded terms with NULL translation_group, an unrealistic shape that real migrations never produce and that masked the bug. Migration tests that insert terms without translation_group don't exercise the loader join, so they're unaffected.

Changeset: present, emdash: patch, leads with a user-facing "Fixes…".

Headline: the code is clean and I'd sign off. The only thing worth a note is changeset prose that leans on implementation mechanics more than AGENTS.md asks for — a minor suggestion, not a blocker.

"emdash": patch
---

Fixes collection `where` taxonomy filters not matching localized term slugs (#1480). The loader's taxonomy filter joined `taxonomies` on `t.id = content_taxonomies.taxonomy_id`, but since the i18n migration the junction stores the term's `translation_group`, not a row id — the two coincide only for the default-locale term. As a result, filtering a non-default locale by its localized term slug matched nothing, and the filter silently ignored the query locale entirely. The join now keys on `translation_group` and scopes to the query locale (when one is set), matching the pattern used by every other taxonomy lookup, so localized term filtering works and a term slug resolves in the active locale.

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.

[suggestion] Per AGENTS.md, a changeset is release notes a user reads while upgrading: lead with a present-tense verb, describe the observable effect, and leave out internal mechanics (file names, refactors, how you implemented it) — one sentence is often enough. The first sentence here does that well, but the rest of the entry is implementation detail (t.id = content_taxonomies.taxonomy_id, translation_group, "the junction stores…", "the join now keys on…"). A user running the new version only needs to know the behavior changed, not the join keys.

Suggested change
Fixes collection `where` taxonomy filters not matching localized term slugs (#1480). The loader's taxonomy filter joined `taxonomies` on `t.id = content_taxonomies.taxonomy_id`, but since the i18n migration the junction stores the term's `translation_group`, not a row id — the two coincide only for the default-locale term. As a result, filtering a non-default locale by its localized term slug matched nothing, and the filter silently ignored the query locale entirely. The join now keys on `translation_group` and scopes to the query locale (when one is set), matching the pattern used by every other taxonomy lookup, so localized term filtering works and a term slug resolves in the active locale.
Fixes collection `where` taxonomy filters matching nothing when filtering a non-default locale by its localized term slug. Term slugs now resolve in the active query locale (#1480).

@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-review No maintainer or bot review yet labels Jun 23, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 24, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/auth-atproto

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

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

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

emdash

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

create-emdash

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

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

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

@emdash-cms/plugin-cli

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

@emdash-cms/plugin-types

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

@emdash-cms/registry-client

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

@emdash-cms/registry-lexicons

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

@emdash-cms/sandbox-workerd

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-field-kit

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: 6bb429f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core bot:review Trigger an emdashbot code review on this PR cla: signed review/approved Approved; no new commits since size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Collection where taxonomy filter joins on t.id (term id) instead of translation_group, so it only matches default-locale term slugs

2 participants