Pushing into subdocument with Mongoose - node.js

I have this data structure. I would like to push a new sibling into a specific members sibling array:
const FamilySchema = mongoose.Schema({
username: { type: String, required: true },
family_name: { type: String, required: true },
password: { type: String, required: true },
members: [
{
fname: { type: String, required: true },
lname: { type: String, required: true },
mname: { type: String },
birth_date: { type: String },
birth_town: { type: String },
birth_state: { type: String },
death_date: { type: String },
death_town: { type: String },
death_state: { type: String },
short_bio: { type: String },
long_bio: {type: String },
parents: [
{
parent_id: { type: String, required: true }
}
],
siblings: [
{
sibling_id: { type: String, required: true }
}
],
children: [
{
children_id: { type: String, required: true }
}
],
pictures: [
{
url: { type: String, required: true }
}
],
}
]
});
I want to push a new sibling into the sibling array for a SPECIFIC member in the larger family document. I'm trying to do it like this, and it's not working.
Family
.update(
{
_id: FAMILY_ID,
"members._id" : MEMBER_ID
},
{
$push : {
"members.$.siblings" : {
"sibling_id": THE_VALUE_I_WANT_ADDED
}
}
}
);
Any thoughts? My Mongoose knowledge is failing me.

Related

Reference to an object inside an array in another collection

Following is the WorkingDay model
const workingDaySchema = new mongoose.Schema({
date: { type: String, unique: true, required: true },
availableSlots: [
{
startTime: Date,
endTime: Date,
size: Number,
enrolledUsers: [{ type: Schema.Types.ObjectId, ref: 'Response' }]
}
]
})
module.exports = mongoose.model('WorkingDay', workingDaySchema);
And the following is the Response model:
const responseSchema = new Schema(
{
id: { type: String },
name: { type: String, required: true },
age: { type: String },
gender: { type: String },
height: { type: String },
weight: { type: String },
food: { type: String },
phone: { type: String },
email: { type: String },
category: { type: Array },
answers: { type: Object },
assignedSlot: { type: Schema.Types.ObjectId, ref: availableSlots, default: null }
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Response", responseSchema);
Every object inside the availableSlots array has it own unique _id.
How to create a reference to an object inside availableSlots from the assignedSlot field of response? Also how to populate the same?
I tried referencing the availableSlots objects using
assignedSlot: { type: Schema.Types.ObjectId, ref: availableSlots, default: null }
and
assignedSlot: { type: Schema.Types.ObjectId, ref: "WorkingDay.availableSlots", default: null }
but it did not work.
const { ObjectId } = mongoose.Schema.Types;
assignedSlot: [{ type: ObjectId ,ref:"WorkingDay"}]
Given your schema structure, you should reference the WorkingDay collection:
assignedSlot: { type: Schema.Types.ObjectId, ref: 'WorkingDay', default: null }
Then, you can populate the reference with:
const response = await Response.findOne({}).populate('assignedSlot').exec();
if (response && response.assignedSlot) {
console.log(response.assignedSlot.availableSlots);
}

Mongoose $push whole array in database

I'm having an error on my database where the sub array that I push to my database is missing and created a new id which means it detects data pushed inside.
here's the data I pushed. (toDeliver array has 4 objects inside).
I'm trying to send the whole array along with the string outside of the array.
after the request here what I receive on my database which is mongoDB.
the object inside toDeliver array is incomplete and created a ObjectId.
but the string outside of array was save the database.
here's my schema.
const OwnerSchema = mongoose.Schema({
username: {
require: true,
type: String,
},
password: {
require: true,
type: String,
},
isAdmin: {
type: Boolean,
default: true,
},
store: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
quantity: {
type: Number,
required: true,
},
categoryfilter: {
type: String,
required: true
},
description: {
type: String,
required: true,
},
specs: {
type: String,
required: true
},
imageBase64: {
type: String,
required: true,
},
timestamp: {
type: String,
required: true,
}
}
],
delivery: [
{
clientname: {
type: String,
required: true
},
address: {
type: String,
required: true
},
email: {
type: String,
required: true
},
number: {
type: Number,
required: true
},
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
],
toDeliverPaidViaPaypal: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
}
]
}
]
});
here's my backend.
export const delivery = async (req,res) => {
const { id } = req.params;
console.log(id);
console.log(req.body);
try {
if(!id) return res.status(404).json({ message: 'ID not found' });
await OwnerModels.findByIdAndUpdate(id,
{
$push: {
delivery:
{
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver:
[{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}]
,
toDeliverPaidViaPaypal: []
}
}
},
{
new: true,
},(err,res)=> {
if(err) return console.log({ error: err });
console.log({ result: res.delivery });
}).clone();
} catch (error) {
res.status(500).json({ message: 'Server error' });
}
}
hope ya guys can help me. thank you
I think you need to add square brackets around the toDeliver object to make it an array like your console object:
$push: {
delivery: {
clientname: req.body.delivery[0].clientname,
address: req.body.delivery[0].address,
email: req.body.delivery[0].email,
number: req.body.delivery[0].number,
toDeliver: [{
product_identifier: req.body.delivery[0].toDeliver.product_identifier,
productname: req.body.delivery[0].toDeliver.productname,
price: req.body.delivery[0].toDeliver.price
}],
toDeliverPaidViaPaypal: []
}
}
Also add "_id: false" to toDelivery in your schema to repress id from being generated for the sub-object:
toDeliver: [
{
product_identifier: {
type: String,
require: true,
},
productname: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
_id: false,
}
],

