Update nested array field inside another array in a mongo db document - javascript

I'm just wondering how I can update a nested array field inside a mongo db document.
Here is how my schema looks like:
const userSchema = new Schema({
email: {type: String, unique: true, lowercase: true},
password: String,
firstName: String,
lastName : String,
role: String,
children: Array
});
This is how the document looks like:
{
"_id" : ObjectId("5b3570f3150a0b57a4e7421e"),
"children" : [
{
"fullName" : "John doe",
"yearGroup" : "4",
"absences" : [],
"id" : "765"
}
],
"email" : "jdoe#gmail.com",
"firstName" : "John",
"lastName" : "Doe",
"role" : "parent",
"__v" : 1
}
Where I want to push a new object into to 'absences' array.

to do this you have to use the mongodb $push operator via the update operation, which i guess that is what you want to do, but you did not specify your match query. To push to absences do this ( i assume the match query is children.fullName )
db.ops.update( { "children.fullName": "John doe" } , { $push: { "children.$.absences": "data to push" } } );
the $ placeholder tells mongodb to replace it self ( i.e $ ) with the matched array index.
incase you want to prevent duplicate elements in absences field you have to use the $addToSet operator
db.ops.update( { "children.fullName": "John doe" } , { $addToSet: { "children.$.absences": "data to push" } } );

Related

Mongoose - Model.updateOne isn't working for me when adding an embeded document to an existing document

When I run this code in node.js my embedded document id doesn't match with the related id in the other collection.
const fruitSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
rating: {
type: Number,
min: 1,
max: 10,
},
review: String
});
const Fruit = mongoose.model("Fruit", fruitSchema);
const watermelon = new Fruit({
name: "Watermelon",
rating: 7,
review: "Meh."
});
// watermelon.save();
const personSchema = new mongoose.Schema({
name: String,
age: Number,
favouriteFruit: fruitSchema
});
const Person = mongoose.model("Person", personSchema);
const person = new Person({
name: "John",
age: 37,
favouriteFruit: watermelon
});
person.save();
As a result, in MongoDB I get, From
db.fruits.find()
{
"_id" : ObjectId("5e7444c37ce78dd238d0f994"),
"name" : "Watermelon",
"rating" : 7,
"review" : "Meh.",
"__v" : 0
}
From
db.people.find()
{
"_id" : ObjectId("5e7451d971df90096974be01"),
"name" : "John",
"age" : 37,
"favouriteFruit" :
{
"_id" : ObjectId("5e7451d971df90096974be00"),
"name" :"Watermelon",
"rating" : 7,
"review" : "Meh."
},
"__v" : 0
}
I think I'm missing something with the Model.updateOne method.
I'm just trying to add an embedded document into another document.
I'm just a beginner, so any links or help would be amazing!
Thanks!
Why such behavior?
The reason you are having different _ids in the two fruits object that are supposed to be the same is that you did not add the _id property to the fruit schema and because _id is a required property of all MongoDB document, mongoose would help you auto-generate a new _id when it creates the query to send to the database. The _id it generates when you run watermelon.save() is different from the _id it generates when you run person.save(), this is why you are seeing the two different _ids.
The fix:
What you need to do is add the _id property to the fruit schema(and maybe the person schema to avoid further surprises) and then explicitly generate an _id your self before saving the document into the database.
The fruit schema should be like this after adding the _id property:
const fruitSchema = new mongoose.Schema({
_id: mongoose.ObjectId,
name: {
type: String,
required: true
},
rating: {
type: Number,
min: 1,
max: 10,
},
review: String
});
And when instantiating the fruit model, add the _id value yourself:
const watermelon = new Fruit({
_id: mongoose.Types.ObjectId(), // this helps generate a unique objectId
name: "Watermelon",
rating: 7,
review: "Meh."
});

Mongo shell db.model.find() not showing all the documents

I've defined my schema as follow but when I did db.Titles.find() from mongo shell I didn't get all the documents I have listed. What's wrong did I do ? The author is not showing in the mongo shell. I've tried to add some random document in the list still not showing in the mongo ? Once a model is created, can't it be updated or change? This created a problem when I try to access the list in template. Thanks, in advance.
model :
var mongoose = require("mongoose");
var tSchema = new mongoose.Schema({
title: String,
sypnosis: String,
category: Array,
author: {
username: String,
id: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
},
parts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Part"
}
],
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
var Title = mongoose.model("Title", tSchema);
module.exports = Title;
Mongo shell query :
db.titles.find().pretty()
{
"_id" : ObjectId("5e0d140e7bf04c6a60f97dbd"),
"category" : [ ],
"parts" : [ ],
"comments" : [ ],
"title" : "New Title",
"sypnosis" : "This si a sypnosis",
"__v" : 0
}

Update nested array and populate before that with mongoose

