Skip to content

Commit

Permalink
Removed use of subqueries in email analytics queries
Browse files Browse the repository at this point in the history
closes https://linear.app/tryghost/issue/ENG-790/remove-use-of-sub-queries-in-email-analytics

Avoiding sub queries means we don't have a process tied up for longer than necessary and we can more easily see if one of the queries is non-performant.

- extracted the count queries into separate queries and used the retrieved values in the final update query
- removed a query by moving the email open rate calculation into JS as we've already fetched the necessary data before that point
  • Loading branch information
kevinansfield committed Apr 3, 2024
1 parent 83d4484 commit 4f5a7b4
Showing 1 changed file with 13 additions and 8 deletions.
21 changes: 13 additions & 8 deletions ghost/core/core/server/services/email-analytics/lib/queries.js
Expand Up @@ -41,10 +41,14 @@ module.exports = {
},

async aggregateEmailStats(emailId) {
const [deliveredCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND delivered_at IS NOT NULL', [emailId]);
const [openedCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND opened_at IS NOT NULL', [emailId]);
const [failedCount] = await db.knex('email_recipients').count('id as count').whereRaw('email_id = ? AND failed_at IS NOT NULL', [emailId]);

await db.knex('emails').update({
delivered_count: db.knex.raw(`(SELECT COUNT(id) FROM email_recipients WHERE email_id = ? AND delivered_at IS NOT NULL)`, [emailId]),
opened_count: db.knex.raw(`(SELECT COUNT(id) FROM email_recipients WHERE email_id = ? AND opened_at IS NOT NULL)`, [emailId]),
failed_count: db.knex.raw(`(SELECT COUNT(id) FROM email_recipients WHERE email_id = ? AND failed_at IS NOT NULL)`, [emailId])
delivered_count: deliveredCount.count,
opened_count: openedCount.count,
failed_count: failedCount.count
}).where('id', emailId);
},

Expand All @@ -56,15 +60,16 @@ module.exports = {
.where('emails.track_opens', true)
.first() || {};

const [emailCount] = await db.knex('email_recipients').count('id as count').whereRaw('member_id = ?', [memberId]);
const [emailOpenedCount] = await db.knex('email_recipients').count('id as count').whereRaw('member_id = ? AND opened_at IS NOT NULL', [memberId]);

const updateQuery = {
email_count: db.knex.raw('(SELECT COUNT(id) FROM email_recipients WHERE member_id = ?)', [memberId]),
email_opened_count: db.knex.raw('(SELECT COUNT(id) FROM email_recipients WHERE member_id = ? AND opened_at IS NOT NULL)', [memberId])
email_count: emailCount.count,
email_opened_count: emailOpenedCount.count
};

if (trackedEmailCount >= MIN_EMAIL_COUNT_FOR_OPEN_RATE) {
updateQuery.email_open_rate = db.knex.raw(`
ROUND(((SELECT COUNT(id) FROM email_recipients WHERE member_id = ? AND opened_at IS NOT NULL) * 1.0 / ? * 100), 0)
`, [memberId, trackedEmailCount]);
updateQuery.email_open_rate = Math.round(emailOpenedCount.count / trackedEmailCount * 100);
}

await db.knex('members')
Expand Down

0 comments on commit 4f5a7b4

Please sign in to comment.