Mongoose: populate an object inside an array - node.js

I have a schema exported like that:
const PackageSchema = new Schema({
name: { type: String, required: true },
maneuver: [
{
maneuverId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: ManeuverMainly,
},
period: { type: String, enum: ["day", "night"], required: true },
},
],
timestamp: { type: Date, default: Date.now() },
});
When I make a find() like that:
Package.find().populate("maneuver", "name").exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});
My populate method does not work. How can I populate my every maneuverId from PackageSchema with my name column from ManeuverMainlySchema?
Obs: my ManeuverMainlySchema bellow:
const ManeuverMainlySchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: true },
timestamp: { type: Date, default: Date().now },
});

taken from Mongoose populate with array of objects containing ref you have to specify the field within the object of the array you want to populate against.
Package.find().populate("maneuver.maneuverId", "name").exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});

Package.find().populate(["maneuver.maneuverId", "name"]).exec((err, data) => {
if (err) {
res.status(500).send({ message: "Failed!" });
return;
}
res.status(200).send(data);
});
If you want to populate one of them, don't need to use array in populate as
populate("maneuver.maneuverId") or populate("name").

Related

problem when creating one to many data in mongoose

so i'm creating CRUD with relation of two collections, then i got problem, i can't do push from first collection data to second collection. this is my code.
Schema
const CourseSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
price: Number,
video: String,
category: String,
status: Number,
lessons: [
{
type: Schema.Types.ObjectId,
ref: "Lessons",
},
],
},
{
timestamps: true,
}
);
const course = mongoose.model("Courses", CourseSchema);
const LessonSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
video: String,
status: Number,
},
{ timestamps: true }
);
const Lessons = mongoose.model("Lessons", LessonSchema);
code to do data push
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
Course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data._id } },
{ safe: true, upsert: true, new: true }
);
})
is there any solution for my problem? please help me, i'm just learning about one to many relation in nodejs using mongoose
You need a callback function to make it works.
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data } },
{ safe: true, upsert: true, new: true },
function (err, newdoc) { // callback function
if (err) {
console.log(err);
} else {
console.log("completed");
}
}
);
})

how to save document and update another which are dependent on each other with mongoose

Here is my bid model.
const BidSchema = new Schema({
auctionKey: {
type: mongoose.Types.ObjectId,
ref: "Auction",
required: true
},
amount: { type: String, required: true },
userName: { type: String, required: true },
});
And, here is my Auction Model (Notice the relationships between these two models).
const AuctionSchema = new Schema({
title: { type: String, required: true },
startDate: { type: Date, required: true },
closeDate: { type: Date, required: true },
initialBidAmount: { type: Number, required: true },
bidIncrementAmount: { type: Number, required: true },
bids: [
{
type: mongoose.Types.ObjectId,
ref: 'Bid'
}
]
});
When user bids for any auction, I'm saving bid in bids collection and updating auctions collection using mongoose findOneAndUpdate.
const postBid = async (req, res, next) => {
const { auctionKey } = req.body;
const bid = new BidModel(req.body);
bid.save(error => {
if (error) {
res.status(500).json({ message: "Could not post bid." });
}
});
const aucById = await AuctionModel.findOneAndUpdate(
{ _id: auctionKey },
{ $push: { bids: bid } }
).exec((error: any, auction: IAuction) => {
if (error) {
res.status(500).json({ message: "Could not post bid." });
} else {
res.status(201).json({ bid });
}
});
};
For any reason if any of these two (save bid and findOneAndUpdate) throws any error I want nothing to be saved into database. I mean to say either they should save and update or nothing should be done on database.
I have tried using mongoose session and transaction but got this error.
MongoError: This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.
Is there any way to work out in this scenario?
If I understand your problem right, you can just delete created document in:
.exec((error: any, auction: IAuction) => {
if (error) {
// here, by using .deleteOne()
res.status(500).json({ message: "Could not post bid." });
}
Or just change structure of your code, so only when two are successfully created, they will be saved and response will be sent.

How do I remove an array of referenced Objects when deleting the main document?

This is my MongoDB schema:
const MenuSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
caterer: {
type: Schema.Types.ObjectId,
ref: 'User'
},
products: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
}, { timestamps: true })
const ProductSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
price: {
type: String
}
}, { timestamps: true })
What I'm wondering - is how I can delete the array of products, at the same time as I delete the main "Menu" document? When I remove the Menu, I can also assume that the products belonging to the menu should be removed.
At the moment this is how I remove the menu (and tried to remove its products):
await Menu.findOneAndDelete({ _id: req.params.menu_id }, (err, response) => {
if (err) {
console.error(err);
}
Product.remove({ _id: { $in: req.body.products }}, (err, res) => {
if (err) {
console.error(err);
}
console.log('Deleted products');
});
});
However, the products do not get removed. Any suggestions?
Mongoose provides a pre and post middleware on your schema. Which means you can delete all the referenced documents before or after you do an operation on the current schema.
Read more here.
Here's an example, inside your schema add this:
const MenuSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
caterer: {
type: Schema.Types.ObjectId,
ref: 'User'
},
products: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}]
}, { timestamps: true })
const ProductSchema = new Schema({
name: {
type: String,
require: true
},
category: {
type: String,
require: true
},
description: {
type: String,
require: true
},
image: {
type: String,
},
price: {
type: String
}
}, { timestamps: true })
MenuSchema.post('remove', removeProducts);
function removeProducts(doc) {
Products.remove({_id: { $in: doc.products}})
}
Assuming Products is the name of your model.
Try This It works for Me.
await Menu.findOneAndDelete({ _id: req.params.menu_id }, (err, response) => {
if (err) {
console.error(err);
}
Product.remove({ _id: { $in: response.products }}, (err, res) => {
if (err) {
console.error(err);
}
console.log('Deleted products');
});
});
You can use post schema hooks of mongoose as below
schema.post('remove', function(doc) {
console.log('%s has been removed', doc._id);
});
Mongoose Post Hook
But the best approach is to use transactions to execute multiple operations on the database as below.
let session = null;
db.startSession()
.then((_session) =>{
session = _session;
session.startTransaction();
return Menu.deleteOne({ _id: req.params.menu_id });
})
.then(()=> Product.deleteMany({ _id: { $in: req.body.products }}))
.then(()=>{
session.commitTransaction();
})
.catch((err)=>{
session.abortTransaction()
return handleError(err);
})
Mongoose Transactions

