Kal
Kal

Reputation: 1774

mongodb query - Upsert a value and then get the incremented value

The following query updated the collection and increments the field count. I want to send the updated count as a response without making another api call.

The variable usage does not project the values.

handler.put(async (req, res) => {
  console.log("in backend api to update geo count")
  await req.db.collection('analytics').updateOne(
    {
    _id: getDateMonYear(new Date(), "mmddyyyy"),
    type: "Geo",
    },
    {
      $inc: { "count": 1},
    },
    {upsert: true},
    
  );
  res.status(200).send({usage});
});

Expected result sent back as json response:

{
    "_id": "Sept292022",
    "type": "Geo",
    "count": 162
}

Edit, I made it work by hitting the collection again like below but is there a way to just return in the update above?

  const usage = await req.db.collection('analytics')
  .findOne(
    {
      _id: getDateMonYear(new Date(), "mmddyyyy"),
    }
  );
  res.json({status:200, usage});
});

Upvotes: 0

Views: 68

Answers (2)

nimrod serok
nimrod serok

Reputation: 16033

Use {returnNewDocument: true} in the options, to get the updated document in the response:

await req.db.collection('analytics').updateOne(
    {
    _id: getDateMonYear(new Date(), "mmddyyyy"),
    type: "Geo",
    },
    {
      $inc: { "count": 1},
    },
    {upsert: true, returnNewDocument: true}
  );

Upvotes: 1

user20042973
user20042973

Reputation: 5065

Based on the edits it looks like things are moving in the right direction. I'm a bit confused by the comments, so perhaps this answer will help clarify things.

My understanding of your current server code is that it looks like the following now:

handler.put(async (req, res) => {
  console.log("in backend api to update geo count")
  await req.db.collection('analytics').updateOne(
    {
    _id: getDateMonYear(new Date(), "mmddyyyy"),
    type: "Geo",
    },
    {
      $inc: { "count": 1},
    },
    {upsert: true},
    
  );
  const usage = await req.db.collection('analytics')
  .findOne(
    {
      _id: getDateMonYear(new Date(), "mmddyyyy"),
    }
  );
  res.status(200).send({usage});
});

Was the problem originally that the usage object that the client was receiving was not the actual document that was updated?

If both of these things are correct, then using findAndModify() is the correct approach here. The problem with the original code is that the output of updateOne() is a summary of the operation as opposed to the document itself. On the other hand, the findAndModify() operation returns the document that was modified (either before or after). Also, this operation has been around for many years, introduced long before 4.4.

So I think your code should look something like the following to achieve your desired result:

handler.put(async (req, res) => {
  console.log("in backend api to update geo count")
  const usage = await req.db.collection('analytics').findAndModify({
    query: {
    _id: getDateMonYear(new Date(), "mmddyyyy"),
    type: "Geo",
    },
    update: {
      $inc: { "count": 1},
    },
    upsert: true,
  });
  res.status(200).send({usage});
});

If this doesn't solve your problem, it would also be helpful to confirm what driver and driver version you are using.

Upvotes: 1

Related Questions