From 9fc1482d9d31a192dc77cdb42f58218a2a7803e4 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Sun, 18 Aug 2019 12:00:59 -0700 Subject: [PATCH] discourse: save superfluous query of `likes` table (#1302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: When inserting a “like” action with `INSERT OR IGNORE` semantics, we also learn whether the action had any effect. We can use this bit to avoid a separate query checking whether the “like” already exists. As mentioned here: Test Plan: Running `yarn test` passes as is, and fails if you change `addLike` to always return either `changed: true` or `changed: false`. wchargin-branch: discourse-likes-one-query --- src/plugins/discourse/mirror.js | 54 +++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/plugins/discourse/mirror.js b/src/plugins/discourse/mirror.js index 36e6ce6..cf4b04b 100644 --- a/src/plugins/discourse/mirror.js +++ b/src/plugins/discourse/mirror.js @@ -391,39 +391,49 @@ export class Mirror implements DiscourseData { // who we either haven't scanned in the last week, or who have been active // since our last scan. This would likely improve the performance of this // section of the update significantly. - const insertLike = db.prepare( - dedent`\ - INSERT INTO likes ( - post_id, timestamp_ms, username - ) VALUES ( - :post_id, :timestamp_ms, :username - ) - ` - ); - const alreadySeenLike = db - .prepare( + + /** + * Add a like action to the database. The user of the like is + * assumed to already exist in the database; if this is not known to + * be the case, run `addUser(like.username)` first. + * + * Returns a status indicating whether the database changed as a + * result of this call. + */ + const addLike: (like: LikeAction) => {|+changed: boolean|} = (() => { + const query = db.prepare( dedent`\ - SELECT * FROM likes - WHERE post_id = :post_id AND username = :username + INSERT OR IGNORE INTO likes ( + post_id, + timestamp_ms, + username + ) VALUES ( + :post_id, + :timestamp_ms, + :username + ) ` - ) - .pluck(); + ); + return function addLike(like: LikeAction) { + const runResult = query.run({ + post_id: like.postId, + timestamp_ms: like.timestampMs, + username: like.username, + }); + return {changed: runResult.changes > 0}; + }; + })(); for (const user of this.users()) { let offset = 0; let upToDate = false; while (!upToDate) { const likeActions = await this._fetcher.likesByUser(user, offset); possiblePageSize = Math.max(likeActions.length, possiblePageSize); - for (const {timestampMs, postId, username} of likeActions) { - if (alreadySeenLike.get({post_id: postId, username: username})) { + for (const like of likeActions) { + if (!addLike(like).changed) { upToDate = true; break; } - insertLike.run({ - post_id: postId, - timestamp_ms: timestampMs, - username: username, - }); } if (likeActions.length === 0 || likeActions.length < possiblePageSize) { upToDate = true;