How To Remove Particular Objects From A Nested Array In Mongoose? - node.js

I need a function to remove particular objects from a nested array ,please check the code as follow ,I have already tried a lot times ,but fail ..Could you please help me ?Thank you so much in advance!
UserSchema :
userName: {
type: String,
},
specialList: [
{
type: mongoose.Types.ObjectId,
ref: "Friend",
},
],
FriendSchema:
userName:{
type:string
}
Now ,I need a function to delete some of the friends in a user's specialList by their user's id,
For instance ,
//this is not working like I wish ...I have no idea what is going on ...
const needToRmoveList = ["123","456"];
await UserInfo.findOneAndUpdate(
{ _id: "345" },
{ $pull: { specialList: { id: { $in: [needToRmoveList] } } } },
{ new: true }
);

Related

Querying objects for an array of attributes in Mongoose

I am trying to fetch chats for an array of users.
Chats schemas are defined like this:
const ChatSchema = new Schema<IChatSchema>(
{
messages: [
{
type: Schema.Types.ObjectId,
ref: "MessageSchema",
},
],
participants: [
{
type:Schema.Types.ObjectId,
ref: "UserSchema",
}
]
},
{
timestamps: true,
}
);
I have two usersnames 'A' and 'B' and I want to query common chats of those two users. Any idea how to do it?
User schema
const UserSchema = new Schema<IUserSchema>(
{
username: {
type: String,
required: true,
unique:true,
},
},
{
timestamps: true,
}
);
I tried this approach but did not work.
let chat = await Chats.find({
participants: { $elemMatch: { username: usernames } },
})
I also tried this
let chat = await Chats.find({
"participants.username": { $all: usernames },
})
I can think of a few ways:
Option A: Use aggregation to lookup the participants, then match the usernames
Option B: Use find to retrieve the user records from Users, then query Chats for matching ObjectID values
Option C: modify the schema so the chats also contain the usernames, so you can query them directly

Push values into array with mongoose

I have a problem pushing values into an array with mongoose (yes I have read many topics about it and tried many ways to do it).
So I have this schema
const Postit = new Schema({
text: {
type: String,
required: true
},
status: {
type: String,
default: 'TODO'
},
modified: {
type: Date,
default: Date.now
},
user: {
type: ObjectId,
ref: 'User',
required: true
},
collaborators: [String]
})
And I'm trying to push a string in the collaborators property where the queries match.
So this is the method I use to update it
addCollaborator(uid, pid) {
return Postit.updateOne({
_id: pid,
user: uid
},
{ $push: { collaborators: 'pepe' } },
(err, raw) => {
//TO DO
})
}
But nothing happens. The query match because if I change $push for $set and put a new value to status property for example it updates.
The funny thing is that if I run it in mongodb client terminal it works.
db.postits.updateOne({
_id: ObjectId("5beb1492cf484233f8e21ac1"),
user: ObjectId("5beb1492cf484233f8e21abf")
},
{ $push: {collaborators: 'pepe' }
})
What i'm doing wrong?
Pick promises or callbacks but do not mix them together. Either you do:
addCollaborator(uid, pid) {
Postit.updateOne({
_id: mongoose.Types.ObjectId(pid),
user: uid
},
{ $push: { collaborators: 'pepe' } },
(err, raw) => {
// TO DO
})
}
Or you do:
addCollaborator(uid, pid) {
return Postit.updateOne({
_id: mongoose.Types.ObjectId(pid),
user: uid
},
{ $push: { collaborators: 'pepe' } }).exec()
.then(result => {
// TO DO
})
}
Also make sure your objectId is an actual mongoose.Types.ObjectId
Ok, the problem is I was using an old schema.
The code works perfectly. Enough code for today...

Why $pull does not work when using mongodb on node js [duplicate]

