Sequelize exclude returns different value - node.js

So when I query without attribute: exclude... it returns me the correct user, but when I insert that in the query it returns a completly different user. Here is the code that I'm inserting.
const user = await Users.findOne(
{
attributes: { exclude: ["password", "admin", "createdAt", "updatedAt"] },
},
{ where: { id: req.user.id } }
);
res.json({ user });
Any ideas why is this happening?
Thanks in advance

You'r passing the where condition to the second paramter, but according to the docs: https://sequelize.org/docs/v6/core-concepts/model-querying-finders/#findone
Model.findOne only has one parameter which is the query options.
Move your where condition into the object for the first parameter.
const user = await Users.findOne({
attributes: {
exclude: ["password", "admin", "createdAt", "updatedAt"],
},
where: { id: req.user.id },
});
res.json({ user });

Related

Sequelize How to delete an occurence in an association table

I'm having two tables user and group, a manytomany relation between them, thereofre a third table group_has_user
given a user I'm trying to remove some groups,
I tried :
Model.User.findOne({
where: {
"id": POST.id
},
include: [Model.Project]
}).then(function (user) {
user.Project.destroy({
where:{
"id": ids
}
})
})
where id is the user id and ids, is a list of groups that I wan to
remove
but this code doesn't works, project is undefined, and also I don't think that what I want could be done this way, can anyone help ?
You can get the Projects you want to remove and then call the generated removeProject method on each project you got:
Model.User.findOne({
where: {
"id": POST.id
},
include: [Model.Project]
}).then(function (user) {
Model.Projcet.find({
where: {
id: {
[Op.in]: ids
}
}
}).then(projectsToRemove => {
projectsToRemove.forEach(p => {
await user.removeProject(p);
})
})
})
Source: Official documentation

Mongoose $push keeps adding two entries

