Await find/insert/update on server

Hi,

I am not 100% sure when to use await inside an async function on the server when using find, findOne, insert and update.

For example is this valid on the server (?):

const someFunc = async args => {
  const {color} = args
  const carIds = await carsCollection.find({color}).map(car => car._id);
  const busIds = await busCollection.find({color}).map(bus => bus._id);
  const registrationNumbers = await registrationCollection.find({vehicleId: {$in: [...carIds, ...busIds]}})
  return registrationNumbers;
}

or is find/findOne blocking on the server and I can safely do this:

const someFunc = args => {
  const {color} = args;
  const carIds = carsCollection.find({color}).map(car => car._id);
  const busIds = busCollection.find({color}).map(bus => bus._id);
  const registrationNumbers = registrationCollection.find({vehicleId: {$in: [...carIds, ...busIds]}})
  return registrationNumbers;
}

The meteor documentation states that insert and update are blocking on the server.
Does this mean that I do not need to use await for insert and update?

So on the server it makes no sense to write(?):

const carId = await carCollection.insert({a:1});

Also it makes no sense on the server to write (?):

const carId = await carCollection.update({},{$set{a:2}});

Similarly does this make sense on the server for remove(?):

export const remove = args => {
  const {systemId} = args;
  const promiseArray = [
    Systems.remove(systemId),
    Catalogs.remove({systemId}),
    Xrefs.remove({systemId}),
    Zones.remove({systemId}),
    Links.remove({systemId}),
    Nodes.remove({systemId}),
    UserSettings.remove({systemId})
  ];
  const prom = Promise.all(promiseArray).catch(err => {
    throw new Meteor.Error('systems remove err', err);
  });
  return prom;
};

As long as you do not use callbacks on the server for find, findOne etc., you do not need to use async / await. In fact, they are completely pointless, since Meteor’s Mongo api does not return Promises. So, your second example is perfectly fine.

2 Likes
const someFunc = args => {
  const {color} = args;
  const carIds = carsCollection.find({color}).map(car => car._id);
  const busIds = busCollection.find({color}).map(bus => bus._id);
  const registrationNumbers = registrationCollection.find({vehicleId: {$in: [...carIds, ...busIds]}})
  return registrationNumbers;
}

So I can be 100% certain that I will have a complete array of carIds at the start of line 4?

  const busIds = busCollection.find({color}).map(bus => bus._id);

Also if I simplify the remove function to:

export const remove = args => {
  const {systemId} = args;

    Systems.remove(systemId),
    Catalogs.remove({systemId}),
    Xrefs.remove({systemId}),
    Zones.remove({systemId}),
    Links.remove({systemId}),
    Nodes.remove({systemId}),
    UserSettings.remove({systemId})
};

Will Systems always be removed, before Catalogs?

“Will Systems always be removed, before Catalogs?”
In common cases, the answer is yes, but in case we’ll look deep in Mongo architecture it depends on the current load, locks on documents, etc. If you not in real high load right now, then you can rest and give Meteor to do all the necessary job to deal with async underneath. And even when you will have highload, questions should be addressed to how exactly you will configure Mongo

1 Like

Yes.

If insert is blocking on the server is it safe to use in a forEach loop, or is it better to use a for…of loop to wait for the insert operation to complete?

Below an example where a copy of all ‘nodes’ are made, their systemId changed and then the copies inserted into the collection:

      const systemId = '123';
      const newSystemId = '321';
      const newNodeIds = [];

      Nodes.find({systemId}).forEach(oldNode => {
        const newNode = JSON.parse(JSON.stringify(oldNode));
        delete newNode._id;
        Object.assign(newNode, {systemId: newSystemId});
        const newNodeId = Nodes.insert(newNode);
        newNodeIds.push(newNodeId);
      });

Will the newNodeIds array be populated in the same order as the forEach loop?
The alternative would be

      const systemId = '123';
      const newSystemId = '321';
      const newNodeIds = [];

      for (const oldNode of Nodes.find({systemId}).fetch()) {
        const newNode = JSON.parse(JSON.stringify(oldNode));
        delete newNode._id;
        Object.assign(newNode, {systemId: newSystemId});
        const newNodeId = Nodes.insert(newNode);
        newNodeIds.push(newNodeId);
      }

Your first example will be fine. I use forEach frequently in similar code.

Your code does look a little complex - the use of JSON.parse(JSON.stringify... seems unnecessary. How about:

Nodes.find({systemId}).forEach(oldNode => {
  const { _id, ...newNode } = oldNode;
  newNode.systemId = newSystemId;
  const newNodeId = Nodes.insert(newNode);
  newNodeIds.push(newNodeId);
});

Interesting. oldNode does not have a property newNode. Is this a special way of creating a copy of oldNode?
I use JSON.parse(JSON.stringify()) to create a deep clone, as I am prone to mutating mistakes in general.

Thanks for the forEach re-assurance. It looks like I can use insert with forEach safely

My bad - I missed the destructuring (...) on newNode. I’ve edited the code to include that :slightly_smiling_face:

Yes - that copies all but _id from oldNode to newNode.

Very effective!

You’ve reminded me that I can make my code much simpler by using destructuring to exclude/rename variables to suit the code below it.

Thanks!

1 Like