2020-04-22 12:00:52 -07:00
|
|
|
const DataLoader = require("dataloader");
|
2020-05-23 12:47:06 -07:00
|
|
|
const { normalizeRow } = require("./util");
|
2020-04-22 12:00:52 -07:00
|
|
|
|
2020-07-31 22:11:32 -07:00
|
|
|
const buildColorLoader = (db) => {
|
|
|
|
const colorLoader = new DataLoader(async (colorIds) => {
|
|
|
|
const qs = colorIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM colors WHERE id IN (${qs}) AND prank = 0`,
|
|
|
|
colorIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesByColorId = new Map(entities.map((e) => [e.id, e]));
|
|
|
|
|
|
|
|
return colorIds.map(
|
|
|
|
(colorId) =>
|
|
|
|
entitiesByColorId.get(String(colorId)) ||
|
|
|
|
new Error(`could not find color ${colorId}`)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
colorLoader.loadAll = async () => {
|
|
|
|
const [rows, _] = await db.execute(`SELECT * FROM colors WHERE prank = 0`);
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
for (const color of entities) {
|
|
|
|
colorLoader.prime(color.id, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entities;
|
|
|
|
};
|
|
|
|
|
|
|
|
return colorLoader;
|
2020-04-25 03:42:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const buildColorTranslationLoader = (db) =>
|
|
|
|
new DataLoader(async (colorIds) => {
|
|
|
|
const qs = colorIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM color_translations
|
|
|
|
WHERE color_id IN (${qs}) AND locale = "en"`,
|
|
|
|
colorIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesByColorId = new Map(entities.map((e) => [e.colorId, e]));
|
|
|
|
|
|
|
|
return colorIds.map(
|
|
|
|
(colorId) =>
|
2020-05-31 16:00:59 -07:00
|
|
|
entitiesByColorId.get(String(colorId)) ||
|
2020-07-31 22:11:32 -07:00
|
|
|
new Error(`could not find translation for color ${colorId}`)
|
2020-04-25 03:42:05 -07:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-08-31 18:25:42 -07:00
|
|
|
const buildSpeciesLoader = (db) => {
|
|
|
|
const speciesLoader = new DataLoader(async (speciesIds) => {
|
|
|
|
const qs = speciesIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM species WHERE id IN (${qs})`,
|
|
|
|
speciesIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesBySpeciesId = new Map(entities.map((e) => [e.id, e]));
|
|
|
|
|
|
|
|
return speciesIds.map(
|
|
|
|
(speciesId) =>
|
|
|
|
entitiesBySpeciesId.get(String(speciesId)) ||
|
|
|
|
new Error(`could not find color ${speciesId}`)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
speciesLoader.loadAll = async () => {
|
|
|
|
const [rows, _] = await db.execute(`SELECT * FROM species`);
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
for (const species of entities) {
|
|
|
|
speciesLoader.prime(species.id, species);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entities;
|
|
|
|
};
|
|
|
|
|
|
|
|
return speciesLoader;
|
2020-04-25 03:42:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const buildSpeciesTranslationLoader = (db) =>
|
|
|
|
new DataLoader(async (speciesIds) => {
|
|
|
|
const qs = speciesIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM species_translations
|
|
|
|
WHERE species_id IN (${qs}) AND locale = "en"`,
|
|
|
|
speciesIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesBySpeciesId = new Map(entities.map((e) => [e.speciesId, e]));
|
|
|
|
|
|
|
|
return speciesIds.map(
|
|
|
|
(speciesId) =>
|
2020-05-31 16:00:59 -07:00
|
|
|
entitiesBySpeciesId.get(String(speciesId)) ||
|
2020-04-25 03:42:05 -07:00
|
|
|
new Error(`could not find translation for species ${speciesId}`)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const loadAllPetTypes = (db) => async () => {
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT species_id, color_id FROM pet_types`
|
|
|
|
);
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
return entities;
|
|
|
|
};
|
|
|
|
|
2020-07-02 14:33:47 -07:00
|
|
|
const buildItemLoader = (db) =>
|
2020-04-23 14:23:46 -07:00
|
|
|
new DataLoader(async (ids) => {
|
|
|
|
const qs = ids.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM items WHERE id IN (${qs})`,
|
|
|
|
ids
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesById = new Map(entities.map((e) => [e.id, e]));
|
|
|
|
|
|
|
|
return ids.map(
|
|
|
|
(id) =>
|
2020-07-31 22:11:32 -07:00
|
|
|
entitiesById.get(String(id)) ||
|
|
|
|
new Error(`could not find item with ID: ${id}`)
|
2020-04-23 14:23:46 -07:00
|
|
|
);
|
|
|
|
});
|
2020-04-22 11:51:36 -07:00
|
|
|
|
2020-04-22 12:00:52 -07:00
|
|
|
const buildItemTranslationLoader = (db) =>
|
|
|
|
new DataLoader(async (itemIds) => {
|
|
|
|
const qs = itemIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM item_translations WHERE item_id IN (${qs}) AND locale = "en"`,
|
|
|
|
itemIds
|
|
|
|
);
|
2020-04-22 11:51:36 -07:00
|
|
|
|
2020-04-23 01:08:00 -07:00
|
|
|
const entities = rows.map(normalizeRow);
|
2020-04-22 14:55:12 -07:00
|
|
|
const entitiesByItemId = new Map(entities.map((e) => [e.itemId, e]));
|
2020-04-22 12:00:52 -07:00
|
|
|
|
|
|
|
return itemIds.map(
|
|
|
|
(itemId) =>
|
2020-07-02 14:33:47 -07:00
|
|
|
entitiesByItemId.get(String(itemId)) ||
|
2020-04-22 12:00:52 -07:00
|
|
|
new Error(`could not find translation for item ${itemId}`)
|
|
|
|
);
|
|
|
|
});
|
2020-04-22 11:51:36 -07:00
|
|
|
|
2020-08-17 01:41:38 -07:00
|
|
|
const buildItemSearchLoader = (db, loaders) =>
|
2020-04-24 21:17:03 -07:00
|
|
|
new DataLoader(async (queries) => {
|
|
|
|
// This isn't actually optimized as a batch query, we're just using a
|
|
|
|
// DataLoader API consistency with our other loaders!
|
|
|
|
const queryPromises = queries.map(async (query) => {
|
2020-09-01 17:35:41 -07:00
|
|
|
// Split the query into words, and search for each word as a substring
|
|
|
|
// of the name.
|
|
|
|
const words = query.split(/\s+/);
|
|
|
|
const wordMatchersForMysql = words.map(
|
|
|
|
(word) => "%" + word.replace(/_%/g, "\\$0") + "%"
|
|
|
|
);
|
|
|
|
const matcherPlaceholders = words
|
|
|
|
.map((_) => "t.name LIKE ?")
|
|
|
|
.join(" AND ");
|
2020-04-24 21:17:03 -07:00
|
|
|
const [rows, _] = await db.execute(
|
2020-04-25 00:43:01 -07:00
|
|
|
`SELECT items.*, t.name FROM items
|
2020-04-24 21:17:03 -07:00
|
|
|
INNER JOIN item_translations t ON t.item_id = items.id
|
2020-09-01 17:35:41 -07:00
|
|
|
WHERE ${matcherPlaceholders} AND t.locale="en"
|
2020-04-24 21:17:03 -07:00
|
|
|
ORDER BY t.name
|
|
|
|
LIMIT 30`,
|
2020-09-01 17:35:41 -07:00
|
|
|
[...wordMatchersForMysql]
|
2020-04-24 21:17:03 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
2020-08-17 01:41:38 -07:00
|
|
|
for (const item of entities) {
|
|
|
|
loaders.itemLoader.prime(item.id, item);
|
|
|
|
}
|
|
|
|
|
2020-04-24 21:17:03 -07:00
|
|
|
return entities;
|
|
|
|
});
|
|
|
|
|
|
|
|
const responses = await Promise.all(queryPromises);
|
|
|
|
|
|
|
|
return responses;
|
|
|
|
});
|
|
|
|
|
2020-08-17 01:41:38 -07:00
|
|
|
const buildItemSearchToFitLoader = (db, loaders) =>
|
2020-04-25 00:43:01 -07:00
|
|
|
new DataLoader(async (queryAndBodyIdPairs) => {
|
|
|
|
// This isn't actually optimized as a batch query, we're just using a
|
|
|
|
// DataLoader API consistency with our other loaders!
|
2020-04-25 01:55:48 -07:00
|
|
|
const queryPromises = queryAndBodyIdPairs.map(
|
2020-09-01 20:06:54 -07:00
|
|
|
async ({ query, bodyId, zoneIds = [], offset, limit }) => {
|
2020-04-25 01:55:48 -07:00
|
|
|
const actualOffset = offset || 0;
|
|
|
|
const actualLimit = Math.min(limit || 30, 30);
|
|
|
|
|
2020-09-01 17:35:41 -07:00
|
|
|
const words = query.split(/\s+/);
|
|
|
|
const wordMatchersForMysql = words.map(
|
|
|
|
(word) => "%" + word.replace(/_%/g, "\\$0") + "%"
|
|
|
|
);
|
|
|
|
const matcherPlaceholders = words
|
|
|
|
.map((_) => "t.name LIKE ?")
|
|
|
|
.join(" AND ");
|
2020-09-01 20:06:54 -07:00
|
|
|
const zoneIdsPlaceholder =
|
|
|
|
zoneIds.length > 0
|
|
|
|
? `swf_assets.zone_id IN (${zoneIds.map((_) => "?").join(", ")})`
|
|
|
|
: "1";
|
2020-04-25 01:55:48 -07:00
|
|
|
const [rows, _] = await db.execute(
|
2020-04-25 20:16:24 -07:00
|
|
|
`SELECT DISTINCT items.*, t.name FROM items
|
2020-04-25 00:43:01 -07:00
|
|
|
INNER JOIN item_translations t ON t.item_id = items.id
|
|
|
|
INNER JOIN parents_swf_assets rel
|
|
|
|
ON rel.parent_type = "Item" AND rel.parent_id = items.id
|
|
|
|
INNER JOIN swf_assets ON rel.swf_asset_id = swf_assets.id
|
2020-09-01 17:35:41 -07:00
|
|
|
WHERE ${matcherPlaceholders} AND t.locale="en" AND
|
2020-09-01 20:06:54 -07:00
|
|
|
(swf_assets.body_id = ? OR swf_assets.body_id = 0) AND
|
|
|
|
${zoneIdsPlaceholder}
|
2020-04-25 00:43:01 -07:00
|
|
|
ORDER BY t.name
|
2020-04-25 01:55:48 -07:00
|
|
|
LIMIT ? OFFSET ?`,
|
2020-09-01 20:06:54 -07:00
|
|
|
[
|
|
|
|
...wordMatchersForMysql,
|
|
|
|
bodyId,
|
|
|
|
...zoneIds,
|
|
|
|
actualLimit,
|
|
|
|
actualOffset,
|
|
|
|
]
|
2020-04-25 01:55:48 -07:00
|
|
|
);
|
2020-04-25 00:43:01 -07:00
|
|
|
|
2020-04-25 01:55:48 -07:00
|
|
|
const entities = rows.map(normalizeRow);
|
2020-04-25 00:43:01 -07:00
|
|
|
|
2020-08-17 01:41:38 -07:00
|
|
|
for (const item of entities) {
|
|
|
|
loaders.itemLoader.prime(item.id, item);
|
|
|
|
}
|
|
|
|
|
2020-04-25 01:55:48 -07:00
|
|
|
return entities;
|
|
|
|
}
|
|
|
|
);
|
2020-04-25 00:43:01 -07:00
|
|
|
|
|
|
|
const responses = await Promise.all(queryPromises);
|
|
|
|
|
|
|
|
return responses;
|
|
|
|
});
|
|
|
|
|
2020-09-06 15:49:08 -07:00
|
|
|
let lastKnownUpdate = "1970-01-01"; // start it out very old!
|
|
|
|
let lastResult = [];
|
2020-09-06 02:50:04 -07:00
|
|
|
const buildItemsThatNeedModelsLoader = (db) =>
|
|
|
|
new DataLoader(async (keys) => {
|
|
|
|
// Essentially, I want to take easy advantage of DataLoader's caching, for
|
|
|
|
// this query that can only run one way ^_^` There might be a better way to
|
|
|
|
// do this!
|
|
|
|
if (keys.length !== 1 && keys[0] !== "all") {
|
|
|
|
throw new Error(`this loader can only be loaded with the key "all"`);
|
|
|
|
}
|
|
|
|
|
2020-09-06 15:49:08 -07:00
|
|
|
// Call the query as a procedure, defined in `setup-mysql.sql`. It will
|
|
|
|
// only run the query if modeling data has been changed since the timestamp
|
|
|
|
// we provide; otherwise, it skips the query and returns no rows, which is
|
|
|
|
// much faster! (The query takes a few seconds to run.)
|
|
|
|
const [results, _] = await db.query(
|
|
|
|
`
|
|
|
|
CALL GetItemsThatNeedModelsIfNotCached(?, @LastActualUpdate);
|
|
|
|
SELECT @LastActualUpdate;
|
|
|
|
`,
|
|
|
|
[lastKnownUpdate]
|
|
|
|
);
|
|
|
|
|
|
|
|
// The query will return 2 or 3 results.
|
|
|
|
// Result 1 (optional): The rows produced by the CALL, if it ran the query.
|
|
|
|
// Or, if it skipped the query, this is omitted.
|
|
|
|
// Result 2 (required): The MySQL summary of the effects of the CALL.
|
|
|
|
// Result 3 (required): The 1-row table contianing @LastActualUpdate.
|
|
|
|
//
|
|
|
|
// So, check the number of results. If it's 2, then there was no change,
|
|
|
|
// and we should return our cached value. Or, if it's 3, then we should
|
|
|
|
// update our cache.
|
|
|
|
if (results.length === 2) {
|
|
|
|
return [lastResult];
|
|
|
|
}
|
|
|
|
|
|
|
|
const [rows, __, varRows] = results;
|
2020-09-06 02:50:04 -07:00
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
2020-09-06 15:49:08 -07:00
|
|
|
lastKnownUpdate = varRows[0]["@LastActualUpdate"];
|
|
|
|
lastResult = entities;
|
|
|
|
|
2020-09-06 02:50:04 -07:00
|
|
|
return [entities];
|
|
|
|
});
|
|
|
|
|
2020-04-23 01:08:00 -07:00
|
|
|
const buildPetTypeLoader = (db) =>
|
2020-06-24 19:05:07 -07:00
|
|
|
new DataLoader(async (petTypeIds) => {
|
|
|
|
const qs = petTypeIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM pet_types WHERE id IN (${qs})`,
|
|
|
|
petTypeIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return petTypeIds.map((petTypeId) =>
|
|
|
|
entities.find((e) => e.id === petTypeId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const buildPetTypeBySpeciesAndColorLoader = (db, loaders) =>
|
2020-08-17 01:33:34 -07:00
|
|
|
new DataLoader(
|
|
|
|
async (speciesAndColorPairs) => {
|
|
|
|
const conditions = [];
|
|
|
|
const values = [];
|
|
|
|
for (const { speciesId, colorId } of speciesAndColorPairs) {
|
|
|
|
conditions.push("(species_id = ? AND color_id = ?)");
|
|
|
|
values.push(speciesId, colorId);
|
|
|
|
}
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-08-17 01:33:34 -07:00
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM pet_types WHERE ${conditions.join(" OR ")}`,
|
|
|
|
values
|
|
|
|
);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-08-17 01:33:34 -07:00
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesBySpeciesAndColorPair = new Map(
|
|
|
|
entities.map((e) => [`${e.speciesId},${e.colorId}`, e])
|
|
|
|
);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-08-17 01:33:34 -07:00
|
|
|
for (const petType of entities) {
|
|
|
|
loaders.petTypeLoader.prime(petType.id, petType);
|
|
|
|
}
|
2020-06-24 19:05:07 -07:00
|
|
|
|
2020-08-17 01:33:34 -07:00
|
|
|
return speciesAndColorPairs.map(({ speciesId, colorId }) =>
|
|
|
|
entitiesBySpeciesAndColorPair.get(`${speciesId},${colorId}`)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
{ cacheKeyFn: ({ speciesId, colorId }) => `${speciesId},${colorId}` }
|
|
|
|
);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-08-01 15:30:26 -07:00
|
|
|
const buildSwfAssetLoader = (db) =>
|
|
|
|
new DataLoader(async (swfAssetIds) => {
|
|
|
|
const qs = swfAssetIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM swf_assets WHERE id IN (${qs})`,
|
|
|
|
swfAssetIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return swfAssetIds.map((swfAssetId) =>
|
|
|
|
entities.find((e) => e.id === swfAssetId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const buildItemSwfAssetLoader = (db, loaders) =>
|
2020-09-01 17:00:27 -07:00
|
|
|
new DataLoader(
|
|
|
|
async (itemAndBodyPairs) => {
|
|
|
|
const conditions = [];
|
|
|
|
const values = [];
|
|
|
|
for (const { itemId, bodyId } of itemAndBodyPairs) {
|
|
|
|
conditions.push(
|
|
|
|
"(rel.parent_id = ? AND (sa.body_id = ? OR sa.body_id = 0))"
|
|
|
|
);
|
|
|
|
values.push(itemId, bodyId);
|
|
|
|
}
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-09-01 17:00:27 -07:00
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT sa.*, rel.parent_id FROM swf_assets sa
|
2020-04-23 01:08:00 -07:00
|
|
|
INNER JOIN parents_swf_assets rel ON
|
|
|
|
rel.parent_type = "Item" AND
|
|
|
|
rel.swf_asset_id = sa.id
|
|
|
|
WHERE ${conditions.join(" OR ")}`,
|
2020-09-01 17:00:27 -07:00
|
|
|
values
|
|
|
|
);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-09-01 17:00:27 -07:00
|
|
|
const entities = rows.map(normalizeRow);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-09-01 17:00:27 -07:00
|
|
|
for (const swfAsset of entities) {
|
|
|
|
loaders.swfAssetLoader.prime(swfAsset.id, swfAsset);
|
|
|
|
}
|
2020-08-01 15:30:26 -07:00
|
|
|
|
2020-09-01 17:00:27 -07:00
|
|
|
return itemAndBodyPairs.map(({ itemId, bodyId }) =>
|
|
|
|
entities.filter(
|
|
|
|
(e) =>
|
|
|
|
e.parentId === itemId && (e.bodyId === bodyId || e.bodyId === "0")
|
|
|
|
)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
{ cacheKeyFn: ({ itemId, bodyId }) => `${itemId},${bodyId}` }
|
|
|
|
);
|
2020-04-23 01:08:00 -07:00
|
|
|
|
2020-08-01 15:30:26 -07:00
|
|
|
const buildPetSwfAssetLoader = (db, loaders) =>
|
2020-04-23 14:23:46 -07:00
|
|
|
new DataLoader(async (petStateIds) => {
|
|
|
|
const qs = petStateIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT sa.*, rel.parent_id FROM swf_assets sa
|
|
|
|
INNER JOIN parents_swf_assets rel ON
|
|
|
|
rel.parent_type = "PetState" AND
|
|
|
|
rel.swf_asset_id = sa.id
|
|
|
|
WHERE rel.parent_id IN (${qs})`,
|
|
|
|
petStateIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
2020-08-01 15:30:26 -07:00
|
|
|
for (const swfAsset of entities) {
|
|
|
|
loaders.swfAssetLoader.prime(swfAsset.id, swfAsset);
|
|
|
|
}
|
|
|
|
|
2020-04-23 14:23:46 -07:00
|
|
|
return petStateIds.map((petStateId) =>
|
|
|
|
entities.filter((e) => e.parentId === petStateId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-06-24 19:05:07 -07:00
|
|
|
const buildOutfitLoader = (db) =>
|
|
|
|
new DataLoader(async (outfitIds) => {
|
|
|
|
const qs = outfitIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM outfits WHERE id IN (${qs})`,
|
|
|
|
outfitIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return outfitIds.map((outfitId) => entities.find((e) => e.id === outfitId));
|
|
|
|
});
|
|
|
|
|
|
|
|
const buildItemOutfitRelationshipsLoader = (db) =>
|
|
|
|
new DataLoader(async (outfitIds) => {
|
|
|
|
const qs = outfitIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM item_outfit_relationships WHERE outfit_id IN (${qs})`,
|
|
|
|
outfitIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return outfitIds.map((outfitId) =>
|
|
|
|
entities.filter((e) => e.outfitId === outfitId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-04-23 14:23:46 -07:00
|
|
|
const buildPetStateLoader = (db) =>
|
2020-06-24 19:05:07 -07:00
|
|
|
new DataLoader(async (petStateIds) => {
|
|
|
|
const qs = petStateIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM pet_states WHERE id IN (${qs})`,
|
|
|
|
petStateIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return petStateIds.map((petStateId) =>
|
|
|
|
entities.find((e) => e.id === petStateId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
const buildPetStatesForPetTypeLoader = (db, loaders) =>
|
2020-04-23 14:23:46 -07:00
|
|
|
new DataLoader(async (petTypeIds) => {
|
|
|
|
const qs = petTypeIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
2020-05-02 20:48:32 -07:00
|
|
|
`SELECT * FROM pet_states
|
2020-08-28 22:58:39 -07:00
|
|
|
WHERE pet_type_id IN (${qs})
|
2020-08-31 00:32:17 -07:00
|
|
|
ORDER BY (mood_id IS NULL) ASC, mood_id ASC, female DESC,
|
2020-08-31 00:37:12 -07:00
|
|
|
unconverted DESC, glitched ASC, id DESC`,
|
2020-04-23 14:23:46 -07:00
|
|
|
petTypeIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
2020-06-24 19:05:07 -07:00
|
|
|
for (const petState of entities) {
|
|
|
|
loaders.petStateLoader.prime(petState.id, petState);
|
|
|
|
}
|
|
|
|
|
2020-04-23 14:23:46 -07:00
|
|
|
return petTypeIds.map((petTypeId) =>
|
|
|
|
entities.filter((e) => e.petTypeId === petTypeId)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-04 05:57:21 -07:00
|
|
|
const buildUserLoader = (db) =>
|
|
|
|
new DataLoader(async (ids) => {
|
|
|
|
const qs = ids.map((_) => "?").join(",");
|
2020-09-02 16:09:11 -07:00
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM users WHERE id IN (${qs})`,
|
|
|
|
ids
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesById = new Map(entities.map((e) => [e.id, e]));
|
|
|
|
|
|
|
|
return ids.map(
|
|
|
|
(id) =>
|
|
|
|
entitiesById.get(String(id)) ||
|
|
|
|
new Error(`could not find user with ID: ${id}`)
|
|
|
|
);
|
2020-09-04 05:57:21 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
const buildUserOwnedClosetHangersLoader = (db) =>
|
|
|
|
new DataLoader(async (userIds) => {
|
|
|
|
const qs = userIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT closet_hangers.*, item_translations.name as item_name FROM closet_hangers
|
|
|
|
INNER JOIN items ON items.id = closet_hangers.item_id
|
|
|
|
INNER JOIN item_translations ON
|
|
|
|
item_translations.item_id = items.id AND locale = "en"
|
|
|
|
WHERE user_id IN (${qs}) AND owned = 1
|
|
|
|
ORDER BY item_name`,
|
|
|
|
userIds
|
|
|
|
);
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
return userIds.map((userId) =>
|
|
|
|
entities.filter((e) => e.userId === String(userId))
|
|
|
|
);
|
|
|
|
});
|
2020-09-02 16:09:11 -07:00
|
|
|
|
2020-09-01 01:13:03 -07:00
|
|
|
const buildZoneLoader = (db) => {
|
|
|
|
const zoneLoader = new DataLoader(async (ids) => {
|
2020-08-17 18:49:37 -07:00
|
|
|
const qs = ids.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM zones WHERE id IN (${qs})`,
|
|
|
|
ids
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesById = new Map(entities.map((e) => [e.id, e]));
|
|
|
|
|
|
|
|
return ids.map(
|
|
|
|
(id) =>
|
|
|
|
entitiesById.get(String(id)) ||
|
|
|
|
new Error(`could not find zone with ID: ${id}`)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-01 01:13:03 -07:00
|
|
|
zoneLoader.loadAll = async () => {
|
|
|
|
const [rows, _] = await db.execute(`SELECT * FROM zones`);
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
|
|
|
|
for (const zone of entities) {
|
|
|
|
zoneLoader.prime(zone.id, zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
return entities;
|
|
|
|
};
|
|
|
|
|
|
|
|
return zoneLoader;
|
|
|
|
};
|
|
|
|
|
2020-08-17 18:49:37 -07:00
|
|
|
const buildZoneTranslationLoader = (db) =>
|
|
|
|
new DataLoader(async (zoneIds) => {
|
|
|
|
const qs = zoneIds.map((_) => "?").join(",");
|
|
|
|
const [rows, _] = await db.execute(
|
|
|
|
`SELECT * FROM zone_translations WHERE zone_id IN (${qs}) AND locale = "en"`,
|
|
|
|
zoneIds
|
|
|
|
);
|
|
|
|
|
|
|
|
const entities = rows.map(normalizeRow);
|
|
|
|
const entitiesByZoneId = new Map(entities.map((e) => [e.zoneId, e]));
|
|
|
|
|
|
|
|
return zoneIds.map(
|
|
|
|
(zoneId) =>
|
|
|
|
entitiesByZoneId.get(String(zoneId)) ||
|
|
|
|
new Error(`could not find translation for zone ${zoneId}`)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-04-23 14:23:46 -07:00
|
|
|
function buildLoaders(db) {
|
2020-06-24 19:05:07 -07:00
|
|
|
const loaders = {};
|
|
|
|
loaders.loadAllPetTypes = loadAllPetTypes(db);
|
|
|
|
|
2020-07-31 22:11:32 -07:00
|
|
|
loaders.colorLoader = buildColorLoader(db);
|
2020-06-24 19:05:07 -07:00
|
|
|
loaders.colorTranslationLoader = buildColorTranslationLoader(db);
|
2020-07-02 14:33:47 -07:00
|
|
|
loaders.itemLoader = buildItemLoader(db);
|
2020-06-24 19:05:07 -07:00
|
|
|
loaders.itemTranslationLoader = buildItemTranslationLoader(db);
|
2020-08-17 01:41:38 -07:00
|
|
|
loaders.itemSearchLoader = buildItemSearchLoader(db, loaders);
|
|
|
|
loaders.itemSearchToFitLoader = buildItemSearchToFitLoader(db, loaders);
|
2020-09-06 02:50:04 -07:00
|
|
|
loaders.itemsThatNeedModelsLoader = buildItemsThatNeedModelsLoader(db);
|
2020-06-24 19:05:07 -07:00
|
|
|
loaders.petTypeLoader = buildPetTypeLoader(db);
|
|
|
|
loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader(
|
|
|
|
db,
|
|
|
|
loaders
|
|
|
|
);
|
2020-08-01 15:30:26 -07:00
|
|
|
loaders.swfAssetLoader = buildSwfAssetLoader(db);
|
|
|
|
loaders.itemSwfAssetLoader = buildItemSwfAssetLoader(db, loaders);
|
|
|
|
loaders.petSwfAssetLoader = buildPetSwfAssetLoader(db, loaders);
|
2020-06-24 19:05:07 -07:00
|
|
|
loaders.outfitLoader = buildOutfitLoader(db);
|
|
|
|
loaders.itemOutfitRelationshipsLoader = buildItemOutfitRelationshipsLoader(
|
|
|
|
db
|
|
|
|
);
|
|
|
|
loaders.petStateLoader = buildPetStateLoader(db);
|
|
|
|
loaders.petStatesForPetTypeLoader = buildPetStatesForPetTypeLoader(
|
|
|
|
db,
|
|
|
|
loaders
|
|
|
|
);
|
2020-08-31 18:25:42 -07:00
|
|
|
loaders.speciesLoader = buildSpeciesLoader(db);
|
2020-06-24 19:05:07 -07:00
|
|
|
loaders.speciesTranslationLoader = buildSpeciesTranslationLoader(db);
|
2020-09-02 16:09:11 -07:00
|
|
|
loaders.userLoader = buildUserLoader(db);
|
2020-09-04 05:57:21 -07:00
|
|
|
loaders.userOwnedClosetHangersLoader = buildUserOwnedClosetHangersLoader(db);
|
2020-08-17 18:49:37 -07:00
|
|
|
loaders.zoneLoader = buildZoneLoader(db);
|
|
|
|
loaders.zoneTranslationLoader = buildZoneTranslationLoader(db);
|
2020-06-24 19:05:07 -07:00
|
|
|
|
|
|
|
return loaders;
|
2020-04-23 14:23:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = buildLoaders;
|