Ad

Sails' Model.findOrCreate To Return The Query Result In A For Loop

I receive from the frontend an array of item names itemsNames. I check if the items are already in the DB or new records should be created. I wrote the following for-loop. I want the loop to await until each query is executed, and return each item to a list to later on do some more processes. If I remove the .exec() method, the query returns the created or found item. How can I expose the created item and make the for-loop await until each registry is checked? I want to have the registry of created or already existing items. In the following example screenshot all items were new.

The for-loop:

  let dbList;

  for (var i = 0; i < itemsNames.length; i++) {
    let itemsNames= itemsNames[i];
    dbList+= await Item.findOrCreate({ name: itemsNames}, { name: itemsNames})
    .exec( (err, item, wasCreated) => {
      if (err) {
        throw 'Unavailable';
      }
      if(wasCreated) {
        sails.log('Created a new Item: ' + item.name);
      } else {
        sails.log('Found the existing Item: ' + item.name);
      }
      return item;
    });
    console.log('returned from findOrCreate Items: ' + i + ' ' + dbList);
  }

  console.log('Loop end');

  return dbList;

This is the Console output:

enter image description here

I am using Sails 1.2.4

Ad

Answer

Because findOrCreate does not accept arrays as parameters you, indeed, need to loop through each item. This is best done using async module. Also mentioned here.

To install async run npm i async --save in the project root. You can then const async = require('async'); or you can add it as a global module by modifing config/globals.js. In globals.js change async: false, to async: require('async'),.

Here is a simple async.map code that will do what you want:

async.map(
    items, // your items array [{"name": "foo"}, {"name": "bar"}, ...]
    (item, cb) => Item
        .findOrCreate(item, item)
        .exec((err, item, created) => cb(err, {item, created})),
    (err, data) => console.log(err || data) // data will contain array of data in {item: Item, created: true/false} format
);

If you're looking to make this functionality in to a controller action here is an example:

findOrCreateMultiple(req, res) {
    async.map(
        req.param('items'),
        (item, cb) => Item
            .findOrCreate(item, item)
            .exec((err, item, created) => cb(err, {item, created})),
        (err, data) => {
            if (err) return res.serverError(err);
            res.json(data);
        }
    );
}

You send:

{
    "items": [{"name": "foo"}, {"name": "bar"}, {"name": "biz"}]
}

foo and bar already existed, biz was created. Response:

[
  {
    "item": {
      "createdAt": 1589379855770,
      "updatedAt": 1589379855770,
      "id": 1,
      "name": "foo"
    },
    "created": false
  },
  {
    "item": {
      "createdAt": 1589379855774,
      "updatedAt": 1589379855774,
      "id": 2,
      "name": "bar"
    },
    "created": false
  },
  {
    "item": {
      "createdAt": 1589380146471,
      "updatedAt": 1589380146471,
      "id": 3,
      "name": "biz"
    },
    "created": true
  }
]
Ad
source: stackoverflow.com
Ad