get_emails_by_ids.mjs 2.9 KB

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