From 3701de5056d7b19c860bced6cc14c963cce7a5ae Mon Sep 17 00:00:00 2001 From: Sasha <64744993+r1tsuu@users.noreply.github.com> Date: Fri, 9 May 2025 13:24:48 +0300 Subject: [PATCH] templates: fix categories search sync (#12359) Fixes https://github.com/payloadcms/payload/issues/9449 Previously, search sync with categories didn't work and additionally caused problems with Postgres. Additionally, ensures that when doing synchronization, all the categories are populated, since we don't always have populated data inside hooks. --- .../localization/src/search/beforeSync.ts | 48 ++++++++++++------- .../localization/src/search/fieldOverrides.ts | 2 +- templates/website/src/payload-types.ts | 9 +++- templates/website/src/search/beforeSync.ts | 46 ++++++++++++------ .../website/src/search/fieldOverrides.ts | 2 +- .../with-vercel-website/src/payload-types.ts | 6 ++- .../src/search/beforeSync.ts | 46 ++++++++++++------ .../src/search/fieldOverrides.ts | 2 +- 8 files changed, 108 insertions(+), 53 deletions(-) diff --git a/examples/localization/src/search/beforeSync.ts b/examples/localization/src/search/beforeSync.ts index d0c03c83c1..2d9c7be7ef 100644 --- a/examples/localization/src/search/beforeSync.ts +++ b/examples/localization/src/search/beforeSync.ts @@ -1,11 +1,11 @@ import { BeforeSync, DocToSync } from '@payloadcms/plugin-search/types' -export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc, payload }) => { +export const beforeSyncWithSearch: BeforeSync = async ({ req, originalDoc, searchDoc }) => { const { doc: { relationTo: collection }, } = searchDoc - const { slug, id, categories, title, meta, excerpt } = originalDoc + const { slug, id, categories, title, meta } = originalDoc const modifiedDoc: DocToSync = { ...searchDoc, @@ -20,24 +20,40 @@ export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc, } if (categories && Array.isArray(categories) && categories.length > 0) { - // get full categories and keep a flattened copy of their most important properties - try { - const mappedCategories = categories.map((category) => { - const { id, title } = category + const populatedCategories: { id: string | number; title: string }[] = [] + for (const category of categories) { + if (!category) { + continue + } - return { - relationTo: 'categories', - id, - title, - } + if (typeof category === 'object') { + populatedCategories.push(category) + continue + } + + const doc = await req.payload.findByID({ + collection: 'categories', + id: category, + disableErrors: true, + depth: 0, + select: { title: true }, + req, }) - modifiedDoc.categories = mappedCategories - } catch (err) { - console.error( - `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, - ) + if (doc !== null) { + populatedCategories.push(doc) + } else { + console.error( + `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, + ) + } } + + modifiedDoc.categories = populatedCategories.map((each) => ({ + relationTo: 'categories', + categoryID: String(each.id), + title: each.title, + })) } return modifiedDoc diff --git a/examples/localization/src/search/fieldOverrides.ts b/examples/localization/src/search/fieldOverrides.ts index d5c1e98ee0..fb9b7a2ad4 100644 --- a/examples/localization/src/search/fieldOverrides.ts +++ b/examples/localization/src/search/fieldOverrides.ts @@ -52,7 +52,7 @@ export const searchFields: Field[] = [ type: 'text', }, { - name: 'id', + name: 'categoryID', type: 'text', }, { diff --git a/templates/website/src/payload-types.ts b/templates/website/src/payload-types.ts index 1dfe387bcb..5978fa225e 100644 --- a/templates/website/src/payload-types.ts +++ b/templates/website/src/payload-types.ts @@ -54,6 +54,7 @@ export type SupportedTimezones = | 'Asia/Singapore' | 'Asia/Tokyo' | 'Asia/Seoul' + | 'Australia/Brisbane' | 'Australia/Sydney' | 'Pacific/Guam' | 'Pacific/Noumea' @@ -624,6 +625,7 @@ export interface Form { label?: string | null; width?: number | null; defaultValue?: string | null; + placeholder?: string | null; options?: | { label: string; @@ -791,8 +793,9 @@ export interface Search { categories?: | { relationTo?: string | null; - id?: string | null; + categoryID?: string | null; title?: string | null; + id?: string | null; }[] | null; updatedAt: string; @@ -1355,6 +1358,7 @@ export interface FormsSelect { label?: T; width?: T; defaultValue?: T; + placeholder?: T; options?: | T | { @@ -1458,8 +1462,9 @@ export interface SearchSelect { | T | { relationTo?: T; - id?: T; + categoryID?: T; title?: T; + id?: T; }; updatedAt?: T; createdAt?: T; diff --git a/templates/website/src/search/beforeSync.ts b/templates/website/src/search/beforeSync.ts index 5fc5f128ec..2d9c7be7ef 100644 --- a/templates/website/src/search/beforeSync.ts +++ b/templates/website/src/search/beforeSync.ts @@ -1,6 +1,6 @@ import { BeforeSync, DocToSync } from '@payloadcms/plugin-search/types' -export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc }) => { +export const beforeSyncWithSearch: BeforeSync = async ({ req, originalDoc, searchDoc }) => { const { doc: { relationTo: collection }, } = searchDoc @@ -20,24 +20,40 @@ export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc } if (categories && Array.isArray(categories) && categories.length > 0) { - // get full categories and keep a flattened copy of their most important properties - try { - const mappedCategories = categories.map((category) => { - const { id, title } = category + const populatedCategories: { id: string | number; title: string }[] = [] + for (const category of categories) { + if (!category) { + continue + } - return { - relationTo: 'categories', - id, - title, - } + if (typeof category === 'object') { + populatedCategories.push(category) + continue + } + + const doc = await req.payload.findByID({ + collection: 'categories', + id: category, + disableErrors: true, + depth: 0, + select: { title: true }, + req, }) - modifiedDoc.categories = mappedCategories - } catch (_err) { - console.error( - `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, - ) + if (doc !== null) { + populatedCategories.push(doc) + } else { + console.error( + `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, + ) + } } + + modifiedDoc.categories = populatedCategories.map((each) => ({ + relationTo: 'categories', + categoryID: String(each.id), + title: each.title, + })) } return modifiedDoc diff --git a/templates/website/src/search/fieldOverrides.ts b/templates/website/src/search/fieldOverrides.ts index 6d086cb95f..14f81fd61b 100644 --- a/templates/website/src/search/fieldOverrides.ts +++ b/templates/website/src/search/fieldOverrides.ts @@ -49,7 +49,7 @@ export const searchFields: Field[] = [ type: 'text', }, { - name: 'id', + name: 'categoryID', type: 'text', }, { diff --git a/templates/with-vercel-website/src/payload-types.ts b/templates/with-vercel-website/src/payload-types.ts index 4f797274c2..7ac31f6edf 100644 --- a/templates/with-vercel-website/src/payload-types.ts +++ b/templates/with-vercel-website/src/payload-types.ts @@ -793,8 +793,9 @@ export interface Search { categories?: | { relationTo?: string | null; - id?: string | null; + categoryID?: string | null; title?: string | null; + id?: string | null; }[] | null; updatedAt: string; @@ -1461,8 +1462,9 @@ export interface SearchSelect { | T | { relationTo?: T; - id?: T; + categoryID?: T; title?: T; + id?: T; }; updatedAt?: T; createdAt?: T; diff --git a/templates/with-vercel-website/src/search/beforeSync.ts b/templates/with-vercel-website/src/search/beforeSync.ts index 5fc5f128ec..2d9c7be7ef 100644 --- a/templates/with-vercel-website/src/search/beforeSync.ts +++ b/templates/with-vercel-website/src/search/beforeSync.ts @@ -1,6 +1,6 @@ import { BeforeSync, DocToSync } from '@payloadcms/plugin-search/types' -export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc }) => { +export const beforeSyncWithSearch: BeforeSync = async ({ req, originalDoc, searchDoc }) => { const { doc: { relationTo: collection }, } = searchDoc @@ -20,24 +20,40 @@ export const beforeSyncWithSearch: BeforeSync = async ({ originalDoc, searchDoc } if (categories && Array.isArray(categories) && categories.length > 0) { - // get full categories and keep a flattened copy of their most important properties - try { - const mappedCategories = categories.map((category) => { - const { id, title } = category + const populatedCategories: { id: string | number; title: string }[] = [] + for (const category of categories) { + if (!category) { + continue + } - return { - relationTo: 'categories', - id, - title, - } + if (typeof category === 'object') { + populatedCategories.push(category) + continue + } + + const doc = await req.payload.findByID({ + collection: 'categories', + id: category, + disableErrors: true, + depth: 0, + select: { title: true }, + req, }) - modifiedDoc.categories = mappedCategories - } catch (_err) { - console.error( - `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, - ) + if (doc !== null) { + populatedCategories.push(doc) + } else { + console.error( + `Failed. Category not found when syncing collection '${collection}' with id: '${id}' to search.`, + ) + } } + + modifiedDoc.categories = populatedCategories.map((each) => ({ + relationTo: 'categories', + categoryID: String(each.id), + title: each.title, + })) } return modifiedDoc diff --git a/templates/with-vercel-website/src/search/fieldOverrides.ts b/templates/with-vercel-website/src/search/fieldOverrides.ts index 6d086cb95f..14f81fd61b 100644 --- a/templates/with-vercel-website/src/search/fieldOverrides.ts +++ b/templates/with-vercel-website/src/search/fieldOverrides.ts @@ -49,7 +49,7 @@ export const searchFields: Field[] = [ type: 'text', }, { - name: 'id', + name: 'categoryID', type: 'text', }, {