Skip to content

Commit

Permalink
Merge pull request #3905 from owid/explorer-indexing-resolve-indicato…
Browse files Browse the repository at this point in the history
…r-id
  • Loading branch information
marcelgerber authored Aug 27, 2024
2 parents ca35d73 + 1c75696 commit 4834847
Showing 1 changed file with 89 additions and 10 deletions.
99 changes: 89 additions & 10 deletions baker/algolia/indexExplorerViewsToAlgolia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getIndexName } from "../../site/search/searchClient.js"
import { SearchIndexName } from "../../site/search/searchTypes.js"
import { groupBy, keyBy, orderBy } from "lodash"
import { MarkdownTextWrap } from "@ourworldindata/components"
import { DbRawVariable } from "@ourworldindata/utils"

export type ExplorerBlockGraphers = {
type: "graphers"
Expand All @@ -30,6 +31,7 @@ interface ExplorerViewEntry {
viewQueryParams: string

viewGrapherId?: number
viewFirstYIndicator?: string | number // Variable ID or ETL path

/**
* We often have several views with the same title within an explorer, e.g. "Population".
Expand Down Expand Up @@ -111,7 +113,7 @@ const getExplorerViewRecordsForExplorerSlug = async (

const defaultSettings = explorerDecisionMatrix.defaultSettings

const records = explorerDecisionMatrix
let records = explorerDecisionMatrix
.allDecisionsAsQueryParams()
.map((choice, i) => {
explorerDecisionMatrix.setValuesFromChoiceParams(choice)
Expand All @@ -133,7 +135,7 @@ const getExplorerViewRecordsForExplorerSlug = async (

const record: Omit<
ExplorerViewEntry,
"viewTitleIndexWithinExplorer"
"viewTitleIndexWithinExplorer" | "titleLength"
> = {
viewTitle: explorerDecisionMatrix.selectedRow.title,
viewSubtitle: explorerDecisionMatrix.selectedRow.subtitle,
Expand All @@ -142,10 +144,14 @@ const getExplorerViewRecordsForExplorerSlug = async (
explorerDecisionMatrix
),
viewGrapherId: explorerDecisionMatrix.selectedRow.grapherId,
viewFirstYIndicator:
explorerDecisionMatrix.selectedRow.yVariableIds
?.trim()
.split(" ")
.at(0),
viewQueryParams: explorerDecisionMatrix.toString(),

viewIndexWithinExplorer: i,
titleLength: explorerDecisionMatrix.selectedRow.title?.length,
numNonDefaultSettings: nonDefaultSettings.length,
}
return record
Expand All @@ -158,7 +164,7 @@ const getExplorerViewRecordsForExplorerSlug = async (

if (grapherIds.length) {
console.log(
`Fetching grapher info from ${grapherIds.length} graphers for explorer ${slug}`
`Fetching grapher configs from ${grapherIds.length} graphers for explorer ${slug}`
)
const grapherIdToTitle = await trx
.table("charts")
Expand All @@ -182,27 +188,102 @@ const getExplorerViewRecordsForExplorerSlug = async (
}
record.viewTitle = grapherInfo.title
record.viewSubtitle = grapherInfo.subtitle
record.titleLength = grapherInfo.title?.length
}
}
}

// Resolve the `yIndicatorIds` field
const yIndicatorIds = records
.map((record) => record.viewFirstYIndicator)
.filter((id) => id !== undefined)
.filter((id) => id !== "")

if (yIndicatorIds.length) {
console.log(
`Fetching indicator metadata from ${yIndicatorIds.length} indicators for explorer ${slug}`
)

type IndicatorRecord = Pick<
DbRawVariable,
| "id"
| "catalogPath"
| "titlePublic"
| "display"
| "name"
| "descriptionShort"
>
// The `yIndicatorId` can be a variable ID or a catalog path, and we want to resolve both
const indicatorIdToTitle: IndicatorRecord[] = await trx
.table("variables")
.select(
"id",
"catalogPath",
"name",
"titlePublic",
"display",
"name",
"descriptionShort"
)
.whereIn("id", yIndicatorIds)
.orWhereIn("catalogPath", yIndicatorIds)

const indicatorsKeyedByIdAndCatalogPath = indicatorIdToTitle.reduce(
(acc, indicator) => {
acc[indicator.id] = indicator
if (indicator.catalogPath)
acc[indicator.catalogPath] = indicator
return acc
},
{} as Record<string | number, IndicatorRecord>
)

for (const record of records) {
if (record.viewFirstYIndicator !== undefined) {
const indicatorInfo =
indicatorsKeyedByIdAndCatalogPath[
record.viewFirstYIndicator
]
if (indicatorInfo === undefined) {
console.warn(
`Indicator id ${record.viewFirstYIndicator} not found for explorer ${slug}`
)
continue
}

// This is the fallback chain for the grapher title. it's complicated.
record.viewTitle =
record.viewTitle ??
indicatorInfo.titlePublic ??
(indicatorInfo.display
? JSON.parse(indicatorInfo.display).name
: undefined) ??
indicatorInfo.name
record.viewSubtitle =
record.viewSubtitle ?? indicatorInfo.descriptionShort
}
}
}

// Drop any views where we couldn't obtain a title, for whatever reason
records = records.filter((record) => record.viewTitle !== undefined)

// Remove Markdown from viewSubtitle; do this after fetching grapher info above, as it might also contain Markdown
records.forEach((record) => {
const recordsWithTitleLength = records.map((record) => {
if (record.viewSubtitle) {
record.viewSubtitle = new MarkdownTextWrap({
text: record.viewSubtitle,
fontSize: 10, // doesn't matter, but is a mandatory field
}).plaintext
}
})
return { ...record, titleLength: record.viewTitle.length }
}) as Omit<ExplorerViewEntry, "viewTitleIndexWithinExplorer">[]

// Compute viewTitleIndexWithinExplorer:
// First, sort by score descending (ignoring views_7d, which is not relevant _within_ an explorer).
// Then, group by viewTitle.
// Finally, ungroup again, and keep track of the index of each element within the group.
const recordsSortedByScore = orderBy(
records,
recordsWithTitleLength,
(record) => computeScore(record),
"desc"
)
Expand All @@ -216,8 +297,6 @@ const getExplorerViewRecordsForExplorerSlug = async (
}))
)

// TODO: Handle indicator-based explorers

return recordsWithIndexWithinExplorer
}

Expand Down

0 comments on commit 4834847

Please sign in to comment.