Save to mongDB 3rd level nested array of objects - node.js

I am trying to save data into a teams database with the following model sample. This is a 3rd level nested Array of Objects for MongoDB using Mongoose.
const teamSchema = new Schema({
name: {
type: String,
required: true
},
who: {
type: String,
required: true
},
textManifesto: {
type: String,
required: false
},
videoManifesto: {
type: String,
required: false
},
competencies: {
competency: [{
type: Schema.Types.ObjectId,
ref: 'Competency',
required: true
}]
},
members: [{
member: {
type: Schema.Types.ObjectId,
ref: 'Applicant',
required: true
},
position: {
type: String,
required: true
},
memberCompetencies: {
competency: [{
type: Schema.Types.ObjectId,
ref: 'Competency',
required: true
}]
},
evaluatedCompetencies: {
competency: [{
type: Schema.Types.ObjectId,
ref: 'Competency',
required: false
}]
}
}],
},
I basically used push method to push the array elements into a variable and then try to save into the database but the database comes up empty for Members.memberCompetencies. It just shows and empty array.
//Map Team Competencies
for (let competency of teamCompetencies) //array is your array variable, i suppose
newTeamCompetencies.push({ competency: competency, _id: mongoose.Types.ObjectId() });
//console.log(newTeamCompetencies);
//Map My Competencies
for (let competency of myCompetencies) //array is your array variable, i suppose
newMyCompetencies.push({ _id: mongoose.Types.ObjectId(), competency: competency });
// console.log(newMyCompetencies);
team = await new Team({
name: teamName,
textManifesto: textManifesto,
who: who,
});
//save collective expected competencies of team
team.competencies = newMyCompetencies;
//save member details
team.members = ({
member: res.locals.applicant._id,
position: 'Leader',
memberCompetencies: newMyCompetencies,
})
team = await team.save();
console.log(team.members);
Expected result
[{"memberCompetencies":{ '0':
{ _id: 5d1e128b2a9f1c74907e5ba9,
competency: '5d1dd97206660707754eefb3' },
'1':
{ _id: 5d1e128b2a9f1c74907e5baa,
competency: '5d1dd9d506660707754eefb4' } },,"evaluatedCompetencies":{"competency":[]},"_id":"5d1e1393f640587531b0fd48","member":"5d19999df6f9c678e891af14","position":"Leader"}]
Actual result
[{"memberCompetencies":{"competency":[]},"evaluatedCompetencies":{"competency":[]},"_id":"5d1e1393f640587531b0fd48","member":"5d19999df6f9c678e891af14","position":"Leader"}]

Found the problem. The model was wrong. Members section of the model should be
members: {
member: {
type: Schema.Types.ObjectId,
ref: 'Applicant',
required: true
},
position: {
type: String,
required: true
},
memberCompetencies: {
competency: [{
type: Schema.Types.ObjectId,
ref: 'Competency',
required: true
}]
},
evaluatedCompetencies: {
competency: [{
type: Schema.Types.ObjectId,
ref: 'Competency',
required: false
}]
}
},
It should be
members: {...}
NOT
members: [{...}]

Related

mongoose query, find where array of tags id includes at least one id from query tags array

I have article schema :
const schema = new Schema({
title: {
type: String,
unique: true,
required: [true, 'Title is required'],
},
slug: {
type: String,
slug: "title",
slugPaddingSize: 2,
unique: true
},
editor: {
type: String
},
duration: {
type: String
},
image: Object,
category: { type: Schema.Types.ObjectId, ref: 'Category' },
language: { type: Schema.Types.ObjectId, ref: 'Language' },
level: { type: Schema.Types.ObjectId, ref: 'Level' },
tag: [{ type: Schema.Types.ObjectId, ref: 'Tag' }]
},{
timestamps: true
})
Need to find every articles which tag contains at least one of tag id from req.query params (it can be also array of id's or just single id).
Thanks

How to populate a field from an array of objects of another a collection that it's not a model in mongoose

for example I have two collections,
1st collection schema (test):
const testSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: [true, "le nom d'examen est obligatoire!"],
},
year: {
type: Number,
required: [true, "l'année est obligatoire"],
},
questions: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Question",
},
],
clinicalCase: [
{
_id: {
type: mongoose.Schema.Types.ObjectId,
index: true,
required: true,
auto: true,
},
text: String,
},
],
},
{
timestamps: true,
}
);
2nd schema:
const questionSchema = new mongoose.Schema({
test: {
type: mongoose.Schema.Types.ObjectId,
ref: "Test",
},
questionString: {
type: String,
required: [true, "la question est obligatoire"],
},
explanation: String,
options: [option],
tag: {
type: mongoose.Schema.Types.ObjectId,
ref: "Tag",
},
clinicalCase: {
type: mongoose.Schema.Types.ObjectId,
}
});
so my problem here is when I query 2nd schema, I want to populate the clinicalCase field with 1st schema, is it possible to do it with populate method or not ? can you help me with it please

