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
});
Related
I have tried so many times to add new field to the existing MongoDB document but I failed. I tried following code to do the job but nothing happened.
Here is the User model.
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true }
);
Here is the code to add new field to the document.
const updateDocument = async () => {
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
}
);
console.log(updatedUser);
};
updateDocument();
NOTE 1: lastName field does not exist in the MongoDB document and in the UserSchema. I want to add that field to the MongoDB document.
NOTE 2: The same code works when I update the existing field inside the document but it does not work when adding new field.
You need to pass strict:false as an option to findOneAndUpdate.
According to the mongoose doc:
The strict option, (enabled by default), ensures that values passed to our model constructor that were not specified in our schema do not get saved to the db.
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
},
{ strict: false }
);
An alternative way is to pass this parameter when you defined the schema:
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true, strict: false }
);
I am working on a MERN stack project where A user can perform CRUD operations on Goals. I use mongoose for object modeling. I want to create a virtual field named stepAvg to find out some information about each goal by using the step model.
Relationship information
Each user has many goals.
Each goal has many steps.
GoalModel.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const StepModel = require("./StepModel");
const GoalSchema = new Schema({
category: { type: Schema.ObjectId, ref: "Category", required: true },
title: { type: String, required: true },
startDate: { type: Date, required: true },
completionDate: { type: Date, required: true },
commitment: { type: String, required: true },
obstacle: { type: String, default: null },
celebration: { type: String, default: null },
user: { type: Schema.ObjectId, ref: "User", required: true },
steps: [{ type: Schema.Types.ObjectId, ref: "Step"}],
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
}, {timestamps: true});
GoalSchema.virtual('stepAvg').get(async function() {
let steps = await StepModel.find({ goal: this.id });
// if I console steps it return the data correctly.
let totalSteps = steps.length;
if (totalSteps) {
let completedSteps = steps.filter(function(step) {
return step.isCompleted;
}).length;
let avg = ( completedSteps / totalSteps) * 100;
return avg;
}
return 0;
});
module.exports = mongoose.model("Goal", GoalSchema);
As you can see I create a virtual field stepAvg but every time it gives me the empty object. It actually returns the promise.
Postman screenshot
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've got the following data structure:
const schema = new Schema({
userId: { type: String, required: true },
image: { type: String, required: true},
name: { type: String, required: true },
description: { type: String, required: true },
subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'SubCategories' }]
}, {
strict: 'throw'
});
export const Plan = mongoose.model('Plan', schema);
const schema = new Schema({
userId: { type: String, required: true },
name: { type: String, required: true },
description: { type: String, required: true }
}, {
strict: 'throw'
});
export const SubCategories = mongoose.model('SubCategories', schema);
and I want to save each of the subCategories in a separate collection each time a new Plan entity is created.
This requires me to iterate the subCategories and to create each one of the sub-category.
for example:
plan.subCategories = await Promise.all(plan.subCategories.map(async (s: IWorkoutDay) => {
const sub = new SubCategories(s);
sub.userId = userId;
await sub.save();
return sub;
}));
const p = new Plan(plan);
p.userId = userId;
await p.save();
and also each time I want to edit the subCategories of the element I'm required to iterate find the element and to update each one separately.
is there an easier way to achieve this? since it seems pretty complicated to me.
I have the following message schema in mongoose:
var messageSchema = mongoose.Schema({
userID: { type: ObjectId, required: true, ref: 'User' },
text: { type: String, required: true }
},
{
timestamps: true
});
Is there anyway to ignore the updatedAt timestamp? Messages won't be updated so updatedAt will be wasted space
Maybe even better with Mongoose v5 is to do the following;
const schema = new Schema({
// Your schema...
}, {
timestamps: { createdAt: true, updatedAt: false }
})
Edit I've amended the answer to reflect the better option to use the default as per #JohnnyHK
You can handle this yourself by declaring the createdAt (or whatever you want to call it) in your schema:
mongoose.Schema({
created: { type: Date, default: Date.now }
...
Alternatively we can also update values on new document in a pre save hook:
messageSchema.pre('save', function (next) {
if (!this.created) this.created = new Date;
next();
})
Along those lines is also the flag isNew which you can use to check if a document is new.
messageSchema.pre('save', function (next) {
if (this.isNew) this.created = new Date;
next();
})
Older topic but there may be a better option depending on your schema...
If you're sticking with the default of having mongodb/mongoose auto-gen _id, there's already a timestamp built in. If all you need is "created" and not "updated" just use...
document._id.getTimestamp();
From MongoDB docs here...
ObjectId.getTimestamp()
And Here... stackoverflow
Mongoose timestamp interface has these optional fields.
interface SchemaTimestampsConfig {
createdAt?: boolean | string;
updatedAt?: boolean | string;
currentTime?: () => (Date | number);
}
We can pass the boolean for the field we want(createdAt: true and updatedAt: true will add both fields).
We can use the currentTime function to overwrite the date format.
example:
import mongoose from 'mongoose';
const { Schema } = mongoose;
const annotationType = ['NOTES', 'COMMENTS'];
const referenceType = ['TASKS', 'NOTES'];
const AnnotationSchema = new Schema(
{
sellerOrgId: {
type: String,
required: true,
},
createdById: {
type: String,
required: true,
},
annotationType: {
type: String,
enum: annotationType,
},
reference: {
id: { type: String, index: true },
type: {
type: String,
enum: referenceType,
},
},
data: Schema.Types.Mixed,
},
{ timestamps: { createdAt: true },
);
const AnnotationModel = mongoose.models.annotation || mongoose.model('annotation', AnnotationSchema);
export { AnnotationModel, AnnotationSchema };