Skip to content

Commit 42da87b

Browse files
authored
fix(plugin-search): deleting docs even when there's a published version (#10993)
Fixes #9770 If you had a published document but then created a new draft it would delete the search doc, this PR adds an additional find to check if an existing published doc exists before deleting the search doc. Also adds a few jsdocs to plugin config
1 parent 2a1ddf1 commit 42da87b

File tree

4 files changed

+124
-10
lines changed

4 files changed

+124
-10
lines changed

packages/plugin-search/src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,25 @@ export type SearchPluginConfig = {
4242
defaultPriorities?: {
4343
[collection: string]: ((doc: any) => number | Promise<number>) | number
4444
}
45+
/**
46+
* Controls whether drafts are deleted from the search index
47+
*
48+
* @default true
49+
*/
4550
deleteDrafts?: boolean
4651
localize?: boolean
52+
/**
53+
* We use batching when re-indexing large collections. You can control the amount of items per batch, lower numbers should help with memory.
54+
*
55+
* @default 50
56+
*/
4757
reindexBatchSize?: number
4858
searchOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>
59+
/**
60+
* Controls whether drafts are synced to the search index
61+
*
62+
* @default false
63+
*/
4964
syncDrafts?: boolean
5065
}
5166

packages/plugin-search/src/utilities/syncDocAsSearchIndex.ts

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,42 @@ export const syncDocAsSearchIndex = async ({
142142
}
143143
}
144144
if (deleteDrafts && status === 'draft') {
145-
// do not include draft docs in search results, so delete the record
146-
try {
147-
await payload.delete({
148-
id: searchDocID,
149-
collection: searchSlug,
150-
req,
151-
})
152-
} catch (err: unknown) {
153-
payload.logger.error({ err, msg: `Error deleting ${searchSlug} document.` })
145+
// Check to see if there's a published version of the doc
146+
// We don't want to remove the search doc if there is a published version but a new draft has been created
147+
const {
148+
docs: [docWithPublish],
149+
} = await payload.find({
150+
collection,
151+
draft: false,
152+
locale: syncLocale,
153+
req,
154+
where: {
155+
and: [
156+
{
157+
_status: {
158+
equals: 'published',
159+
},
160+
},
161+
{
162+
id: {
163+
equals: id,
164+
},
165+
},
166+
],
167+
},
168+
})
169+
170+
if (!docWithPublish) {
171+
// do not include draft docs in search results, so delete the record
172+
try {
173+
await payload.delete({
174+
id: searchDocID,
175+
collection: searchSlug,
176+
req,
177+
})
178+
} catch (err: unknown) {
179+
payload.logger.error({ err, msg: `Error deleting ${searchSlug} document.` })
180+
}
154181
}
155182
}
156183
} else if (doSync) {

test/plugin-search/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default buildConfigWithDefaults({
6464
searchOverrides: {
6565
access: {
6666
// Used for int test
67-
delete: ({ req: { user } }) => user.email === devUser.email,
67+
delete: ({ req: { user } }) => user?.email === devUser.email,
6868
},
6969
fields: ({ defaultFields }) => [
7070
...defaultFields,

test/plugin-search/int.spec.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,78 @@ describe('@payloadcms/plugin-search', () => {
133133
expect(results).toHaveLength(0)
134134
})
135135

136+
it('should not delete a search doc if a published item has a new draft but remains published', async () => {
137+
const publishedPage = await payload.create({
138+
collection: 'pages',
139+
data: {
140+
_status: 'published',
141+
title: 'Published title!',
142+
},
143+
})
144+
145+
// wait for the search document to be potentially created
146+
// we do not await this within the `syncToSearch` hook
147+
await wait(200)
148+
149+
const { docs: results } = await payload.find({
150+
collection: 'search',
151+
depth: 0,
152+
where: {
153+
'doc.value': {
154+
equals: publishedPage.id,
155+
},
156+
},
157+
})
158+
159+
expect(results).toHaveLength(1)
160+
161+
// Create a new draft
162+
await payload.update({
163+
collection: 'pages',
164+
id: publishedPage.id,
165+
draft: true,
166+
data: {
167+
_status: 'draft',
168+
title: 'Draft title!',
169+
},
170+
})
171+
172+
// This should remain with the published content
173+
const { docs: updatedResults } = await payload.find({
174+
collection: 'search',
175+
depth: 0,
176+
where: {
177+
'doc.value': {
178+
equals: publishedPage.id,
179+
},
180+
},
181+
})
182+
183+
expect(updatedResults).toHaveLength(1)
184+
185+
await payload.update({
186+
collection: 'pages',
187+
id: publishedPage.id,
188+
data: {
189+
_status: 'draft',
190+
title: 'Drafted again',
191+
},
192+
})
193+
194+
// Should now be deleted given we've unpublished the page
195+
const { docs: deletedResults } = await payload.find({
196+
collection: 'search',
197+
depth: 0,
198+
where: {
199+
'doc.value': {
200+
equals: publishedPage.id,
201+
},
202+
},
203+
})
204+
205+
expect(deletedResults).toHaveLength(0)
206+
})
207+
136208
it('should sync changes made to an existing search document', async () => {
137209
const pageToReceiveUpdates = await payload.create({
138210
collection: 'pages',

0 commit comments

Comments
 (0)