how to dynamically change whole object (inc. array) in mongodb? - mongodb

I use following code to dynamically update whole object in mongoDB:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.updateOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},{
$set: req.body.array[i]
}).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}
but the code is not working, when model own array:
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const exerciseSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
}
}, { _id : false })
const workout_planSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
},
description: {
type: String
},
user_ID: {
type: String,
required: [true, 'required!']
},
exercises: [exerciseSchema]
})
const workout_plan = mongoose.model('workout_plan', workout_planSchema)
module.exports = workout_plan
I would like to update whole object with totally new values, staying only with the same _id.
For example , I have following value in DB:
"_id": "604a16f6cf847c1810c8fd08",
"title": "1",
"user_ID": "Test",
"exercises": [{
"title": "123"
}],
and I am sending array which looks like this:
"_id": "604a16f6cf847c1810c8fd08",
"title": "2",
"user_ID": "Test",
"exercises": [{
"title": "234"
},{
"title": "235"
}],
and the result should be same as the array I am sending. How can I change my code to reach this?
PS: Basiclly I want to make object in DB = sent object

so the answer is to change the main function like this:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.replaceOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},
req.body.array[i]
).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}

Try this one:
for( let elem of req.body.array ) {
let workout_plan = await Model.findById(elem._id);
workout_plan.title = elem.title;
workout_plan.user_ID = elem.user_ID;
workout_plan.exercises = elem.exercises;
await workout_plan.save();
}

Related

Update using positional operator ($) in mongoose

I have a document containing an array of objects. I wanted to update a particular element in the array. Tried using MongoDB shell, it works fine. But when I use in Mongoose in NodeJs, it is not working. The command is same in both the cases.
NodeJs code
const updateAttendance = await classModel.updateOne(
{
_id: item.classId,
'studentAttendance.studentId': item.studentId,
},
{ $set: { 'studentAtendance.$.present': true } }
)
Schema defination
const mongoose = require('mongoose')
const moment = require('moment')
const student = mongoose.Schema({
studentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
unique: true,
},
present: {
type: Boolean,
default: false,
},
})
const classes = mongoose.Schema({
date: {
type: String,
required: true,
default: moment().format('DD/MM/YYYY'),
validate: {
validator: (value) => {
return moment(value, 'DD/MM/YYYY', true).isValid()
},
message: 'Provide a valid date in the format of DD/MM/YYYY',
},
},
courseId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Course',
},
studentAttendance: [
{
type: student,
},
],
})
module.exports = mongoose.model('Class', classes)
Sample data
{
"date": "20/06/2021",
"_id": "60cf5446970dc063e40356d3",
"courseId": "60ce2c3aca275c868089ac48",
"studentAttendance": [
{
"present": false,
"_id": "60cf5446970dc063e40356d4",
"studentId": "60ce315f9f83a24544414705"
},
{
"present": false,
"_id": "60cf5446970dc063e40356d5",
"studentId": "60ce31ba9f83a2454441470a"
},
{
"present": false,
"_id": "60cf5446970dc063e40356d6",
"studentId": "60ce38e49f83a24544414712"
}
],
"__v": 0
}
What am I doing wrong or where is the problem?
Without looking at the schema def, just taking a punt in the dark that you dont explicitly say its an ObjectId.
Easy solve, just wrap "item.studentId" in mongoose.Types.ObjectId().
So your new code would be like
const updateAttendance = await classModel.updateOne({
_id: mongoose.Types.ObjectId(item.classId),
'studentAttendance.studentId': mongoose.Types.ObjectId(item.studentId),
},
{ $set: { 'studentAtendance.$.present': true } }
)
Don't forget const mongoose = require('mongoose');
Based on the update your update statement needs 'updating'. try fixing the spelling of studentAttendance vs studentAtendance in the $set statement.

One to one with populate mongoose not working

