Mongoose populate depending on conditions - mongodb

My service uses MongoDB and Mongoose. I have two DBs: Users and Posts. In Posts schema I have parameters:
"author", that contains userId from Users DB
"anonymous", a boolean-parameter that shows if the post is anonymous or not.
I can't solve the problem: when I request data from Posts DB I want to populate author in the "author" parameter only for non-anonymous posts, for anonymous ones I'd like to return null or not to return this parameter at all.
I've tried to use "match", but it doesn't work.
How can I solve this problem?
Thank you.
Code example:
const postSchema = mongoose.Schema(
{
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
anonymous: {
type: Boolean,
required: true,
default: false,
},
content: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
},
{
timestamps: true,
}
);
For population I use pre:
postSchema.pre(/^find/, function (next) {
this.populate({
path: 'author',
select: '_id login',
});
next();
});

Related

How to develop nested condition query in mongoDB

I am pretty new to mongoDb and want to apply nested query.
I have a business schema like this:
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
businessType: {
type: Schema.Types.ObjectId,
ref: "businessCategory",
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
select: false,
},
review: {
type: [reviewSchema],
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
Business has a review where user can do the review and reviewSchema is
const reviewSchema = new mongoose.Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "users",
required: true,
},
rating: {
type: Number,
enum: [1, 2, 3, 4, 5],
},
reviewArray: {
type: [singleReviewSchema],
},
},
{ timestamps: true }
);
One user can do many reviews, and it has reviewArray.
ReviewArray schema is
const singleReviewSchema = new mongoose.Schema(
{
title: {
type: String,
},
description: {
type: String,
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
How to fetch the business with a condition business: isDeleted:false and its reviews with singleReviewSchema: isDeleted:false
I dont know your model names, so please replace path with correct names
but it might look like:
businnesModel.find({isDeleted: false})
.populate({
path: 'reviewModelName',
model: 'review',
populate: {
path: 'reviewArray',
model: 'singleReviewModelName',
match: {
isDeleted : false
}
}
})
It should provide you array of businessModel documents - even when their singleReviews array will be empty (because all of reviews are deleted, or there was zero reviews). So you have to filter it out in JS.
To avoid filtering in JS, and to do it a bit more efficient way for mongodb, you can go with aggregate instead.

How to populate field of an embedded document?

I have an OrderSchema, which contains "Invoice" as an embedded schema. I want to populate a field ("series") from the nested schema.
The schema looks like the following:
const OrderSchema = new Schema({
success: {
type: Boolean,
},
invoice: {
type: new Schema({
series: {
// NEEDS TO POPULATE
type: Schema.Types.ObjectId,
ref: "Series",
required: true,
},
number: {
type: Number,
required: true,
},
}, {
_id: false,
timestamps: false
}),
required: true,
},
});
Here, I need to populate the path "invoice.series". How can I achieve this?
You can populate it like this
OrderModel.find(query)
.populate({
path: 'invoice',
populate: {
path: 'series',
}
})
.exec(function(err, docs) {});
or optionally you can do this ...
OrderModel.find(query)
.populate("invoice.series")
.exec(function(err, docs) {});

Is there any way in mongoose to access other Schema fields and update those fileds when a "document" is saved into the DB?

I'm currently working on a Library Management System and I'm using MongoDB as my Database so in there I have 4 schemas
User 2) Book 3) Review 4) bookIssue (which handles all the book
issuing)
I'll mention just my User and Book Issue schemas here coz I only want help regarding these two Schemas,
bookIssueHistory: { type: Array, default: null, }
Whenever a book is issued via "bookIssue" Schema I want to store that book's "id" in to the "bookIssueHistory" array (mentioned above) which is in my "userSchema", so I've mentioned both of my schemas below:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please enter your name'],
},
email: {
type: String,
required: [true, 'Please enter your email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please enter a valid email'],
},
photo: String,
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please enter your password'],
minlength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Re-Enter your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Entered password and confirmed password do not match',
},
},
passwordChangedAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
noOfBooksIssued: {
type: Number,
default: 0,
},
currentlyIssuedBooks: {
type: Number,
max: [3, 'You are only allowed to issue 3 books at a time'],
default: 0,
},
bookIssueHistory: {
type: Array,
default: null,
},
active: {
type: Boolean,
default: true,
select: false,
},
});
my book issue schema looks like this:
const bookIssueSchema = mongoose.Schema({
issuedAt: {
type: Date,
default: Date.now,
},
totalIssues: {
type: Number,
default: 0,
},
book: {
type: mongoose.Schema.ObjectId,
ref: 'Book',
required: [true, 'issue must belong to a book.'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'issue must belong to a user.'],
},
});
You can use mongoose middleware, in particular the pre-save hook to run some logic before bookIssue get inserted into the database.
bookIssueSchema.pre('save', function () {
// you can access the current document to be saved by `this`
if (this.isNew) { // apply to new bookIssue only
await this.model('User').findByIdAndUpdate(this.user, {
$addToSet: { bookIssueHistory: this.book } // use $addToSet to ensure distinct values, otherwise use $push
})
}
})
Important: The pre-save hook will be run only when you use BookIssue.create() or bookIssue.save() and not when you run BookIssue.insertMany()

MongoDB: Set and get Sub Document Schema

I'm using mongoose and I have users collection shown below, but I now want to allow the user to save a number of articles, an article has a title, subtitle, and body, One user can have many articles.
How can I restructure the users collection to allow the articles to be added
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
timestamps: true,
}
);
I'm using the below to set new data to the user's collection, how do I adapt it to allow me to set and get the new articles detailed above?
const confirmed = await userModel
.findOneAndUpdate(
{ email },
{
$set: { password },
}
)
.exec();
You can set the option strict: false and add(save) new fields to your schema.
const userSchema: Schema = new Schema(
{
email: { type: String, required: true, unique: true },
fullName: { type: String, required: true },
password: { type: String, required: true },
},
{
strict: false,
timestamps: true,
}
);
Here is the docs

Ensure a unique index on nested reference on a mongoose schema

What I want is that a user can like a post only once, hence I uniquely indexed the user in the likes array to ensure the same, but it isn't working and I can't find out what is wrong here .
The schema looks like this :
const mongoose = require('mongoose')
const postSchema = new mongoose.Schema({
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User' // User model
},
text: {
type: String,
required: [true, 'Post must have some text']
},
likes: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}
],
comments: [
{
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
text: {
type: String,
required: [true, 'Comment must have some text']
},
addedAt: {
type: Date,
default: Date.now
}
}
],
createdAt: {
type: Date,
default: Date.now
}
})
postSchema.pre(/^find/, function(next) {
this.populate({
path: 'author',
select: 'name avatar'
}).populate({
path: 'comments.author',
select: 'name avatar'
})
next()
})
// Ensure a user can like a post only once
postSchema.index({ 'likes.user': 1 }, { unique: true })
const Post = mongoose.model('Post', postSchema)
module.exports = Post
However when I send a post request to like a post twice via the same user it
shows no error.
Here is the postman output
I have tried both the ways listed in this, but none of them worked in this case.
Mongoose Index on a field in nested document
How do I ensure a user can like a post only once from the schema itself ?
Try saving likes in this format in the database
likes:[{type:mongoose.Schema.Types.ObjectId,ref: 'User'}]
making it
likes:[ObjectId("5af03111967c60501d97781f")]
and when the post like API is hit do
{$addToSet: {likedBy: userId}}
in update query,addToSet ensures no duplicate ids are maintained in the array.