i'm trying to do a pretty simple operation, pull an item from an array with Mongoose on a Mongo database like so:
User.update({ _id: fromUserId }, { $pull: { linkedUsers: [idToDelete] } });
fromUserId & idToDelete are both Objects Ids.
The schema for Users goes like this:
var UserSchema = new Schema({
groups: [],
linkedUsers: [],
name: { type: String, required: true, index: { unique: true } }
});
linkedUsers is an array that only receives Ids of other users.
I've tried this as well:
User.findOne({ _id: fromUserId }, function(err, user) {
user.linkedUsers.pull(idToDelete);
user.save();
});
But with no luck.
The second option seem to almost work when i console the lenghts of the array at different positions but after calling save and checking, the length is still at 36:
User.findOne({ _id: fromUserId }, function(err, user) {
console.log(user.linkedUsers.length); // returns 36
user.linkedUsers.pull(idToDelete);
console.log(user.linkedUsers.length); // returns 35
user.save();
});
So it looks like i'm close but still, no luck. Both Ids are sent via the frontend side of the app.
I'm running those versions:
"mongodb": "^2.2.29",
"mongoose": "^5.0.7",
Thanks in advance.
You need to explicitly define the types in your schema definition i.e.
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
linkedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }]
and then use either
User.findOneAndUpdate(
{ _id: fromUserId },
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
or
User.findByIdAndUpdate(fromUserId,
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
I had a similar issue. I wanted to delete an object from an array, using the default _id from mongo, but my query was wrong:
const update = { $pull: { cities: cityId }};
It should be:
const update = { $pull: { cities: {_id: cityId} }};

add a item inside a nested schema mongoose with addToSet

I know populating schemas is not a new question but I am having a little trouble following the logic on this in regards to multiple schemas. I am working with
"mongoose": "^4.8.5",
"express": "^4.15.0",
I have a schema with a collection of caffeine drinks. When a user selects a drink i would like for that drink to be assigned to the user.
** If at any point I am missing something simple in the architecture please let me know. This project has been my intro to mongodb.
I am reading through populating on the mongoose documentation http://mongoosejs.com/docs/populate.html.
Essentially, if I am to assign the drinks to the list it looks like I want to add them as a reference in an array. This was my approach with caffeine_list
const SelectedDrinks = require('./userDrinks');
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
caffeine_list: caffeine_list: [ // attempting to reference selected drinks
{
type: mongoose.Schema.Types.ObjectId,
ref: 'SelectedDrinks'
}
]
})
SelectedDrinks comes from the schema below. I added a reference to the user as the creator below
const User = require('./user');
let userDrinkSchema = new mongoose.Schema({
creator : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
caffeine: Number,
mgFloz: Number,
name: String,
size: Number,
updated_at: {
type: Date,
default: Date.now()
}
});
This is where I start to get confused. I initially tried populate but could not get it going. If that was correct please let me know.
In regards to my task of adding a selected drink to the user I used addToSet. I was hoping that this would give me the drink info. I did my set up like so....
const User = require('../../models/user');
const UserDrinks = require('../../models/userDrinks');
router.post('/addDrink', (req, res, next) => {
let newDrink = new UserDrinks({
creator: req.body.creator,
caffeine: req.body.caffeine,
mgFloz: req.body.mgFloz,
name: req.body.name,
size: req.body.size,
updated_at: req.body.updated_at
});
newDrink.save( (err) => {
if(err) {
res.send(err);
} else {
User.findOne({ _id: newDrink.creator}, (err, user) => {
user.caffeine_list.addToSet(newDrink)
user.save( function (err) {
if(err) {
console.log(err);
}else {
res.status(201).json(newDrink);
}
})
})
}
})
});
However, after i do a post in postman I check caffeine_list and the result is
"caffeine_list" : [
ObjectId("58d82a5ff2f85e3f21822ab5"),
ObjectId("58d82c15bfdaf03f853f3864")
],
Ideally I would like to have an array of objects being passed with the caffeine info like so
"caffeine_list" : [
{
"creator": "58d6245cc02b0a0e6db8d257",
"caffeine": 412,
"mgFloz": 218.7,
"name": "1.95 Perfect Drink!",
"size": 42.93,
"updated_at": "2017-03-24T18:04:06.357Z"
}
]
Change your else part with below code instead of findOne and save use update
User.update(
{ _id: newDrink.creator},
{ $addToSet:{
caffeine_list: newDrink
}}).exec(function (err, updatedrink){
if(err) {
console.log(err);
}else {
res.status(201).json(updatedrink);
}
})
Although I am not sure this is the best approach I did find this to be give me the result that I was desiring. I had to make two small changes and I was able to get the caffeine_list to give me the desired response
I had to access the schema for selected drinks
const SelectedDrinks = require('./userDrinks').schema; //** need schema
Afterwards I was able to change
caffeine_list: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'UserDrinks' // name of the file
}
]
to
caffeine_list: [SelectedDrinks]
Now that I have the schema I am able to add the drinks directly into the caffeine_list on the UserSchema.

