Issue with virtual population in Mongoose - node.js

I am using mongo DB with node.js and all models is in common folder and using virtual population to communicate between the models.For Example model1->model2->model3(model1 have the dependencies of model2 and model2 have the dependencies of model3).
Here is the code:
var packageGroupSchema = new Schema(
{
packageGroupCode: { type: String, required: true, unique: true},
packageGroupName: { type: String, required: true },
description: String,
**moduleId: { type: Schema.Types.ObjectId, required: true },**
active: Boolean,
createdDate: { type: Date, default: Date.now},
lastUpdatedDate: { type: Date, default: Date.now}
},
{
toJSON: { virtuals: true }
}
);
packageGroupSchema.virtual('moduleData', {
ref: 'Module',
localField: 'moduleId',
foreignField: '_id',
justOne: true
});
2nd Model:
//module Schema
var moduleSchema = new Schema(
{
moduleCode: { type: String, required: true, unique: true},
moduleName: { type: String, required: true },
description: String,
**categoryId: { type: Schema.Types.ObjectId, required: true },**
active: Boolean,
createdDate: { type: Date, default: Date.now},
lastUpdatedDate: { type: Date, default: Date.now}
},
{
toJSON: { virtuals: true },
toObject: {virtuals: true}
}
);
moduleSchema.virtual('categoryData', {
ref: 'Category',
localField: 'categoryId',
foreignField: '_id',
justOne: true
});
If I am runing the api for first model so I am geeting the data of dependencris(category) form the 2nd model.
Here is the result I am getting:
{
"_id": "59410a5dab85113a7cdc0507",
"packageGroupCode": "P02",
"packageGroupName": "package2",
"description": "package2",
"moduleId": "5940efeadb8fe72a8cb4e056",
"active": true,
"__v": 0,
"lastUpdatedDate": "2017-06-14T11:30:43.798Z",
"createdDate": "2017-06-14T11:30:43.798Z",
"moduleData": {
"_id": "5940efeadb8fe72a8cb4e056",
"moduleName": "module name",
**"categoryData": null,**
**"id": "5940efeadb8fe72a8cb4e056"**
},
**"id": "59410a5dab85113a7cdc0507"**
}
Can anybody tell me what I have to do to ignore catergoryData in moduleDate and id repetition.
Expexted JSON should be like this:
{
"_id": "59410a5dab85113a7cdc0507",
"packageGroupCode": "P02",
"packageGroupName": "package2",
"description": "package2",
"moduleId": "5940efeadb8fe72a8cb4e056",
"active": true,
"__v": 0,
"lastUpdatedDate": "2017-06-14T11:30:43.798Z",
"createdDate": "2017-06-14T11:30:43.798Z",
"moduleData": {
"_id": "5940efeadb8fe72a8cb4e056",
"moduleName": "module name"
}
}

if your result is stored in "theObject" you can remove the keys like this:
delete theObject["id"];
delete theObject["moduleData"]["id"];
delete theObject["moduleData"]["categoryData"];

Related

Issue when trying to store some data in a MongoDB data model

I want to store some data in the following MongoDB model:
import mongoose from 'mongoose';
const orderSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
orderItems: [
{
name: { type: String, required: true },
qty: { type: Number, required: true },
image: { type: String, required: true },
price: { type: Number, required: true },
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Product',
},
},
],
shippingAdress: {
address: { type: String, required: true },
city: { type: String, required: true },
postalCode: { type: String, required: true },
state: { type: String, required: false },
country: { type: String, required: true },
},
paymentMethod: {
type: String,
required: true,
},
paymentResult: {
id: { type: String },
status: { type: String },
update_time: { type: String },
email_address: { type: String },
},
taxPrice: {
type: Number,
required: true,
default: 0.0,
},
shippingPrice: {
type: Number,
required: true,
default: 0.0,
},
totalPrice: {
type: Number,
required: true,
default: 0.0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
paidAt: {
type: Date,
},
isDelivered: {
type: Boolean,
required: true,
default: false,
},
deliveredAt: {
type: Date,
},
},
{ timestamps: true }
);
const Order = mongoose.model('Order', orderSchema);
export default Order;
But I am not able to post the following sample data to the database
{
"orderItems": [
{
"product": "6060f2eb6bea3f2280c08a4c",
"name": "Airpods Wireless Bluetooth Headphones",
"image": "/images/airpods.jpg",
"price": 89.99,
"qty": 3
}
],
"user": "6060f2eb6bea3f2280c08a4b",
"shippingAddress": {
"address": "1st Avenue Main St",
"city": "Boston",
"postalCode": "02011",
"country": "USA"
},
"paymentMethod": "Stripe",
"itemsPrice": 269.97,
"taxPrice": 40.50,
"shippingPrice": 20.00,
"totalPrice": 330.47
}
as I get the following error:
By the way I am trying to get that done through Postman
"Order validation failed: shippingAdress.country: Path shippingAdress.country is required., shippingAdress.postalCode: Path shippingAdress.postalCode is required., shippingAdress.city: Path shippingAdress.city is required., shippingAdress.address: Path shippingAdress.address is required."
You have a typo in your Model shippingAdress. It should be shippingAddress.

