MongoDB how to populate - node.js

i have 2 Schemas. 1. ProjectBug and 2. ProjectBugImage so i am uploading an image and saving it in a different collection.
See the Schemas below Number 1
const mongoose = require('mongoose');
let ProjectBugSchema = new mongoose.Schema({
projectID: {
type: mongoose.Schema.ObjectId
},
completed: {
type: Boolean,
default: false
},
components: Boolean,
groupSelect: String,
appsAffected: Array,
title:String,
description:String,
selectedcomponents: Array,
resources: Array,
isLive: Boolean,
issueNumber: String,
projectBugMianID: String,
date: String,
reporter: String,
foundOn: String,
testedDevel: Boolean,
testedLive: Boolean,
testedStaging: Boolean,
platform: String,
linked: Boolean,
linkedTo: String,
type: String,
appPassword: String,
appUsername: String,
percentageComplete: String,
status: String,
releaseID: {
type: String
},
createdAt:{
type: Date,
default: Date.now
},
},{
timestamps: true
},{
toJSON: { virtuals: true },
toObject: { virtuals: true },
});
ProjectBugSchema.virtual('bugImage', {
ref: 'bugImage',
localField: '_id',
foreignField: 'bugID'
});
module.exports = mongoose.model('ProjectBug', ProjectBugSchema);
Number 2
const mongoose = require('mongoose')
const ProjectBugImageSchema = new mongoose.Schema({
bugID:{
type: mongoose.Schema.ObjectId,
},
fileName:{
type: String,
},
path: {
type: String,
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('bugImage', ProjectBugImageSchema)
And then i my controller i have
exports.index = (req, res) => {
ProjectBug.find({})
.populate('bugImage', '', null, { })
.exec((err, bugs) => {
if(err){
res.send(err)
}
res.send(bugs)
})
}
Strange thing is this used to work, and now it does not anymore, as you can see i have the ProjectbugSchema.vertual('bugImage', {...}) with the ref to bugImage and the local field to _id and foreignField to 'bugID' so that would match the ProjectBugImage.bugID with the local field of ProjectBug _id
what i get when i get data. Response
{
"completed": false,
"appsAffected": [],
"selectedcomponents": [],
"resources": [],
"_id": "5e8195fcc7939644cc322793",
"groupSelect": "",
"title": "asdasd",
"description": "<p>asdasd</p>",
"issueNumber": "",
"date": "2020-03-30",
"reporter": "5d305d3a9fe62f5578d0e209",
"type": "bug",
"foundOn": "",
"isLive": false,
"testedDevel": false,
"testedLive": false,
"testedStaging": false,
"platform": "",
"appUsername": "",
"appPassword": "",
"percentageComplete": "",
"status": "",
"createdAt": "2020-03-30T06:47:24.751Z",
"updatedAt": "2020-03-30T06:47:24.751Z",
"__v": 0
}
All i want is all the images that has bugID to be with the projectbug where the ids match. and put them into an array. Am i doing something wrong in the populate?? or somewhere else?

So i figured it out, in the model where i create the virtual i had
,{
toJSON: { virtuals: true },
toObject: { virtuals: true },
});
I took that out closed the Schema and underneath i put
ProjectBugSchema.set('toObject', { virtuals: true })
ProjectBugSchema.set('toJSON', { virtuals: true })
Small change but seems to be the answer.

Related

removing object id from an array not working in mongoose

I am trying to remove logged-in users objectId from an array field called interest but it doesnt work.
model
const projectSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
isShowcase: {
type: Boolean,
required: false,
default: false
},
isCollab: {
type: Boolean,
required: false,
default: false
},
title: {
type: String,
required: true,
trim: true
},
description: {
type: String,
required: true,
trim: true
},
thumbnail: {
type: String,
required: true,
trim: true
},
media: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Media',
required: false,
}],
skillsNeeded: [{
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'Skill'
}],
contributors: [{
user: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'User'
},
role: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'Skill'
},
required: false,
}],
isOpen: {
type: Boolean,
required: false,
default: false
},
interest: [{
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'User'
}]
}, {
timestamps: true
})
the route to remove interest
// delete an interest on a collaboration
router.delete('/api/project/:id/apply', auth, async (req, res) => {
const project = await Project.findOne({ _id: req.params.id, isOpen: true })
if (!project) {
res.status(500).send({ "message": "you cannot show interest at the moment" })
}
try {
project.interest = project.interest.filter((objid) => {
return objid != req.user._id
})
await project.save()
console.log(project);
return res.status(204).send(project)
} catch (e) {
res.status(500).send()
}
})
below is the project I am trying to remove a users object id from the interest
{
"isShowcase": false,
"isCollab": true,
"media": [],
"skillsNeeded": [
"5f9fc49a0282a127e4aca0fa",
"5faa596e9d54a92ab805a7a9"
],
"isOpen": true,
"interest": [
"5fa35ac783fe7042fcbbf12a"
],
"_id": "5fb98a36cc3b62aa788adbe0",
"title": "American gods",
"description": "it a story about a war between new and old gods",
"thumbnail": "https://images-na.ssl-images-amazon.com/images/S/pv-target-images/fc3ef790bcbc143d2b5b76b454b7cdcc68a19d60f629e328121713590294a895._RI_V_TTW_.jpg",
"owner": "5fa4f0af4a7bf5471c41e225",
"contributors": [],
"createdAt": "2020-11-21T21:44:22.818Z",
"updatedAt": "2020-11-22T08:22:02.377Z",
"__v": 1
}
when I run the delete request to that route, I get status 204 but the project is not returned, and when I check back on the project, I still see the project with the user's objectID still in the array.
the logged-in users object
{
"skills": [],
"isEmailConfirmed": false,
"isAdmin": false,
"isActive": true,
"hasSpecialPrevilege": false,
"_id": "5fa35ac783fe7042fcbbf12a",
"fullname": "olayinka odedeyi",
"username": "yinkus",
"email": "olayinkaodedeyi#gmail.com",
"createdAt": "2020-11-05T01:52:07.378Z",
"updatedAt": "2020-11-22T10:37:08.307Z",
"__v": 4,
"bio": "a great girl"
}
Please, what could be preventing the logged-in user id from getting removed when the delete route to do that is run?
The problem lies in how you are comparing the two objectids in filter function. You need to use .equals() method available on objectId instance.
project.interest = project.interest.filter((objid) => {
return !objid.equals(req.user._id)
})

