Reputation: 5291
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
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
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