Remove _id attributes before inserting into database (using node & mongoose) - node.js

I am using mongodb 3.6.20, mongoose 5.10.9 and node 14.13.1
The case is the following.
I want to insert a complex object to db. The object contains an array of objects where each object also contains an array of objects. These objects given by a client which sets the ids(O do not have power over the client to change it). I want to remove the ids and let mongo drivers to handle them and generate new ones.
what is given:
let obj = {
property1: {
property2: "str",
property3: 3
},
property4 : [{
_id: "a valid mongo id",
property5: "str",
property6: [{
_id: "another valid mongo id",
property7: "str"
}]
}]
}
what I want to provide to insert query:
let obj = {
property1: {
property2: "str",
property3: 3
},
property4 : [{
property5: "str",
property6: [{
property7: "str"
}]
}]
}
I have tried to remove them recursively but the call stack is exceeded.
Is there any clever way I can achieve that? The option {_id: false} and {id:false} that I found on mongoose documentation are actually only for the returned documents of a query

What about this?
function removeIDs (obj) {
Object.keys(obj).forEach(function (key) {
if (Array.isArray(obj[key])) {
delete obj[key][0]._id;
return removeIDs(obj[key][0]);
}
});
}
removeIDs(obj)

Related

Can't push items in mongo array

I can't push items into MongoDB array every time that i try to push a new element it creates an empty object and i cant figure out why,
I already used the
Collection.Array.push({element})&
Collection.save()
but i cant figure out a solution
This is My Schema
const Schema = mongoose.Schema;
var ParticipantSchema = new Schema({
nom:{Type:String},
prenom:{Type:String},
email:{Type:String}
})
var CompetitionSchema = new Schema({
nom:String,
date:Date,
place:String,
participant :[ParticipantSchema]
})
module.exports = mongoose.model("Competition",CompetitionSchema);
This is my funtion
exports.addParticipant=function(req,res){
var newParticipant={
"nom":req.body.nom,
"prenom":req.body.prenom,
"email":req.body.email
}
Competition.updateOne(
{ _id:req.body.id},
{ $push: { participant: newParticipant } },
(err,done)=>{
return res.json(done)
}
);
}
the result is always an empty object like below
{
"_id": "5ded0eeb85daa100dc5e57bf",
"nom": "Final",
"date": "2019-01-01T23:00:00.000Z",
"place": "Sousse",
"participant": [
{
"_id": "5ded0eeb85daa100dc5e57c0"
},
{
"_id": "5dee3c1b08474e27ac70672e"
}
],
"__v": 0
}
There is no problem in your code, the only problem is that in schema definition you have Type, but it must be type.
If you update your ParticipantSchema like this, it will work:
var ParticipantSchema = new Schema({
nom: { type: String },
prenom: { type: String },
email: { type: String }
});
You are using another Schema in the Array. This results in so-called subdocuments (https://mongoosejs.com/docs/subdocs.html). Mongoose does not populate subdocuments by default. So all you see is just the _id. You can use the populate method to see all subdocuments in detail. ( https://mongoosejs.com/docs/populate.html ) .
Example :
Competition.
find({}).
populate('participant').
exec(function (err, comps) {
//
});
You can either use populate on the Model or on the Document. For populating a document, take a look at https://mongoosejs.com/docs/api.html#document_Document-populate . There is also a auto-populate plugin available via npm but in most cases it's not necessary : https://www.npmjs.com/package/mongoose-autopopulate .

returning matching elements using MongoDB 4.0 find function

Mongodb returns non-matching elements in nested array
Here's my database sample:
const users = [{
'username':'jack',
'songs':[{
'song':'Another Love',
'likes':false
}, {
'song':"i'm into you",
'likes': true
}]
}, {
'username':'Stromae',
'songs':[{
'song':'Take me to church',
'likes':false
}, {
'song':"Habits",
'likes': true
}]
}];
I'm trying to find the following row:
const query = {'username':'Stromae' , 'songs.song':'Take me to church','songs.likes':true};
const result = await usersTable.find(query).project({'songs.$':1}).toArray();
as you see I'm trying to find a user who called "Stromae" and has a "Take me to church" song and he don't like it.
I'm expecting as result to be null, while the result is:
{ _id: 5d302809e734acbc5ffa2a8f,
songs: [ { song: 'Take me to church', likes: false } ] }
as you see from the result it ignores that I need "likes" field to be true.
As per my understanding you are trying to match data from 'songs' array which satisfying both the conditions for the 'song' and 'likes' both fields. But you haven't provided the logic like check both the fields for same array element. That's why it is checking this fields in whole 'songs' array.
To check condition for single array element you can use $elemMatch and for checking both the conditions are satisfying or not use the $and operator.
You can use your Mongo query as:
db.usersTable.find({
"username": "Stromae",
songs: {
$elemMatch: {
$and: [
{
"song": "Take me to church"
},
{
"likes": true
}
]
}
}
})
Just update your 'find' query, you might get your required result.

MongoDB, Mongoose find all where _id is equal to key of object in array

Struggling with a query, I have a collection of users:
{
"_id": "5c87bda25fdaasdf00171001e1",
"name": "UserTwo Name",
"email": "email2#example.com"
},{
"_id": "5c87bda25fda8b00171001e1",
"name": "User Name",
"email": "email#example.com"
}
I also have an array of objects containing a _userId key. I need to get all users where the _id of the user equals the value of _userId in my array.
[
{_userId: "5c87bda25fda8b00171001e1"},
{_userId: "5c87bda25fdaasdf00171001e1"},
]
What I have so far is:
User.find(
{
_id: { $in: req.body.users }
}
)
Which should be fine if req.body.users was just an array of ids, not array of objects.
Any help would be greatly appreciated.
Thanks,
You should modify your query like this:
User.find({
_id: { $in: req.body.users.map(user => ObjectId(user._userId) }
});
You need to convert _userId string to ObjectId. And that will be done with map function.
You have to turn the array
[
{_userId: "5c87bda25fda8b00171001e1"},
{_userId: "5c87bda25fdaasdf00171001e1"},
]
into
[
"5c87bda25fda8b00171001e1",
"5c87bda25fdaasdf00171001e1",
]
So how can we do that ?
Example 1 : using Array.map which iterate through the specified array, call a function and then create a new array with values being the ones returned by the function it called.
console.log([{
_userId: "5c87bda25fda8b00171001e1",
},
{
_userId: "5c87bda25fdaasdf00171001e1",
},
].map(x => x._userId));
Example 2 : Using a well known for loop and create the array ourselves
const arr = [{
_userId: "5c87bda25fda8b00171001e1"
}, {
_userId: "5c87bda25fdasdf0017101e1"
}, ];
const newArr = [];
for (let i = 0; i < arr.length; i += 1) {
newArr.push(arr[i]._userId);
}
console.log(newArr);
PS : As #Ivan Vasiljevic proposed, when building the array of id, you can turn the string into ObjectID objects. It's not mandatory to do it using mongoose.find ; it is tho using mongoose.aggregate.

Mongodb positional operators $ or $[] do not work for array elements

{
"_id": "5aa9535c6c4f437de713452a",
...
"ht_score": "[0-1]",
"ft_score": "[1-3]",
"et_score": null,
"penalty_local": null,
"penalty_visitor": null,
"comp_name": "UEFA Champions League",
"predictions": [
{
"name": "Ilker Baltaci",
"userid": "*******",
"userFbid": "*****",
"local_team_score": "3",
"away_team_score": "1",
"status": "FT"
},
{
"name": "M. Mustermann",
"userid": "*******",
"userFbid": "*****",
"status": "FT"
}
],
"match_id": "2324756"
}
I have in my mongo db the above collection structure and I want to update some fields of the document as well a field in all objects that reside inside the nested array(predictions).
The reason for the bulk operation is that this code is run as migration over multiple documents.
The problem is that everthing inside $set till "predictions.$[].status" : "FT" works fine but somehow the positional operator $[] does not seem to work.If i replace it with an number index it updates the corresponding object inside the array.I want to update all nested documents inside the array.
I tried both $[] and $ to access status but no luck.
var bulk = db.collection('bets').initializeUnorderedBulkOp();
return new Promise(function (resolve, reject) {
try {
predictions.forEach(function (currentPrediction) {
//Find one and update
let match = games[currentPrediction.match_id];
const query = {'_id': new ObjectID(currentPrediction._id)};
bulk.find(query).update(
{
$set: {
status: match.status,
timer: match.timer,
localteam_score: match.localteam_score,
visitorteam_score: match.visitorteam_score,
ht_score: match.ht_score,
ft_score: match.ft_score,
et_score: match.et_score,
penalty_local: match.penalty_local,
penalty_visitor: match.penalty_visitor,
"predictions.$[].status" : "FT"
}
},{ multi: true });
})
bulk.execute(function (err) {
resolve(predictions)
});
}
catch
(e) {
reject(e);
}
}
UPDATE: I get the following error
"errmsg": "cannot use the part (predictions of predictions.$[].status) to traverse the element ({predictions: [ { name: \"Ilker Baltaci\", userid: \"***\", userFbid: \"*****\", local_team_score: \"3\", away_team_score: \"0\", status: \"FT22\" }, { name: \"M. Mustermann\", userid: \"***\", userFbid: \"****\", status: \"FT\" } ]})",
$[] is a new feature of v3.6.
For it to work you need mongodb v3.6, and set FeatureCompatibilityVersion to "3.6".
$ updates a single element in the array.
For it to work your query should include a filter for elements in the array, e.g.
const query = {'_id': new ObjectID(currentPrediction._id), "predictions.status" : "FT"};
The $ refers to the first matching element, and without filter there are no matches.

mongodb remove document if array count zero after $pull in a single query

I have a requirement where my comments schema looks like the following
{
"_id": 1,
"comments": [
{ "userId": "123", "comment": "nice" },
{ "userId": "124", "comment": "super"}
]
}
I would like to pull the elements based on the userId field.
I am doing the following query
comments.update({},{$pull:{comments:{userId:"123"}}})
My requirement is that if the array length became zero after the pull operator I need to remove the entire document for some reason.Is there a away to do this in a single query?
PS:I am using the mongodb driver.Not the mongoose
If I'm reading your question right, after the $pull, if the comments array is empty (zero length), then remove the document ({ _id: '', comments: [] }).
This should remove all documents where the comments array exists and is empty:
comments.remove({ comments: { $exists: true, $size: 0 } })
I had a similar requirement and used this (using mongoose though):
await Attributes.update({}, { $pull: { values: { id: { $in: valueIds } } } }, { multi: true })
await Attributes.remove({ values: { $exists: true, $size: 0 } })
Not sure if it's possible to do this in one operation or not.
You can use middlewares for this.
http://mongoosejs.com/docs/middleware.html
Write a pre/post update method in mongodb to check your condition.

Resources