Skip to content

Commit 210fa04

Browse files
meili-bors[bot]Ra0Rmeili-botbrunoocasalidependabot[bot]
authored
Merge #972
972: Feature: Implementation of createMany lifecycle hook r=brunoocasali a=Ra0R # Pull Request - Implemented `createMany`-Hook that is available since`Strapi v4.9.0`, the `createMany()`-Lifecycle hook properly returns a count and a set of ids after creation. - Test of the implemented function also extended mock to properly test behaviour - Bumped peerDependencies to `"peerDependencies": { "`@strapi/strapi":` "^4.9.0" }` ## Related issue Fixes #965 Probably fixes #399 (I am not sure how tasks are updated upon restart) ## What does this PR do? - ... ## PR checklist Please check if your PR fulfills the following requirements: - [X] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)? - [X] Have you read the contributing guidelines? However, I could not lint since there would be changes in 67 to remove carriage-returns( `error Delete `␍` prettier/prettier` ) - [X] Have you made sure that the title is accurate and descriptive of the changes? Thank you so much for contributing to Meilisearch! Co-authored-by: Renato <[email protected]> Co-authored-by: Renato Rao <[email protected]> Co-authored-by: meili-bot <[email protected]> Co-authored-by: Bruno Casali <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bruno Casali <[email protected]>
2 parents c0961aa + 14918c8 commit 210fa04

File tree

4 files changed

+183
-32
lines changed

4 files changed

+183
-32
lines changed

