I'm trying to update an object by assigning it a new field, which is defined in the schema, like this:
exports.updatePlot = async (req, res) => {
let modifications = {};
modifications = Object.assign(modifications, req.body.props);
const id = modifications.id;
try {
const updatedPlot = await Plot.findByIdAndUpdate(
id,
{ $set: modifications },
{ new: true }
);
console.log(('updated plot saved:', updatedPlot));
res.json({
updatedPlot
});
} catch (e) {
console.log('error', e);
return res.status(422).send({
error: { message: 'e', resend: true }
});
}
};
This works when I modify existing fields. However, when I try to add a new field (which is defined in the Mongoose schema, but does not exist in the object in the database), it fails. Meaning, there is no error, but the new field is not added.
Any ideas?
According to mongoose documentation of findByIdAndUpdate
new: bool - true to return the modified document rather than the original. defaults to false
upsert: bool - creates the object if it doesn't exist. defaults to false.
You are mistaking new with upsert
Moreover as #Rahul Sharma said and looking at mongoose documentation example, you do not need to use $set
Related
I have created a sigle app with a Schema and a Model to create a Collection and insert some Documents.
I have my todoModel.js file:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const todoSchema = new Schema({
username: String,
todo: String,
isDone: Boolean,
hasAttachment: Boolean
});
const Todos = mongoose.model("Todo", todoSchema);
module.exports = Todos;
Then I have created a setUpController.js file with a sample of my Documents. Then I create a Model and I pass my sample of Documents and my Schema. I create a response to send tje result in JSON.
Everything good here, as I get the result in json when accessing to the route.
Here is the code:
Todos.create(sampleTodos, (err, results) => {
if (!err) {
console.log("setupTodos sample CREATED!")
res.send(results);
}
else {
console.log(`Could not create the setupTodos Database sample, err: ${err}`);
}
});
My problem is that this Documents don´t get saved in the collection !! When I access to the database, nothing is there.
This is my app.js file:
mongoose.connect("mongodb://localhost:27017/nodeTodo")
.then(connection => {
app.listen(port);
})
.catch(err => {
console.log(`Could not establish Connection with err: ${err}`);
});
Could anyone help me please ?
Thank you
Try creating an instance and making the respective function call of that instance. In your case, save the document after creating an instance and it works like a charm.
const newTodos = new Todos({
username: "username",
todo: "todos",
isDone: false,
hasAttachment: flase
});
const createdTodo = newTodos.save((err, todo) => {
if(err) {
throw(err);
}
else {
//do your staff
}
})
after the collection is created you can use the function inserMany to insert also a single document the function receives an array of objects and automatically saves it to the given collection
example:
Pet = new mongoose.model("pet",schemas.petSchema)
Pet.insetMany([
{
//your document
}])
it will save only one hardcoded document
I hope it was helpful
We are creating a discord bot for task management based on teams. We're using a collection as a team. In code to find a team and add tasks to it we use: const Team = await mongoose.connection.collection(teamName, {strict: true}); and strict is supposed to make it so that the collection isn't created when it's not found but instead this happens:
I've tried with no luck as the mongoose has no documentation on the options and everything I've tried doesn't work.
How do I make mongoose.connection.collection(teamName) return an error if teamName isn't a collection name/isn't found?
You are right, strict: true should do it, but doesn't.
Mongoose doesn't mention the options it accepts, though usually it takes the ones used by the underlying mongo client.
But it does mention in the documentation that it makes missing collections:
Retrieves a collection, creating it if not cached.
I looked into the repo, and the collection method always makes a missing collection
Connection.prototype.collection = function(name, options) {
const defaultOptions = {
autoIndex: this.config.autoIndex != null ? this.config.autoIndex : >this.base.options.autoIndex,
autoCreate: this.config.autoCreate != null ? this.config.autoCreate : >this.base.options.autoCreate
};
options = Object.assign({}, defaultOptions, options ? utils.clone(options) : {});
options.$wasForceClosed = this.$wasForceClosed;
if (!(name in this.collections)) {
this.collections[name] = new Collection(name, this, options);
}
return this.collections[name];
};
The mongo-client collection does take a strict parameter.
You can access it from mongoose.connection.client.db()
Update
Here is how you can call it:
mongoose.connection.client.db().collection(teamName, {strict: true}, function (error, collection) {
console.log('error', error);
console.log('collection', collection);
})
Example
>
> mongoose.connection.client.db().collection('nonExistantCollectionName', {strict: true}, function () { console.log(arguments) })
undefined
> [Arguments] {
'0': MongoError: Collection nonExistantCollectionName does not exist. Currently in strict mode.
at Function.create (./node_modules/mongodb/lib/core/error.js:57:12)
at toError (./node_modules/mongodb/lib/utils.js:130:22)
at ./node_modules/mongodb/lib/db.js:482:9
at ./node_modules/mongodb/lib/utils.js:704:5
at handleCallback (./node_modules/mongodb/lib/utils.js:109:55)
at ./node_modules/mongodb/lib/cursor.js:840:66
at ./node_modules/mongodb/lib/utils.js:704:5
at ./node_modules/mongodb/lib/cursor.js:925:9
at CommandCursor._endSession (./node_modules/mongodb/lib/core/cursor.js:397:7)
at ./node_modules/mongodb/lib/cursor.js:923:12
at maybePromise (./node_modules/mongodb/lib/utils.js:692:3)
at CommandCursor.close (./node_modules/mongodb/lib/cursor.js:916:12)
at ./node_modules/mongodb/lib/cursor.js:840:27
at ./node_modules/mongodb/lib/core/cursor.js:739:9
at handleCallback (./node_modules/mongodb/lib/core/cursor.js:32:5)
at ./node_modules/mongodb/lib/core/cursor.js:683:38
at _setCursorNotifiedImpl (./node_modules/mongodb/lib/core/cursor.js:696:10)
at setCursorNotified (./node_modules/mongodb/lib/core/cursor.js:683:3)
at done (./node_modules/mongodb/lib/core/cursor.js:458:16)
at queryCallback (./node_modules/mongodb/lib/core/cursor.js:503:20)
at ./node_modules/mongodb/lib/core/cursor.js:548:9
at ./node_modules/mongodb/lib/utils.js:704:5
at executeCallback (./node_modules/mongodb/lib/operations/execute_operation.js:65:7)
at callbackWithRetry (./node_modules/mongodb/lib/operations/execute_operation.js:112:14)
at ./node_modules/mongodb/lib/operations/command_v2.js:102:9
at ./node_modules/mongodb/lib/core/connection/pool.js:405:18
at processTicksAndRejections (internal/process/task_queues.js:79:11) {
driver: true
},
'1': null
}
You can use this code to check if a collection exists or not in a specific database - and then perform actions as needed.
const mongoose = require('mongoose');
const uri = 'mongodb://127.0.0.1:27017/';
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: 'test'
};
(async () => {
const conn = await mongoose.createConnection(uri, opts);
let collNames = await conn.db.listCollections().toArray();
collNames = collNames.map(e => e.name);
console.log(collNames);
console.log(collNames.includes('existing_coll')); // true
console.log(collNames.includes('non_existing_coll')); // false
await conn.close();
})(uri, opts);
I am practicing my express.js skills by building a relational API and am struggling to populate keys in a schema.
I am building it so I have a list of properties, and those properties have units. The units have a propertyId key.
This is currently returning an empty array, whereas if i remove the populate({}) it returns an array of ObjectIds.
I've read a number of posts and some people solved this by using .populate({path: 'path', model: Model}); but this doesn't seem to be doing the trick. I think it might be the way I am adding a propertyId to the unit but I'm not sure. Can anyone see where I am going wrong? Any help will be massively appreciated.
Here are the schemas.
Property:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PropertySchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
units: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'unit'
}
]
});
module.exports = Property = mongoose.model('property', PropertySchema);
Unit:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UnitSchema = new Schema({
title: {
type: String,
required: true
},
propertyId: {
type: Schema.Types.ObjectId,
ref: 'property'
}
});
module.exports = Unit = mongoose.model('unit', UnitSchema);
I am then creating the unit like this:
-- api/properties/:id/units --
router.post('/:id/units', async (req, res) => {
// Get fields from req.body
const { title } = req.body;
// Get current property
const property = await Property.findById(req.params.id);
try {
// Throw error if no property
if (!property) {
return res.status(400).json({ msg: 'Property not found' });
}
// Create new unit
const newUnit = new Unit({
title,
propertyId: req.params.id
});
// Add new unit to property's units array
property.units.unshift(newUnit);
// Save property
await property.save();
// Return successful response
return res.status(200).json(property);
} catch (error) {
console.error(error.message);
return res.status(500).send('Server error');
}
});
And trying to populate in the GET request
-- /api/properties/:id/units --
const Unit = require('../../models/Unit');
router.get('/:id/units', async (req, res) => {
const property = await Property.findOne({ _id: req.params.id }).populate({path: 'units', model: Unit});
const propertyUnits = property.units;
return res.status(200).json(propertyUnits);
});
If i remove the .populate({path: 'units', model: Unit});, I get a list of unit id's like this:
[
"5ff7256cda2f5bfc1d2b9108",
"5ff72507acf9b6fb89f0fa4e",
"5ff724e41393c7fb5a667dc8",
"5ff721f35c73daf6d0cb5eff",
"5ff721eb5c73daf6d0cb5efe",
"5ff7215332d302f5ffa67413"
]
I don't know, why you don't try it like this:
await Property.findOne({ _id: req.params.id }).populate('units')
I've been try that code above and it's working.
Note: Make sure to check your req.params.id is not null or undefined and make sure the data you find is not empty in your mongodb.
Updated: I've been try your code and it's working fine.
The issue was caused by inconsistent naming and not saving the new created unit as well as the updated property.
I double checked all my schema exports and references and noticed I was using UpperCase in some instances and LowerCase in others, and saved the newUnit as well as the updated property in the POST request and it worked.
i can not able to update nested data in my mongodb. here is my "update" module at back-end side.
exports.updateOne = (req, res) => {
if (!req.body) {
return res.status(400).send({
message: "Data to update can not be empty!"
});
}
const {id} = req.params;
console.log(req.body);
User.findByIdAndUpdate(id, req.body, { useFindAndModify: false, new: true}).populate('basic')
.then(data => {
if (!data) {
res.status(404).send({
message: `Cannot update User with id=${id}. Maybe User was not found!`
});
} else
res.send({ message: "User was dupdated successfully." , data});
})
.catch(err => {
res.status(500).send({
message:
err.message || "Error updating User with id=" + id
});
});
};
and my front-end side is;
onChangePosition(e) {
const position = e.target.value;
this.setState(prevState => ({
currentStaff: {
...prevState.currentStaff,
basic:
{
...prevState.currentStaff.basic,
position:position
}
}
}));
}
onChangeEmail(e) {
const emailBusiness = e.target.value;
this.setState(prevState => ({
currentStaff: {
...prevState.currentStaff,
emailBusiness:emailBusiness
}
}));
}
updateStaff() {
StaffDataService.updateOne(
this.state.currentStaff.id,
this.state.currentStaff
).then(response => {
console.log(response.data);
})
.catch(e => {
console.log(e);
})
}
i can change state properly, and my sending data "req.body" is what i want (it is an object). There is no problem.
as you see above, i can update "email" because it is on the main body of object, but can not update "position" (nested element) because it is inside of basic (populated data).
i tried different methods by mongoose, and tried "$set" command.
Can anyone solve this?
To update, the nested value/object in your document, you should use dot notations, so it depends from the req.body variable value.
req.body shouldn't be a Mongoose doc. In such case you mongoose.toObject.
Second thing is:
[update] Object should be: field_with_subdocument.key_value: updated_propery
like this:
/** Example doc */
{
_id: 1,
parent_field: {
baby_field: value
}
}
/** Inside async function */
...await Model.findByIdAndUpdate(id, { "parent_field.baby_field": value })
Also, take a look at [`{overwrite: true}`](https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate) option. It might be useful to you.
I faced the same issue, In my case, the defined mongoose schema for that model did not match the nested Object I was passing to the findByIdAndUpdate method. let me simplify it, here is the model
import { model, Schema } from 'mongooose';
const UserModel = model('user', new Schema({
profile: {
avatar: String,
bio: String,
}
}));
And here is the update query:
async function getDefaultProfile() {
const user = await UserModel.findById(process.env.DEFAULT_USER);
return user.profile;
}
const profile = await getDefaultProfile();
UserModel.findByIdAndUpdate('user id', {
$set: {
profile: profile
}
});
The important note was that my getDefaultProfile function returns a mongoose nested object, not a pure object. So in the returned object, I had $set, $get, etc function. So as you know this object is not what we define in the mongoose model, therefore the mongoose ignores it.
So I guess you have the same problem, or something close to my issue.
What should I do?
Run your project in debugging mode.
then check req.body or whatever that gives you the nested object (in my case getDefaultProfile).
Check it with your model, Are they equal?
And if that solution does not work for you, please try this solution, write a utility function:
export async function flatObjectAndSeparateThemByDot(
object: any,
): Promise<any> {
const res: any = {};
(function recurse(obj: any, current?: string) {
for (const key in obj) {
const value = obj[key];
// joined keys with dot
const newKey = current ? current + '.' + key : key;
if (value && typeof value === 'object') {
// it's a nested object, so do it again
recurse(value, newKey);
} else {
// it's not an object, so set the property
res[newKey] = value;
}
}
})(object);
return res;
}
then you can pass your nested object to this function and you will get something like this: { "profile.avatar": "lorem ipsum", "profile.bio": "bio temp" }. So to show you how this function works I will write a sample code:
const sampleProfile = {
profile: {
avatar: "asd",
bio: "sample"
}
}
const profile = await flatObjectAndSeparateThemByDot(sampleProfile);
await UserModel.findByIdAndUpdate('user id', {
$set: {
// other fields,
...profile
}
});
In order to be able to compare the pre and post-save version of a document, I am trying to lookup the document in a pre hook, and then use that to see what's changed in the doc in the post save hook.
But for some reason I'm getting a "Customer.findOne() is not a function" error. This doesn't make any sense to me because I've imported the model into this triggers file, and then, in my function I do this:
const Customer = require("../customer");
// Get a version of the document prior to changes
exports.preSave = async function(doc) {
console.log("preSave firing with doc._id", doc._id); // this ObjectId logs correctly
if (!doc) return;
this.preSaveDoc = await Customer.findOne({ _id: doc._id }).exec();
console.log("this.preSaveDoc: ", this.preSaveDoc);
};
Again, this code produces an error:
"Customer.findOne() is not a function"
FYI, the relevant code in my Customer model looks like this:
let Schema = mongoose
.Schema(CustomerSchema, {
timestamps: true
})
.pre("count", function(next) {
next();
})
.pre("save", function(next) {
const doc = this;
trigger.preSave(doc);
next();
})
.post("save", function(doc) {
trigger.postSave(doc);
})
.post("update", function(doc) {
trigger.postSave(doc);
})
.post("findOneAndUpdate", function(doc) {
trigger.postSave(doc);
});
module.exports = mongoose.model("Customer", Schema);
What am I missing here? Why would this code produce this error on a very standard MongoDB operation?
This problem has already been solved.
If your mongoDb version is 3.6 or more, you can use change streams
Change streams lets you know what changed in your document. A background process runs in mongoDb which notifies your code an event(CREATE, UPDATE, DELETE) took place and passes you the document. You can filter on your fields to know the exact value.
You can refer this blog
This is a classic use case where change streams can be applied. Better than reinventing the wheel :)
Here is how you can get this to work. Instead of looking up the pre-saved/pre-transformed version of the document via Mongoose like this:
// Get a version of the document prior to changes
exports.preSave = async function(doc) {
console.log("preSave firing with doc._id", doc._id); // this ObjectId logs correctly
if (!doc) return;
this.preSaveDoc = await Customer.findOne({ _id: doc._id }).exec();
console.log("this.preSaveDoc: ", this.preSaveDoc);
};
... look up the document this way:
// Get a version of the document prior to changes
exports.preSave = async function(doc) {
let MongoClient = await require("../../config/database")();
let db = MongoClient.connection.db;
db.collection("customers")
.findOne({ _id: doc._id })
.then(doc => {
this.preSaveDoc = doc;
});
};