soft delete from an array of objects using mongoose

I am trying to soft delete an object from array using mongoose-delete lib
I Have a schema like that :
const mongooseDelete = require('mongoose-delete');
const number = new Schema({
allocation_date: { type: Date, required: true, default: Date.now() },
number: { type: String, required: true },
virtual_number: { type: String, required: true },
virtual_number_id: { type: Number, required: true, unique: true },
});
const schema = new Schema(
{
allocation_id: { type: String, required: true, unique: true },
numbers: { type: [number], required: true },
},
{
timestamps: true,
},
);
number.plugin(mongooseDelete, { deletedAt : true });
exemple data :
{
"_id": "5f7c14f9388ee9ebc0b28abc",
"allocation_id": "5_2_9387323_1"
"numbers": [
{
"allocation_date": "2020-10-06T06:55:41.197Z",
"_id": "5f7c14f997d996f7988fe8cf",
"number": "*********",
"virtual_number": "**********",
"virtual_number_id": *******,
},
{
"allocation_date": "2020-10-06T06:59:41.197Z",
"_id": "5f7c14f997d996f7988fe8cf",
"number": "*********",
"virtual_number": "**********",
"virtual_number_id": *******
}
],
"createdAt": "2020-10-06T06:55:53.367Z",
"updatedAt": "2020-10-06T06:55:53.367Z",
}
When I tried to use the delete method as follow :
const deleteNumber = await VirtualNumberAllocation.findOne(
{
'numbers.virtual_number': virtualNumber.virtualNumber,
allocation_id: virtualNumber.allocationID,
});
const activeVirtualNumber = deleteNumber.numbers.filter(
(number) => number.virtual_number==virtualNumber.virtualNumber,
);
await activeVirtualNumber[0].delete();
I'm getting this error in console:
mongoose: calling `save()` on a subdoc does **not** save the document to MongoDB, it only runs save middleware. Use `subdoc.save({ suppressWarning: true })` to hide this warning if you're sure this behavior is right for your app.
Does anyone have experience with soft deleting from an array using the library?

How to merge/partial update on sub document which is an array

const projectSchema = mongoose.Schema({
title: { type: String, required: true, trim: true },
projectId: { type: String, required: true, trim: true, unique: true },
company: { type: mongoose.Schema.Types.ObjectId, ref: 'Company', required: true },
description: { type: String, trim: true },
remark: { type: String, trim: true },
active: { type: Boolean, required: true },
participants: [{
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
active: { type: Boolean, required: true },
timeSpent: [{
date: { type: Date, required: true },
hour: { type: Number, required: true }
}]
}]
});
I've searched through a lot of SO questions, but none of them seems to be able to solve my problem. Sometimes I need to update one of the field from one participant, I want to update it in a way that when
//this data to apply update on the first element of the participants array
const partialParticipantUpdate = {
active: false
}
can be easily update to the sub document without rewriting all the other property. Basically like findByIdAndUpdate not findByIdAndReplace. I've tried aggregation but its only for reading data not writing data. I've also tried
let p = await Project.update({
"_id": mongoose.Types.ObjectId("5ee22f90e550c32194fb7a91"),
"participants._id": mongoose.Types.ObjectId("5ee4b5bd4fdcdb0c8813f5f5")
}, {
"$set": { "participants.0": partialParticipantUpdate } })
console.log(p);
However some of the participants property is removed after the update
{
"_id": { "$oid": "5ee22f90e550c32194fb7a91" },
"title": "Geo Chart Example",
"projectId": "myId",
"company": { "$oid": "5ee22ca46c7fc4358ced20a1" },
"description": "asd",
"remark": "re",
"active": true,
"participants": [{
"_id": { "$oid": "5ee4f37f4d8c234cc02d405a" },
"active": false,
"timeSpent": []
},
{
"_id": { "$oid": "5ee4c8a955ed7f23445a3cbc" },
"user": { "$oid": "5ee0bdc318998236706b5e5a" },
"active": true,
"timeSpent": []
}
],
"__v": 0
}
The update did change the first participant's "active" property from true to false, but it also lost some of the property like "user" and "timeSpent" array will get emptied as well
Finally I found the solution
const partialParticipantUpdate = {
active: false
}
let p = await Project.findById("5ee22f90e550c32194fb7a91");
let x = p.participants.id("5ee7224be4e4386084b87d3c");
x.set(partialParticipantUpdate);
let response = await p.save({ validateBeforeSave: false });
console.log(response);
The only downside is that I have to communicate with mongoose twice

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);
});

Issue with virtual population in Mongoose

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"];

Resources