diff --git a/baker/GrapherBaker.tsx b/baker/GrapherBaker.tsx index 2efc3a6c52..ad061bdbb3 100644 --- a/baker/GrapherBaker.tsx +++ b/baker/GrapherBaker.tsx @@ -57,7 +57,10 @@ import { logErrorAndMaybeCaptureInSentry } from "../serverUtils/errorLog.js" import { getTagToSlugMap } from "./GrapherBakingUtils.js" import { knexRaw } from "../db/db.js" -import { getRelatedChartsForVariable } from "../db/model/Chart.js" +import { + getRelatedChartsForVariable, + getRelatedChartsForChart, +} from "../db/model/Chart.js" import pMap from "p-map" const renderDatapageIfApplicable = async ( @@ -193,11 +196,18 @@ export async function renderDataPageV2( // Get the charts this variable is being used in (aka "related charts") // and exclude the current chart to avoid duplicates - datapageData.allCharts = await getRelatedChartsForVariable( - knex, - variableId, - grapher && "id" in grapher ? [grapher.id as number] : [] - ) + if (grapher && "id" in grapher) { + datapageData.allCharts = await getRelatedChartsForChart( + knex, + grapher.id as number + ) + } else { + datapageData.allCharts = await getRelatedChartsForVariable( + knex, + variableId, + [] + ) + } datapageData.relatedResearch = await getRelatedResearchAndWritingForVariable(knex, variableId) diff --git a/db/migration/1736933781669-AddRelatedChartsTable.ts b/db/migration/1736933781669-AddRelatedChartsTable.ts new file mode 100644 index 0000000000..2e4c2a91e1 --- /dev/null +++ b/db/migration/1736933781669-AddRelatedChartsTable.ts @@ -0,0 +1,32 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddRelatedChartsTable1736933781669 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`-- sql + CREATE TABLE related_charts ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + chartId INT NOT NULL, + relatedChartId INT NOT NULL, + label VARCHAR(255) NOT NULL, + reviewer VARCHAR(255) DEFAULT NULL, + reason TEXT DEFAULT NULL, + score DOUBLE DEFAULT NULL, + updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT related_charts_ibfk_1 + FOREIGN KEY (chartId) REFERENCES charts (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT related_charts_ibfk_2 + FOREIGN KEY (relatedChartId) REFERENCES charts (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + KEY idx_related_charts_chartId (chartId), + UNIQUE KEY uq_chartId_relatedChartId_reviewer (chartId, relatedChartId, reviewer) + ) + `) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE related_charts`) + } +} diff --git a/db/model/Chart.ts b/db/model/Chart.ts index 7a74b18698..5aec3d3538 100644 --- a/db/model/Chart.ts +++ b/db/model/Chart.ts @@ -607,6 +607,33 @@ export const getRelatedChartsForVariable = async ( ) } +export const getRelatedChartsForChart = async ( + knex: db.KnexReadonlyTransaction, + chartId: number +): Promise => { + return db.knexRaw( + knex, + `-- sql + SELECT + chart_configs.slug, + chart_configs.full->>"$.title" AS title, + chart_configs.full->>"$.variantName" AS variantName, + MAX(chart_tags.keyChartLevel) as keyChartLevel + FROM charts + JOIN chart_configs ON charts.configId=chart_configs.id + INNER JOIN chart_tags ON charts.id=chart_tags.chartId + INNER JOIN related_charts ON charts.id=related_charts.relatedChartId + WHERE related_charts.chartId = ${chartId} + AND related_charts.reviewer = 'production' + AND related_charts.label = 'good' + AND chart_configs.full->>"$.isPublished" = "true" + GROUP BY charts.id + ORDER BY related_charts.score DESC + LIMIT 6 + ` + ) +} + export const getChartEmbedUrlsInPublishedWordpressPosts = async ( knex: db.KnexReadonlyTransaction ): Promise => { diff --git a/package.json b/package.json index d744e32607..ca46f4d05a 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "startLocalCloudflareFunctions": "wrangler pages dev", "startDeployQueueServer": "node --enable-source-maps ./itsJustJavascript/baker/startDeployQueueServer.js", "startLernaWatcher": "lerna watch --scope '@ourworldindata/*' -- lerna run build --scope=\\$LERNA_PACKAGE_NAME --include-dependents", - "startTmuxServer": "node_modules/tmex/tmex dev \"yarn startLernaWatcher\" \"yarn startAdminDevServer\" \"yarn startViteServer\"", + "startTmuxServer": "node_modules/tmex/tmex dev \"yarn startLernaWatcher\" \"yarn startAdminDevServer\" \"yarn startSiteFront\"", "startViteServer": "vite dev", "startSiteFront": "./devTools/vite/startVite.sh", "startLocalBakeServer": "http-server ./localBake -p 3000",