I would like to add a photo into a country in mongoose. But country is an array and photo too. Here is my user schema :
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
birthDate: {
type: Date,
required: true
},
sex: {
type: String,
required: true
},
countries: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Country',
photos: [
{
base64: {
type: String,
required: true
},
title: String,
description: String
}
]
}
],
admin: {
type: Number,
required: true
}
});
Here is what I got as data into mongoDB :
The problem is that I only got the id of countries. And I would like to use another field of the document country. Populate works well when I want to get data, but how to populate and then use the fields to update with mongoDB?
Moreover, I don't know how to update data into nested array, I tried :
User.findOneAndUpdate(
{
"name": "CHARLAT",
"countries": "5d2d847b06f2f94118a36518"
},
{ $push : { "countries.photos" : {
base64: "bla"
} }}
)
As you can see, I use a hand written id for country... I could do a find query before on country but can we use populate here?
And I got this in Postman :
Thank you in advance for your help !
If the type is ObjectId, it can't have a photos field, since it's just an _id. It is a reference to another collection.
Updated answer after comments :
The best thing to do IMO is to create a Photo model which would have the file path and the country's _id. The User model would only store a list of Photos [_id].
UserSchema :
{
.....
photos : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Photo'
}],
.....
}
PhotoSchema :
{
country : {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country'
},
path : String
}
Then, query your Users this way, by populating the photos, and inside each photo, populating the countries :
UserModel
.find(conditions)
.populate({
path: 'photos',
model: 'Photo'
populate: {
path: 'country',
model: 'Country'
}
})
.lean() // Faster and lighter for read-only, simply returns an object
So you should get a User object like this :
{
.....
name : "John",
photos : [{
country : {
name : "Country 1",
code : "C1" // or whatever field you have in your Country model
},
path: "path/to/photo1.jpg"
},
{
country : {
name : "Country 2",
code : "C2"
},
path: "path/to/photo2.jpg"
}]
.....
}

How we remove null error encounter in referencing node js mongodb

I have to use populate method for referencing my second collection. I have two collections one is levels and second child.
I have referenced to my level collection through child collection. For this, I use the populated method but it returns null.
This is node js code
function(){
var query = {'_id': result._id };
var params = 'usercurrLevelId';
childQuizInfo.findOne(query, params).populate({
path : 'usercurrLevelId',
model : 'Level',
select : '_id level_num age'
}).exec(function(err,levelinfo){
if(err) return next(err);
res.send(levelinfo);
});
}
This level schema
var LevelSchema = new Schema({
_id: { type: String },
age: { type: Number },
level_num: { type: Number },
min_score: { type: Number },
max_questions: { type: Number }
});
var Level = mongoose.model('Level', LevelSchema);
This is child schema
usercurrLevelId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Level',
index: true
}
This is the level stored in mongoDB
{
"_id" : ObjectId("57cb1ef8b0b05a2ce9b0c24b"),
"age" : 5,
"level_num" : 1,
"min_score" : 12,
"max_questions" : 30
}
{
"_id" : ObjectId("57cb1ef9b0b05a2ce9b0c24c"),
"age" : 5,
"level_num" : 2,
"min_score" : 15,
"max_questions" : 33
}
This is the child stored in mongoDB
{
"_id" : ObjectId("57cc0410d51045452644251b"),
"usercurrLevelId" : ObjectId("57cb1ef8b0b05a2ce9b0c24b")
}
And the output i get
{
"_id": "57cc0410d51045452644251b",
"usercurrLevelId": null,
"__v": 0
}
I using this node js query so that I get level_num and _id from my level schema using this referencing.

checking if an array contains a post id in a mongodb document

Let's say I want to look up a user by their _id and check if the "liked" value (array) contains a certain post _id. How do I query the db for this action? Is it okay to just store these _id's in an array or does mongodb convention prefer something else to store as a reference to other documents?
So I just want to check if the user has the post _id in the "liked" array.
var users = new mongoose.Schema({
name : {type: String, unique : true, required : true, dropDups: true},
password : {type: String, required : true}, //hash
liked : [String],
created : {type: Date, default: Date.now}
});
Here is how I think this might look:
function checkIfLiked() {
let uname = "Jim";
let postId = "abc";
//check if $USER has `postId` in $LIKED
user.findOne({$USER: uname},{$LIKED: {$in: postId} }, function(err, results) {
//do something after check
});
}
For the user data
{ "_id" : ObjectId("56effca6e668e15e2eaa6dfe"), "liked" : [ "11", "23", "4" ], "name" : "aa" }
{ "_id" : ObjectId("56effcb1e668e15e2eaa6dff"), "liked" : [ "1", "2", "3" ], "name" : "bb" }
To check the user name aa with 4 in liked array
> db.user.find({liked: '4', name: 'aa'})
{ "_id" : ObjectId("56effca6e668e15e2eaa6dfe"), "liked" : [ "11", "23", "4" ], "name" : "aa" }
but
> db.user.find({liked: '2', name: 'aa'})
No matched result.
Is it okay to just store these _id's in an array or does mongodb convention prefer something else to store as a reference to other documents?
Mongoose population could do that, you can define the user schema as below
var users = new mongoose.Schema({
name : {type: String, unique : true, required : true, dropDups: true},
password : {type: String, required : true}, //hash
liked : [{ type: Schema.Types.ObjectId, ref: 'User' }],
created : {type: Date, default: Date.now}
});
var User = mongoose.model('User', users);

Categories