mongoose index don't create

I tried to create index from two fields in mongoose Schema but it didn't work
this two fields are the id of two other schema and i want to be unique
i get the ids from "mongoose.Schema.ObjectId"
this is my code:
const reviewSchema = new mongoose.Schema(
{
review: {
type: String,
required: [ true, 'Review can not be empty!' ],
},
rating: {
type: Number,
min: 1,
max: 5,
},
createdAt: {
type: Date,
default: Date.now,
},
tour: {
type: mongoose.Schema.ObjectId,
ref: 'Tour',
required: [ true, 'Review must belong to a tour.' ],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [ true, 'Review must belong to a user' ],
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);
reviewSchema.index({ tour: 1, user: 1 }, { unique: true });
I found it was a bug from atlas ,I don't know why but it couldnt create index with options
I create a local data base and now it works

How to select all the experiences from a specific user profile in Node and Mongoose from embedded schema

I'm building an API in NodeJS Express and MongoDB using Mongoose.
I created a Profile model that embeds the Experience schema.
Like:
{
"_id": "5e26ff6d5be84a3aeeb2f7bb",
"username": "some"
<fields of the profile>
"experience": [
{
"image": "",
"createdAt": "2020-01-21T13:41:01.873Z",
"updatedAt": "2020-01-21T13:41:01.873Z",
"_id": "5e26ff6d5be84a3aeeb2f7bc",
"title": "Senior Dev",
"role": "Dev",
"company": "ArosaDev",
"startDate": "2018-12-03T23:00:00.000Z",
"endDate": null,
"description": "",
"area": ""
}
],
"createdAt": "2020-01-21T13:41:01.874Z",
"updatedAt": "2020-01-21T13:41:01.874Z",
"__v": 0
}
The problem is I have to create an endpoint GET which gets for one profile all the experiences.
experienceRouter.get("/:username", async(req, res) => {
console.log(req.params.username);
const experiences = await Profiles.findOne({"username":req.params.username} ??? );
res.send(experiences);
});
I know I should select the embedded field experience and get back all the experiences but I don't know how should I do with Mongoose in my route.
I don't know what comes next after I select the username and how I can select the all experience for the username I'm requested.
I'm new to this and cannot find any good references explaining to me that for good.
I will appreciate an example of how a route like this should be done.
My model:
// Profile Schema model
// Embedded we have the Experience as []
const mongoose = require("mongoose");
const { isEmail } = require("validator");
const experienceSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date
},
description: {
type: String
},
area: {
type: String
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
})
const profileSchema = new mongoose.Schema({
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: true,
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [
experienceSchema
],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
});
const collectionName = "profiles";
const Profile = mongoose.model(collectionName, profileSchema);
module.exports = Profile;
replace your code with below
You can mention your fields which you need in the second argument of function this is called projection as mentioned
So for including of fields use 1 and for excluding fields use 0
experienceRouter.get("/:username", async(req, res) => {
console.log(req.params.username);
const profile = await Profiles.findOne({"username":req.params.username},{experience:1 ,username:1, _id:0}).lean();
res.send(profile);
});

aggregate base on match in the second collection