server/src/__mocks__/meilisearch.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const updateSettingsMock = jest.fn(() => 10)
44
const deleteDocuments = jest.fn(() => {
55
return [{ taskUid: 1 }, { taskUid: 2 }]
66
})
7+
const deleteDocument = jest.fn(() => 3)
8+
79
const getStats = jest.fn(() => {
810
return {
911
databaseSize: 447819776,
@@ -41,6 +43,7 @@ const mockIndex = jest.fn(() => ({
4143
addDocuments: addDocumentsMock,
4244
updateDocuments: updateDocumentsMock,
4345
updateSettings: updateSettingsMock,
46+
deleteDocument,
4447
deleteDocuments,
4548
getStats: getIndexStats,
4649
}))

server/src/__tests__/meilisearch.test.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,106 @@ describe('Tests content types', () => {
275275
])
276276
})
277277

278+
test('Test to update entries linked to multiple indexes in Meilisearch', async () => {
279+
const pluginMock = jest.fn(() => ({
280+
// This rewrites only the needed methods to reach the system under test (removeSensitiveFields)
281+
service: jest.fn().mockImplementation(() => {
282+
return {
283+
async actionInBatches({ contentType = 'restaurant', callback }) {
284+
await callback({
285+
entries: [
286+
{
287+
id: 1,
288+
title: 'title',
289+
internal_notes: 'note123',
290+
secret: '123',
291+
},
292+
{
293+
id: 2,
294+
title: 'abc',
295+
internal_notes: 'note234',
296+
secret: '234',
297+
},
298+
],
299+
contentType,
300+
})
301+
},
302+
getCollectionName: ({ contentType }) => contentType,
303+
addIndexedContentType: jest.fn(),
304+
subscribeContentType: jest.fn(),
305+
getCredentials: () => ({}),
306+
}
307+
}),
308+
}))
309+
310+
// Spy
311+
const client = new Meilisearch({ host: 'abc' })
312+
313+
const meilisearchService = createMeilisearchService({
314+
strapi: {
315+
plugin: pluginMock,
316+
contentTypes: {
317+
restaurant: {
318+
attributes: {
319+
id: { private: false },
320+
title: { private: false },
321+
internal_notes: { private: true },
322+
secret: { private: true },
323+
},
324+
},
325+
},
326+
config: {
327+
get: jest.fn(() => ({
328+
restaurant: {
329+
noSanitizePrivateFields: ['internal_notes'],
330+
indexName: ['customIndex', 'anotherIndex'],
331+
},
332+
})),
333+
},
334+
log: mockLogger,
335+
},
336+
contentTypes: {
337+
restaurant: {
338+
attributes: {
339+
id: { private: false },
340+
title: { private: false },
341+
internal_notes: { private: true },
342+
secret: { private: true },
343+
},
344+
},
345+
},
346+
})
347+
348+
const mockEntryUpdate = { attributes: { id: 1 } }
349+
350+
const mockEntryCreate = {
351+
_meilisearch_id: 'restaurant-1',
352+
id: 3,
353+
title: 'title',
354+
internal_notes: 'note123',
355+
publishedAt: null,
356+
}
357+
358+
const tasks = await meilisearchService.updateEntriesInMeilisearch({
359+
contentType: 'restaurant',
360+
entries: [mockEntryUpdate, mockEntryCreate],
361+
})
362+
363+
expect(strapi.log.info).toHaveBeenCalledTimes(4)
364+
expect(strapi.log.info).toHaveBeenCalledWith(
365+
'A task to update 1 documents to the Meilisearch index "customIndex" has been enqueued.',
366+
)
367+
expect(strapi.log.info).toHaveBeenCalledWith(
368+
'A task to update 1 documents to the Meilisearch index "anotherIndex" has been enqueued.',
369+
)
370+
expect(client.index('').updateDocuments).toHaveBeenCalledTimes(2)
371+
expect(client.index('').deleteDocument).toHaveBeenCalledTimes(2)
372+
373+
expect(client.index).toHaveBeenCalledWith('customIndex')
374+
expect(client.index).toHaveBeenCalledWith('anotherIndex')
375+
expect(tasks).toEqual([3, 3, 10, 10])
376+
})
377+
278378
test('Test to get stats', async () => {
279379
const customStrapi = createStrapiMock({})
280380

server/src/services/lifecycle/lifecycle.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,41 @@ export default ({ strapi }) => {
4242
)
4343
})
4444
},
45-
async afterCreateMany() {
46-
strapi.log.error(
47-
`Meilisearch does not work with \`afterCreateMany\` hook as the entries are provided without their id`,
48-
)
45+
async afterCreateMany(event) {
46+
const { result } = event
47+
const meilisearch = strapi
48+
.plugin('meilisearch')
49+
.service('meilisearch')
50+
51+
const nbrEntries = result.count
52+
const ids = result.ids
53+
54+
const entries = []
55+
const BATCH_SIZE = 500
56+
for (let pos = 0; pos < nbrEntries; pos += BATCH_SIZE) {
57+
const batch = await contentTypeService.getEntries({
58+
contentType: contentTypeUid,
59+
start: pos,
60+
limit: BATCH_SIZE,
61+
filters: {
62+
id: {
63+
$in: ids,
64+
},
65+
},
66+
})
67+
entries.push(...batch)
68+
}
69+
70+
meilisearch
71+
.updateEntriesInMeilisearch({
72+
contentType: contentTypeUid,
73+
entries: entries,
74+
})
75+
.catch(e => {
76+
strapi.log.error(
77+
`Meilisearch could not update the entries: ${e.message}`,
78+
)
79+
})
4980
},
5081
async afterUpdate(event) {
5182
const { result } = event

server/src/services/meilisearch/connector.js

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -131,40 +131,57 @@ export default ({ strapi, adapter, config }) => {
131131
if (!Array.isArray(entries)) entries = [entries]
132132

133133
const indexUids = config.getIndexNamesOfContentType({ contentType })
134-
await Promise.all(
134+
135+
const addDocuments = await sanitizeEntries({
136+
contentType,
137+
entries,
138+
config,
139+
adapter,
140+
})
141+
142+
// Check which documents are not in sanitized documents and need to be deleted
143+
const deleteDocuments = entries.filter(
144+
entry => !addDocuments.map(document => document.id).includes(entry.id),
145+
)
146+
// Collect delete tasks
147+
const deleteTasks = await Promise.all(
135148
indexUids.map(async indexUid => {
136149
const tasks = await Promise.all(
137-
entries.map(async entry => {
138-
const sanitized = await sanitizeEntries({
139-
entries: [entry],
140-
contentType,
141-
config,
142-
adapter,
143-
})
144-
145-
if (sanitized.length === 0) {
146-
const task = await client.index(indexUid).deleteDocument(
147-
adapter.addCollectionNamePrefixToId({
148-
contentType,
149-
entryId: entry.id,
150-
}),
151-
)
152-
153-
strapi.log.info(
154-
`A task to delete one document from the Meilisearch index "${indexUid}" has been enqueued (Task uid: ${task.taskUid}).`,
155-
)
156-
157-
return task
158-
} else {
159-
return client
160-
.index(indexUid)
161-
.updateDocuments(sanitized, { primaryKey: '_meilisearch_id' })
162-
}
150+
deleteDocuments.map(async document => {
151+
const task = await client.index(indexUid).deleteDocument(
152+
adapter.addCollectionNamePrefixToId({
153+
contentType,
154+
entryId: document.id,
155+
}),
156+
)
157+
158+
strapi.log.info(
159+
`A task to delete one document from the Meilisearch index "${indexUid}" has been enqueued (Task uid: ${task.taskUid}).`,
160+
)
161+
162+
return task
163163
}),
164164
)
165-
return tasks.flat()
165+
return tasks
166166
}),
167167
)
168+
169+
// Collect update tasks
170+
const updateTasks = await Promise.all(
171+
indexUids.map(async indexUid => {
172+
const task = client.index(indexUid).updateDocuments(addDocuments, {
173+
primaryKey: '_meilisearch_id',
174+
})
175+
176+
strapi.log.info(
177+
`A task to update ${addDocuments.length} documents to the Meilisearch index "${indexUid}" has been enqueued.`,
178+
)
179+
180+
return task
181+
}),
182+
)
183+
184+
return [...deleteTasks.flat(), ...updateTasks]
168185
},
169186

170187
/**

0 commit comments

Comments
 (0)