refactor modeling code a bit to use syncToDb fn

This commit is contained in:
Emi Matchu 2020-09-18 08:04:46 -07:00
parent e817ba705b
commit 8793d8b570
2 changed files with 118 additions and 167 deletions

View file

@ -184,157 +184,149 @@ Array [
], ],
], ],
Array [ Array [
"INSERT INTO items "INSERT INTO items (category, created_at, id, price, rarity_index, thumbnail_url, type, updated_at, weight_lbs, zones_restrict) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
(
id, zones_restrict, thumbnail_url, category, type, rarity_index,
price, weight_lbs, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
",
Array [ Array [
"Gift",
2020-01-01T00:00:00.000Z,
"37229", "37229",
"0000000000000000000000000000000000000000000000", 0,
101,
"http://images.neopets.com/items/gif_magicball_table.gif", "http://images.neopets.com/items/gif_magicball_table.gif",
"Gift", "Gift",
"Gift",
101,
0,
1,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000000000000000",
"Special",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"37375", "37375",
"0000000000000000000000000000000000000000000000000000",
"http://images.neopets.com/items/bg_moonstars.gif",
"Special",
"Mystical Surroundings",
75,
209, 209,
1, 75,
"http://images.neopets.com/items/bg_moonstars.gif",
"Mystical Surroundings",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000000000000000000000",
"Clothes",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"38911", "38911",
"0000000000000000000000000000000000001100000000000000", 980,
92,
"http://images.neopets.com/items/clo_zafara_agent_hood.gif", "http://images.neopets.com/items/clo_zafara_agent_hood.gif",
"Clothes", "Clothes",
"Clothes",
92,
980,
1,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000001100000000000000",
"Clothes",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"38912", "38912",
"0000000000000000000101000000000000000000000000000000", 1476,
90,
"http://images.neopets.com/items/clo_zafara_agent_robe.gif", "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
"Clothes", "Clothes",
"Clothes",
90,
1476,
1,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000101000000000000000000000000000000",
"Clothes",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"38913", "38913",
"0000000000000000000000000000000000000000000000000000", 1177,
88,
"http://images.neopets.com/items/clo_zafara_agent_gloves.gif", "http://images.neopets.com/items/clo_zafara_agent_gloves.gif",
"Clothes", "Clothes",
"Clothes",
88,
1177,
1,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000000000000000000000",
"Toys",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"43014", "43014",
"0000000000000000000000000000000000000000000000",
"http://images.neopets.com/items/toy_stringlight_illleaf.gif",
"Toys",
"Toy",
80,
1033, 1033,
1, 80,
"http://images.neopets.com/items/toy_stringlight_illleaf.gif",
"Toy",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000000000000000",
"Clothes",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"43397", "43397",
"0000000000000000000000000000000000000000000000", 0,
500,
"http://images.neopets.com/items/mall_staff_jewelled.gif", "http://images.neopets.com/items/mall_staff_jewelled.gif",
"Clothes", "Clothes",
"Clothes",
500,
0,
1,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
1,
"0000000000000000000000000000000000000000000000",
"Clothes",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"48313", "48313",
"0000000000000000000000000000000000000000000000000000", 0,
101,
"http://images.neopets.com/items/clo_altcuplogo_brooch.gif", "http://images.neopets.com/items/clo_altcuplogo_brooch.gif",
"Clothes", "Clothes",
"Clothes", 2020-01-01T00:00:00.000Z,
101,
0,
1, 1,
2020-01-01T00:00:00.000Z, "0000000000000000000000000000000000000000000000000000",
2020-01-01T00:00:00.000Z,
], ],
], ],
Array [ Array [
"INSERT INTO item_translations "INSERT INTO item_translations (created_at, description, item_id, locale, name, rarity, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);",
(item_id, locale, name, description, rarity, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?, ?);",
Array [ Array [
2020-01-01T00:00:00.000Z,
"What does this ball actually do?",
"37229", "37229",
"en", "en",
"Magic Ball Table", "Magic Ball Table",
"What does this ball actually do?",
"Special", "Special",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"Dont forget to wish upon a star.",
"37375", "37375",
"en", "en",
"Moon and Stars Background", "Moon and Stars Background",
"Dont forget to wish upon a star.",
"Uncommon", "Uncommon",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"Hide your face and hair so no one can recognise you.",
"38911", "38911",
"en", "en",
"Zafara Agent Hood", "Zafara Agent Hood",
"Hide your face and hair so no one can recognise you.",
"Very Rare", "Very Rare",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"This robe is great for being stealthy.",
"38912", "38912",
"en", "en",
"Zafara Agent Robe", "Zafara Agent Robe",
"This robe is great for being stealthy.",
"Very Rare", "Very Rare",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"Dont leave any trace that you were there with these gloves.",
"38913", "38913",
"en", "en",
"Zafara Agent Gloves", "Zafara Agent Gloves",
"Dont leave any trace that you were there with these gloves.",
"Rare", "Rare",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"These leaves almost look magical with their gentle glow.",
"43014", "43014",
"en", "en",
"Green Leaf String Lights", "Green Leaf String Lights",
"These leaves almost look magical with their gentle glow.",
"Uncommon", "Uncommon",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"This jewelled staff shines with a magical light.",
"43397", "43397",
"en", "en",
"Jewelled Staff", "Jewelled Staff",
"This jewelled staff shines with a magical light.",
"Artifact", "Artifact",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
"Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.",
"48313", "48313",
"en", "en",
"Altador Cup Brooch", "Altador Cup Brooch",
"Even the announcers of the Altador Cup celebrate. This was given out by the Advent Calendar in Y11.",
"Special", "Special",
2020-01-01T00:00:00.000Z, 2020-01-01T00:00:00.000Z,
2020-01-01T00:00:00.000Z,
], ],
], ],
] ]

