Bazinga777
Bazinga777

Reputation: 5291

mongodb increment if exists otherwise insert

In the code below i want to insert a value if an itemId and timestamp doesn't exist otherwise if it already exists I want to increment it

Collection.update({
                itemId: itemId,
                timestamp: moment.unix(timestamp).set('seconds', 0).toDate(),
            },
            {
                '$inc': {
                    'data.ph1': data.ph1,
                    'data.ph2': data.ph2,
                    'data.ph3': data.ph3,
                    'data.total': data.total
                },
            },
            { upsert: true },
            (err,resp) => {
                if(err) {
                    reject(err);
                } else {  
                    resolve('minute value incremented' + gatewayId );
                }
            });

It ends up creating multiple documents instead. Thank you in advance.

Upvotes: 0

Views: 1413

Answers (2)

mikezter
mikezter

Reputation: 2463

Edit: You actually don't NEED the unique index for upserts to work, but it's still recommended when you are doing a lot of upserts and/or the collection is quite big.

Edit: As Neil Yunn pointed out, you already have set the seconds-part of the timestamp to zero, but forgot to do the same with the milliseconds-part.

You should provide a unique field with your upsert, so that MongoDB has a chance at finding a potentially exisiting record. Your fields itemId and timestamp should be marked as a unique index in the collection.

In a Mongo shell type

db.items.createIndex({"itemId": 1, "timestamp": 1}, {"unique": true})

replace items with the actual name of your collection.

Upvotes: 0

Neil Lunn
Neil Lunn

Reputation: 151190

You still have the milliseconds part ( or at least it would appear that way), so this will still insert a new document as long as that part is unique within the current minute.

Here's your code which incorrectly leaves the milliseconds in place:

var timestamp = ( Date.now() / 1000 );

console.log(new Date());
console.log(timestamp);
console.log(moment.unix(timestamp).set("seconds",0).toDate());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>

And when you remove the milliseconds as well it's correct:

var timestamp = ( Date.now() / 1000 );

console.log(new Date());
console.log(timestamp);
console.log(moment.unix(timestamp).set("seconds",0).set("milliseconds",0).toDate());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>

Or simply round the timestamp value before you pass it in:

var timestamp = ( Date.now() / 1000 )
console.log(new Date());
timestamp = timestamp - ( timestamp % 60 );
console.log(timestamp);
console.log(moment.unix(timestamp).toDate());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>

So when the date actually rounded to the "minute" as you were expecting, then it's okay.

Bottom line here is that an "upsert" only ever occurs when the conditions supplied ( as essentially the "key" of the document ) do not actually match any existing document, so something is clearly different in each request and the most likely candidate is the timestamp value in your code and it's subsequent conversion.

That said unless you are actually reading timestamp from an external or already defined source, then the best way to get the "current time" is to simply use the vanilla Date methods:

var timestamp = Date.now();
timestamp = timestamp - ( timestamp % ( 1000 * 60 ) );
console.log(new Date(timestamp));

That should do the job without needing to mess around with additional imported libraries.

So you really should be looking at why you have your current code and if you are reading a value already defined then actually "ensure" the milliseconds and seconds are removed in the rounding, as the modulo approach typically does best.

The only other cause outside of the timestamp itself is the actual itemId value being changed in each request. But if you're doing that then the action is expected and will of course create a new document where the "combination" does not exist because that is what it is meant to do.

Upvotes: 1

Related Questions