Sequelize: Increment and updated_at in same method - node.js

Using Sequelize is there a way to increment a value and also update the updated_at column in the same call? I would assume there is a way to do this as using the increment method is updating the table so the updated_at column should be updated at the same time, without having to waste a second call only to update the updated_at column.
await account.increment(
["balance"],
{
by: updateAmount,
},
{ transaction: transaction }
);
await account.update(
{
updated_at: new Date()
},
{ transaction: transaction }
);
The docs dont seem to mention it

No. As the docs say increment is a convenience method. If you need to do more then incrementing then you have to use the update method.

Related

How to improve the performance of query in mongodb?

I have a collection in MongoDB with more than 5 million documents. Whenever I create a document inside the same collection I have to check if there exists any document with same title and if it exists then I don't have to add this to the database.
Example: here is my MongoDB document:
{
"_id":ObjectId("3a434sa3242424sdsdw"),
"title":"Lost in space",
"desc":"this is description"
}
Now whenever a new document is being created in the collection, I want to check if the same title already exists in any of the documents and if it does not exists, then only I want to add it to the database.
Currently, I am using findOne query and checking for the title, if it not available only then it is added to the database. I am facing the performance issue in this. It is taking too much time to do this process. Please suggest a better approach.
async function addToDB(data){
let result= await db.collection('testCol').findOne({title:data.title});
if(result==null){
await db.collection('testCol').insertOne(data);
}else{
console.log("already exists in db");
}
}
You can reduce the network round trip time which is currently 2X. Because you execute two queries. One for find then one for update. You can combine them into one query as below.
db.collection.update(
<query>,
{ $setOnInsert: { <field1>: <value1>, ... } },
{ upsert: true }
)
It will not update if already exists.
db.test.update(
{"key1":"1"},
{ $setOnInsert: { "key":"2"} },
{ upsert: true }
)
It looks for document with key1 is 1. If it finds, it skips. If not, it inserts using the data provided in the object of setOnInsert.

Sequelize: Can't do update on column UpdatedAt

I've an express apps that use Sequelize as ORM. Basically, columns createdAt and updatedAt auto-generated by Sequelize.
Every time I do update data in a row except column updatedAt, the column updatedAt will be updated based on current datetime.
But when I'm trying to update the column updatedAt, it didn't update the value of column updatedAt.
I've several way based on docs and another issue in so, like:
value.set('createdAt', new Date());
value.save().then(value1 => {
console.log('UpdatedAt has been updated');
next();
}
)
and
Token.update(
{
updatedAt: new Date
},
{
where: {
token_id: token
}
}
).then(value1 => {
console.log('Token UpdatedAt has been updated');
}
)
But non of both work.
Anyone know why? and how to fix that. Thankyou.
The short and simple way for this is :
Token.changed('updatedAt', true); // <--- This will update the updatedAt field

Does Mongoose upsert operation update/renew default schema values?