I'm new to mongoose and mongodb.
I have two collection (cart and produk)
1 cart have 1 produk, and I get the cart and populate the product but is not show the data relations.
Here the code:
routing
router.route('/relations/:app_id')
.get(cartController.relation);
model (cartModel)
var mongoose = require('mongoose');
var cartSchema = mongoose.Schema({
app_id: {
type: String,
required: true
},
product_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Produk'
},
qty: Number
});
var collectionName = 'cart';
var Cart = module.exports = mongoose.model('Cart', cartSchema, collectionName);
module.exports.get = function (callback, limit) {
Cart.find(callback).limit(limit);
}
model (produkModel)
var mongoose = require('mongoose');
// Setup schema
var produkSchema = new Schema({
name: {
type: String,
required: true
},
stok: Number
});
// Export Cart model
var collectionName = 'produk';
var Produk = module.exports = mongoose.model('Produk', produkSchema, collectionName);
module.exports.get = function (callback, limit) {
Produk.find(callback).limit(limit);
}
controller (cartController)
Cart = require('../model/cartModel');
exports.relation = function (req, res) {
const showCart = async function() {
const carto = await Cart.find().select('app_id product_id qty').populate("produk");
return carto;
};
showCart()
.then(cs => {
return apiResponse.successResponseWithData(res, "Operation success", cs);
})
.catch(err => console.log(err));
};
// Result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": "60112f3a25e6ba2369424ea3"
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": "603f5aff9437e12fe71e6d41",
"qty": 1
}
]
}
expecting result
{
"status": 1,
"message": "Operation success",
"data": [
{
"_id": "60af72022d57d542a41ffa5a",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"qty": 1,
"product_id": {
"_id": "60112f3a25e6ba2369424ea3",
"name": "snack"
}
},
{
"_id": "60b020536ccea245b410fb38",
"app_id": "CvR4dTTjC7qgr7gA2yoUnIJnjRXaYokD6uc2pkrp",
"product_id": {
"_id": "603f5aff9437e12fe71e6d41",
"name": "snack"
}
"qty": 1
}
]
}
what I miss ???
Thanks for your help
You need to pass the path to populate or an object specifying parameters to .populate(). So in this case, Your code should be:
const carto = await Cart.find().select('app_id product_id qty').populate("product_id");

Mongoose query not showing subdocument

