I have 2 models like this
const testRunSchema = new mongoose.Schema({
testTimeSlot: {
type: String,
required: true
},
testDate: {
type: String,
required: true
},
diagnosticData: [Object],
notes: String,
active: {
type: Boolean,
default: true
}
}, {
timestamps: true,
strict: false
})
const testingSchema = new mongoose.Schema({
testId: {
type: mongoose.Schema.ObjectId,
required: true
},
testDetails: {
//dummy data
},
contactDetails: {
//dummy data
},
testRunDetails: [testRunSchema], //is this a best way?
runByAssistant: Boolean
}, {
timestamps: true
});
module.exports = mongoose.model('Testing', testingSchema)
Now i want to access the testTimeSlot (which is first model) using the testId of the second model.
My Solution:
I can access the testimeSlot of first model, using testId because data of first model is available in testRunDetails of seconf model.
Problem With this solution:
Since testRunSchema is defined as a array in second model, its not easy and efficient to access the testTimeSlot of every array element.
What is the best way to solve this issue?
what you thought is correct it's not easy to access them besides the more the array gets filled the slower the query will be. So it would be better if you separate the testRunSchema and save the reference of it's data in to the testingSchema
const testingSchema = new mongoose.Schema({
testId: {
type: mongoose.Schema.ObjectId,
required: true
},
testDetails: {
//dummy data
},
contactDetails: {
//dummy data
},
testRunDetails:{
type:[Schema.Types.ObjectId], ref:'testRunSchema'
},//like this in here the object id of testRunSchema
runByAssistant: Boolean
}, {
timestamps: true
});
when you want to query, you just need to use polulate() in mongoose
you can read it in here
Related
I am trying to do validation within the schema to validate the length of an array based on another documents length of its array. Suppose:
const Level = mongoose.Schema({
level: {
type: Array,
required: true,
items: {
exercise: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exercise",
required: true,
},
accuracyThreshold: { type: Number, required: true },
timeThreshold: { type: Number, required: true },
},
},
});
const UserLevelProgress = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
level: {
type: mongoose.Schema.Types.ObjectId,
ref: "Level",
required: true,
},
progress: {
type: Array,
required: true,
validate: {
validator: (progress) => {
return progress.length === level.level.length; // level.level.length is not valid
},
message: () => "level progress legnth is invalid!",
},
items: {
accuracyScore: { type: Number, required: true },
timeScore: { type: Number, required: true },
},
},
});
module.exports = mongoose.model("UserLevelProgress", UserLevelProgress);
module.exports = mongoose.model("Level", Level);
I have a validator function in UserLevelProgress, but obviously level is not valid object. How do I get access to this object?
The short answer is: You could implement this validation logic, but is it really a good idea to do this?
Downsights
validation is taking place in the pre('save') hook. This means that once you initially save of update a document, the validation will run.
since Level documents are stored in a different collection, the values (including length of the array) can change without UserLevelProgress documents noticing. This way your validator would not rerun (as said before it's run in pre('save') hook and your documents would technically be not valid anymore.
Better approach
Check if Level schema can be included as a nested document, instead of a referenced one. This way validation would be much easier and data will not be able to become invalid unnoticed.
Implementation if you still want to follow your intended approach
validate: {
validator: async function(progress) {
const level = await Level.findById(this.level);
return progress.length === level.level.length; // level.level.length is not valid
},
message: () => "level progress legnth is invalid!",
},
In Node, Mongoose I am trying to achieve something like Laravel's created_by and updated_by columns, these 2 columns are automatically updated by the ORM in every INSERT and UPDATE operation to that particular table.
For example, below is the Material schema, here I want to store the createdBy and updatedBy column with userId whenever any user performs INSERT and UPDATE operations respectively in the materials collection.
I know this can be done while saving the records in particular controller method, but every time in every method I will have to add this and it becomes a pain, so I am looking for a solution where I can define something in the Model itself and it does the job just like the mongoose-delete package.
const mongoose = require('mongoose')
const mongoose_delete = require('mongoose-delete')
const materialSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
type: {
type: String,
required: true,
enum: ['hard', 'soft'],
},
image: {
type: String,
default: null
},
active: {
type: Boolean,
default: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null
},
updatedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
default: null
}
}, { timestamps: true })
materialSchema.plugin(mongoose_delete, { deletedAt : true, deletedBy : true, deletedByType: String })
const Material = mongoose.model("Material", materialSchema)
module.exports = Material
I am using node.js express.js and mongoose to develop my back end, I have this mongoose schema where I want to add a field called NumberRecords for every stored document this field will have the length value of the array csvData, I looked around some similar question here and I found I can use this pre function which I did but I get nothing on my database no field called NumberRecords is being added.
How do I fix this?
const rowSchema = new mongoose.Schema({
SESSION_ID: {
type: String,
required: false,
},
CARD_NUMBER: {
type: String,
required: false,
},
TERMINAL_ID: {
type: String,
required: false,
},
EXTERNAL_STAN: {
type: String,
required: false,
},
})
const csvSchema = new mongoose.Schema(
{
fileName: { type: String, required: true },
csvData: { type: [rowSchema], required: true },
NumberRecords: { type: Number, required: true },
},
{
timestamps: true,
}
);
csvSchema.pre("validate", function (next) {
this.NumberRecords = this.csvData.length;
next();
});
module.exports = mongoose.model("csvRecords", csvSchema);
I have the following Schema type called Orders. I am using Arrays of SchemaTypes in some properties. When I save it to the database, it's saving everything fine. I can open the database and see all the data there.
But the problem happens in one property called "files", whenever I try to use find() or findOne() or findById(), this property always comes empty, even if I have data to show.
This is my Schemas:
const statusChildSchema = new Mongoose.Schema({
...
});
const shippingChildSchema = new Mongoose.Schema({
...
});
const fileChildSchema = new Mongoose.Schema({
path: { type: String, required: true, trim: true },
type: { type: String, required: true },
});
const ordersSchema = new Mongoose.Schema(
{
// Relationships
creator: {
type: Mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Users',
autopopulate: false,
},
template: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Templates',
},
// Common
status: { type: String, trim: true, default: 'new', required: true },
// Child schemas
status_updates: [statusChildSchema],
shipping: [shippingChildSchema],
files: [fileChildSchema],
// Date properties
timings: {
created_at: { type: Date, default: Date.now, required: true },
...
},
},
{ collection: 'Orders', toJSON: { virtuals: true } }
);
Both statusChildSchema and shippingChildSchema is working normally. The problem is only with fileChildSchema. They are very similar, so I don't know what to do. I have researched in Mongoose documents and nothing helpful have been found.
This is the part of my code:
const order = await OrdersModel.findOne({
_id: orderId,
creator: userId,
});
console.log(order.files); // always printing "[]" empty array
I fixed it by installing last version of Mongoose (Version 5.11.8) and restarting everything. Looks like a bug.
I have a field in my mongoose schema called "active" and I wanted to know if there is any way that every date expired in a particular document, then the "active" field would change to false. how should I do that if so, What is the easiest way to do this? else, what is recommended?
And below is my Schema;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schema = new Schema({
user_id: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
hash: {
type: String,
required: true
},
active: {
type: Boolean,
},
role: {
type: String,
required: true
},
createdDate: {
type: Date,
default: Date.now
}
});
schema.set('toJSON', { virtuals: true });
module.exports = mongoose.model('User', schema);
You can do this with a feature in mongo called Change Streams that allow you to access real-time data changes. You can subscribe to the changes of a single collection or the whole database and react to them. You can also filter for specific changes or transforms. For your case an example would be something like this.
EDIT: Change streams implementation is only available on replica sets.
const pipeline = [
{ $match: { expire_date: {$lt: Date.now()} } },
{ $set: { active: false } }
];
const collection = db.collection('user');
const changeStream = collection.watch(pipeline);
changeStream.on('change', next => {
// process next document
});