Mongoose findOneAndUpdate return old AND new values - node.js

I have update query like this
Balances.findOneAndupdate({_id: }, {$inc: {balance: 10}}, { new: true })
In post middleware i've got
schema.post('findOneAndUpdate', function (result) {
result === updated document
})
How can i get old and new(both of them) values (for logging) without multiple queries ?
Thanks.

If someone need, i solve it with transactions.
const session = await dbInstance.startSession()
await session.startTransaction()
query ...
query ...
await session.commitTransaction()
session.endSession()

Related

MongoDB updateOne returning null for upsertedId

I'm trying to get the upsertedId for the updated doucment, but it's returning null.
Here's the function for updating a doucment and trying to return result.upsertedId:
async function run() {
try {
await client.connect()
const collection = client.db("database").collection("collection")
const filter = { username: `${username}`}
const options = { upsert: true };
const updateDoc = {
$set: { date: new Date(), topartists: data.body.items, toptracks: toptracks }
};
await collection.updateOne(filter, updateDoc, options)
.then(result => {
console.log(`${result.matchedCount} document(s) matched the filter, updated ${result.modifiedCount} document(s)`)
console.log(result.upsertedId)
})
console.log('done1')
} finally {
await client.close();
}
}
and here's the console output of the function:
1 document(s) matched the filter, updated 1 document(s)
null
done1
I'm just starting with MongoDB, and the end goal here is to get
_id. It works there is nothing in my database (when it has to make a new document instead of updating one).
That will not be the case.
Your log clearly says - your query matched 1 document and it updated one document.
If your update results in insert, then response of your update operation will have upsert ids. - Means, matched count is 0.

Transaction Multi Collection MongoDB by Mongoose

I want to make a transaction of MongoDB by Mongoose.
Mongoose version is ^5.8.9
Here is code to explain more details.
Below is my question.
how to match session on each collection. because I looking forward to use a session for each collection. however i fail to find a solution.
If one collection of User or Score, Transaction should be rollback and change work never to apply collection.
Is it possible ?
// here is just sample. it is not able to run by below the code
const session = await User.startSession();
await session.startTransaction();
try {
const user = await User.create(object);
const score = await Score.updateOne({userId : user._id }, {score : { $inc :5 }} );
// if one of User or Score will be failed, transaction should be rollback.
await session.commitTransaction();
} catch(e) {
console.log(e)
await session.abortTransaction();
} finally {
await session.endSession();
}

How to update some data based on array value in Mongoose?

I'd like to update some data in Mongoose by using array value that I've find before.
Company.findById(id_company,function(err, company) {
if(err){
console.log(err);
return res.status(500).send({message: "Error, check the console. (Update Company)"});
}
const Students = company.students;
User.find({'_id':{"$in" : Students}},function(err, users) {
console.log(Students);
// WANTED QUERY : Update company = null from Users where _id = Students[];
});
});
Students returns users._id in array with object inside, and I use that to find users object, and then I want to set null a field inside users object, that field named as "company". How I can do that? Thank you.
From what you posted (I took the liberty to use Promises but you can roughly achieve the same thing with callbacks), you can do something like:
User.find({'_id':{"$in" : Students}})
.then( users =>{
return Promise.all( users.map( user => {
user.company = null;
return user.save()
}) );
})
.then( () => {
console.log("yay");
})
.catch( e => {
console.log("failed");
});
Basically, what I'm doing here is making sure .all() user models returned by the .find() call are saved properly, by checking the Promised value returned for .save()ing each of them.
If one of these fails for some reasons, Promise.all() return a rejection you can catch afterhand.
However, in this case, each item will be mapped to a query to your database which is not good. A better strategy would be to use Model.update(), which will achieve the same, in, intrinsically, less database queries.
User.update({
'_id': {"$in": Students}
}, {
'company': <Whatever you want>
})
.then()
use .update but make sure you pass option {multi: true} something like:
User.update = function (query, {company: null}, {multi: true}, function(err, result ) { ... });

Sequelize query inside then()

