Reference to an object inside an array in another collection - node.js

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

Related

How to populate the nested object id

Here is my schema, I want to join the address in ScheduleSchema to addressDetails in CustomerSchema to list the all address details in a table, how to achieve this?
const CustomerSchema = new Schema(
{
addressDetails: [
{
block: {
type: String,
},
building_number: {
type: String,
},
is_default: {
type: Boolean,
default: false,
},
},
],
},
const SheduleSchema = new Schema(
{
customer: {
type: ObjectId,
required: true,
ref: "Customer",
},
address: {
type: ObjectId
},
week: {
type: String
}
},
schemaOptions
);
you must to use ref and populate for address key check the documnetation, and add this property to the Schemas like this :
const SheduleSchema = new Schema(
{
customer: {
type: ObjectId,
required: true,
ref: "Customer",
},
address: {
type: mongoose.Types.ObjectId,
ref: "Customer",
},
week: {
type: String
}
},
schemaOptions
);
use populate for get all addresses wit details
await SheduleSchema.find().populate({
path : "address"
})

How can I fetch data from more than one collections in Mongo using mongoose

How I can display details data of a profile when those data stored in more than one collections
tried this link
const profile = await userKushala
.find({_id: '5cd407c741f9e7373f10362c'})
.populate({
path: 'settingId',
populate : {
path : 'specialty',
populate : {
path: 'hospital_attached'
}
}
})
// First Collection(Basic Info.)
const userRegisterSchema = new Schema({
userType: {
type: String,
required: true
},
mobile: {
type: Number,
required: true,
unique: true
},
userId: {
type: String,
required: true
},
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
gender: {
type: String,
required: true
},
settingId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'providerSetting'
}
})
// Second Collection(Detailed Info.)
const serviceProfileUpdate = new Schema({
specialty :[{
type: mongoose.Schema.Types.ObjectId,
ref: 'specialtyMasterCsv'
}],
category: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'categoryMasterCsv'
}],
subscriptionPlan: {
type: mongoose.Schema.Types.ObjectId,
ref: 'planMasterCsv'
},
medicine: {
type : mongoose.Schema.Types.ObjectId,
ref: 'kushalaUser'
}
})
//Data in Mongo
{
"_id":{
"$oid":"5cd93ea6bd96e43664f49bf3"
},
"specialty":[
{
"$oid":"5cda85f26ffe60259a75ba17"
},
{
"$oid":"5cda85f26ffe60259a75ba18"
}
],
"category":[
{
"$oid":"5cda85f26ffe60259a75ba17"
},
{
"$oid":"5cda85f26ffe60259a75ba18"
}
],
"subscriptionPlan":{
"$oid":"5cda85f26ffe60259a75ba17"
},
"medicine":{
"$oid":"5cd407c741f9e7373f10362c"
},
"__v":{
"$numberInt":"0"
}
}
Expected Result will be from all the collections data should fetch but with the code I have it's giving the result till specialty but after that it's printing only ObjectID
This is what I have done and it's working still if anyone has a better solution, most welcome. Hope it helps someone.
const profile = await userKushala
.find({_id: '5cd407c741f9e7373f10362c'})
.populate({
path: 'settingId',
populate : {
path : 'specialty category subscriptionPlan medicine'
}
})
Try to populate like this:
.populate([
{path:'category',model:'categoryMasterCsv'},
{path:'subscriptionPlan',model:'planMasterCsv'},
{path:'medicine',model:'kushalaUser'}])
This is the method I use daily.

How do you run pre functions when creating a new document for the first time from a model with mongoose