Mongoose Schema:
new Schema({
...
createDate: { type: Date, default: Date.now },
updateDate: { type: Date, default: Date.now }
});
Upsert operation:
const upsertDoc = {
...
}
Model.update({ key: 123 }, upsertDoc, { upsert: true })
when I upsert with update or findOneAndUpdate the default schema values createDate and updateDate are always renewed no matter document is inserted or updated. It's same when I use $set (in which of course I don't pass dates).
I don't seem to find anything to tell if it's an expected behavior. I expect dates to be added only on insert and not update, unless explicitly set.
If you are looking for "proof" of the expected behavior, then look no further than the source code itself. Particularly within the schema.js main definition:
updates.$setOnInsert = {};
updates.$setOnInsert[createdAt] = now;
}
return updates;
};
this.methods.initializeTimestamps = function() {
if (createdAt && !this.get(createdAt)) {
this.set(createdAt, new Date());
}
if (updatedAt && !this.get(updatedAt)) {
this.set(updatedAt, new Date());
}
return this;
};
this.pre('findOneAndUpdate', _setTimestampsOnUpdate);
this.pre('update', _setTimestampsOnUpdate);
this.pre('updateOne', _setTimestampsOnUpdate);
this.pre('updateMany', _setTimestampsOnUpdate);
}
function _setTimestampsOnUpdate(next) {
var overwrite = this.options.overwrite;
this.update({}, genUpdates(this.getUpdate(), overwrite), {
overwrite: overwrite
});
applyTimestampsToChildren(this);
next();
}
So there you can see all the 'pre' middleware handlers being registered for each of the "update" method variants and to the same functional code. These all essentially modify the $set operator in any "update" you issue to include the updatedAt field, or whatever name you mapped to that key in the schema options.
The actual statement sent with "upsert" actions uses $setOnInsert for the createdAt field or mapped option name ( see the top of the listing ). This action only applies when an "upsert" actually occurs, so documents that exist and are merely matches for any of the "update" methods are never actually touched by this value.
Those operators are part of how MongoDB works and not really to do with mongoose, but the code shown here shows how mongoose "adjusts" your "update" actions in order to include these additional operations.
For reference the whole main function in schema.js which works out what to apply currently begins at Line #798 for the genUpdates() function as called in the bottom part of the listing shown here yet the top part is the last few lines of that function where the keys of $setOnInsert get defined.
So in summary, YES every "update" action is intentional that the updatedAt mapped field has the current Date value assigned, and also that the "updates" are modified to include the $setOnInsert action which only applies when a new document is created as the result of an "upsert" action for the createdAt mapped field.
Well, I'd always recommend to use the provided and recommended way to manage createdAt and updatedAt by mongoose. Simply by passing timeStamp: true as schema options.
This is always a best practice and lets you not to be worried about such behaviors.
I use it and I never see a problem with timestamps using update or findOneAndUpdate.
Here is how you use it
new Schema({
... //Your schema
},{ timestamps: true})

Efficient multi-document upsert in mongo

I have a node.js app that updates a local mongo (3.0) data store from remote API, and I'm trying to make it as efficient as possible.
Every record in the collection has a unique remoteId property. After calling the API I get a set of records. Then I should update the local documents with new properties where ones with matching remoteId already exist, do inserts where they don't, and mark documents that exist locally but not in the remote data set as inactive.
My current solution is this (mongoose code, stripped out callbacks / promises for clarity, assume it runs synchronously):
timestamp = new Date
for item in remoteData
collection.findOneAndUpdate { remoteId: item.remoteId }, { updatedAt: timestamp, /* other properties */ }, { upsert: true }
collection.update { updatedAt: { $lt: timestamp} }, { active: false }, { multi: true }
Seems straightforward enough. But when dealing with tens of thousands of documents, it gets quite slow.
I looked at Bulk.upsert from mongo documentation, but that seems to work only when your document finding queries are static.
What could I do here?
Turns out I didn't fully grasp the mongo Bulk api - I had missed that it's basically an array of commands that gets sent to database when you call execute. In the end, this is what I had to do:
timestamp = new Date
bulkOp = collection.initializeUnorderedBulkOp()
for item in remoteData
bulkOp.find({ remoteId: item.remoteId }).upsert().updateOne { updatedAt: timestamp, /* other properties */ }
bulkOp.execute()
collection.update { updatedAt: { $lt: timestamp} }, { active: false }, { multi: true }

Mongodb inserting object values

I want to record user events to my mongodb collection. Is it possible to set up a simple query to only store the last recorded time stamp for an event for an arbitrary, dynamically changing set of events (so when a new event is received, it is inserted)?
I know the following doesn't work, but i wanted to give an idea of what I'm thinking:
uievents.update({_id:id},{
$set: {userName:user,
("events."+eventName): {
eventName: eventName,
serverTime: curTime,
browserTime: btime,
value: value
}},{$upsert:true});
Easiest way to do his is really simple:
query = {$set: {}}
query["$set"]["event."+eventName] = {
eventName: eventName,
serverTime: curTime,
browserTime: btime,
value: value
}
uievents.update({_id:id},query,{$upsert:true});

Resources