get_emails_by_ids.mjs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { scriptRunner } from './lib/ScriptRunner.mjs'
  2. import fs from 'node:fs'
  3. import readline from 'node:readline'
  4. import minimist from 'minimist'
  5. import {
  6. db,
  7. ObjectId,
  8. READ_PREFERENCE_SECONDARY,
  9. } from '../app/src/infrastructure/mongodb.js'
  10. /**
  11. * This script extracts user emails given a list of newline separated IDs
  12. *
  13. * Usage:
  14. * - Locally:
  15. * - docker compose exec web bash
  16. * - node scripts/get_emails_by_ids.mjs
  17. * - On the server:
  18. * - rake run:pod[staging,web]
  19. * - node scripts/get_emails_by_ids.mjs
  20. * - exit
  21. * - kubectl cp web-standalone-prod-XXXXX:/tmp/emails.txt ~/emails.txt
  22. */
  23. function usage() {
  24. console.log(
  25. `
  26. User email extraction, outputs to /tmp/emails.txt
  27. Usage:
  28. node scripts/get_emails_by_ids.js [--inputPath=<path>] [--outputPath=<path>] [--batchSize=<number>]
  29. Options:
  30. --help Show this screen
  31. --inputPath=<path> Input file path (default: ids.txt)
  32. --outputPath=<path> Output file path (default: /tmp/emails.txt)
  33. --batchSize=<number> Number of emails to be fetched in one query
  34. Description:
  35. This script extracts user emails given a list of newline separated IDs
  36. `
  37. )
  38. }
  39. function parseArgs() {
  40. const argv = minimist(process.argv.slice(2), {
  41. string: ['inputPath', 'outputPath'],
  42. bool: ['help'],
  43. number: ['batchSize'],
  44. default: {
  45. help: false,
  46. inputPath: 'ids.txt',
  47. outputPath: '/tmp/emails.txt',
  48. batchSize: 1000,
  49. },
  50. })
  51. if (argv.help) {
  52. usage()
  53. process.exit(0)
  54. }
  55. return argv
  56. }
  57. async function processBatch(idBatch, writeStream) {
  58. try {
  59. const cursor = db.users.find(
  60. {
  61. _id: { $in: idBatch },
  62. },
  63. {
  64. projection: {
  65. _id: 0,
  66. email: 1,
  67. },
  68. readPreference: READ_PREFERENCE_SECONDARY,
  69. }
  70. )
  71. for await (const doc of cursor) {
  72. if (doc.email) {
  73. writeStream.write(doc.email + '\n')
  74. }
  75. }
  76. } catch (err) {
  77. console.error('Error processing batch:', err)
  78. }
  79. }
  80. async function main(trackProgress) {
  81. const args = parseArgs()
  82. const readStream = fs.createReadStream(args.inputPath)
  83. const writeStream = fs.createWriteStream(args.outputPath)
  84. const rl = readline.createInterface({
  85. input: readStream,
  86. crlfDelay: Infinity,
  87. })
  88. let idBatch = []
  89. for await (const line of rl) {
  90. const id = line.trim()
  91. if (id) {
  92. try {
  93. idBatch.push(new ObjectId(id))
  94. } catch (e) {
  95. console.warn(`Skipping invalid ObjectId: ${id}`)
  96. }
  97. }
  98. if (idBatch.length >= args.batchSize) {
  99. await processBatch(idBatch, writeStream)
  100. idBatch = []
  101. }
  102. }
  103. if (idBatch.length > 0) {
  104. await processBatch(idBatch, writeStream)
  105. }
  106. writeStream.end()
  107. console.log(`✅ Success! Found emails written to ${args.outputPath}`)
  108. await trackProgress('Job finished')
  109. }
  110. try {
  111. await scriptRunner(main)
  112. process.exit(0)
  113. } catch (error) {
  114. console.error(error)
  115. process.exit(1)
  116. }