This is quite a large schema, but I'm trying to handle validation of a list of ObjectId's myself because I don't want errors returned when the passed values are objects
var thingSchema = new Schema({
// origin Id to relate states when new states are created
thingId: { type: Schema.ObjectId, ref: 'thing' },
hash: { type: String },
// organzational properties
title: { type: String },
names: { type: [String] },
realms: { type: [String], default: ["all", "things"] },
// values
text: { type: String },
number: { type: Number },
url: { type: String },
json: { type: {} },
list: { type: [] },
boolean: { type: Boolean },
binary: {
data: { type: Buffer },
// binary type .PNG .JPG for decoding if
extension: { type: String }
},
// family relations | inventory of other things | child things | states
things: { type: [{ type: Schema.ObjectId, ref: 'thing' }], default: [] },
parents: { type: [{ type: Schema.ObjectId, ref: 'thing' }], default: [] },
states: { type: [{ type: Schema.ObjectId, ref: 'thing' }], default: [] },
properties: { type: [{ type: Schema.ObjectId, ref: 'thing' }], default: [] }, // extendable schema support
// ownership / privacy / access control / billing
owner: { type: Schema.ObjectId, ref: 'entity' },
owners: { type: [{ type: Schema.ObjectId, ref: 'entity' }] },
payees: { type: [{ type: Schema.ObjectId, ref: 'entity' }] },
// smart contract variable/dynamic pricing
contract: {
type: [{
contractType: { type: String, required: true },
thing: { type: Schema.ObjectId, ref: 'entity', required: true },
amount: { type: Number, required: true, default: 1 },
multiplier: { type: Number, required: true, default: 1 },
unit: { type: String, required: true }
}]
},
// meta data
created: { type: Date, default: Date.now() },
inception: { type: Date, default: Date.now() },
age: { type: Number, default: 0 },
lifetime: { type: Number, default: 0, min: 0, max: Infinity },
location: { type: String },
valueType: { type: String, enum: [
String,
Object,
Number,
Array,
]
},
// state meta data
stateId: { type: String },
stateCreated: { type: Date, default: Date.now() },
},{
usePushEach: true
})
thingSchema.pre('validate', function(next){
// custom validate passed properties, parents, things, and states to make sure they're not objects
let validations = ['properties', 'things', 'parents', 'states']
for(var i=0;i<validations.length;i++){
for(var j=0;j<this[validations[i]].length;j++){
console.log(this[validations[i]][j])
console.log(i)
console.log(j)
if(!(this[validations[i]][j] instanceof Schema.ObjectId)){
this[validations[i]].splice(j, 1)
j--
}
}
}
next()
})
var thingModel = node.model('thing', thingSchema)
But when I run
let thingModelled = new thingModel(JSON.parse(JSON.stringify(thing)))
the pre hook isn't run, I can't tell if these pre hooks are only run on save functions?
Or how to get hooks run on model creations?
this is the error when running
new thingModel(data)
ValidationError: properties: Cast to Array failed for value "[ { title: 'options',
4|agoraser | list: [ 'editing', 'edited' ],
4|agoraser | things: [],
4|agoraser | properties: [] } ]" at path "properties"
4|agoraser | at new ValidationError (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/error/validation.js:28:11)
4|agoraser | at model.Document.invalidate (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/document.js:1658:32)
4|agoraser | at model.Document.$set (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/document.js:760:10)
4|agoraser | at model._handleIndex (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/document.js:590:14)
4|agoraser | at model.Document.$set (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/document.js:550:24)
4|agoraser | at model.Document (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/document.js:77:12)
4|agoraser | at model.Model (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/model.js:55:12)
4|agoraser | at new model (/media/rick/hyper/Dropbox/host-root/var/www/nodes/agora/server/node_modules/mongoose/lib/model.js:3879:13)
To validate some field before saveyou can use schemaName.path('fieldName').validate(function(value, done){....},"error message") this signature. This method is called pre save and if a validation rule is violated and save is aborted and the error is returned to your callback.
For example if you want to create a validation for states so can follow this
thingSchema.path('states').validate(function (value, done) {
// your logic for validation
// value will contain the value of state field
if(validate) {
return done(true);
} else {
return done(false);
}
}, 'Invalid state');

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 Populate Virtuals

How to populate array of objects not by _id? I need it because I use "market_hash_name" as id in my project and it's more effectively then use _id:
let schema = new mongoose.Schema({
steamid: {
type: String,
unique: true,
index: true,
required: true
},
inventory: [{
market_hash_name: String,
name: String,
assetid: {
type: String
},
instanceid: {
type: String,
default: '0'
},
contextid: {
type: String,
default: '2'
},
amount: {
type: Number,
default: 1
},
ongoing_price_manipulation: {
type: Boolean
},
price: {
type: Number
}
}]
});
I used virtuals but only for a separate model and it works:
schema.virtual('item_data', {
ref: 'Steamlytics',
localField: 'market_hash_name',
foreignField: 'market_hash_name'
});
this.find(query).populate("item_data").exec((err, items) => {});

Resources