I can't get mongoose to show subdocument when running find() while it displays perfectly well in mongodb shell.
Subdocument should be embedded based on my schema, not objectId referenced, so I shouldn't be running any black magic voodoo to get my data to show up.
const UserSchema = new mongoose.Schema({
username: String;
xp: Number;
//etc.
});
const RoomSchema = new mongoose.Schema({
timestamp: { type: Date, default: Date.now },
status: { type: String, enum: ["pending", "ongoing", "completed"]},
players: {
type: [{
points: { type: Number, default: 0 },
position: String,
user: UserSchema
}],
maxlength:2
}
});
After adding a new room with:
let room = new Room(coreObj);
room.players.push({
points: 0,
position: 'blue',
user: userObj //where userObj is a result of running findById on User model
});
It displays nicely in mongo shell, when running db.rooms.find({}).pretty() I can see that full document has been added. However, when running on mongoose model:
Room.find({}).exec((err,rooms)=>{
console.log(rooms[0].toJSON());
});
I don't see user subdocument, moreover I cannot see user field entirely! What seems to be the problem?
logged json from mongoose model:
{
"status": "pending",
"_id": "5cf5a25c050db208641a2076",
"timestamp": "2019-06-03T22:42:36.946Z",
"players": [
{
"points": 0,
"_id": "5cf5a25c050db208641a2077",
"position": "blue"
}
],
"__v": 0
}
json from mongo shell:
{
"_id" : ObjectId("5cf5a25c050db208641a2076"),
"status" : "pending",
"timestamp" : ISODate("2019-06-03T22:42:36.946Z"),
"players" : [
{
"points" : 0,
"_id" : ObjectId("5cf5a25c050db208641a2077"),
"position" : "blue",
"user" : {
"xp" : 0,
"_id" : ObjectId("5cf2da91a45db837b8061270"),
"username" : "bogdan_zvonko",
"__v" : 0
}
}
],
"__v" : 0
}
Keeping best practice in mind, I think it would be more appropriate to reference the UserSchema in the RoomSchema. Something like:
...
user: {
type: Schema.Types.ObjectId,
ref: 'UserSchema'
}
Then you would store the user._id in that field.
This way, if the user is modified, your RoomSchema is always referencing the correct information. You could then get the user using Mongoose's populate
I'm not entirely sure why you can't see the sub-sub-document, but this code example printed it correctly for me. Example was originally posted in https://mongoosejs.com/docs/subdocs.html but modified slightly to contain sub-sub-document so it looks similar to your code:
var grandChildSchema = new mongoose.Schema({ name: 'string' });
var childSchema = new mongoose.Schema({ name: 'string', grandChild: grandChildSchema });
var parentSchema = new mongoose.Schema({ children: [childSchema] });
var Parent = mongoose.model('Parent', parentSchema);
var parent = new Parent({
children: [
{ name: 'Matt', grandChild: {name: 'Matt Jr'} },
{ name: 'Sarah', grandChild: {name: 'Sarah Jr'} }
]
})
parent.save(function() {
Parent.find().exec(function(err, res) {
console.log(JSON.stringify(res[0]))
mongoose.connection.close()
})
});
Executing this code resulted in:
{
"_id": "5cf7096408b1f54151ef907c",
"children": [
{
"_id": "5cf7096408b1f54151ef907f",
"name": "Matt",
"grandChild": {
"_id": "5cf7096408b1f54151ef9080",
"name": "Matt Jr"
}
},
{
"_id": "5cf7096408b1f54151ef907d",
"name": "Sarah",
"grandChild": {
"_id": "5cf7096408b1f54151ef907e",
"name": "Sarah Jr"
}
}
],
"__v": 0
}
This was tested using Mongoose 5.5.12.
Note that I was using JSON.stringify() to print the document instead of using Mongoose's toJSON().
I just met a very similar problem, i think i got it.
the whole point is in model which you use:
const RoomSchema = new mongoose.Schema({
...
players: {
type: [{
...
user: UserSchema
...
but then you make
room.players.push({
points: 0,
position: 'blue',
user: userObj //where userObj is a result of running findById on User model
});
so you are missing the "type" subfield, so your doc is not compliant with your RoomSchema and mongoose do not show the parts which does not fit schema.

Mongoose virtual field for nested field returning null

My Schema looks like this, where associated is an array of String which are not _id and i use the Virtual method to populate another field in anoher Schema
const DetailSchema = mongoose.Schema({
candidate:{
.
.
associated: [String],
.
.
}
}, { toObject: { virtuals: true }, toJSON: { virtuals: true } });
My Virtual looks like
DetailSchema.virtual('associatedJobs', {
ref: 'Jobs',
localField: 'candidate.associated',
foreignField: 'jobID',
justOne: false
});
The returned field is always null. Is there something wrong?
Your reference to Jobs (ref: 'Jobs',), it maybe Job (ref: 'Job',) if you've declared your model is Job (without 's')
Your associatedJobs will be returned not in object, this is example, it maybe with format:
{
"candidate": {
"associated": [
"J2",
"J3",
"J5"
]
},
"_id": "5c1b4ab6683beb0b8162c80f",
"id": "D1",
"__v": 0,
"associatedJobs": [
{
"_id": "5c1b4ab6683beb0b8162c80b",
"jobID": "J2",
"name": "Job name 2",
"__v": 0
},
{
"_id": "5c1b4ab6683beb0b8162c80c",
"jobID": "J3",
"name": "Job name 3",
"__v": 0
},
{
"_id": "5c1b4ab6683beb0b8162c80e",
"jobID": "J5",
"name": "Job name 5",
"__v": 0
}
]
}
This is my solution for your problem, you can download on gist to run on local https://gist.github.com/huynhsamha/a728afc3f0010e49741ca627750585a0
My simple schemas as your schemas:
var DetailSchema = new Schema({
id: String,
candidate: {
associated: [String]
}
}, {
toObject: { virtuals: true },
toJSON: { virtuals: true }
});
var JobSchema = new Schema({
jobID: String,
name: String
});
DetailSchema.virtual('associatedJobs', {
ref: 'Job',
localField: 'candidate.associated',
foreignField: 'jobID',
justOne: false
});
var Detail = mongoose.model('Detail', DetailSchema);
var Job = mongoose.model('Job', JobSchema);
And you should add populate when find:
const d = await Detail.findOne({ id: 'D1' }).populate('associatedJobs');
You should mention both schema structure to find the error in your query ,I have post example of virtual field may be you get some help
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost:27017/test', { useMongoClient: true });
var PersonSchema = new mongoose.Schema({
name: String,
band: String
});
var BandSchema = new mongoose.Schema({
name: String
}, { toObject: { virtuals: true } });
BandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // Find people where `localField`
foreignField: 'band', // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false
});
var Person = mongoose.model('Person', PersonSchema);
var Band = mongoose.model('Band', BandSchema);

Populating array in mogo

I have created the following Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Player = require('./player');
var gameSchema = new Schema({
created_at: Date,
nrOfCards: String,
players: [{
sticks: String,
player: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player'
}
}],
});
var Game = mongoose.model('Game', gameSchema);
The saving part works fine and a saved object may look something like this:
"_id": "57dd11aca0c36114588fd250",
"nrOfCards": "3",
"__v": 0,
"players": [
{
"_id": "57d415e527c20f3ed2416e05",
"age": "33"
},
{
"_id": "57d417df2186d53f3d49c996",
"age": "73"
},
{
"_id": "57d41d85ec315d4234010c7d",
"age": "20"
}
]
},
After having saved an object I would like to have it returned with the player-field populated. Here is my attempt:
app.post('/api/games', function(req, res) {
Game.create({
players : req.body.activePlayers,
nrOfCards: req.body.nrOfCards,
}, function(err, game) {
if (err) {
res.send(err);
} else {
Game.findOne(game)
.populate('players.player')
.exec(function (err, newgame) {
if (err) return handleError(err);
console.log(newgame);
res.json(newgame);
});
}
});
});
Thinking that the .populate('players.player') should do the trick , but I'm receiving the unpopulated field containing the _id of player only.
Tips appreciated. Thanks!
Use
player: {
type: Schema.Types.ObjectId,
ref: 'Player'
}
into your schema.