Remove object from nested array failed - javascript

A team has many projects. Im trying to delete a project, and so I need to delete it in marcsEquipa[] too.
TEAM SCHEMA
const EquipaSchema = new mongoose.Schema({
trab1: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
trab2: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
trab3: {
type: Schema.Types.ObjectId,
required: true,
ref: 'Trab'
},
teamName: {
type: String,
required: true
},
marcsEquipa: [{
type: Schema.Types.ObjectId,
ref: 'Marcacao'
}]
},
{collection: 'Equipas'})
Function Delete Project
exports.deleteMarc = async (req,res) => {
console.log("Deleting Project..");
console.log(req.params._id);
console.log(req.params.equipa);
try{
console.log("1");
const equipa = await
Equipas.updateOne({ _id: req.params.equipa}, { $pull: { marcsEquipa: { _id: req.params._id}}}, { multi: true });
equipa.save();
console.log("1");
//await Marcacao.deleteOne({_id: req.params._id});
res.status(200).json();
console.log("1");
}catch(err) {
res.status(400).json({message: err});
}
}
I've tried this and it manages to delete the Project but it doesn't delete it within the array of Projects in Team. Can anyone help?

You are trying to pull the item with _id property, but the items are just the string representation of ObjectId. So, instead of this:
{ $pull: { marcsEquipa: { _id: req.params._id }}}
do this:
{ $pull: { marcsEquipa: req.params._id }}

Related

Delete related records using pre middleware in mongoose

I have 3 schemas including:
Building
const BuildingSchema = mongoose.Schema({
address: { type: String, required: true },
numberOfRooms: { type: Number, default: 0 },
});
Room
const RoomSchema = mongoose.Schema({
roomNumber: { type: Number, required: true, unique: false },
building: {
type: mongoose.Schema.Types.ObjectId,
ref: "Building",
required: true,
unique: false,
},
});
Agreement
const AgreementSchema = mongoose.Schema({
agreementNumber: { type: Number, unique: true },
room: {
type: mongoose.Schema.Types.ObjectId,
ref: "Room",
required: true,
unique: false,
},
});
My scenario is after deleting a building then
All rooms related to building
All agreements related to room
will be deleted too, currently I know how to delete rooms related to building:
BuildingSchema.pre("deleteOne", function (next) {
Room.deleteMany({ building: this._conditions._id }).exec();
next();
});
So how I can do it using pre middleware ?
My mongoose version: ^6.0.12
Thank you!
BuildingSchema.pre("remove", function (next) {
Room.deleteMany({ building: this._conditions._id }).exec();
next();
});
remember remove the building using building.remove() mongoose base function :)
This is my solution.
BuildingSchema.pre("deleteOne", async function (next) {
const buildingId = this._conditions._id;
await Room.find({ building: buildingId }, (err, rooms) => {
rooms.map(async (item) => {
await Agreement.deleteMany({ room: item._id }); // => Delete all Agreements related to Room
});
}).clone();
await Room.deleteMany({ building: buildingId }); // => Delete all Rooms related to Building
});

why Virtual Populate not working on Node js and mongoose? Scenario : Product and Review by user

