N+1 Selects Solution
async function loadCommentsBadDontDoThis(ids: string[]): Promise<Comment[]> {
// Please don't.
}
async function loadComment(id: string): Promise<Comment> {
// Do this: one ID as an input, one row as an output.
} Traditional List Based Approach
import { map, uniq, keyBy } from "lodash";
async function loadUsers(ids: string): Promise<User[]> {
return sql.query("SELECT * FROM users WHERE id = ANY($1)", ids);
}
async function loadTopics(ids: string): Promise<Topic[]> {
return sql.query("SELECT * FROM topics WHERE id = ANY($1)", ids);
}
async function loadComments(ids: string[]): Promise<Comment[]> {
return sql.query("SELECT * FROM comments WHERE id = ANY($1)", ids);
}
// Loads data using just 3 SQL queries.
app.get("/comments", async (req, res) => {
const commentIDs = String(req.query.ids).split(",");
const comments = keyBy(await loadComments(commentIDs), "id");
const topicIDs = uniq(map(comments, (comment) => comment.topic_id));
const topics = keyBy(await loadTopics(topicIDs), "id");
const userIDs = uniq([
...map(comments, (comment) => comment.creator_id),
...map(topics, (topic) => topic.creator_id),
]);
const users = keyBy(await loadUsers(userIDs), "id");
res.json(
map(comments, (comment) => ({
comment,
commentCreator: users[comment.creator_id],
topic: topics[comment.topic_id],
topicCreator: users[topics[comment.topic_id].creator_id],
}))
);
});Ent Framework Approach: Automatic Batching
Helper Loading Methods
Batching vs. JOINs
Last updated