/ src / utils / keyword-counters.js
keyword-counters.js
  1  /**
  2   * Keyword Counter Updates
  3   *
  4   * Utilities for incrementing keyword counters on the happy path.
  5   * Each stage updates its respective counter when sites are processed.
  6   */
  7  
  8  import { run } from './db.js';
  9  import Logger from './logger.js';
 10  
 11  const logger = new Logger('KeywordCounters');
 12  
 13  /**
 14   * Increment assets_scraped_count for a keyword
 15   * Called after assets (screenshots) are captured
 16   *
 17   * @param {string} keyword
 18   * @param {string} countryCode
 19   * @returns {Promise<void>}
 20   */
 21  export async function incrementAssetsScraped(keyword, countryCode) {
 22    try {
 23      await run(
 24        `
 25        UPDATE keywords
 26        SET assets_scraped_count = assets_scraped_count + 1,
 27            updated_at = CURRENT_TIMESTAMP
 28        WHERE keyword = $1 AND country_code = $2
 29      `,
 30        [keyword, countryCode]
 31      );
 32    } catch (error) {
 33      logger.warn(`Failed to increment assets_scraped_count for "${keyword}": ${error.message}`);
 34    }
 35  }
 36  
 37  /**
 38   * Increment low_scoring_count for a keyword
 39   * Called after scoring when site scores < 82
 40   *
 41   * @param {string} keyword
 42   * @param {string} countryCode
 43   * @returns {Promise<void>}
 44   */
 45  export async function incrementLowScoring(keyword, countryCode) {
 46    try {
 47      await run(
 48        `
 49        UPDATE keywords
 50        SET low_scoring_count = low_scoring_count + 1,
 51            updated_at = CURRENT_TIMESTAMP
 52        WHERE keyword = $1 AND country_code = $2
 53      `,
 54        [keyword, countryCode]
 55      );
 56    } catch (error) {
 57      logger.warn(`Failed to increment low_scoring_count for "${keyword}": ${error.message}`);
 58    }
 59  }
 60  
 61  /**
 62   * Increment rescored_count for a keyword
 63   * Called after rescoring stage
 64   *
 65   * @param {string} keyword
 66   * @param {string} countryCode
 67   * @returns {Promise<void>}
 68   */
 69  export async function incrementRescored(keyword, countryCode) {
 70    try {
 71      await run(
 72        `
 73        UPDATE keywords
 74        SET rescored_count = rescored_count + 1,
 75            updated_at = CURRENT_TIMESTAMP
 76        WHERE keyword = $1 AND country_code = $2
 77      `,
 78        [keyword, countryCode]
 79      );
 80    } catch (error) {
 81      logger.warn(`Failed to increment rescored_count for "${keyword}": ${error.message}`);
 82    }
 83  }
 84  
 85  /**
 86   * Batch increment assets_scraped_count for multiple sites
 87   * More efficient than individual updates
 88   *
 89   * @param {Array<{keyword: string, country_code: string}>} sites
 90   * @returns {Promise<void>}
 91   */
 92  export async function batchIncrementAssetsScraped(sites) {
 93    if (!sites || sites.length === 0) return;
 94  
 95    for (const site of sites) {
 96      if (site.keyword && site.country_code) {
 97        try {
 98          await run(
 99            `
100            UPDATE keywords
101            SET assets_scraped_count = assets_scraped_count + 1,
102                updated_at = CURRENT_TIMESTAMP
103            WHERE keyword = $1 AND country_code = $2
104          `,
105            [site.keyword, site.country_code]
106          );
107        } catch (error) {
108          logger.warn(`Failed to increment for "${site.keyword}": ${error.message}`);
109        }
110      }
111    }
112  }
113  
114  /**
115   * Batch increment low_scoring_count for multiple sites
116   *
117   * @param {Array<{keyword: string, country_code: string}>} sites
118   * @returns {Promise<void>}
119   */
120  export async function batchIncrementLowScoring(sites) {
121    if (!sites || sites.length === 0) return;
122  
123    for (const site of sites) {
124      if (site.keyword && site.country_code) {
125        try {
126          await run(
127            `
128            UPDATE keywords
129            SET low_scoring_count = low_scoring_count + 1,
130                updated_at = CURRENT_TIMESTAMP
131            WHERE keyword = $1 AND country_code = $2
132          `,
133            [site.keyword, site.country_code]
134          );
135        } catch (error) {
136          logger.warn(`Failed to increment for "${site.keyword}": ${error.message}`);
137        }
138      }
139    }
140  }
141  
142  /**
143   * Batch increment rescored_count for multiple sites
144   *
145   * @param {Array<{keyword: string, country_code: string}>} sites
146   * @returns {Promise<void>}
147   */
148  export async function batchIncrementRescored(sites) {
149    if (!sites || sites.length === 0) return;
150  
151    for (const site of sites) {
152      if (site.keyword && site.country_code) {
153        try {
154          await run(
155            `
156            UPDATE keywords
157            SET rescored_count = rescored_count + 1,
158                updated_at = CURRENT_TIMESTAMP
159            WHERE keyword = $1 AND country_code = $2
160          `,
161            [site.keyword, site.country_code]
162          );
163        } catch (error) {
164          logger.warn(`Failed to increment for "${site.keyword}": ${error.message}`);
165        }
166      }
167    }
168  }