mongoose modify multi level subdocument then save not work normally

I have a Torrent item, it has subdocument array named '_replies' to saved user comments, and every comment also include subdocument array '_replies' to saved user reply, this is my all schema define:
var CommentSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
comment: {
type: String,
default: '',
trim: true
},
_replies: [this],
createdat: {
type: Date,
default: Date.now
},
editedby: {
type: String,
default: '',
trim: true
},
editedat: {
type: Date,
default: ''
}
});
var TorrentSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
torrent_filename: {
type: String,
default: '',
trim: true,
required: 'filename cannot be blank'
},
torrent_title: {
type: String,
default: '',
trim: true,
required: 'title cannot be blank'
},
_replies: [CommentSchema]
});
mongoose.model('Torrent', TorrentSchema);
mongoose.model('Comment', CommentSchema);
the first level comment of torrent update/delete fine, the code of server controller likes below:
exports.update = function (req, res) {
var torrent = req.torrent;
torrent._replies.forEach(function (r) {
if (r._id.equals(req.params.commentId)) {
r.comment = req.body.comment;
r.editedat = Date.now();
r.editedby = req.user.displayName;
torrent.save(function (err) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(torrent); //return data is Correct, and save to mongo is Correct
}
});
}
});
};
but when i used Alike function to update/delete _replies._replies, it can return Correct json of torrent to response, Unfortunate, the save to mongo not fine, the code:
exports.SubUpdate = function (req, res) {
var torrent = req.torrent;
torrent._replies.forEach(function (r) {
if (r._id.equals(req.params.commentId)) {
r._replies.forEach(function (s) {
if (s._id.equals(req.params.subCommentId)) {
s.comment = req.body.comment;
s.editedat = Date.now();
s.editedby = req.user.displayName;
torrent.save(function (err) {
if (err) {
return res.status(422).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(torrent);//return data is Correct, but save to mongo is incorrect
}
});
}
});
}
});
};
also, i can delete first level comment, but can not delete second level comment reply, all the json data of torrent is correct, only not save to mongo.
what can i do more?
I already solve it, i add this code before .save().
torrent.markModified('_replies');
it work fine!

Mongoose: value from Model

I have the following model:
var requestSchema = new Schema({
description: { type: String, required: true },
country: { type: String, index: true },
shipping: [shipping],
deliveryLoc: { type: String, index: true },
price: { type: Number, default: 0 },
})
I now want to get the price using mongoose and I am not sure which command I have to use.
I tried:
var pricy = _.first(_.where(request.price));
and it does not work, I get undefined even through through other queries in the same file I can get "shipping".
Getting the shipping type works with the following command:
var shipping = _.first(_.where(request.shipping, { type: shippingType }));
Am I using the wrong command?
You should be able to use the select method as follows:
// find a request
var query = Request.findOne();
// selecting the `price` field
query.select('price');
// execute the query at a later time
query.exec(function (err, request) {
if (err) return handleError(err);
console.log('The price is $%s.', person.price) // The price is $6.92
});
or if passing a callback:
var Request = mongoose.model('Request', requestSchema);
// find each request with a country matching 'Zimbabwe', selecting the `price` field
Request.findOne({ 'country': 'Zimbabwe' }, 'price', function (err, request) {
if (err) return handleError(err);
console.log('The price is $%s.', request.price) // The price is $6.92.
});
First, you need to create your schema like that:
var items = new Schema({
description: { type: String, required: true },
country: { type: String, index: true },
shipping: [shipping],
deliveryLoc: { type: String, index: true },
price: { type: Number, default: 0 },
});
After that you need to compile the new schema and add it to the database:
items = mongoose.model("Items", items); // The table name will be "Items"
When the model is created, you can execute your query (find or findOne):
items.findOne({price: request.price}, function (error, item) {
if (error) {
console.log(error);
} else {
console.log(item);
}
});
The full code:
var mongoose, Schema;
mongoose = require("mongoose");
Schema = mongoose.Schema;
var items = new Schema({
description: { type: String, required: true },
country: { type: String, index: true },
shipping: [shipping],
deliveryLoc: { type: String, index: true },
price: { type: Number, default: 0 },
});
items = mongoose.model("Items", items);
items.findOne({price: request.price}, function (error, item) {
if (error) {
console.log(error);
} else {
console.log(item);
}
});

Resources