Add elements in nested document then retrieve the _id

I have the following collection definition:
// Includes
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
// Create required sub schemas
const subSchema0 = new Schema({
value: String,
});
const subSchema = new Schema({
idWordsLibraryName: {
type: Schema.Types.ObjectId,
ref: 'WordsLibrary1_0',
},
type: String,
values: [
subSchema0,
],
});
const schema = new Schema({
version_: String,
idWordsLibraryName: {
type: Schema.Types.ObjectId,
ref: 'WordsLibrary1_0',
},
idsDads: [{
type: Schema.Types.ObjectId,
ref: 'LocationStructure1_0',
}],
params: [
subSchema,
],
});
Summary -> One document with nested parameters with nested values.
I have the following request that add some values into a particular parameter
this.findOneAndUpdate({
_id: data.idLocationStructure,
'params._id': data.idLocationStructureParameter,
}, {
$push: {
'params.$.values': {
$each: dataToPush,
},
},
}, {
new: true,
});
It works as expected.
What I want now is to get the _id of pushed elements, but without loading all values of the parameter.
I have tried to use the select option of findOneAndUpdate but it don't work using the projection:
this.findOneAndUpdate({
_id: data.idLocationStructure,
'params._id': data.idLocationStructureParameter,
}, {
$push: {
'params.$.values': {
$each: dataToPush,
},
},
}, {
new: true,
select: {
'params.$.values': 1,
},
});
It gets me:
{
"_id": "57273904135f829c3b0739dd",
"params": [
{},
{},
{},
{},
],
},
I have tried to perform a second request to get the _ids as well, but it don't work either:
this.find({
_id: data.idLocationStructure,
'params._id': data.idLocationStructureParameter,
}, {
_id: 1,
'params.$.values': {
$slice: -nbAdded,
},
});
If you have any idea of how retrieving the _id of the pushed values without loading all values of the parameter, you are very welcome :)
Well after tons of researches all over the web and stack overflow <3 I have found a solution, which is:
this.aggregate([{
$match: {
_id: new mongoose.Types.ObjectId(data.idLocationStructure),
},
},
{
$unwind: '$params',
}, {
$match: {
'params._id': new mongoose.Types.ObjectId(data.idLocationStructureParameter),
},
},
{
$unwind: '$params.values',
},
{
$sort: {
'params.values._id': -1
},
},
{
$limit: nbAdded,
},
{
$project: {
_id: '$params.values._id',
},
},
]);
If you experience the same problem, here is the explaination:
$match makes me taking the good high level document
$unwind makes me to go into the params array in the document we $match
$match makes me taking the good parameter
$unwind makes me to go into the values array
I $sort all values by _id DESC
I $limit to the number of values I added previsoulsy
I change the name of the _id (like an alias)
So I got as result an array that contains the last added values _ids

Resources