user9150719
user9150719

Reputation:

Insert multiple none-existed documents to MongoDB using mongoose

I want to save multiple documents which are tags found on a post indicated by hash sign '#'. I have tags in an array. for example:

var tags = ['banana', 'apple', 'orange', 'pie']

I loop over them and convert the to document objects (to insert them into DB). the problem is that I want to insert a new document to collection only if it never inserted before. and if it inserted before I want to increment inserted document's userCounter property by one.

var tags = ['banana', 'apple', 'pie', 'orange'];

var docs = tags.map(function(tagTitle) {
  return {
    title: tagTitle,
    // useCounter: ??????????
  };
});

var Hashtag = require('./models/Hashtag');

/**
* it creates multiple documents even if a document already exists in the collection,
* I want to increment useCounter property of each document if they exist in the collection;
* not creating new ones.
* for example if a document with title ptoperty of 'banana' is inserted before, now increment
* document useCounter value by one. and if apple never inserted to collection, create a new document 
* with title of 'apple' and set '1' as its initial useCounter value
*/

Hashtag.create(docs)
.then(function(createdDocs) {

})
.then(null, function(err) {
  //handle errors
});

Upvotes: 0

Views: 140

Answers (2)

Jonas Wilms
Jonas Wilms

Reputation: 138567

async function findOrIncrease(title) {      
   let hashtag = await Hashtag.findOne({title});
   if(hashtag) {
     hashtag.userCounter++;
   } else {
     hashtag = new Hashtag({title, userCounter: 1});
   }
   await hashtag.save();
 }

Usable as:

  (async function() {
    for(const title of tags)
      await findOrIncrease(title);
  })()

Or if you want to execute all in parallel:

  tags.forEach(findOrIncrease);

You can speed that up by using mongodbs indexes.

Upvotes: 1

user9150719
user9150719

Reputation:

Thanks @Jonas W for response, Also there is another solution I found. I think it might be better (because it's clearer and faster in performance) to make some promises based on tags array and resolve tag document from these promises (or reject them with some reasons). Then use Promise.all() to make a fullfilled promise providing mongoose documents (created or updated based on some conditions). It's something like this:

// some other chains
.then((xxxx) => {
        const hashtagsTitles = require('../../src/hashtags/hashtagParser').hashtags(req.newPost.caption);
        const Hashtag = require('../../src/database/models/Hashtag');

        let findOrIncrease = title =>
            new Promise((resolve, reject) => {
                Hashtag.findOne({
                        title
                    })
                    .then((hashtag) => {
                        if (!hashtag) {
                            new Hashtag({
                                    title,
                                    usageCount: 0
                                }).save()
                                .then(hashtag => resolve(hashtag._id))
                                .catch(err => reject(err));
                        } else {
                            hashtag.usageCount++;
                            hashtag.save()
                                .then(hashtag => resolve(hashtag._id))
                                .catch(err => reject(err));
                        }
                    })
                    .catch(err => reject(err));
            });

        let promiseArr = hashtagsTitles.map((hashtagTitle) =>
            findOrIncrease(hashtagTitle)
        );

        return Promise.all(promiseArr)
            .then(results => results)
            .catch(err => {
                throw err
            });
    })
    .then((hashtags) => {
        hashtags.forEach((hashtag) => req.newPost.hashtags.push(hashtag));
    })
    //there might be some other chains

Also there is a good guide here: Mongoose - Create document if not exists, otherwise, update- return document in either case

Upvotes: 0

Related Questions