Building Mongoose models for an Express Nodejs API with refpath

I'm new to building rest api's with mongoose and express and have a question on how to use refPath correctly on my Models files and allowing for an array of items.
Below I have included the code for a model (built thus far) and would love any input on if I'm even close to building this correctly.
I will also include a screenshot that visually depicts the relationships I'm trying to create.
Those who answer questions here are GODS and I appreciate all the help this community has given me over the years!
const mongoose = require("mongoose");
const slugify = require("slugify");
const AlertSchema = new mongoose.Schema({
parentId: {
type: mongoose.Schema.ObjectId,
required: true,
refPath: "parentModel",
},
parentModel: {
type: String,
required: true,
enum: ["orgs", "clients"],
},
status: { type: String, default: "no-status" },
departments: [{ type: mongoose.Schema.Types.ObjectId, ref: "orgs" }],
createdAt: { type: Date, default: Date.now },
createdByType: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
createdById: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
groups: [{ type: String, default: "unGrouped" }],
stage: [{ type: mongoose.Schema.Types.ObjectId, ref: "stages" }],
children: { type: String },
resource: {
type: String,
match: [
/https?:\/\/(www\.)?[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()#:%_\+.~#?&//=]*)/,
"Please use a valid URL with HTTP or HTTPS",
],
},
notes: [{ type: mongoose.Schema.Types.ObjectId, ref: "notes" }],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "comments" }],
priority: { type: String },
assignedTo: [{ type: mongoose.Schema.Types.ObjectId, ref: "users" }],
title: {
type: String,
required: [true, "Please add a title"],
maxlength: [50, "Title cannot be more than 50 characters"],
},
message: {
type: String,
required: [true, "Please add a message"],
maxlength: [500, "Message cannot be more than 500 characters"],
},
slug: String,
});
//create alert slug from the title
AlertSchema.pre("save", function (next) {
console.log("Slugify Ran", this.name);
this.slug = slugify(this.title, { lower: true });
next();
});
module.exports = mongoose.model("Testalert", AlertSchema);
Desired relationships diagram:

mongoose is not able to populate ref id with default value empty object

My schema is as shown below:
const order = new Schema({
order_status: Number,
foodtruck_id: { type: Schema.Types.ObjectId, ref: 'foodtruck' },
customer_id: { type: Schema.Types.ObjectId, ref: 'user' },
items: [{ type: Schema.Types.ObjectId, ref: 'items' }],
user_type: Boolean,
order_time: Date,
order_rating: { type: Number, default: 5.0 },
order_issue_comments: String,
order_special_instruction: String,
order_total: Number,
order_location: String,
order_coupon_code: String,
payment_id: { type: Schema.Types.ObjectId, ref: 'payment' },
order_meta: { type: Schema.Types.Mixed, ref: 'order_sub_info', default: {} }
}, { versionKey: false }, { minimize: false });
my query is as shown below:
order.find({
'foodtruck_id': foodtruck_id.trim()
}).populate('customer_id', {
'_id': 1,
'user_name': 1,
'email_id': 1,
'ph_no': 1,
'login_type': 1
}).populate('items').
populate('order_meta', 'order_otp').exec((err, orderList) => {
if (err) res.json({
status: '500',
message: err
});
else {
console.log("called");
res.json({
status: '200',
message: 'Order list',
data: orderList
});
}
});
For this query,it is giving me Cast to ObjectId failed for value at path _id as order_meta has default value {}. How to have effective populate query so that It can take care of this testcase?
It is not good idea to put empty object in a place, where reference id is expected. Both - for having problem with populate and for common sense too (if it is field which has reference, it should be null/undefined or reference itself).
It is common that you want to transform your data at some endpoint, but it should not interfere with database or business logic of application.
You can defined toJSON method that should be used for your model. In your case
const order = new Schema({
order_status: Number,
foodtruck_id: { type: Schema.Types.ObjectId, ref: 'foodtruck' },
customer_id: { type: Schema.Types.ObjectId, ref: 'user' },
items: [{ type: Schema.Types.ObjectId, ref: 'items' }],
user_type: Boolean,
order_time: Date,
order_rating: { type: Number, default: 5.0 },
order_issue_comments: String,
order_special_instruction: String,
order_total: Number,
order_location: String,
order_coupon_code: String,
payment_id: { type: Schema.Types.ObjectId, ref: 'payment' },
order_meta: { type: Schema.Types.ObjectId, ref: 'order_sub_info'}
}, { versionKey: false }, { minimize: false });
order.options.toJSON = {
transform(zipRequestDocument, ret, options) { // eslint-disable-line no-unused-vars
if (!ret.order_meta){
ret.order_meta = {};
}
},
};