Here are my user and product schemas:
const productSchema = new Schema({
//...
addedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "users"
}
});
const userSchema = new Schema({
//...
addedItems: [{
type: mongoose.Schema.ObjectId,
ref: "products"
}]
});
mongoose.model("products", productSchema);
mongoose.model("users", userSchema);
In my Node back end route I do this query:
User.findOneAndUpdate(
{ _id: req.body.id },
{ $push: { addedItems: newProduct._id } },
{ upsert: true, new: true },
function(err, doc) {
console.log(err, doc);
}
);
The console.log prints out this:
{
//...
addedItems: [ 5ab0223118599214f4dd7803 ]
}
Everything looks good. I go to actually look at the data using the front-end website for my mongo db; I'm using mlab.com, and this is what shows:
{
//...
"addedItems": [
{
"$oid": "5ab0223118599214f4dd7803"
},
{
"$oid": "5ab0223118599214f4dd7803"
}
]
}
Question: What the heck happened? Why does it add an additional entry into addedItems ?! Even though my console.log only showed one.
Note:
I tested to see if the backend route was being called more than once. It is not.
It seems to be a problem with $push because if I just have { addedItems: newProduct._id } then only one entry goes in, but it overwrites the entire array.
Edit:
Made a test project to produce the same results: https://github.com/philliprognerud/test-mcve-stackoverflow
Can anyone figure out what's going on?
The problem is caused by your mixed used of promises (via async/await) and callbacks with the findOneAndUpdate call which ends up executing the command twice.
To fix the problem:
const updatedUser = await User.findOneAndUpdate(
{ id: userID },
{ $push: { addedItems: newProduct.id } },
{ upsert: true, new: true }
);
console.log(updatedUser);
Future readers note that the use of await isn't shown here in the question, but is in the MCVE.
I am facing similar issue. Just landed to this page. I find that previous answer is not very descriptive. So posting this:
export const updateUserHandler = async (req, res) => {
const request = req.body;
await User.findOneAndUpdate( //<== remove await
{ _id: request.id },
{ $push: { addedItems: newProduct._id } },
{ upsert: true, new: true },
(findErr, findRes) => {
if (findErr) {
res.status(500).send({
message: 'Failed: to update user',
IsSuccess: false,
result: findErr
});
} else {
res.status(200).send({
message: 'Success: to update user',
IsSuccess: true,
result: findRes
});
}
}
);
}
Here there are two async calls one is the async and other is await. Because of this there are two entries in the document. Just remove await from await User.findOneAndUpdate. It will work perfectly.
Thanks!!
When you await Query you are using the promise-like, specifically, .then() and .catch(() of Query. Passing a callback as well will result in the behavior you're describing.
If you await Query and .then() of Query simultaneously, would make the query execute twice
use:
await Model.findOneAndUpdate(query, doc, options)
OR
Model.findOneAndUpdate(query, doc, options, callback)
This code $push keeps adding two entries:
const ali={ "_id": "5eaa39a18e7719140e3f4430" };
// return await customerModel.findOneAndUpdate(
// ali,
// {
// "$push": {
// "address": [objAdr],
// },
// },
// function (error: any, success: any) {
// if (error) {
// console.log(error);
// } else {
// console.log(success);
// }
// }
// );
My solutions working true:
return await customerModel
.findOneAndUpdate(
{ _id: ids },
{ $push: { "address": objAdr } }
)
.catch((err: string | undefined) => new Error(err));

Sequelize and querying on complex relations

I'm trying to understand how best to perform a query over multiple entities using Sequelize and Node.js.
I have defined a model "User" which has a belongsToMany relation with a model "Location". I then have a model "Asset" which also has a belongsToMany relation with "Location". When I have an instance of a User I would like to fetch all Assets that are associated with Locations that the User is associated with.
I tried the following which doesn't seem to work...
user.getLocations().then(function(userLocations) { return Asset.findAll({ where: { "Locations" : { $any : userLocations } }) })
Could anyone offer any suggestions?
Try this query:
User.findById(user_id, {
include: [{
model: Location,
required: true
}]
}).then(user => Asset.findAll({
where: {
user_id: user.id,
location_id: {
$in: user.locations.map(location => location.id)
}
}
})).then(assets => {
// The rest of your logic here...
});
This was the final result...
User.findById(user_id, {
include: [{
model: Location,
as: 'Locations', // Was needed since the original relation was defined with 'as'
required: true
}]
}).then(user => Asset.findAll({
include: [{
model: Location,
as: 'Locations',
where: {
id: {
$in: user.Locations.map(location => location.id)
}
}
}]
})).then(assets => {
// The rest of your logic here...
});

Use sequelize to query by separate junction tables between same two tables

SQLite3- I have a users table and a songNodes table. A user can favorite songs, and a user can 'fork' songs. I have two separate junction tables that represent these unique by purpose but otherwise identical relationships.
Sequelize- The only way I know how to perform a junction query in sequelize is as follows-
var myForks = function(userId, callback) {
User.findOne({
where: {
id: userId
}
})
.then(function(userObj) {
userObj.getSongNodes() //this is where I need to specify
.then(function(stuff) { //a junction table
callback(stuff);
})
})
};
I have tried to research this extensively and I can not find a way to specify which junction table I want to use with the automatically generated function 'getSongNodes.' Any help/guidance would be greatly appreciated!
I guess you have three tables which names are User, Song, SongNode. You have to define associations between these.
You can define associations like the following lines;
models.User.belongsToMany(models.Song, {
"constraints": false,
"foreignKey": "userId",
"through": {
model: models.SongNode,
unique: false
}
});
models.Song.belongsToMany(models.User, {
"constraints": false,
"foreignKey": "songId",
"through": {
model: models.SongNode,
unique: false
}
});
After; you can use relations between models like this;
var myForks = function(userId, callback) {
User
.findOne({
"where": { "id": userId }
})
.then(function(user) {
user
.getSongNodes()
.then(function(songNodes) {
callback(songNodes);
});
});
};
or you can like this too;
var myForks = function(userId, callback) {
User
.findOne({
"where": { "id": userId },
"include": [Song]
})
.then(function(user) {
callback(user.Songs);
});
};
I hope it works

Populating nested properties with Mongoose

In the API I'm trying to write with Node and Mongoose, the following query:
User.findOne({username: req.params.username}, "-_id -__v")
.populate({path: "songs", select: "-_id -__v"})
.populate({path: "following", select: "-_id username email"})
.exec(function(err, user) {
res.send(user);
});
returns the following JSON:
{
"email": "alice#alice.org",
"username": "alice",
"following": [
{
"username": "john",
"email": "john#john.org"
}
],
"songs": [
{
"slug": "dvorak-cello-concerto",
"artist": "5403e825cc9c45e9c55c4e7d",
"title": "Cello Concerto"
}
]
}
In the songs schema, I've setup artist as the following:
artist: {type: Schema.Types.ObjectId, ref: 'User'}
What is the best way to also populate the 'artist' property in every song that's populated in the initial query, rather than just its _id which references the user that song belongs to?
I figured out a way to do this, but please correct me if there's a better/cleaner way of doing it.
User.findOne({username: req.params.username}, "-_id -__v")
.populate({path: "songs", select: "-_id -__v"})
.exec(function(err, user) {
Songs.populate(user, {
path: 'songs.artist',
select: '-_id username',
model: 'User'
}, function (err, user) {
res.send(user);
});
});
Also, I'm only returning the 'username' value for the user which is all I need, but 'artist' still ends up being an object with that one property. If anybody knows how to return that value just as a string.
I'd love to know how.

Resources