Return all docs when all docs are deleted in a col

2019-08-19 04:45发布

问题:

I am using the code below to remove all docs in a collection in Firestore. My question is, how to execute a function which will return the result (promise) of 'deleteCollection' among with an array with the deleted docs? I want to loop over all the docs. I surely can query all the data first, loop through it and than execute this function, but than the data gets read twice.

Another option would be creating a empty global variable in which the delete function adds the doc. But how safe is this? If I am deleting two huge collections, the array gets populated with two different docs. That wouldn't be correct as well.

I also tried to modify the 'new Promise' return, but that function parameters can only contain resolve or reject, no arrays.

So I want a function which calls the deleteCollection and than I want to loop over the deleted data. Is this possible when I want to read the data only once?

function deleteCollection(db, collectionRef, batchSize) {
  var query = collectionRef.limit(batchSize);

  return new Promise(function(resolve, reject) {
      deleteQueryBatch(db, query, batchSize, resolve, reject);
  });
}

function deleteQueryBatch(db, query, batchSize, resolve, reject) {
  query.get()
      .then((snapshot) => {
          if (snapshot.size == 0) {
              return 0;
          }
          var batch = db.batch();
          snapshot.docs.forEach(function(doc) {
              batch.delete(doc.ref);
          });
          return batch.commit().then(function() {
              return snapshot.size;
          });
      }).then(function(numDeleted) {
          if (numDeleted <= batchSize) {
              resolve();
              return;
          }
          process.nextTick(function() {
              deleteQueryBatch(db, query, batchSize, resolve, reject);
          });
      })
      .catch(reject);
}

Edited from Bergi's answer

const results = deleteCollection(db, db.collection("deleteme"), 1)
//Now I want to work with the results array, 
//but the function is still deleting documents

function deleteCollection(db, collectionRef, batchSize) {
  return deleteQueryBatch(db, collectionRef.limit(batchSize), batchSize, new Array());
}
async function deleteQueryBatch(db, query, batchSize, results) {
  const snapshot = await query.get();
  if (snapshot.size > 0) {
    let batch = db.batch();
    snapshot.docs.forEach(doc => {
      results.push(doc); <-- the TypeError
      batch.delete(doc.ref);
    });
    await batch.commit();
  }
  if (snapshot.size >= batchSize) {
    return deleteQueryBatch(db, query, batchSize);
  } else {
    return results;
  }
}

回答1:

First of all, avoid the Promise constructor antipattern:

function deleteCollection(db, collectionRef, batchSize) {
  var query = collectionRef.limit(batchSize);
  return deleteQueryBatch(db, query, batchSize);
}

function deleteQueryBatch(db, query, batchSize) {
  return query.get().then(snapshot => {
    if (snapshot.size == 0) return 0;
    var batch = db.batch();
    snapshot.docs.forEach(doc => { batch.delete(doc.ref); });
    return batch.commit().then(() => snapshot.size);
  }).then(function(numDeleted) {
    if (numDeleted >= batchSize) {
      // I don't think process.nextTick is really necessary
      return deleteQueryBatch(db, query, batchSize);
    }
  });
}

(You might also want to use async/await syntax which really simplifies the code and makes the algorithm better understandable:

async function deleteQueryBatch(db, query, batchSize) {
  const snapshot = await query.get();
  if (snapshot.size > 0) {
    let batch = db.batch();
    snapshot.docs.forEach(doc => { batch.delete(doc.ref); });
    await batch.commit();
  }
  if (snapshot.size >= batchSize) {
    // await new Promise(resolve => process.nextTick(resolve));
    return deleteQueryBatch(db, query, batchSize);
  }
}

creating a empty global variable in which the delete function adds the doc. But how safe is this? If I am deleting two huge collections, the array gets populated with two different docs. That wouldn't be correct as well.

No, don't do this. Just pass the array that you're populating with the results as an argument through the recursive calls and return it in the end:

function deleteCollection(db, collectionRef, batchSize) {
  return deleteQueryBatch(db, collectionRef.limit(batchSize), batchSize, []);
}
async function deleteQueryBatch(db, query, batchSize, results) {
  const snapshot = await query.get();
  if (snapshot.size > 0) {
    let batch = db.batch();
    snapshot.docs.forEach(doc => {
      results.push(doc);
      batch.delete(doc.ref);
    });
    await batch.commit();
  }
  if (snapshot.size >= batchSize) {
    return deleteQueryBatch(db, query, batchSize, results);
  } else {
    return results;
  }
}