Mongoose Complex Query by Subdocument

I need to find a Project by either Owner, Manager, or one of the Team Members. Here's how the schema looks like:
var project = new mongoose.Schema({
title: { type: String, require: true },
slug: { type: String, require: true },
description: { type: String, require: true },
descriptionHtml: { type: String, require: true },
nextVanityId: { type: Number, default: 1 },
owner: { type: ObjectId, ref: 'Member', require: true },
teams: [{ type: ObjectId, ref: 'Team' }],
managers: [{ type: ObjectId, ref: 'Member' }]
};
var team = new mongoose.Schema({
name: { type: String, require: true },
slug: { type: String, require: true },
project: { type: ObjectId, require: true, ref: 'Project' },
created: { type: Date, require: true, default: Date.now },
members: [{ type: ObjectId, default: null, ref: 'Member' }]
};
My query method looks like this:
function findByMember (member, done) {
var id = member._id;
Project
.find()
.or([
{ owner: id },
{ managers: id },
{ 'teams.members': id }
])
.exec(done);
}
Currently it works for both owners and managers, but I'm drawing blanks when querying the members collection of each team. What should I use?
In order for your query to work you can approach the problem in 2 ways.
Solution 1
Have your teams as an embedded document in projects:
var team = new mongoose.Schema({
name: { type: String, require: true },
slug: { type: String, require: true },
project: { type: ObjectId, require: true, ref: 'Project' },
created: { type: Date, require: true, default: Date.now },
members: [{ type: ObjectId, default: null, ref: 'Member' }]
};
var project = new mongoose.Schema({
title: { type: String, require: true },
slug: { type: String, require: true },
description: { type: String, require: true },
descriptionHtml: { type: String, require: true },
nextVanityId: { type: Number, default: 1 },
owner: { type: ObjectId, ref: 'Member', require: true },
teams: [team],
managers: [{ type: ObjectId, ref: 'Member' }]
};
This way your project document will have the team information and the teams.members makes sense.
Solution 2
Denormalise your Team data to have only the relevant information embedded:
var team = new mongoose.Schema({
name: { type: String, require: true },
slug: { type: String, require: true },
project: { type: ObjectId, require: true, ref: 'Project' },
created: { type: Date, require: true, default: Date.now },
members: [{ type: ObjectId, default: null, ref: 'Member' }]
};
var project = new mongoose.Schema({
title: { type: String, require: true },
slug: { type: String, require: true },
description: { type: String, require: true },
descriptionHtml: { type: String, require: true },
nextVanityId: { type: Number, default: 1 },
owner: { type: ObjectId, ref: 'Member', require: true },
teams: [{
_id: { type: ObjectId, ref: 'Team' },
members: [{ type: ObjectId, default: null, ref: 'Member' }]
}],
managers: [{ type: ObjectId, ref: 'Member' }]
};
In this second approach you need to keep the Team document and the denormalised data in sync.

Resources