View file

@ -155,22 +155,12 @@ async function saveModelingData(
customPetData, customPetData,
{ db, itemLoader, itemTranslationLoader } { db, itemLoader, itemTranslationLoader }
) { ) {
const itemIds = Object.keys(customPetData.object_info_registry); const objectInfos = Object.values(customPetData.object_info_registry);
const [items, itemTranslations] = await Promise.all([
itemLoader.loadMany(itemIds),
itemTranslationLoader.loadMany(itemIds),
]);
const rowsToInsert = []; const incomingItems = objectInfos.map((objectInfo) => [
const rowsToUpdate = []; String(objectInfo.obj_info_id),
for (const index in itemIds) { {
const itemId = itemIds[index]; id: String(objectInfo.obj_info_id),
const item = items[index];
const itemTranslation = itemTranslations[index];
const objectInfo = customPetData.object_info_registry[itemId];
const objectInfoFields = {
id: itemId,
zonesRestrict: objectInfo.zones_restrict, zonesRestrict: objectInfo.zones_restrict,
thumbnailUrl: objectInfo.thumbnail_url, thumbnailUrl: objectInfo.thumbnail_url,
category: objectInfo.category, category: objectInfo.category,
@ -178,110 +168,79 @@ async function saveModelingData(
rarityIndex: objectInfo.rarity_index, rarityIndex: objectInfo.rarity_index,
price: objectInfo.price, price: objectInfo.price,
weightLbs: objectInfo.weight_lbs, weightLbs: objectInfo.weight_lbs,
},
]);
const incomingItemTranslations = objectInfos.map((objectInfo) => [
String(objectInfo.obj_info_id),
{
itemId: String(objectInfo.obj_info_id),
locale: "en",
name: objectInfo.name, name: objectInfo.name,
description: objectInfo.description, description: objectInfo.description,
rarity: objectInfo.rarity, rarity: objectInfo.rarity,
}; },
]);
if (item instanceof Error) { await Promise.all([
// New item, we'll just insert it! syncToDb("items", itemLoader, db, incomingItems),
syncToDb(
"item_translations",
itemTranslationLoader,
db,
incomingItemTranslations
),
]);
}
/**
* Syncs the given data to the database: for each incoming row, if there's no
* matching row in the loader, we insert a new row; or, if there's a matching
* row in the loader but its data is different, we update it; or, if there's
* no change, we do nothing.
*
* Automatically sets the `createdAt` and `updatedAt` timestamps for inserted
* or updated rows.
*
* Will perform one call to the loader, and at most one INSERT, and at most one
* UPDATE, regardless of how many rows we're syncing.
*/
async function syncToDb(tableName, loader, db, incomingRows) {
const loaderKeys = incomingRows.map(([key, _]) => key);
const currentRows = await loader.loadMany(loaderKeys);
const rowsToInsert = [];
for (const index in incomingRows) {
const [_, incomingRow] = incomingRows[index];
const currentRow = currentRows[index];
if (currentRow instanceof Error) {
rowsToInsert.push({ rowsToInsert.push({
...objectInfoFields, ...incomingRow,
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
}); });
continue;
} }
const itemFields = {
id: item.id,
zonesRestrict: item.zonesRestrict,
thumbnailUrl: item.thumbnailUrl,
category: item.category,
type: item.type,
rarityIndex: item.rarityIndex,
price: item.price,
weightLbs: item.weightLbs,
name: itemTranslation.name,
description: itemTranslation.description,
rarity: itemTranslation.rarity,
};
if (objectsShallowEqual(objectInfoFields, itemFields)) {
// Existing item, no change!
continue;
}
// Updated item, so we'll update it!
rowsToUpdate.push({
...objectInfoFields,
updatedAt: new Date(),
});
} }
if (rowsToInsert.length > 0) { if (rowsToInsert.length > 0) {
const itemQs = rowsToInsert // Get the column names from the first row, and convert them to
.map((_) => "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") // underscore-case instead of camel-case.
.join(", "); const rowKeys = Object.keys(rowsToInsert[0]).sort();
const itemTranslationQs = rowsToInsert const columnNames = rowKeys.map((key) =>
.map((_) => "(?, ?, ?, ?, ?, ?, ?)") key.replace(/[A-Z]/g, (m) => "_" + m[0].toLowerCase())
.join(", "); );
const itemValues = rowsToInsert.map((row) => [ const columnsStr = columnNames.join(", ");
row.id, const qs = columnNames.map((_) => "?").join(", ");
row.zonesRestrict, const rowQs = rowsToInsert.map((_) => "(" + qs + ")").join(", ");
row.thumbnailUrl, const rowFields = rowsToInsert.map((row) => rowKeys.map((key) => row[key]));
row.category, await db.execute(
row.type, `INSERT INTO ${tableName} (${columnsStr}) VALUES ${rowQs};`,
row.rarityIndex, rowFields.flat()
row.price, );
row.weightLbs,
row.createdAt,
row.updatedAt,
]);
const itemTranslationValues = rowsToInsert.map((row) => [
row.id,
"en",
row.name,
row.description,
row.rarity,
row.createdAt,
row.updatedAt,
]);
// NOTE: Hmm, I tried to use multiple statements to combine these, but I
// guess it doesn't work for prepared statements?
await Promise.all([
db.execute(
`INSERT INTO items
(
id, zones_restrict, thumbnail_url, category, type, rarity_index,
price, weight_lbs, created_at, updated_at
)
VALUES ${itemQs};
`,
itemValues.flat()
),
db.execute(
`INSERT INTO item_translations
(item_id, locale, name, description, rarity, created_at, updated_at)
VALUES ${itemTranslationQs};`,
itemTranslationValues.flat()
),
]);
} }
// TODO: Update the items that need updating! // TODO: Update rows that need updating
}
/** Given two objects with the same keys, return whether their values match. */
function objectsShallowEqual(a, b) {
for (const key in a) {
if (a[key] !== b[key]) {
return false;
}
}
return true;
} }
module.exports = { typeDefs, resolvers }; module.exports = { typeDefs, resolvers };