I have review and product model.If user give review on specific product(id) then it is stored in review model database but i donot like to store user review in product model database .so, i used virtual populate in product model instead of child referencing.After using virtual properties,if we use product id to see details,we can see review of user in json format but not saved in database.But the problem is my virtual properties (In Product Model) not working as it doesnt show review of user in json format when i send the request in that product id which already have review by user(stored in review model database).what is the problem here?
User Review on Product (id) stored in database
Sending Request of that product id to see review of user in json format using virtual properties(but no review found in json)
In Product Model
const productSchema = new Schema({
name: {
type: String,
required: true,
trim: true,
},
slug: {
type: String,
required: true,
unique: true,
},
price: {
type: String,
required: true,
},
quantity: {
type: Number,
required: true,
},
description: {
type: String,
required: true,
trim: true,
},
offer: {
type: Number,
},
discount: {
type: Number,
},
productPictures: [{
img: {
type: String,
},
}, ],
mainCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub1Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub2Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "admin",
required: true,
},
vendor: {
type: mongoose.Schema.Types.ObjectId,
ref: "vendor",
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updatedAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
},
{
toJson: { virtuals: true },
toObject: { virtuals: true },
}
);
productSchema.virtual("reviews", {
ref: "review",
foreignField: "product",
localField: "_id",
// justOne: true
});
const Product = mongoose.model("product", productSchema);
module.exports = Product;
In Review Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const moment = require("moment");
const reviewSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: [true, "Review must belong to user"],
},
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "product",
required: [true, "Review must belong to the product"],
},
review: {
type: String,
required: [true, "Review cannot be empty"],
},
rating: {
type: Number,
min: 1,
max: 5,
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updateddAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
}, {
toJson: { virtuals: true },
toObject: { virtuals: true },
});
// pre middleware and populating user and product(we can also do populate in getAllReview in controller)
reviewSchema.pre(/^find/, function(next) {
// ^find here is we use regex and can able to find,findOne ...etc
this.populate({
path: "product",
select: " _id name",
}).populate({
path: "user",
select: " _id fullName",
});
next()
});
const Review = mongoose.model("review", reviewSchema);
module.exports = Review;
In Review.js
const Review = require("../../models/Review.Models")
exports.createReview = async(req, res) => {
const review = await Review.create(req.body)
return res.status(201).json({
status: true,
review
})
}
exports.getAllReviews = async(req, res) => {
try {
const reviews = await Review.find()
return res.status(200).json({
status: true,
totalReviews: reviews.length,
reviews
})
} catch (error) {
return res.status(400).json({
status: false,
error
})
}}
In Product.js
const Product = require("../../models/Product.Models");
exports.getProductDetailsById = async(req, res) => {
try {
const { productId } = req.params;
// const { productId } = req.body;
if (productId) {
const products = await Product.findOne({ _id: productId })
.populate('reviews')
return res.status(200).json({
status: true,
products,
});
} else {
console.log("error display");
return res.status(400).json({
status: false,
error: "params required...",
});
}
} catch (error) {
return res.status(400).json({
status: false,
error: error,
});
}
try this in Product.js
try {
if (productId) {
const products = await Product.findOne({ _id: productId }).populate(
"reviews"
);
console.log(products);
if (products) {
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});
only need to add on response sending
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});

Mongoose $or condition for nested array

There are my schemas
//ProjectModel
const ProjectSchema: Schema = new Schema(
owner: { type: Schema.Types.ObjectId, ref: "User" },
users: [{type: Schema.Types.ObjectId, ref: "ProjectUser", unique: true }]
);
//Project User model
const ProjectUserSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, ref: "User", require: true },
role: {
type: String,
default: 'basic',
enum: ["basic", "projectuser", "moderator", "admin"]
},
project: { type: Schema.Types.ObjectId, ref: "Project", require: true },
},
{
timestamps: true,
usePushEach: true,
}
);
The User model has common fields like password, name, etc.
I want to find User of a ProjectModel either among the owner (UserSchema) or among users (ProjectUserSchema)
ProjectModel.findOne()
.or([{ owner: req.params.user }, { "users.user": req.params.user }])
.then(project => {
res.json(project);
});
But it returns null. And condition .or([{ owner: req.params.user }, { "users._id": "PROJECT USER ID" }]) doesn't work either.
What should I do?
You need to convert the incoming req.params.user from string to ObjectId. Try this:
const mongoose = require('mongoose');
const getProjectByUser = (req, res) => {
let userId = mongoose.Types.ObjectId(req.params.user);
ProjectModel
.findOne({
$or: [
{ "owner": userId },
{ "users": userId }
]
})
.then(project => {
res.json(project);
})
.catch(e => {
res.json({ error: "Error!" });
});
}

posts.populate is not a function