Trying to:
get a list of users
from the user details get the trips created by the users
and based on the output performing some actions
The following is the code I am trying to run.
models.user.findAll({})
.then(function (users) {
for (var i = 0; i < users.length; i++) {
var userName = users[i].name;
var userEmail = users[i].email;
models.trip.findOne({ attributes: [ [userName, 'name'], [userEmail, 'email'], 'id' ], where: { userId: users[i].id } })
.then(function (trip) {
if (trip == null) {
//Send Emails
}
});
}
})
.catch(function (error){ // enter code here
console.log(">>>>>",error);
});
Due to call back the second Sequelize does not run correctly.
Can you please advice on how to approach this issue? Is it using asyncawait/coyield?
You should debug this by using console.log for example. First you should try to print your first callback result, may be the database connection is not properly configured, may be the table is empty. there are many reasons. also it's more comfortably to use .forEach method instead of 'for' loop
array.forEach((item, i, arr)=>{
///....
})
Don't use the async method in for and any loop. It is better to use Promise.all or any async library.
The code will be like that.
var tasks = []
users.forEach(function (user) {
tasks.push(models.trip({/*some attrs*/}).then(function (trip){
if (trip) return Promise.resolve()
return sendEmailPromise(user)
}))
})
Promise.all(tasks).then(function() {
//done
}).catch(errorHandler)
If the models had associations, such that you could just include the other model and add where clause to it.
Sequelize Docs
User.hasMany(Trips, {foreignKey: 'userId'});
User.findAll({
include: {
model: Trips,
where: {userId: id}
}
});

find by _id with Mongoose

I am having trouble with a simple findById with mongoose.
Confirmed the item exists in the DB
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
With mongoose
Story.findById(topic.storyId, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
won't find it.
I also tried converting to a mongoId, still cannot be found (even though mongoose supposedly does this for you)
var mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid}).exec();
I'm actually trying to use this with typescript, hence the await.
I also tried the Story.findById(id) method, still cannot be found.
Is there some gotcha to just finding items by a plain _id field?
does the _id have to be in the Schema? (docs say no)
I can find by other values in the Schema, just _id can't be used...
update: I wrote a short test for this.
describe("StoryConvert", function() {
it("should read a list of topics", async function test() {
let topics = await Topic.find({});
for (let i = 0; i < topics.length; i ++) {
let topic = topics[i];
// topics.forEach( async function(topic) {
let storyId = topic.storyId;
let mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid});
// let story = await Story.findById(topic.storyId).exec();
// assert.equal(topic.storyId, story._id);
logger.info("storyId", storyId);
logger.info("mid", mid);
logger.info("story", story);
Story.findOne({_id: storyId}, function(err, res) {
if (err) {
logger.error(err);
} else {
logger.info("no error");
}
logger.info("res1", res);
});
Story.findOne({_id: mid}, function(err, res) {
logger.info("res2", res);
});
Story.findById(mid, function(err, res) {
logger.info("res3", res);
// assert.isNotNull(res);
});
}
});
});
It will return stuff like
Testing storyId 572f16439c0d3ffe0bc084a4
Testing mid 572f16439c0d3ffe0bc084a4
Testing story null
Testing no error
Testing res1 null
Testing res2 null
Testing res3 null
I noticed that topic.storyId is a string
not sure if that would cause any issues mapping to the other table.
I tried also adding some type defs
storyId: {
type: mongoose.Schema.Types.ObjectId,
required: false
}
Because this query finds the doc in the shell:
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
That means that the type of _id in the document is actually a string, not an ObjectId like Mongoose is expecting.
To find that doc using Mongoose, you'd have to define _id in the schema for Story as:
_id: { type: String }
If your Mongo schema is configured to use Object Id, you query in nodeJS using
models.Foo.findById(id)
where Foo is your model and id is your id.
here's a working example
router.get('/:id', function(req, res, next) {
var id = req.params.id
models.Foo.findById(id)
.lean().exec(function (err, results) {
if (err) return console.error(err)
try {
console.log(results)
} catch (error) {
console.log("errror getting results")
console.log(error)
}
})
})
In Mongo DB your query would be
{_id:ObjectId('5c09fb04ff03a672a26fb23a')}
One solution is to use mongoose.ObjectId()
const Model = require('./model')
const mongoose = require('mongoose')
Model.find({ id: mongoose.ObjectId(userID) })
It works, but it is weird because we are using id instead of _id
This is how we do it now:
const { mongoose } = require("mongoose");
YourModel.find({ _id: mongoose.Types.ObjectId("572f16439c0d3ffe0bc084a4") });
I got into this scenario too. This was how I solved it;
According to the mongoose documentation, you need to tell mongoose to
return the raw js objects, not mongoose documents by passing the lean option and setting it to true. e.g
Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
in your situation, it would be
Story.findById(topic.storyId, { lean: true }, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
If _id is the default mongodb key, in your model set the type of _id as this:
_id: mongoose.SchemaTypes.ObjectId
Then usind mongoose you can use a normal find:
YourModel.find({"_id": "5f9a86b77676e180c3089c3d"});
models.findById(id)
TRY THIS ONE .
REF LINK : https://www.geeksforgeeks.org/mongoose-findbyid-function/
Try this
Story.findOne({_id:"572b19509dac77951ab91a0b"}, function(err, story){
if (err){
console.log("errr",err);
//return done(err, null);
}else{
console.log(story);
}
});

Resources