Apr 06, 2023 10:17 PM
Hi Airtable community,
My script is taking too much time to complete and going over 30 seconds limit. I would appreciate if you could review it and give me some tips. Here's the code:
let emailSendersTable = base.getTable("Emails to clients");
let listingsTable = base.getTable("Listings");
let emailSendersQuery = await emailSendersTable.selectRecordsAsync();
let updateBatchSize = 50;
let recordsToUpdate = [];
for (let emailSender of emailSendersQuery.records) {
if (!emailSender.getCellValue("Budget") || !emailSender.getCellValue("Size") || !emailSender.getCellValue("Area") || !emailSender.getCellValue("Layout") || !emailSender.getCellValue("Features")) {
continue;
}
// Access client requirements directly from the "Emails to clients" table
let budgetValue = emailSender.getCellValueAsString("Budget");
let budgetRange = budgetValue.match(/(?:~\s*)?¥(\d+,\d+)(?:\s*-\s*¥(\d+,\d+))?(?:\s*~)?/);
let minBudget = 0;
let maxBudget = Infinity;
if (budgetRange) {
if (budgetRange[1] && budgetRange[2]) {
minBudget = parseInt(budgetRange[1].replace(/[^0-9]/g, ""));
maxBudget = parseInt(budgetRange[2].replace(/[^0-9]/g, ""));
} else if (budgetRange[1] && !budgetRange[2]) {
if (budgetValue.startsWith("~")) {
maxBudget = parseInt(budgetRange[1].replace(/[^0-9]/g, ""));
} else if (budgetValue.endsWith("~")) {
minBudget = parseInt(budgetRange[1].replace(/[^0-9]/g, ""));
}
}
}
let size = emailSender.getCellValue("Size") ? emailSender.getCellValue("Size").name : '0m²';
let sizeValue = parseInt(size.match(/(\d+)m²/)[1], 10);
let areaValues = emailSender.getCellValue("Area") ? emailSender.getCellValue("Area").map(area => area.name) : [];
let areaValuesInListings = areaValues.map(area => area + "-ku");
let layoutValue = emailSender.getCellValue("Layout") ? emailSender.getCellValue("Layout").name : null;
let featuresValues = emailSender.getCellValue("Features") ? emailSender.getCellValue("Features").map(feature => feature.name) : [];
let isPetAllowedRequired = featuresValues.includes("Pet allowed");
let sixDaysAgo = new Date();
sixDaysAgo.setDate(sixDaysAgo.getDate() - 10);
let listingsQuery = await listingsTable.selectRecordsAsync({
view: "NEVER TOUCH THIS VIEW! 2",
fields: ["Monthly rent", "Size in m²", "Area (ku and district)", "Layout filter", "Features", "Created date", "Clients (Send emails)"]
});
let filteredListings = listingsQuery.records.filter(record => {
let monthlyRent = record.getCellValue("Monthly rent");
let sizeInSquareMeters = record.getCellValue("Size in m²");
let listingAreaValues = record.getCellValue("Area (ku and district)") ? record.getCellValue("Area (ku and district)").map(area => area.name) : [];
let listingLayoutValue = record.getCellValue("Layout filter") ? record.getCellValue("Layout filter").name : null;
let listingFeaturesValues = record.getCellValue("Features") ? record.getCellValue("Features").map(feature => feature.name) : [];
let isListingCreatedWithinLastSixDays = record.getCellValue("Created date") && new Date(record.getCellValue("Created date")) > sixDaysAgo;
let hasMatchingArea = areaValuesInListings.some(area => listingAreaValues.includes(area));
let hasMatchingLayout = layoutValue ? listingLayoutValue === layoutValue : true;
let hasPetAllowed = isPetAllowedRequired ? listingFeaturesValues.includes("Pet allowed") : true;
return (
monthlyRent > minBudget &&
monthlyRent < maxBudget &&
sizeInSquareMeters > sizeValue &&
hasMatchingArea &&
hasMatchingLayout &&
hasPetAllowed &&
isListingCreatedWithinLastSixDays
);
});
let latestSevenListings = [];
for (let i = 0; i < 7; i++) {
if (filteredListings.length === 0) {
break;
}
let latestListing = filteredListings.reduce((latest, record) => {
return record.getCellValue("Created date") > latest.getCellValue("Created date") ? record : latest;
});
latestSevenListings.push(latestListing);
filteredListings = filteredListings.filter(record => record.id !== latestListing.id);
}
for (let listing of listingsQuery.records) {
let currentSendEmails = listing.getCellValue("Clients (Send emails)") || [];
let filteredSendEmails = currentSendEmails.filter(sendEmail => sendEmail.id !== emailSender.id);
let updateData;
if (latestSevenListings.some(l => l.id === listing.id)) {
updateData = [...filteredSendEmails, { "id": emailSender.id }];
} else {
updateData = filteredSendEmails;
}
recordsToUpdate.push({ id: listing.id, fields: { "Clients (Send emails)": updateData } });
if (recordsToUpdate.length >= updateBatchSize) {
await listingsTable.updateRecordsAsync(recordsToUpdate);
recordsToUpdate = [];
}
}
}
if (recordsToUpdate.length > 0) {
await listingsTable.updateRecordsAsync(recordsToUpdate);
}
Apr 07, 2023 07:28 PM - edited Apr 07, 2023 07:41 PM
Hi,
on a quick view:
move that query outside the main loop (i think, it better to be placed at the beginning part, under the first query)
let listingsQuery = await .....
remove this piece:
if (recordsToUpdate.length >= updateBatchSize) { await listingsTable.updateRecordsAsync(recordsToUpdate); recordsToUpdate = []; }
Modify the code outside the main loop in the end of script to update by batches according to updateBatchSize limit.
In common, typical Airtable script is:
- get/define input data
- transform it into array of output data
- write output data to the table
for loop is a part of 'transform'. So, all await select... must be located before it, and await updateRec.. - after