find value in array mongoose+typescript

I have an schema created with mongoose+typescript like this :
import mongoose, { Schema, Types, Document } from 'mongoose'
import { mongoosePagination, Pagination } from 'mongoose-paginate-ts'
import { Department } from './departmentModel'
import { Designation } from './designationModel'
import { Roles } from './roleModel'
export interface IEmployee extends Document {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
type employeeUser = mongoose.Document & {
userName: string
email: string
firstName: string
lastName: string
designation: Types.ObjectId
department: [Types.ObjectId]
roles: [Types.ObjectId]
projects: string
status: string
reportingManager: [
{
managerId: string
userName: string
},
]
approver: [
{
approverId: string
userName: string
},
]
createdBy: string
updatedBy: string
}
const schema = new Schema(
{
userName: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
designation: {
type: Schema.Types.ObjectId,
ref: Designation,
required: true,
},
department: [{
type: Schema.Types.ObjectId,
ref: Department,
required: true,
}],
roles: [
{
type: Schema.Types.ObjectId,
ref: Roles,
required: true,
},
],
status: {
type: Boolean,
default: true,
required: true,
},
reportingManager: [
{
managerId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
approver: [
{
approverId: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
},
],
createdBy: {
type: String,
immutable: true,
},
updatedBy: {
type: String,
},
},
{ collection: 'user', timestamps: true },
)
schema.plugin(mongoosePagination)
const User: Pagination<IEmployee> = mongoose.model<employeeUser, Pagination<IEmployee>>('User', schema)
export { User }
And trying using like this::
import { IEmployee, User } from '#/models/user'
const getEmployeeDetailsHelper = async (paginationQuery: unknown): Promise<unknown> => {
try {
return await User.find({department: '623aca76efa7cf01ec7c39ac'}).exec()
} catch (err) {
logger.error(JSON.stringify(err))
return err
}
}
but typescript giving me error like:
No overload matches this call.
Overload 1 of 3, '(callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Argument of type '{ department: string; }' is not assignable to parameter of type 'Callback<IEmployee[]>'.
Object literal may only specify known properties, and 'department' does not exist in type 'Callback<IEmployee[]>'.
Overload 2 of 3, '(filter: FilterQuery<IEmployee>, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.
Overload 3 of 3, '(filter: FilterQuery<IEmployee>, projection?: any, options?: QueryOptions, callback?: Callback<IEmployee[]>): Query<IEmployee[], IEmployee, {}, IEmployee>', gave the following error.
Type 'string' is not assignable to type 'Condition<[ObjectId]>'.ts(2769)
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
user.ts(13, 3): The expected type comes from property 'department' which is declared here on type 'FilterQuery<IEmployee>'
Please help.

Pushing an element in an array of array

This is my schema...How can I push an element in an array of an array?
example: I've to insert an element in (patient.problems.feedback) in an already existing document. please help me how to code in node js-MongoDB
let Patient = new Schema({
patient: {
patientId: {
type: String,
required: false
},
name: {
type: String,
required: true
},
age: {
type: String,
required: true
},
gender: {
type: String,
required: true
},
city: {
type: String
},
phoneNumber: {
type: String,
min: 10,
max: 10
},
referredBy: {
type: String
},
createdAt: {
type: String
}
},
Problems: [{
problemId: {
type: String,
required: false
},
problem: {
type: String,
required: true
},
howLongSuffered: {
type: String
},
medicinesFollowed: {
type: String
},
createdAt: {
type: String
},
feedbacks: [{
feedbackId: {
type: String,
required: false
},
feedback: {
type: String,
required: false
},
updatedAt: {
type: String
}
}]
}]
})
**This is my controller
How can I update the existing document by pushing an element into the feedbacks
**
exports.addFeedbackDetailsForExistingProblem = async function(req, res) {
try {
let feedbackCreatedDate = new Date();
feedbackCreatedDate = feedbackCreatedDate.toDateString();
await patientModel.findById(req.params.id).then(async(result) => {
console.log(result + req.body.id);
await result.updateOne({ 'Problems._id': req.body.id }, {
$push: {
'Problems.feedbacks': {
'feedbacks.feedback': req.body.feedback,
'feedbacks.createdAt': feedbackCreatedDate
}
}
})
});
} catch (err) {
res.status(500).send("Something Went Wrong");
}
}

Mongoose sub field aggregation with full text search and project

I have a Mongoose model called Session with a field named course (Course model) and I want to perform full text search on sessions with full text search, also I wanna aggregate results using fields from course sub field and to select some fields like course, date, etc.
I tried the following:
Session.aggregate(
[
{
$match: { $text: { $search: 'web' } }
},
{ $unwind: '$course' },
{
$project: {
course: '$course',
date: '$date',
address: '$address',
available: '$available'
}
},
{
$group: {
_id: { title: '$course.title', category: '$course.courseCategory', language: '$course.language' }
}
}
],
function(err, result) {
if (err) {
console.error(err);
} else {
Session.deepPopulate(result, 'course course.trainer
course.courseCategory', function(err, sessions) {
res.json(sessions);
});
}
}
);
My models:
Session
schema = new mongoose.Schema(
{
date: {
type: Date,
required: true
},
course: {
type: mongoose.Schema.Types.ObjectId,
ref: 'course',
required: true
},
palnning: {
type: [Schedule]
},
attachments: {
type: [Attachment]
},
topics: {
type: [Topic]
},
trainer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'trainer'
},
trainingCompany: {
type: mongoose.Schema.Types.ObjectId,
ref: 'training-company'
},
address: {
type: Address
},
quizzes: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'quiz'
},
path: {
type: String
},
limitPlaces: {
type: Number
},
status: {
type: String
},
available: {
type: Boolean,
default: true
},
createdAt: {
type: Date,
default: new Date()
},
updatedAt: {
type: Date
}
},
{
versionKey: false
}
);
Course
let schema = new mongoose.Schema(
{
title: {
type: String,
required: true
},
description: {
type: String
},
shortDescription: {
type: String
},
duration: {
type: Duration
},
slug: {
type: String
},
slugs: {
type: [String]
},
program: {
content: {
type: String
},
file: {
type: String
}
},
audience: [String],
requirements: [String],
language: {
type: String,
enum: languages
},
price: {
type: Number
},
sections: [Section],
attachments: {
type: [Attachment]
},
tags: [String],
courseCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'course-category',
required: true
},
trainer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'trainer'
},
trainingCompany: {
type: mongoose.Schema.Types.ObjectId,
ref: 'training-company'
},
status: {
type: String,
default: 'draft',
enum: courseStatus
},
path: {
type: String
},
cover: {
type: String,
required: true
},
duration: {
type: Number,
min: 1
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date
}
},
{ versionKey: false }
);
I am not sure if what I tried is gonna bring me what I want and I am getting this error concerning the $unwind operator:
MongoError: exception: Value at end of $unwind field path '$course'
must be an Array, but is a OID
Any kind of help will be really appreciated.
You can try below aggregation.
You are missing $lookup required to pull course document by joining on course object id from session document to id in the course document.
$project stage to keep the desired fields in the output.
Session.aggregate([
{
"$match": {
"$text": {
"$search": "web"
}
}
},
{
"$lookup": {
"from": "courses",
"localField": "course",
"foreignField": "_id",
"as": "course"
}
},
{
"$project": {
"course": 1,
"date": 1,
"address": 1,
"available": 1
}
}
])
Course is an array with one course document. You can use the $arrayElemAt to project the document.
"course": {"$arrayElemAt":["$course", 0]}

Resources