Mongoose specify optional array of objects - mongodb

One of the keys in my mongo collection is
options: [
new mongoose.Schema(
{
answer: {
type: String,
required: true,
},
value: {
type: Number,
min: -10,
max: 10,
required: true,
},
},
{ _id: false }
),
],
The issue I'm having here is that options is optional but when there's no options field filled out, the inserted document has options: []
I believe I'm able to solve this normally by putting a default: undefined but I'm not sure how to go about doing this for this array of objects.
Thanks!

In mongoose an empty array is a default value for an array type. You can override it by using default field this way:
let optionsSchema = new mongoose.Schema(
{
answer: {
type: String,
required: true,
},
value: {
type: Number,
min: -10,
max: 10,
required: true,
},
},
{ _id: false });
const RootSchema = new Schema({
options : {
type: [optionsSchema],
default: undefined
}
})

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.

set field value based on other fields mongoose schema

I am trying to set a value based on the value of two other fields in my schema but im getting the following error
ReferenceError: Cannot access 'isPending' before initialization
here is the model
const mongoose = require('mongoose');
const bookingSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
sitterId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Sitter',
},
start: {
type: Date,
required: true,
},
end: {
type: Date,
required: true,
min: this.start,
},
accepted: {
type: Boolean,
default: false,
},
declined: {
type: Boolean,
default: false,
},
paid: {
type: Boolean,
default: false,
},
pending: {
type: Boolean,
default: true,
set: isPending,
},
});
const isPending = (accepted, declined) => {
!accepted && !declined ? true : false;
};
any ideas? is it possible to do
check this
use this to get access to other fields value:
pending: {
type: Boolean,
default: true,
set: function() { return !this.accepted && !this.declined }
}

How to update a property in a nested array with mongoose

I want to push a Date object from my client into the nested array 'completed_dates' but I cannot figure out how to do so, or if I would need to change my schema in order for it to work.
{
_id: 606f1d67aa1d5734c494bf0a,
name: 'Courtney',
email: 'c#gmail.com',
password: '$2b$10$WQ22pIiwD8yDvRhdQ0olBe6JnnFqV2WOsC0cD/FkV4g7LPtUOpx1C',
__v: 35,
habits: [
{
_id: 6081d32580bfac579446eb81,
completed_dates: [],
name: 'first',
type: 'good',
days: 0,
checked: false
},
{
_id: 6081d32f80bfac579446eb82,
completed_dates: [],
name: 'seconds',
type: 'bad',
days: 0,
checked: false
},
]
}
and this is my schema
const habitSchema = new mongoose.Schema({
name: String,
category: String,
color: {
type: String,
},
date_added: {
type: String,
},
completed_dates: {
type: Array,
}
})
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
min: 6,
max: 255,
},
email: {
type: String,
required: true,
max: 255
},
password: {
type: String,
required: true,
max: 1024,
min: 8,
},
habits: [habitSchema]
})
Here is what I have tried...
I've tried using findOneAndUpdate, using the document id of the logged in user, and trying to manipulate the update object to drill into the nested array. I can access the habits list of the correct user... using this code, but for this new problem, I want to go one level further and push to the 'completed_dates' array of a specific habit (based on name or _id).
//this only adds a habit object to the habits array.
User.findByIdAndUpdate(req.user._id,
{ $pull: { habits: { _id: itemsToDelete } } },
{ new: true , useFindAndModify: false},
function (err, data) {
if (err) {
res.send(err)
} else {
res.send(data.habits)
}
}
)
I have tried building on this existing code by trying to filter down one more level. (this doesn't work.)
const { date, name} = req.body.update
User.findByIdAndUpdate(req.user._id,
{ $push: { 'habits.$[req.body.name].completed_dates': req.body.date} },
{safe: true, upsert: true, new : true, useFindAndModify: false},
function (err, data) {
if (err) {
res.send(err)
} else {
//data.update
res.send(data.habits)
}
}
)
If anyone can link or help me out, I would appreciate it. Thanks

Why isn't my Getter function working with Mongoose?

I have a getter on the price property of my schema.
For some reason, my getter function is not working when I try to query a document from my MongoDB database. The price value comes back exactly as I have it saved in my database, as opposed to a rounded number via Math.floor(v). Fyi, my setter works fine in the same scenario. Any help would be much appreciated!
const schema = mongoose.Schema({
name: { type: String, required: true, lowercase: true },
isPublished: Boolean,
author: {
type: String,
required: function (v) {
return this.isPublished;
},
uppercase:true,
},
price: {
type: Number,
required: true,
get: function (v) {
return Math.floor(v);
},
},
});
const Documents = mongoose.model("Documents", schema);
async function myQuery(id) {
const result = await Documents.findById(id);
if (!result) return debug("Not found...");
debug(result);
}
myQuery("60348d30e7b9bf3878170955");
const schema = mongoose.Schema({
name: { type: String, required: true, lowercase: true },
isPublished: Boolean,
author: {
type: String,
required: function (v) {
return this.isPublished;
},
uppercase: true,
},
price: {
type: Number,
required: true,
get: function (v) {
return Math.floor(v);
},
},
} {
toObject: { getters: true, setters: true },
toJSON: { getters: true, setters: true },
runSettersOnQuery: true
});
Add the following configuration to your schema and give it a try.

Mongoose, proper way to update documents with deep nested schemas

I would like to know what would be the most efficient way to update a document that also has a nested schema in it. Normally I would just use Model.findByIdAndUpdate(id, updatedValues) ..., however if I try to do that with a document that has a deep nested schema I get this error: CastError: Cast to Embedded failed for value "{ _id: 5b1936aab50e727c83687797, en_US: 'Meat', lt_LT: 'Mesa' }" at path "title".
My Schemas look something like:
const titleSchema = new Schema({
en_US: {
type: String,
required: true
},
lt_LT: {
type: String,
default: null
}
})
const categorySchema = new Schema({
title: titleSchema
});
const ingredientSchema = new Schema({
title: {
type: titleSchema,
required: true
},
category: categorySchema,
calories: {
type: Number,
min: 0,
default: 0
},
vitamins: [String]
})
And I try to update like so:
{
title: {
en_US: 'Pork',
lt_LT: 'Kiauliena'
},
category: {
_id: '5b193a230af63a7e80b6acd8',
title: {
_id: '5b193a230af63a7e80b6acd7'
en_US: 'Meat',
lt_LT: 'Mesa'
}
}
}
Note, I get the new category object from a separate collection using just the category _id, but the final update object that goes into the findByIdAndUpdate() function looks like the one above.
The only workout I found is to remove the category from the updated values, update the document via findByIdAndUpdate(), get the updated document, assign the new category to it and save it.
It works just fine and all, but that requires two calls to the database, is it possible to do it in just one?
Try updating your schema like this:
const titleSchema = new Schema({
en_US: {
type: String,
required: true
},
lt_LT: {
type: String,
default: null
}
});
const ingredientSchema = new Schema({
title: {
type: titleSchema,
required: true
},
category: {
title: titleSchema
},
calories: {
type: Number,
min: 0,
default: 0
},
vitamins: [String]
});