@payloadcms/plugin-search
Version:
Search plugin for Payload
187 lines (186 loc) • 6.99 kB
JavaScript
import { addLocalesToRequestFromData, commitTransaction, getAccessResults, headersWithCors, initTransaction, killTransaction } from 'payload';
import { syncDocAsSearchIndex } from './syncDocAsSearchIndex.js';
export const generateReindexHandler = (pluginConfig)=>async (req)=>{
addLocalesToRequestFromData(req);
if (!req.json) {
return new Response('Req.json is undefined', {
status: 400
});
}
const { collections = [] } = await req.json();
const t = req.t;
const searchSlug = pluginConfig?.searchOverrides?.slug || 'search';
const searchCollections = pluginConfig?.collections || [];
const reindexLocales = pluginConfig?.locales?.length ? pluginConfig.locales : req.locale ? [
req.locale
] : [];
const validatePermissions = async ()=>{
const accessResults = await getAccessResults({
req
});
const searchAccessResults = accessResults.collections?.[searchSlug];
if (!searchAccessResults) {
return {
isValid: false,
message: t('error:notAllowedToPerformAction')
};
}
const permissions = [
searchAccessResults.delete,
searchAccessResults.update
];
// plugin doesn't allow create by default:
// if user provided, then add it to check
if (pluginConfig.searchOverrides?.access?.create) {
permissions.push(searchAccessResults.create);
}
// plugin allows reads by anyone by default:
// so if user provided, then add to check
if (pluginConfig.searchOverrides?.access?.read) {
permissions.push(searchAccessResults.read);
}
return permissions.every(Boolean) ? {
isValid: true
} : {
isValid: false,
message: t('error:notAllowedToPerformAction')
};
};
const validateCollections = ()=>{
const collectionsAreValid = collections.every((col)=>searchCollections.includes(col));
return collections.length && collectionsAreValid ? {
isValid: true
} : {
isValid: false,
message: t('error:invalidRequestArgs', {
args: `'collections'`
})
};
};
const headers = headersWithCors({
headers: new Headers(),
req
});
const { isValid: hasPermissions, message: permissionError } = await validatePermissions();
if (!hasPermissions) {
return Response.json({
message: permissionError
}, {
headers,
status: 401
});
}
const { isValid: validCollections, message: collectionError } = validateCollections();
if (!validCollections) {
return Response.json({
message: collectionError
}, {
headers,
status: 400
});
}
const payload = req.payload;
const batchSize = pluginConfig.reindexBatchSize;
const defaultLocalApiProps = {
overrideAccess: false,
req,
user: req.user
};
let aggregateErrors = 0;
let aggregateDocs = 0;
const countDocuments = async (collection)=>{
const { totalDocs } = await payload.count({
collection,
...defaultLocalApiProps,
req: undefined
});
return totalDocs;
};
const deleteIndexes = async (collection)=>{
await payload.delete({
collection: searchSlug,
depth: 0,
select: {
id: true
},
where: {
'doc.relationTo': {
equals: collection
}
},
...defaultLocalApiProps
});
};
const reindexCollection = async (collection)=>{
const totalDocs = await countDocuments(collection);
const totalBatches = Math.ceil(totalDocs / batchSize);
aggregateDocs += totalDocs;
for(let j = 0; j < reindexLocales.length; j++){
// create first index, then we update with other locales accordingly
const operation = j === 0 ? 'create' : 'update';
const localeToSync = reindexLocales[j];
for(let i = 0; i < totalBatches; i++){
const { docs } = await payload.find({
collection,
depth: 0,
limit: batchSize,
locale: localeToSync,
page: i + 1,
...defaultLocalApiProps
});
for (const doc of docs){
await syncDocAsSearchIndex({
collection,
data: doc,
doc,
locale: localeToSync,
onSyncError: ()=>operation === 'create' && aggregateErrors++,
operation,
pluginConfig,
req
});
}
}
}
};
await initTransaction(req);
try {
const promises = collections.map(async (collection)=>{
try {
await deleteIndexes(collection);
await reindexCollection(collection);
} catch (err) {
const message = t('error:unableToReindexCollection', {
collection
});
payload.logger.error({
err,
msg: message
});
await killTransaction(req);
throw new Error(message);
}
});
await Promise.all(promises);
} catch (err) {
return Response.json({
message: err.message
}, {
headers,
status: 500
});
}
const message = t('general:successfullyReindexed', {
collections: collections.join(', '),
count: aggregateDocs - aggregateErrors,
total: aggregateDocs
});
await commitTransaction(req);
return Response.json({
message
}, {
headers,
status: 200
});
};
//# sourceMappingURL=generateReindexHandler.js.map