I'm trying to populate my post's author fields (which is are object ids) with the corresponding author objects which are in a separate collection.
My controller code is as follows:
exports.readPosts = async (req, res) => {
try {
const posts = await Post.find({ board: req.params.board });
await posts.populate("author").execPopulate();
res.send(posts);
} catch (err) {
res.status(400).send(err.message);
}
};
I'm at a loss as to why this isn't working as I have very similar code in another controller method that is working just fine.
All help greatly appreciated.
Below is the relevant Model file:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
trim: true,
},
content: { type: String, required: true, trim: true },
comments: [
{
comment: {
type: String,
required: true,
trim: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
date: {
type: Date,
default: Date.now(),
},
},
],
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
board: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Board",
},
},
{ timestamps: true }
);
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
posts is an array of models. populate must be called on a model. The preferred way to do this is at query time. It probably works on your other controller because you are using a findOne so it is returning the model, not the Array.
const posts = Post
.find({ board: req.params.board })
.populate('author')
.exec();

Can't compare moment dates in MongoDb

So my goal is to retrieve posts with comments that are placed today with Mongoose.
First, I create a start-of-the-day UTC current date object with:
const todayForEvent = moment().startOf('day')
.utc().toDate();
this results in 2019-01-02T06:00:00.000Z
then I want to create a DB search with mongoose to fetch the posts where a comment has been placed today
const posts = await Post.find({
// From this user...
$and: [
// Find normal posts that has comments (recent interactions)
{ _posted_by: userId },
{ comments: { $exists: true, $ne: [] } },
{ 'comments.created_date': { $gte: todayForEvent } }
]
})
Third, I have mongoose comment documents that have a property created_date
const CommentSchema = new Schema({
created_date: {
type: Date,
default: moment().utc().toDate()
}
});
const Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
This is the result document after placing a comment
Everything looks OK but for some reason the posts array is still empty after the database search, can someone please tell me what I did wrong
EDIT: added post schema at request
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PostSchema = new Schema({
content: {
type: String,
trim: true
},
_content_mentions: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
type: {
type: String,
required: true,
enum: ['normal', 'event', 'task']
},
_liked_by: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
comments_count: {
type: Number,
default: 0
},
comments: [{
type: Schema.Types.ObjectId,
ref: 'Comment'
}],
_group: {
type: Schema.Types.ObjectId,
ref: 'Group',
required: true
},
_posted_by: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
task: {
due_to: {
type: String,
default: null
},
_assigned_to: {
type: Schema.Types.ObjectId,
ref: 'User'
},
status: {
type: String,
enum: ['to do', 'in progress', 'done']
}
},
event: {
due_to: {
type: Date,
default: null
},
_assigned_to: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
},
created_date: {
type: Date,
default: Date.now
},
files: [{
orignal_name: {
type: String,
default: null
},
modified_name: {
type: String,
default: null
}
}]
});
const Post = mongoose.model('Post', PostSchema);
module.exports = Post;
EDIT 2: sample post document
{ _id: 5c2d14c30176ac30204809a8,
task: { due_to: null },
event: { due_to: null, _assigned_to: [] },
_content_mentions: [],
_liked_by: [],
comments_count: 1,
comments: [ 5c2d14dc0176ac30204809ab ],
content: '<p>poging 5 duust</p>',
type: 'normal',
_posted_by:
{ _id: 5c292e0e63deb43d9434f664,
profile_pic: 'default_user.png',
first_name: 'Jaspet',
last_name: 'Houthoofd' },
_group: 5c292db763deb43d9434f660,
created_date: 2019-01-02T19:45:07.710Z,
files: [],
__v: 0,
liked_by: [] }
**EDIT 3: sample comment **
{ _content_mentions: [],
created_date: 2019-01-02T21:10:04.456Z,
_id: 5c2d28c251f2bd332cdeaf0a,
content: '<p>hehe</p>',
_commented_by: 5c292db763deb43d9434f65f,
_post: 5c2d1dd254ca0429b470f000,
__v: 0 }
So the problem here is that you have two collections, posts and comments. Based on your Posts schema, comments array contains only ids that reference documents that are stored in second collection. That's why you can check whether that array exists and is not empty but you can't refer directly to these elements.
To fix that you can use $lookup to get those documents from comments into posts and then you can apply your date condition inside $match, try:
let posts = await Post.aggregate([
{ $match: { comments: { $exists: true, $ne: [] }, _postedBy: userId } },
{ $lookup: { from: "comments", localField: "comments", foreignField: "_id", as: "comments" } },
{ $match: { 'comments.created_date': { $gte: todayForEvent } } }
])

Categories