How to update an array inside of a mongoose schema? - node.js

How to update an array inside a mongoose schema with updateOne?
I have one model on my Node.Js application that I've made with mongoose schema.
One of the fields of my schema is an array:
guestsNames: []
I'm already able to save items inside of this array but I didn't find a way to update the items inside of it.
Here is my whole schema:
const screenImageSchema = mongoose.Schema({
company: {
type: String,
require: true,
trim: true
},
guestsNames: [],
imageName: {
type: String,
require: true
},
defaultImageName: {
type: String,
require: true
},
date: {
type: String,
default: Date.now,
require: true
},
activated: {
type: String,
default: 'Enabled'
},
wsType: {
type: String,
default: 'Image'
}
}, {timestamps: true});
...and my updateOne method:
screenImageSchema.methods.updateOne = function(id, screenImage) {
const updatedScreenImage = {
company: screenImage.company,
guestsNames: screenImage.guests,
imageName: screenImage.imageName,
defaultImageName: screenImage.defaultImageName,
date: screenImage.date,
activated: screenImage.activated,
wsType: screenImage.wsType
}
ScreenImage.updateOne(id, updatedScreenImage, {new: true});
}
The 'screenImage' parameter passed to the function is an object with all information that I need, including an array with all strings for guestsNames (I've already checked if the parameters are being passed correctly to the object and they are). All fields are being updated with this piece of code except the guestsNames field. What am I doing wrong and how can I make the guestsNames array be updated correctly?
Cheers.

You can update directly your array like this
ScreenImage.updateOne(id, { $set : { guestNames : newArray }})
You need to use $set to replace the value of a field, see this mongoDB $set

try this if it works
screenImageSchema.methods.updateOne = function(id, screenImage) {
const updatedScreenImage = {
company: screenImage.company,
guestsNames[0]: screenImage.guests,
imageName: screenImage.imageName,
defaultImageName: screenImage.defaultImageName,
date: screenImage.date,
activated: screenImage.activated,
wsType: screenImage.wsType
}
ScreenImage.updateOne(id, updatedScreenImage, {new: true});
}

Related

How to set data default in embedded document mongoose schema

I am working with embedded document and I have set the default data for this model schema but when I am trying to create a new document, collection returned empty array. How can I set a default collection when new document added in a model schema in mongoose?
My model schema definition:
const ActionSchema= new mongoose.Schema({
canEdit: {
type: Boolean,
default: true
},
canDelete: {
type: Boolean,
default: false
},
canMention: {
type: Boolean,
default: true
}
});
const PostSchema = new mongoose.Schema({
title: String,
detail: String,
author: Schema.Types.ObjectId,
action: [ActionSchema]
});
It should be auto added default data every time a new post is added like this:
{
title: 'Happy New Year',
detail: 'Happy New Year 2024',
author: ObjectId(...),
action: [
{
canEdit: true,
canDelete: false,
canMention: true
}
]
}
You only specified type to be an array of ActionSchema, but why should it create the item in the array? If you want that, you need to specify default value for action field too:
const PostSchema = new mongoose.Schema({
title: { type: String },
detail: { type: String },
author: { type: Schema.Types.ObjectId },
action: {
type: [ActionSchema],
default: [
{
canEdit: true,
canDelete: false,
canMention: true
}
]
}
});

Mongoose find() returning empty Array of SchemaType

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.

Mongoose use only createdAt timestamp

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

mongoose - findOneAndUpdate with $set flag

Consider this command:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, updateObj, function(err) {
...
})
versus this:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, { '$set': updateObj }, function(err) {
...
})
While developing my project, I was surprised to find out that the result of the first command is the same as the result of the second command: the updateObj is merged into the existing record in the database, even in the first case when it is supposed to replace it. Is this a bug in mongoose/mongodb or am I doing something wrong? how can I replace an object on update instead of merging it? I'm using mongoose 4.0.7.
Thanks.
==========
Update:
This is the actual WorkPlan schema definition:
workPlanSchema = mongoose.Schema({
planId: { type: String, required: true },
projectName: { type: String, required: true },
projectNumber: { type: String, required: false },
projectManagerName: { type: String, required: true },
clientPhoneNumber: { type: String, required: false },
clientEmail: { type: String, required: true },
projectEndShowDate: { type: Date, required: true },
segmentationsToDisplay: { type: [String], required: false },
areas: [
{
fatherArea: { type: mongoose.Schema.ObjectId, ref: 'Area' },
childAreas: [{ childId : { type: mongoose.Schema.ObjectId, ref: 'Area' }, status: { type: String, default: 'none' } }]
}
],
logoPositions: [
{
lat: { type: Number, required: true },
lng: { type: Number, required: true }
}
],
logoPath: { type: String, required: false },
}, { collection: 'workPlans' });
WorkPlan = mongoose.model('WorkPlan', workPlanSchema);
And this is an example of updateObj:
var updateObj = {
projectManagerName: projectManagerName,
clientEmail: clientEmail,
clientPhoneNumber: clientPhoneNumber,
segmentationsToDisplay: segmentationsToDisplay ? segmentationsToDisplay.split(',') : []
}
Therefore, when I'm NOT using the $set flag, I would expect the field projectNumber, for example, not to exist in the new record, yet I see it is still there.
Mongoose update treats all top level keys as $set operations (this is made more clear in the older docs: Mongoose 2.7.x update docs).
In order to get the behavior you want, you need to set the overwrite option to true:
WorkPlan.findOneAndUpdate({ _id: req.params.id }, updateObj, { overwrite: true }, function(err) {
...
})
See Mongoose Update documentation
In addition to the answer above:
[options.overwrite=false] «Boolean» By default, if you don't include
any update operators in doc, Mongoose will wrap doc in $set for you.
This prevents you from accidentally overwriting the document. This
option tells Mongoose to skip adding $set.
Link to docs: https://mongoosejs.com/docs/api.html#model_Model.update
This is works for me $set in Mongoose 5.10.1,
WorkPlan.where({ _id: req.params.id }).updateOne(updateObj);
Note:if you have inner object then give exact path of each key in updateObj
example:
"Document.data.age" = 19
ref: https://mongoosejs.com/docs/api.html#query_Query-set

Mongoose Populate - array

can someone please help me with population of this schema? I need to populate array of Staff by their userId.
var PlaceSchema = new Schema ({
name: { type: String, required: true, trim: true },
permalink: { type: String },
country: { type: String, required: true },
...long story :D...
staff: [staffSchema],
admins: [adminSchema],
masterPlace:{ type: Boolean },
images: []
});
var staffSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account' },
role: { type: Number }
});
var adminSchema = new Schema ({
userId: { type: Schema.Types.ObjectId, ref: 'Account'}
})
var Places = mongoose.model('Places', PlaceSchema);
I tried to use this query, but without success.
Places.findOne({'_id' : placeId}).populate('staff.userId').exec(function(err, doc){
console.log(doc);
});
Polpulation is intended as a method for "pulling in" information from the related models in the collection. So rather than specifying a related field "directly", instead reference the related fields so the document appears to have all of those sub-documents embedded in the response:
Places.findOne({'_id' : placeId}).populate('staff','_id')
.exec(function(err, doc){
console.log(doc);
});
The second argument just returns the field that you want. So it "filters" the response.
There is more information on populate in the documentation.

Resources