I'm using mongodb.
And I have collection Treatments and collection Patients.
I want to find all treatments that their patient.createdBy is equal to the user who asking the data.
So I tried this
const reminders = await Treatment.aggregate([
{
$lookup: {
from: 'patients',
localField: 'patientId',
foreignField: '_id',
as: 'patient'
}
},
{ $project: { reminders: 1, reminderDate: 1 } },
{ $match: { 'patient.createdBy': { $eq: req.user._id } } }
]);
According to some examples that i saw, it's should work like this.
But it's return me an empty array
If I remove the $match its return me object like this
{
"_id": "5d1e64bdc1506a00045c6a6f",
"date": "2019-07-04T00:00:00.000Z",
"visitReason": "wewwe",
"treatmentNumber": 2,
"referredBy": "wewew",
"findings": "ewewe",
"recommendations": "ewew",
"remarks": "wwewewe",
"patientId": "5cc9a50fd915120004bf2f4e",
"__v": 0,
"patient": [
{
"_id": "5cc9a50fd915120004bf2f4e",
"lastName": "לאון",
"momName": "רןת",
"age": "11",
"phone": "",
"email": "",
"createdAt": "2019-05-01T13:54:23.261Z",
"createdBy": "5cc579d71c9d44000018151f",
"__v": 0,
"firstName": "שרה",
"lastTreatment": "2019-08-02T14:20:08.957Z",
"lastTreatmentCall": true,
"lastTreatmentCallDate": "2019-08-04T15:17:35.000Z"
}
]
}
this is patient schema
const patientSchema = new mongoose.Schema({
firstName: { type: String, trim: true, required: true },
lastName: { type: String, trim: true, required: true },
momName: { type: String, trim: true },
birthday: { type: Date },
age: { type: String, trim: true },
lastAgeUpdate: { type: Date },
phone: { type: String, trim: true },
email: { type: String, trim: true },
createdAt: { type: Date, default: Date.now },
createdBy: { type: mongoose.Schema.Types.ObjectId, required: true },
lastTreatment: { type: Date },
lastTreatmentCall: { type: Boolean },
lastTreatmentCallDate: { type: Date }
});
And this is treatment schema
const treatmentSchema = new mongoose.Schema({
date: { type: Date, default: new Date().toISOString().split('T')[0] },
visitReason: { type: String, trim: true },
treatmentNumber: { type: Number, required: true },
referredBy: { type: String, trim: true },
findings: { type: String, trim: true },
recommendations: { type: String, trim: true },
remarks: { type: String, trim: true },
reminders: { type: String, trim: true },
reminderDate: { type: Date },
patientId: { type: mongoose.Schema.Types.ObjectId }
});
what I'm missing
You have vanished your patient field in the second last $project stage. So instead use it at the end of the pipeline. Also you need to cast your req.user._id to mongoose objectId
import mongoose from 'mongoose'
const reminders = await Treatment.aggregate([
{
$lookup: {
from: 'patients',
localField: 'patientId',
foreignField: '_id',
as: 'patient'
}
},
{ $match: { 'patient.createdBy': { $eq: mongoose.Types.ObjectId(req.user._id) } } },
{ $project: { reminders: 1, reminderDate: 1 } }
])
I think you can add using the pipeline, like below
const reminders = await Treatment.aggregate([
{
$lookup: {
from: 'patients',
localField: 'patientId',
foreignField: '_id',
as: 'patient',
pipeline: [{ $match: { 'age': { $eq: "100" } } }]
}
},
]);

Mongoose retrieves null of my document inner array

I have a collection that one of it's fields is an array of strings. but when I query with find(), it returns null while in the MongoDB Compass I can see it.
here is my document:
{
"_id":"5d4894f23f86a41b84303795",
"position":1,
"garmentTitle":"My first Garment",
"garmentURL":"www.firstgarment.com",
"garmentPictureURL":"/images/first.png",
"brand":"5d49e60e4eface2a00ac58d7",
"garmentMaker":"5d49e8854eface2a00ac58d9",
"garmentPurpose":"5d49e8754eface2a00ac58d8",
"gender":"5d37546f2f8c280adc60b3fe",
"garmentCategory":"5d3a4c7f447a7a3afc71a746",
"fabricComposition":null,
"garmentSizes":["5d4d211f0fe9591facb9a268"] // <<== This is mentioned array
}
Here is my defined schema :
var mongoose = require('mongoose');
var gardataSchema = new mongoose.Schema({
position: {
type: Number,
unique: true,
required: true
},
garmentTitle: {
type: String,
unique: true,
required: true
},
garmentURL: {
type:String,
required:false
},
garmentPictureURL: {
type: String,
required:false,
},
brand:{
type: mongoose.Schema.Types.ObjectId,
ref: 'BR',
required: false
},
garmentMaker: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GM',
required: false
},
garmentPurpose: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GP',
required: false
},
garmentSizes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'GS',
required: false
}],
gender: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GN',
required: false
},
garmentCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GC',
required: false
},
fabricComposition: {
type: mongoose.Schema.Types.ObjectId,
ref: 'FC',
required: false
},
});
var collectionName = 'garmentData';
mongoose.model('GD', gardataSchema, collectionName);
And here is my controller:
module.exports.getOneGarmentDataByID = function (req, res) {
GD.find({_id:req.params.id})
.exec()
.then(result => {
res.status(200).json(result);
})
.catch(err => {
console.log(err);
res.status(500).json({error:err});
});
}
Requested route:
http://localhost:3000/gar/getonegarmentdatabyid/5d4894f23f86a41b84303795
And final result is :
[
{
"garmentSizes": null, <<== returns null!!!
"_id": "5d4894f23f86a41b84303795",
"position": 1,
"garmentTitle": "My first Garment",
"garmentURL": "www.firstgarment.com",
"garmentPictureURL": "/images/first.png",
"brand": "5d49e60e4eface2a00ac58d7",
"garmentMaker": "5d49e8854eface2a00ac58d9",
"garmentPurpose": "5d49e8754eface2a00ac58d8",
"gender": "5d37546f2f8c280adc60b3fe",
"garmentCategory": "5d3a4c7f447a7a3afc71a746",
"__v": 0,
"fabricComposition": null
}
]
As you can see it returns null instead of array object.
Do you have any idea?
Schema type is wrong. Should be:
garmentSizes: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'GS'
}
],
required: false
}

Resources