add itemByName and itemsByName GQL
This commit is contained in:
parent
d701f51c15
commit
f621391446
4 changed files with 118 additions and 0 deletions
|
@ -147,6 +147,37 @@ const buildItemTranslationLoader = (db) =>
|
|||
);
|
||||
});
|
||||
|
||||
const buildItemByNameLoader = (db, loaders) =>
|
||||
new DataLoader(async (names) => {
|
||||
const qs = names.map((_) => "?").join(", ");
|
||||
const [rows, _] = await db.execute(
|
||||
{
|
||||
// NOTE: In our MySQL schema, this is a case-insensitive exact search.
|
||||
sql: `SELECT items.*, item_translations.* FROM item_translations
|
||||
INNER JOIN items ON items.id = item_translations.item_id
|
||||
WHERE name IN (${qs}) AND locale = "en"`,
|
||||
nestTables: true,
|
||||
},
|
||||
names
|
||||
);
|
||||
|
||||
const entities = rows.map((row) => {
|
||||
const item = normalizeRow(row.items);
|
||||
const itemTranslation = normalizeRow(row.item_translations);
|
||||
loaders.itemLoader.prime(item.id, item);
|
||||
loaders.itemTranslationLoader.prime(item.id, itemTranslation);
|
||||
return { item, itemTranslation };
|
||||
});
|
||||
|
||||
return names.map((name) =>
|
||||
entities.find(
|
||||
(e) =>
|
||||
e.itemTranslation.name.trim().toLowerCase() ===
|
||||
name.trim().toLowerCase()
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
const buildItemSearchLoader = (db, loaders) =>
|
||||
new DataLoader(async (queries) => {
|
||||
// This isn't actually optimized as a batch query, we're just using a
|
||||
|
@ -759,6 +790,7 @@ function buildLoaders(db) {
|
|||
loaders.colorTranslationLoader = buildColorTranslationLoader(db);
|
||||
loaders.itemLoader = buildItemLoader(db);
|
||||
loaders.itemTranslationLoader = buildItemTranslationLoader(db);
|
||||
loaders.itemByNameLoader = buildItemByNameLoader(db, loaders);
|
||||
loaders.itemSearchLoader = buildItemSearchLoader(db, loaders);
|
||||
loaders.itemSearchToFitLoader = buildItemSearchToFitLoader(db, loaders);
|
||||
loaders.itemsThatNeedModelsLoader = buildItemsThatNeedModelsLoader(db);
|
||||
|
|
|
@ -104,6 +104,29 @@ describe("Item", () => {
|
|||
`);
|
||||
});
|
||||
|
||||
it("loads items by name", async () => {
|
||||
const res = await query({
|
||||
query: gql`
|
||||
query {
|
||||
itemByName(name: "Moon and Stars Background") {
|
||||
id
|
||||
name
|
||||
thumbnailUrl
|
||||
}
|
||||
itemsByName(names: ["Zafara Agent Robe", "pile of dung"]) {
|
||||
id
|
||||
name
|
||||
thumbnailUrl
|
||||
}
|
||||
}
|
||||
`,
|
||||
});
|
||||
|
||||
expect(res).toHaveNoErrors();
|
||||
expect(res.data).toMatchSnapshot("data");
|
||||
expect(getDbCalls()).toMatchSnapshot("db");
|
||||
});
|
||||
|
||||
it("loads appearance data", async () => {
|
||||
const res = await query({
|
||||
query: gql`
|
||||
|
|
|
@ -724,6 +724,51 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`Item loads items by name: data 1`] = `
|
||||
Object {
|
||||
"itemByName": Object {
|
||||
"id": "37375",
|
||||
"name": "Moon and Stars Background",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/bg_moonstars.gif",
|
||||
},
|
||||
"itemsByName": Array [
|
||||
Object {
|
||||
"id": "38912",
|
||||
"name": "Zafara Agent Robe",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/clo_zafara_agent_robe.gif",
|
||||
},
|
||||
Object {
|
||||
"id": "18579",
|
||||
"name": "Pile of Dung",
|
||||
"thumbnailUrl": "http://images.neopets.com/items/med_booby_5.gif",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Item loads items by name: db 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"nestTables": true,
|
||||
"sql": "SELECT items.*, item_translations.* FROM item_translations
|
||||
INNER JOIN items ON items.id = item_translations.item_id
|
||||
WHERE name IN (?, ?, ?) AND locale = \\"en\\"",
|
||||
"values": Array [
|
||||
"Moon and Stars Background",
|
||||
"Zafara Agent Robe",
|
||||
"pile of dung",
|
||||
],
|
||||
},
|
||||
Array [
|
||||
"Moon and Stars Background",
|
||||
"Zafara Agent Robe",
|
||||
"pile of dung",
|
||||
],
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Item loads items that need models 1`] = `
|
||||
Object {
|
||||
"babyItems": Array [
|
||||
|
|
|
@ -78,6 +78,16 @@ const typeDefs = gql`
|
|||
extend type Query {
|
||||
item(id: ID!): Item
|
||||
items(ids: [ID!]!): [Item!]!
|
||||
|
||||
# Find items by name. Exact match, except for some tweaks, like
|
||||
# case-insensitivity and trimming extra whitespace. Null if not found.
|
||||
#
|
||||
# NOTE: These aren't used in DTI at time of writing; they're a courtesy API
|
||||
# for the /r/Neopets Discord bot's outfit preview command!
|
||||
itemByName(name: String!): Item
|
||||
itemsByName(names: [String!]!): [Item]!
|
||||
|
||||
# Search for items with fuzzy matching.
|
||||
itemSearch(query: String!): ItemSearchResult!
|
||||
itemSearchToFit(
|
||||
query: String!
|
||||
|
@ -271,6 +281,14 @@ const resolvers = {
|
|||
items: (_, { ids }) => {
|
||||
return ids.map((id) => ({ id }));
|
||||
},
|
||||
itemByName: async (_, { name }, { itemByNameLoader }) => {
|
||||
const { item } = await itemByNameLoader.load(name);
|
||||
return item ? { id: item.id } : null;
|
||||
},
|
||||
itemsByName: async (_, { names }, { itemByNameLoader }) => {
|
||||
const items = await itemByNameLoader.loadMany(names);
|
||||
return items.map(({ item }) => (item ? { id: item.id } : null));
|
||||
},
|
||||
itemSearch: async (_, { query }, { itemSearchLoader }) => {
|
||||
const items = await itemSearchLoader.load(query.trim());
|
||||
return { query, items };
|
||||
|
|
Loading…
Reference in a new issue