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.
This commit is contained in:
Sasha
2025-05-09 13:24:48 +03:00
committed by GitHub
parent 09f15ff874
commit 3701de5056
8 changed files with 108 additions and 53 deletions

View File

@@ -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

View File

@@ -52,7 +52,7 @@ export const searchFields: Field[] = [
type: 'text',
},
{
name: 'id',
name: 'categoryID',
type: 'text',
},
{

View File

@@ -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<T extends boolean = true> {
label?: T;
width?: T;
defaultValue?: T;
placeholder?: T;
options?:
| T
| {
@@ -1458,8 +1462,9 @@ export interface SearchSelect<T extends boolean = true> {
| T
| {
relationTo?: T;
id?: T;
categoryID?: T;
title?: T;
id?: T;
};
updatedAt?: T;
createdAt?: T;

View File

@@ -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

View File

@@ -49,7 +49,7 @@ export const searchFields: Field[] = [
type: 'text',
},
{
name: 'id',
name: 'categoryID',
type: 'text',
},
{

View File

@@ -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 extends boolean = true> {
| T
| {
relationTo?: T;
id?: T;
categoryID?: T;
title?: T;
id?: T;
};
updatedAt?: T;
createdAt?: T;

View File

@@ -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

View File

@@ -49,7 +49,7 @@ export const searchFields: Field[] = [
type: 'text',
},
{
name: 'id',
name: 'categoryID',
type: 'text',
},
{