This is my first question here. I tried to save document in my collection, but it doesn't work. Response of function is exactly like I want, but it doesn't save in my db. In another controller (createRoom) foundUser.save() it works, but in this controller it doesn't. Thanks in advance!
I am using mongodb/mongooose and express.
const removeRoom = async (req,res,next) => {
const {roomId, userData} = req.body;
const { userId, token } = userData;
let foundUser;
let updatedRooms;
let indexOfNamespaces;
try {
foundUser = await User.findById(userId)
foundUser.namespaces.forEach((ns,i1)=>{
updatedRooms = ns.rooms.filter((room,i2) => {
if(room.id === roomId){
indexOfNamespaces = i1;
}
return room.id !== roomId
})
})
foundUser.namespaces[indexOfNamespaces].rooms = updatedRooms;
console.log(foundUser);
await foundUser.save();
} catch (err) {
console.log(err);
const error = new HttpError('Sth went wrong [removeRoom]', 500);
return next(error);
}
res.status(201).json({updatedNamespaces: foundUser.namespaces});
}
Mongoose does some optimizations where it will only actually save a field if it "changes". In this case you are modifyting an array, but the array is still the "same" array as in it still === (equals) the previous array. You need to use a new array to replace namespaces.
For example:
foundUser.namespaces = [
...foundUser.namespaces.slice(0, indexOfNamespaces),
{ ...foundUser.namespaces[indexOfNamespaces], rooms: updatedRooms },
...foundUser.namespaces.slice(indexOfNamespaces + 1)
]
Now, when you save Mongoose will see a "new" array that !== (does not equal) the previous array because it is a new instance and it will save it.
Related
user.service.ts
async findWithMail(email:string):Promise<any> {
return this.userRepository.findOne({email});
}
auth.service.ts
async signup(email:string,password:string,name?:string,surname?:string,phone:string){
if(email) {
const users = await this.userService.findWithMail(email);
if(users) {
throw new BadRequestException('email in use');
}
}
if(!password) return {error:"password must be!"};
const salt = randomBytes(8).toString('hex');
const hash = (await scrypt(password,salt,32)) as Buffer;
const result = salt + '.' +hash.toString('hex');
password = result;
const user = await
this.userService.create(email,password,name,surname,phone);
return user;
}
auth.service.spec.ts
let service:AuthService;
let fakeUsersService: Partial<UserService>;
describe('Auth Service',()=>{
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) => Promise.resolve({email,password} as User),
}
const module = await Test.createTestingModule({
providers:[AuthService,{
provide:UserService,
useValue:fakeUsersService
}],
}).compile();
service = module.get(AuthService);
});
it('can create an instance of auth service',async()=> {
expect(service).toBeDefined();
})
it('throws an error if user signs up with email that is in use', async () => {
await service.signup('asdf#asdf.com', 'asdf')
});
})
When ı try to run my test its give me error even this email is not in database its give error: BadRequestException: email in use. I couldnt figure out how to solve problem
You can use isExists method instead of findOne.
Also you can add extra check for your findWithMail method. Check the length of db request result. Like if (dbReqResult.length === 0) return false; else true
please put your attention on your mocked user service, especially on findWithEmail function, this part
beforeEach(async() => {
fakeUsersService = {
findWithMail:() => Promise.resolve([]),
create:(email:string,password:string) =>
Promise.resolve({email,password} as User),
}
...
try to resolve the promise to be null not [] (empty array) or change your if(users) on your auth.service to be if(users.length > 0), why? it because empty array means to be thruthy value so when run through this process on your auth.service
if(email) {
const users = await this.userService.findWithMail(email);
// on this part below
if(users) {
throw new BadRequestException('email in use');
}
}
the 'users' executed to be truthy value so it will invoke the error. I hope my explanation will help you, thank you
For example I want to update a mongoose document in a put request, I have to do this:
app.put('/update', async(req,res) => {
try{
const product = await Product.findById(req.body.id)
product.name = req.body.name
product.price = req.body.price
procut.discount = req.body.discount
// etc...
await product.save()
res.json(product)
}catch(e){
res.json({message: "Error updating the product"})
}
})
I'm asking if there is another faster and developer friendly way of updating products instead of typing each of the document properties and equal them to the req.body.[property]?
You can try the following for object merging
Object.assign(product, req.body)
note: i haven't tried with mongoose collection
You can use updateMany or findOneAndUpdate model methods, but it is more advisable to use .save()
https://mongoosejs.com/docs/api.html#model_Model.updateMany
https://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate
If you want to .save() to look cleaner, you can do like this:
async updateEntity(payload) {
const keysToUpdate = Object.keys(payload)
if (keysToUpdate.length === 0) {
throw new Error('Update payload must not be empty!')
}
const entity = await entityModel.findOne({ _id: redirect })
keysToUpdate.forEach((key) => {
entity[key] = payload[key]
})
await entity.save()}
Okay, I'm trying to get the value from a consult, all looks fine and the consult works but at the moment of pass it to a const, it does not get the object.
let selectid = await pool.query(
`SELECT ID_user FROM user WHERE User_email='${email_consulted}'`
);
const id = selectid.ID_user
What's could be wrong?
According to this example (found here: https://node-postgres.com/api/pool), pool.query returns a promise where result is some kind of an object with 'rows' property:
const { Pool } = require('pg')
const pool = new Pool()
pool.query('SELECT $1::text as name', ['brianc'], (err, result) => {
if (err) {
return console.error('Error executing query', err.stack)
}
console.log(result.rows[0].name) // brianc
})
Considering you are doing 'await', I believe you can refer to result entity like this:
const id = selectid.rows[0].ID_user
The query return an array so you have to assign the index. It might be:
const id = selectid[0].ID_user
I am trying to retrieve all the documents from a MongoDB cluster. I have followed code I've seen online, however I am facing a small problem.
const MongoClient = require('mongodb');
const uri = "mongodb+srv://<user>:<password>#cluster0-10soy.mongodb.net/test?retryWrites=true&w=majority";
var questionsArray = [];
MongoClient.connect(uri, function (err, client) {
const database = client.db("WhatSportWereYouMadeFor");
database.collection("Questions").find({}, (error, cursor) =>{
cursor.each(function(error, item){
if (item == null){
console.log(error);
}
questionsArray.push(item);
});
})
});
module.exports = { questionsArray };
I connect fine to the database, however I've set a breakpoint at the stop variable and that gets hit before any of the documents retrieved from the database get pushed to the questions array.
I've also tried wrapping the code inside an async function and then awaiting it before the stop variable, but still that breakpoint gets hit first and only after the documents get pushed to the array.
What I would do, this wrap the whole thing into a promise, and the export that.
const MyExport = () => {
return new Promise((resolve, reject) => {
var questionsArray = [];
MongoClient.connect(uri, function (err, client) {
const database = client.db("WhatSportWereYouMadeFor");
database.collection("Questions").find({}, (error, cursor) =>{
cursor.each(function(error, item){
if (item == null){
console.log(error);
}
questionsArray.push(item);
});
resolve(questionsArray)
})
});
})
}
module.exports.questionsArray = MyExport
But then when you import it, you need to run and await it
cosnt questionsArrayFunc = require("path/to/this/file").questionsArray
const questionsArray = await questionsArrayFunc()
I hope this is what you looking for. There might be some other way, but I think this works.
I am trying to create a simple back end blog api with user authentication and authorization. It is built with mongoose and express. In my userSchema, I have a property that is an array called "subscribedTo". Here, users can subscribe to different users to get their blogs. The subscribedTo array stores objectIDs of the users that wished to be subscribed too.
Here is my code:
router.get('/blogs', auth, async (req, res) => {
//auth middleware attaches user to the request obj
try {
let blogs = []
req.user.subscribedTo.forEach(async (id) => {
let ownersBlogs = await Blog.find({owner:id})
blogs = [...blogs, ...ownersBlogs]
console.log(blogs)//consoles desired output of users blogs
})
console.log(blogs)//runs first and returns []
res.send(blogs)
}catch(e){
res.status(500).send(e)
}
})
When I use postman for this route it returns [] which is understandable. I can't seem to res.send(blogs) even though the blogs variable returns correctly in the forEach function.
Is there a better way to do this?
You can use without loop like as bellow
Blog.find({ owner: { $in: req.user.subscribedTo } }, function (err, blogResult) {
if (err) {
response.send(err);
} else {
response.send(blogResult);
}
});
OR
send response after loop completed like as bellow
router.get('/blogs', auth, async (req, res) => {
//auth middleware attaches user to the request obj
try {
let blogs = []
let len = req.user.subscribedTo.length;
let i = 0;
if (len > 0) {
req.user.subscribedTo.forEach(async (id) => {
let ownersBlogs = await Blog.find({ owner: id })
blogs = [...blogs, ...ownersBlogs]
console.log(blogs)//consoles desired output of users blogs
i++;
if (i === len) {
//send response when loop reached at the end
res.send(blogs)
}
})
} else {
res.send(blogs);
}
} catch (e) {
res.status(500).send(e)
}
});
You can find all the documents without a foreach loop, use $in
Blog.find({owner:{$in:[array of ids]}});