batchedUpdate.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. const { ReadPreference, ObjectId } = require('mongodb')
  2. const { db, waitForDb } = require('../../app/src/infrastructure/mongodb')
  3. const BATCH_DESCENDING = process.env.BATCH_DESCENDING === 'true'
  4. const BATCH_SIZE = parseInt(process.env.BATCH_SIZE, 10) || 1000
  5. let BATCH_LAST_ID
  6. if (process.env.BATCH_LAST_ID) {
  7. BATCH_LAST_ID = ObjectId(process.env.BATCH_LAST_ID)
  8. } else if (process.env.BATCH_RANGE_START) {
  9. BATCH_LAST_ID = ObjectId(process.env.BATCH_RANGE_START)
  10. }
  11. let BATCH_RANGE_END
  12. if (process.env.BATCH_RANGE_END) {
  13. BATCH_RANGE_END = ObjectId(process.env.BATCH_RANGE_END)
  14. }
  15. async function getNextBatch(collection, query, maxId, projection, options) {
  16. const queryIdField = {}
  17. maxId = maxId || BATCH_LAST_ID
  18. if (maxId) {
  19. if (BATCH_DESCENDING) {
  20. queryIdField.$lt = maxId
  21. } else {
  22. queryIdField.$gt = maxId
  23. }
  24. }
  25. if (BATCH_RANGE_END) {
  26. if (BATCH_DESCENDING) {
  27. queryIdField.$gt = BATCH_RANGE_END
  28. } else {
  29. queryIdField.$lt = BATCH_RANGE_END
  30. }
  31. }
  32. if (queryIdField.$gt || queryIdField.$lt) {
  33. query._id = queryIdField
  34. }
  35. const entries = await collection
  36. .find(query, options)
  37. .project(projection)
  38. .sort({ _id: BATCH_DESCENDING ? -1 : 1 })
  39. .limit(BATCH_SIZE)
  40. .toArray()
  41. return entries
  42. }
  43. async function performUpdate(collection, nextBatch, update) {
  44. return collection.updateMany(
  45. { _id: { $in: nextBatch.map(entry => entry._id) } },
  46. update
  47. )
  48. }
  49. async function batchedUpdate(
  50. collectionName,
  51. query,
  52. update,
  53. projection,
  54. options
  55. ) {
  56. await waitForDb()
  57. const collection = db[collectionName]
  58. options = options || {}
  59. options.readPreference = ReadPreference.SECONDARY
  60. projection = projection || { _id: 1 }
  61. let nextBatch
  62. let updated = 0
  63. let maxId
  64. while (
  65. (nextBatch = await getNextBatch(
  66. collection,
  67. query,
  68. maxId,
  69. projection,
  70. options
  71. )).length
  72. ) {
  73. maxId = nextBatch[nextBatch.length - 1]._id
  74. updated += nextBatch.length
  75. console.log(
  76. `Running update on batch with ids ${JSON.stringify(
  77. nextBatch.map(entry => entry._id)
  78. )}`
  79. )
  80. if (typeof update === 'function') {
  81. await update(collection, nextBatch)
  82. } else {
  83. await performUpdate(collection, nextBatch, update)
  84. }
  85. console.error(`Completed batch ending ${maxId}`)
  86. }
  87. return updated
  88. }
  89. function batchedUpdateWithResultHandling(collection, query, update) {
  90. batchedUpdate(collection, query, update)
  91. .then(updated => {
  92. console.error({ updated })
  93. process.exit(0)
  94. })
  95. .catch(error => {
  96. console.error({ error })
  97. process.exit(1)
  98. })
  99. }
  100. module.exports = {
  101. getNextBatch,
  102. batchedUpdate,
  103. batchedUpdateWithResultHandling,
  104. }