From 189ed899b8f663e6ada7edc7e4e6acdc2a78ffe1 Mon Sep 17 00:00:00 2001 From: Malloo <26630797+MA2153@users.noreply.github.com> Date: Sun, 26 Jul 2026 00:57:53 +0300 Subject: [PATCH 1/4] fix(core): drive taxonomy term counts from the pivot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The consolidated term-count query joined `content_taxonomies` to the content table with an `INNER JOIN`. On stats-blind SQLite/D1 the planner picked `ec_*` as the outer table and re-ran the whole `taxonomy_id IN (SELECT ...)` term list as a pivot-primary-key probe for every visible entry in the collection: SEARCH e USING INDEX idx_ec__deleted_status (deleted_at=?) SEARCH ct USING COVERING INDEX sqlite_autoindex_content_taxonomies_1 (collection=? AND entry_id=? AND taxonomy_id=?) LIST SUBQUERY 1 so the cost was `entries x terms`, not a scan — which is why the composite indexes on the pivot never helped: the pivot was never the driving table. On a collection of ~26k entries with a ~1.4k-term taxonomy one call read ~35.6M rows in ~29s, on every render of a term list or taxonomy filter. Switch to `CROSS JOIN` with the join predicate in `WHERE`. In SQLite that is a join-order hint, not a cartesian product: it pins the pivot as outer, so the terms are seeked on a `(taxonomy_id, collection)` index and the content row is touched once per assignment by primary key. Postgres has statistics and treats it as a plain inner join. Measured on a production D1 with the dataset above: 35,627,677 rows / 28,892ms -> 63,854 rows / 120ms. The predicates are untouched, so the counts are identical. Closes #2237 Co-Authored-By: Claude Opus 5 --- .changeset/great-cases-smile.md | 5 + packages/core/src/taxonomies/term-counts.ts | 14 +- .../taxonomy-term-counts-plan.test.ts | 125 ++++++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 .changeset/great-cases-smile.md create mode 100644 packages/core/tests/integration/taxonomy-term-counts-plan.test.ts diff --git a/.changeset/great-cases-smile.md b/.changeset/great-cases-smile.md new file mode 100644 index 0000000000..6126ecaffd --- /dev/null +++ b/.changeset/great-cases-smile.md @@ -0,0 +1,5 @@ +--- +"emdash": patch +--- + +Fixes taxonomy term counts reading a near-quadratic number of database rows on larger sites. The count query drove from the content table and re-checked the taxonomy's entire term list once per entry, so the cost scaled with entries × terms — on a site with ~26k entries and ~1.4k terms a single call read ~35.6M rows and took ~29s, on every page that renders a term list or taxonomy filter. It now seeks the terms on the `content_taxonomies` index and touches content rows only by primary key: the same call reads ~64k rows in ~120ms. Counts are unchanged. diff --git a/packages/core/src/taxonomies/term-counts.ts b/packages/core/src/taxonomies/term-counts.ts index b7a07b0b7b..792cdd0f32 100644 --- a/packages/core/src/taxonomies/term-counts.ts +++ b/packages/core/src/taxonomies/term-counts.ts @@ -37,6 +37,15 @@ interface CountRow { * join on `taxonomies.id` — the anchor row (id == group) can be deleted while * sibling translations survive, and a plain join on `translation_group` would * multiply counts by the number of locales. + * + * `CROSS JOIN` (with the join predicate in `WHERE`) is a join-order hint, not a + * cartesian product: it pins the pivot as the outer table. Written as an + * `INNER JOIN`, stats-blind SQLite/D1 drives from `ec_*` instead and re-runs + * the whole `taxonomy_id IN (...)` list as a pivot-PK probe for every entry in + * the collection, so a single call reads `entries × terms` rows (#2237). Seeked + * from the pivot it is one `(taxonomy_id, collection)` index seek per term plus + * one primary-key touch per assignment. Postgres has statistics and reorders + * freely — `CROSS JOIN` there is a plain inner join. */ function collectionBranch( db: Kysely, @@ -46,8 +55,9 @@ function collectionBranch( return sql` SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct - INNER JOIN ${sql.ref(`ec_${collection}`)} AS e ON e.id = ct.entry_id - WHERE ct.collection = ${collection} + CROSS JOIN ${sql.ref(`ec_${collection}`)} AS e + WHERE e.id = ct.entry_id + AND ct.collection = ${collection} AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ${taxonomyName}) AND ${buildStatusCondition(db, "published", "e")} AND e.deleted_at IS NULL diff --git a/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts new file mode 100644 index 0000000000..1cec534d2e --- /dev/null +++ b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts @@ -0,0 +1,125 @@ +/** + * Query-plan shape of the consolidated term-count query (#2237). + * + * On stats-blind SQLite/D1 (no ANALYZE, no `sqlite_stat1`) an `INNER JOIN` + * between the pivot and the content table let the planner drive from `ec_*` and + * re-run the `taxonomy_id IN (...)` term list as a pivot-PK probe for every + * entry in the collection — `entries × terms` rows read per branch. Seeking the + * pivot first makes it one index seek per term plus one primary-key touch per + * assignment. + * + * This asserts the plan, not the output (output is covered by + * unit/taxonomies/term-counts). SQLite-only: `EXPLAIN QUERY PLAN` is a SQLite + * concern and, being stats-blind here, the plan is schema-driven — matching D1. + */ + +import Database from "better-sqlite3"; +import { Kysely, SqliteDialect } from "kysely"; +import { afterEach, beforeEach, expect, it } from "vitest"; + +import { runMigrations } from "../../src/database/migrations/runner.js"; +import { ContentRepository } from "../../src/database/repositories/content.js"; +import { TaxonomyRepository } from "../../src/database/repositories/taxonomy.js"; +import type { Database as DatabaseSchema } from "../../src/database/types.js"; +import { SchemaRegistry } from "../../src/schema/registry.js"; +import { fetchVisibleTermCounts } from "../../src/taxonomies/term-counts.js"; + +interface CapturedQuery { + sql: string; + parameters: readonly unknown[]; +} + +let sqlite: Database.Database; +let db: Kysely; +let captured: CapturedQuery[]; + +beforeEach(async () => { + captured = []; + sqlite = new Database(":memory:"); + db = new Kysely({ + dialect: new SqliteDialect({ database: sqlite }), + log(event) { + if (event.level === "query") { + captured.push({ sql: event.query.sql, parameters: event.query.parameters }); + } + }, + }); + + // Deliberately no ANALYZE: matches D1, which never maintains sqlite_stat1. + await runMigrations(db); + const registry = new SchemaRegistry(db); + await registry.createCollection({ slug: "post", label: "Posts", labelSingular: "Post" }); + await registry.createField("post", { slug: "title", label: "Title", type: "string" }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- schema vs Database type + const anyDb = db as any; + const content = new ContentRepository(anyDb); + const tax = new TaxonomyRepository(anyDb); + + // Many terms and many entries: the shape the bad plan multiplies together. + // The plan is stats-blind so the counts are immaterial — they only make the + // two access paths distinguishable to a reader. + const terms = []; + for (let i = 0; i < 5; i++) { + terms.push( + await tax.create({ name: "category", slug: `term-${i}`, label: `Term ${i}`, locale: "en" }), + ); + } + for (let i = 0; i < 20; i++) { + const post = await content.create({ + type: "post", + slug: `post-${i}`, + data: { title: `Post ${i}` }, + status: "published", + locale: "en", + }); + await tax.attachToEntry("post", post.id, terms[i % terms.length]!.id); + } +}); + +afterEach(async () => { + await db.destroy(); +}); + +/** better-sqlite3 only binds primitives; coerce the JS values Kysely captured. */ +function bindable(p: unknown): unknown { + if (typeof p === "boolean") return p ? 1 : 0; + if (p instanceof Date) return p.toISOString(); + if (p === undefined) return null; + return p; +} + +function explain(query: CapturedQuery): string { + const rows = sqlite + .prepare(`EXPLAIN QUERY PLAN ${query.sql}`) + .all(...query.parameters.map(bindable)) as { detail: string }[]; + return rows.map((r) => r.detail).join("\n"); +} + +async function countQueryPlan(): Promise { + captured = []; + await fetchVisibleTermCounts(db, "category", ["post"]); + const query = captured.find((q) => q.sql.includes("content_taxonomies")); + expect(query, "expected a term-count query against the pivot").toBeDefined(); + return explain(query!); +} + +it("seeks the terms on a content_taxonomies index rather than probing the pivot per entry", async () => { + const plan = await countQueryPlan(); + + // The pivot is entered on a `taxonomy_id`-leading index, once per term. + expect(plan).toMatch(/SEARCH ct USING (COVERING )?INDEX idx_content_taxonomies/); + // The pivot's primary key is `(collection, entry_id, taxonomy_id)`. Reaching + // the pivot through it means the planner is driving from `ec_*` and probing + // the whole term list per entry — the #2237 blowup. + expect(plan).not.toContain("sqlite_autoindex_content_taxonomies_1"); + expect(plan).not.toContain("SCAN ct"); +}); + +it("touches the content table only by primary key", async () => { + const plan = await countQueryPlan(); + + expect(plan).toContain("SEARCH e USING"); + expect(plan).toMatch(/SEARCH e USING (COVERING )?INDEX sqlite_autoindex_ec_post_1 \(id=\?\)/); + expect(plan).not.toContain("SCAN e"); +}); From 427aa8202a0622ac474e0e683b1feb1d95d2a5e0 Mon Sep 17 00:00:00 2001 From: "emdashbot[bot]" Date: Sat, 25 Jul 2026 22:06:10 +0000 Subject: [PATCH 2/4] ci: update query-count snapshots --- scripts/query-counts.queries.d1.json | 12 ++++++------ scripts/query-counts.queries.sqlite.json | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/query-counts.queries.d1.json b/scripts/query-counts.queries.d1.json index b1f0754758..9847908b08 100644 --- a/scripts/query-counts.queries.d1.json +++ b/scripts/query-counts.queries.d1.json @@ -43,7 +43,7 @@ "select count(*) as \"count\" from \"_emdash_collections\"": 1, "SELECT COUNT(*) as count FROM \"_emdash_migrations\"": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "UPDATE _emdash_cron_tasks SET status = 'idle', locked_at = NULL WHERE status = 'running' AND locked_at < ?": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, @@ -57,7 +57,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, "GET /contributors (cold)": { @@ -184,7 +184,7 @@ "select count(\"id\") as \"count\" from \"_emdash_comments\" where \"collection\" = ? and \"content_id\" = ? and \"status\" = ?": 1, "select count(*) as \"count\" from \"_emdash_collections\"": 1, "SELECT COUNT(*) as count FROM \"_emdash_migrations\"": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "UPDATE _emdash_cron_tasks SET status = 'idle', locked_at = NULL WHERE status = 'running' AND locked_at < ?": 1 }, "GET /posts/building-for-the-long-term (warm)": { @@ -201,7 +201,7 @@ "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_posts\".id AND t.locale = \"ec_posts\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_posts\".id AND b.locale = \"ec_posts\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"published_at\" DESC, \"id\" DESC LIMIT ?": 1, "SELECT c.*, (SELECT json_object('seo_title', s.seo_title, 'seo_description', s.seo_description, 'seo_image', s.seo_image, 'seo_canonical', s.seo_canonical, 'seo_no_index', s.seo_no_index) FROM \"_emdash_seo\" AS s WHERE s.collection = ? AND s.content_id = \"c\".id LIMIT 1) AS \"_emdash_seo\", (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"c\".id AND t.locale = \"c\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"c\".id AND b.locale = \"c\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" AS c WHERE c.deleted_at IS NULL AND (c.slug = ? OR c.id = ?) LIMIT 1": 1, "select count(\"id\") as \"count\" from \"_emdash_comments\" where \"collection\" = ? and \"content_id\" = ? and \"status\" = ?": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 }, "GET /rss.xml (cold)": { "select \"name\", \"value\" from \"options\" where \"name\" in (...)": 2, @@ -273,7 +273,7 @@ "select count(*) as \"count\" from \"_emdash_collections\"": 1, "SELECT COUNT(*) as count FROM \"_emdash_migrations\"": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "UPDATE _emdash_cron_tasks SET status = 'idle', locked_at = NULL WHERE status = 'running' AND locked_at < ?": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, @@ -287,7 +287,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 } } diff --git a/scripts/query-counts.queries.sqlite.json b/scripts/query-counts.queries.sqlite.json index 58abc70149..b29f9d21a6 100644 --- a/scripts/query-counts.queries.sqlite.json +++ b/scripts/query-counts.queries.sqlite.json @@ -26,7 +26,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, "GET /category/development (warm)": { @@ -39,7 +39,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, "GET /contributors (cold)": { @@ -122,7 +122,7 @@ "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_posts\".id AND t.locale = \"ec_posts\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_posts\".id AND b.locale = \"ec_posts\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"published_at\" DESC, \"id\" DESC LIMIT ?": 1, "SELECT c.*, (SELECT json_object('seo_title', s.seo_title, 'seo_description', s.seo_description, 'seo_image', s.seo_image, 'seo_canonical', s.seo_canonical, 'seo_no_index', s.seo_no_index) FROM \"_emdash_seo\" AS s WHERE s.collection = ? AND s.content_id = \"c\".id LIMIT 1) AS \"_emdash_seo\", (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"c\".id AND t.locale = \"c\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"c\".id AND b.locale = \"c\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" AS c WHERE c.deleted_at IS NULL AND (c.slug = ? OR c.id = ?) LIMIT 1": 1, "select count(\"id\") as \"count\" from \"_emdash_comments\" where \"collection\" = ? and \"content_id\" = ? and \"status\" = ?": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 }, "GET /posts/building-for-the-long-term (warm)": { "select \"a\".\"id\" as \"a_id\", \"a\".\"name\" as \"a_name\", \"a\".\"label\" as \"a_label\", \"a\".\"description\" as \"a_description\", \"w\".\"id\" as \"w_id\", \"w\".\"type\" as \"w_type\", \"w\".\"title\" as \"w_title\", \"w\".\"content\" as \"w_content\", \"w\".\"menu_name\" as \"w_menu_name\", \"w\".\"component_id\" as \"w_component_id\", \"w\".\"component_props\" as \"w_component_props\", \"w\".\"area_id\" as \"w_area_id\", \"w\".\"sort_order\" as \"w_sort_order\", \"w\".\"created_at\" as \"w_created_at\" from \"_emdash_widget_areas\" as \"a\" left join \"_emdash_widgets\" as \"w\" on \"w\".\"area_id\" = \"a\".\"id\" where \"a\".\"name\" = ? order by \"w\".\"sort_order\" asc": 2, @@ -138,7 +138,7 @@ "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_posts\".id AND t.locale = \"ec_posts\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_posts\".id AND b.locale = \"ec_posts\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"published_at\" DESC, \"id\" DESC LIMIT ?": 1, "SELECT c.*, (SELECT json_object('seo_title', s.seo_title, 'seo_description', s.seo_description, 'seo_image', s.seo_image, 'seo_canonical', s.seo_canonical, 'seo_no_index', s.seo_no_index) FROM \"_emdash_seo\" AS s WHERE s.collection = ? AND s.content_id = \"c\".id LIMIT 1) AS \"_emdash_seo\", (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"c\".id AND t.locale = \"c\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"c\".id AND b.locale = \"c\".locale) AS \"_emdash_bylines\" FROM \"ec_posts\" AS c WHERE c.deleted_at IS NULL AND (c.slug = ? OR c.id = ?) LIMIT 1": 1, "select count(\"id\") as \"count\" from \"_emdash_comments\" where \"collection\" = ? and \"content_id\" = ? and \"status\" = ?": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1 }, "GET /rss.xml (cold)": { "select \"value\" from \"options\" where \"name\" = ?": 1, @@ -184,7 +184,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 }, "GET /tag/webdev (warm)": { @@ -197,7 +197,7 @@ "select * from \"taxonomies\" where \"parent_id\" = ? and \"locale\" = ? order by \"label\" asc": 1, "SELECT *, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"ec_pages\".id AND t.locale = \"ec_pages\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"ec_pages\".id AND b.locale = \"ec_pages\".locale) AS \"_emdash_bylines\" FROM \"ec_pages\" WHERE deleted_at IS NULL AND (\"status\" = 'published' OR (\"status\" = 'scheduled' AND \"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY \"created_at\" DESC, \"id\" DESC": 1, "select distinct \"translation_group\" from \"taxonomies\" where \"name\" = ? and \"slug\" in (...)": 1, - "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id WHERE ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, + "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id AS taxonomy_id, COUNT(*) AS count FROM content_taxonomies AS ct CROSS JOIN \"ec_posts\" AS e WHERE e.id = ct.entry_id AND ct.collection = ? AND ct.taxonomy_id IN (SELECT translation_group FROM taxonomies WHERE name = ?) AND (\"e\".\"status\" = 'published' OR (\"e\".\"status\" = 'scheduled' AND \"e\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) AND e.deleted_at IS NULL GROUP BY ct.taxonomy_id) AS per_collection GROUP BY taxonomy_id": 1, "WITH picked AS ( SELECT ct.entry_id AS entry_id, \"ct\".\"published_at\" AS sortval FROM content_taxonomies ct WHERE ct.collection = ? AND \"ct\".\"taxonomy_id\" = ? AND ct.deleted_at IS NULL AND (\"ct\".\"status\" = 'published' OR (\"ct\".\"status\" = 'scheduled' AND \"ct\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY sortval DESC, ct.entry_id DESC ) SELECT r.*, (SELECT json_group_array(json_object('id', t.id, 'name', t.name, 'slug', t.slug, 'label', t.label, 'parent_id', t.parent_id, 'locale', t.locale, 'translation_group', t.translation_group)) FROM \"content_taxonomies\" AS ct CROSS JOIN \"taxonomies\" AS t ON t.translation_group = ct.taxonomy_id WHERE ct.collection = ? AND ct.entry_id = \"r\".id AND t.locale = \"r\".locale) AS \"_emdash_terms\", (SELECT json_group_array(json_object('roleLabel', cb.role_label, 'sortOrder', cb.sort_order, 'byline', json_object('id', b.id, 'slug', b.slug, 'displayName', b.display_name, 'bio', b.bio, 'avatarMediaId', b.avatar_media_id, 'avatarStorageKey', m.storage_key, 'avatarAlt', m.alt, 'avatarBlurhash', m.blurhash, 'avatarDominantColor', m.dominant_color, 'websiteUrl', b.website_url, 'userId', b.user_id, 'isGuest', b.is_guest, 'createdAt', b.created_at, 'updatedAt', b.updated_at, 'locale', b.locale, 'translationGroup', b.translation_group))) FROM \"_emdash_content_bylines\" AS cb CROSS JOIN \"_emdash_bylines\" AS b ON b.translation_group = cb.byline_id LEFT JOIN \"media\" AS m ON m.id = b.avatar_media_id WHERE cb.collection_slug = ? AND cb.content_id = \"r\".id AND b.locale = \"r\".locale) AS \"_emdash_bylines\" FROM picked JOIN \"ec_posts\" AS r ON r.id = picked.entry_id WHERE r.deleted_at IS NULL AND (\"r\".\"status\" = 'published' OR (\"r\".\"status\" = 'scheduled' AND \"r\".\"scheduled_at\" <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))) ORDER BY picked.sortval DESC, picked.entry_id DESC": 1 } } From b10d84a7b62ed8bb703fc7f767398c1eea516e20 Mon Sep 17 00:00:00 2001 From: Malloo <26630797+MA2153@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:13:16 +0300 Subject: [PATCH 3/4] fix(core): trim narrative/issue-referencing comments per review Comments should state the invariant, not the PR story or issue number. --- packages/core/src/taxonomies/term-counts.ts | 11 +++------ .../taxonomy-term-counts-plan.test.ts | 24 ++++--------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/packages/core/src/taxonomies/term-counts.ts b/packages/core/src/taxonomies/term-counts.ts index 792cdd0f32..3cbf5f513f 100644 --- a/packages/core/src/taxonomies/term-counts.ts +++ b/packages/core/src/taxonomies/term-counts.ts @@ -38,14 +38,9 @@ interface CountRow { * sibling translations survive, and a plain join on `translation_group` would * multiply counts by the number of locales. * - * `CROSS JOIN` (with the join predicate in `WHERE`) is a join-order hint, not a - * cartesian product: it pins the pivot as the outer table. Written as an - * `INNER JOIN`, stats-blind SQLite/D1 drives from `ec_*` instead and re-runs - * the whole `taxonomy_id IN (...)` list as a pivot-PK probe for every entry in - * the collection, so a single call reads `entries × terms` rows (#2237). Seeked - * from the pivot it is one `(taxonomy_id, collection)` index seek per term plus - * one primary-key touch per assignment. Postgres has statistics and reorders - * freely — `CROSS JOIN` there is a plain inner join. + * CROSS JOIN with the join predicate in WHERE keeps stats-blind SQLite/D1 from + * reordering content_taxonomies out of the outer position; it touches ec_* only + * by primary key. Postgres treats this as an ordinary inner join and plans freely. */ function collectionBranch( db: Kysely, diff --git a/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts index 1cec534d2e..0089a48d80 100644 --- a/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts +++ b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts @@ -1,16 +1,7 @@ /** - * Query-plan shape of the consolidated term-count query (#2237). - * - * On stats-blind SQLite/D1 (no ANALYZE, no `sqlite_stat1`) an `INNER JOIN` - * between the pivot and the content table let the planner drive from `ec_*` and - * re-run the `taxonomy_id IN (...)` term list as a pivot-PK probe for every - * entry in the collection — `entries × terms` rows read per branch. Seeking the - * pivot first makes it one index seek per term plus one primary-key touch per - * assignment. - * - * This asserts the plan, not the output (output is covered by - * unit/taxonomies/term-counts). SQLite-only: `EXPLAIN QUERY PLAN` is a SQLite - * concern and, being stats-blind here, the plan is schema-driven — matching D1. + * SQLite query-plan regression guard for the consolidated term-count query. + * Output correctness is covered by unit/taxonomies/term-counts; this asserts + * the planner drives from content_taxonomies, not from ec_*. */ import Database from "better-sqlite3"; @@ -56,9 +47,7 @@ beforeEach(async () => { const content = new ContentRepository(anyDb); const tax = new TaxonomyRepository(anyDb); - // Many terms and many entries: the shape the bad plan multiplies together. - // The plan is stats-blind so the counts are immaterial — they only make the - // two access paths distinguishable to a reader. + // Enough rows to make the two access paths visually distinct. const terms = []; for (let i = 0; i < 5; i++) { terms.push( @@ -107,11 +96,8 @@ async function countQueryPlan(): Promise { it("seeks the terms on a content_taxonomies index rather than probing the pivot per entry", async () => { const plan = await countQueryPlan(); - // The pivot is entered on a `taxonomy_id`-leading index, once per term. + // The pivot must be entered on a taxonomy_id-leading index. expect(plan).toMatch(/SEARCH ct USING (COVERING )?INDEX idx_content_taxonomies/); - // The pivot's primary key is `(collection, entry_id, taxonomy_id)`. Reaching - // the pivot through it means the planner is driving from `ec_*` and probing - // the whole term list per entry — the #2237 blowup. expect(plan).not.toContain("sqlite_autoindex_content_taxonomies_1"); expect(plan).not.toContain("SCAN ct"); }); From baafe3c211f50343174985a449a8c5491fb8ac87 Mon Sep 17 00:00:00 2001 From: Malloo <26630797+MA2153@users.noreply.github.com> Date: Sun, 26 Jul 2026 04:26:01 +0300 Subject: [PATCH 4/4] fix(core): tighten changeset and comment per review Keep the changeset user-facing (observable slowness, not internal mechanics); drop the justifying "deliberately". --- .changeset/great-cases-smile.md | 2 +- .../core/tests/integration/taxonomy-term-counts-plan.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/great-cases-smile.md b/.changeset/great-cases-smile.md index 6126ecaffd..8553deac52 100644 --- a/.changeset/great-cases-smile.md +++ b/.changeset/great-cases-smile.md @@ -2,4 +2,4 @@ "emdash": patch --- -Fixes taxonomy term counts reading a near-quadratic number of database rows on larger sites. The count query drove from the content table and re-checked the taxonomy's entire term list once per entry, so the cost scaled with entries × terms — on a site with ~26k entries and ~1.4k terms a single call read ~35.6M rows and took ~29s, on every page that renders a term list or taxonomy filter. It now seeks the terms on the `content_taxonomies` index and touches content rows only by primary key: the same call reads ~64k rows in ~120ms. Counts are unchanged. +Fixes taxonomy term counts reading a near-quadratic number of rows on sites with many entries and terms, causing multi-second delays on pages that render term counts or taxonomy filters. Counts are unchanged. diff --git a/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts index 0089a48d80..2245f0e8be 100644 --- a/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts +++ b/packages/core/tests/integration/taxonomy-term-counts-plan.test.ts @@ -36,7 +36,7 @@ beforeEach(async () => { }, }); - // Deliberately no ANALYZE: matches D1, which never maintains sqlite_stat1. + // No ANALYZE: D1 never maintains sqlite_stat1. await runMigrations(db); const registry = new SchemaRegistry(db); await registry.createCollection({ slug: "post", label: "Posts", labelSingular: "Post" });