Update model with Mongoose, Express, NodeJS - mongodb

I'm trying to update an instantiated model ('Place' - I know it works from other routes) in a MongoDB and have spent a while trying to properly do so. I'm also trying to redirect back to the page that views the 'place' to view the updated properties.
Node v0.4.0, Express v1.0.7, Mongoose 1.10.0
Schema:
var PlaceSchema = new Schema({
name :String
, capital: String
, continent: String
});
Controller/route:
app.put('/places/:name', function(req, res) {
var name = req.body.name;
var capital = req.body.capital;
var continent = req.body.continent;
Place.update({ name: name, capital: capital, continent: continent}, function(name) {
res.redirect('/places/'+name)
});
});
I've tried a bunch of different ways but can't seem to get it.
Also, isn't how I declare the three {name, capital, and continent} variables blocking further operations? Thanks. General debugging help is also appreciated. Console.log(name) (right below the declaration) doesn't log anything.
Jade form:
h1 Editing #{place.name}
form(action='/places/'+place.name, method='POST')
input(type='hidden', name='_method', value='PUT')
p
label(for='place_name') Name:
p
input(type='text', id='place_name', name='place[name]', value=place.name)
p
label(for='place_capital') Capital:
p
input(type='text', id='place_capital', name='place[capital]', value=place.capital)
p
label(for='place_continent') Continent:
p
textarea(type='text', id='place_continent', name='place[continent]')=place.continent
p
input(type="submit")

You have to find the document before updating anything:
Place.findById(req.params.id, function(err, p) {
if (!p)
return next(new Error('Could not load Document'));
else {
// do your updates here
p.modified = new Date();
p.save(function(err) {
if (err)
console.log('error')
else
console.log('success')
});
}
});
works for me in production code using the same setup you have. Instead of findById you can use any other find method provided by mongoose. Just make sure you fetch the document before updating it.

Now, i think you can do this :
Place.findOneAndUpdate({name:req.params.name}, req.body, function (err, place) {
res.send(place);
});
You can find by id too :
Place.findOneAndUpdate({_id:req.params.id}, req.body, function (err, place) {
res.send(place);
});

So now you can find and update directly by id, this is for Mongoose v4
Place.findByIdAndUpdate(req.params.id, req.body, function (err, place) {
res.send(place);
});
Just to mention, if you needs updated object then you need to pass {new: true} like
Place.findByIdAndUpdate(req.params.id, req.body, {new: true}, function (err, place) {
res.send(place);
});

I think your problem is that you are using node 0.4.0 - try moving to 0.2.6 with an it should work. There is an issue logged on github with the bodyDecoder not populating the req.body.variable field in node >= 0.3.0.

Related

Circular Reference Issue in Mongoose pre-hook

In my MongoDB/Node backend environment I am using Mongoose pre and post hook middleware to check what's changed on the document, in order to create some system notes as a result.
One problem I'm running into is that when I try and lookup the record for the document in question I get a "Customer.findOne()" is not a function error. This is ONLY a problem when I'm looking up a record from the same collection from which the model just launched this pre and post hook triggers file. In other words, if my "Customer" model kicks off functions in a pre hook function in an external file, then I get an error if I then try and lookup a Customer with a standard findOne():
My customer model looks something like this:
module.exports = mongoose.model(
"Customer",
mongoose
.Schema(
{
__v: {
type: Number,
select: false
},
deleted: {
type: Boolean,
default: false
},
// Other props
searchResults: [
{
matchKey: String,
matchValue: String
}
]
},
{
timestamps: true
}
)
.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);
})
);
... the problematic findOne() function in the triggers file being called from the model looks like this:
const Customer = require("../../models/customer");
exports.preSave = async function(doc) {
this.preSaveDoc = await Customer.findOne({
_id: doc._id
}).exec();
};
To clarify, this is NOT a problem if I'm using a findOne() to lookup a record from a different collection in this same triggers file. Then it works fine. See below when finding a Contact -- no problem here:
const Contact = require("../../models/contact");
exports.preSave = async function(doc) {
this.preSaveDoc = await Contact.findOne({
_id: doc._id
}).exec();
};
The workaround I've found is to use Mongo instead of Mongoose, like so:
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;
});
}
... but I'd prefer to use Mongoose syntax here. How can I use a findOne() in a pre-hook function being called from the same model/collection as the lookup type?
I have ran similar issue few days ago.
Effectively it is a circular dependency problem. When you call .findOne() on your customer model it doesn't exist as it is not exported yet.
You should probably try something like that :
const customerSchema = mongoose.Schema(...);
customerSchema.pre("save", async function(next) {
const customer = await Customer.findOne({
_id: this._id
}).exec();
trigger.setPreSaveDoc(customer);
next();
})
const Customer = mongoose.model("Customer", customerSchema)
module.export Customer;
Here customer will be defined because it is not called (the pre hook) before its creation.
As an easier way (I am not sure about it) but you could try to move the Contact import in your Trigger file under the save function export. That way I think the decencies may works.
Did it helps ?

Model.find is not a function in mongoose

I am new in node and mongodb. I am trying to query a different model(Event) from another model(Company).
Basically in Event model there is a field called company. I would like to get the company where id is an Event ID.
I have all the event IDs in an array.
let eventIds = [ 5b76a8139dc71a4a12564cd2,
5b9a1685c239342d4635466c,
5b8e753bdbccf803e906aaeb ]
Event Schema --
var EventSchema = new Schema({
title:{type:String,require:true,index:true},
description:{type:String,require:false},
companies:[
{type:Schema.Types.ObjectId,ref:"Company",require:true,index:true}
]
});
In company model --
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
Event = require('./event.js');
var CompanySchema = new Schema({
name:{type:String,require:true,index:true},
description:{type:String,require:false}},{
//no auto indexing at the beginning
autoIndex:true,
//no strict to save changes in the valuesBeforeChange field.
strict:false}
);
CompanySchema.static("searchCompanies",function(callback,criteria){
"use strict";
var That = this;
var query = That.find();
async.waterfall([
function(callback){
let eventIds = [5b76a8139dc71a4a12564cd2,5b9a1685c239342d4635466c,5b8e753bdbccf803e906aaeb ];
Event.find({ $in: eventIds}, function(err, docs){
console.log(docs);
});
}
],function(err,companyResultObj){
callback(err,companyResultObj);
});
});
I am getting Event.find is not a function error message. How can I query a different model(event) from another model(company)
Any help is highly appreciated.
Not sure why but I had to do this in the following way.
Event.find({ $in: eventIds}, function(err, docs){
To
mongoose.model('Event').find({_id:eventIds}, function(err, docs){
which returned 3 documents which are correct.
Use alias when you require file
EventModel = require('./event.js');
then
EventModel.find({ $in: eventIds}, function(err, docs){
console.log(docs);
});
How are you exporting your EventModel?
Assuming you are exporting it like as a module (module.exports = { EventModel }),
you want to go "const Event = require('./event.js').EventModel;"
Then simply go with "Event.find(..."
This worked for me:
In my project I pass a Model into some middleware and started seeing this issue so I used mongoose.model('Name of Model')
example: .get(advancedResults(mongoose.model('Store'), 'stores'), getStores)
If you're importing your model from an index.js or index.ts file, you had
better import the file as:
import YourModel from "./<file>/index"

Receiving Data from Mongodb using Mongoose [duplicate]

I just started learning MongoDB and mongoose. Currently I have the following structure:
database -> skeletonDatabase
collection -> adminLogin
When I run db.adminLogin.find() from the command line I get:
{ "_id" : ObjectId("52lhafkjasfadsfea"), "username" : "xxxx", "password" : "xxxx" }
My connection (this works, just adding it FYI)
module.exports = function(mongoose)
{
mongoose.connect('mongodb://localhost/skeletonDatabase');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log('Conntected To Mongo Database');
});
}
My -js-
module.exports = function(mongoose)
{
var Schema = mongoose.Schema;
// login schema
var adminLogin = new Schema({
username: String,
password: String
});
var adminLoginModel = mongoose.model('adminLogin', adminLogin);
var adminLogin = mongoose.model("adminLogin");
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
}
My console.log() returns as >>>>
So what am I doing wrong here? Why do I not get any data in my console log? Thanks in advance for any help.
mongoose by default takes singular model names and pairs them with a collection named with the plural of that, so mongoose is looking in the db for a collection called "adminLogins" which doesn't exist. You can specify your collection name as the 2nd argument when defining your schema:
var adminLogin = new Schema({
username: String,
password: String
}, {collection: 'adminLogin'});
Had a problem with injecting it within an express route for my api so I changed it thanks to #elkhrz by first defining the schema and then compiling that one model I want to then pull like so:
app.get('/lists/stored-api', (req, res) => {
Apis.find(function(err, apis) {
if (err) return console.error(err);
res.send(apis);
});
});
I wouldn't send it to the body, I would actually do something else with it especially if you plan on making your API a production based application.
Run through this problem and read up on possible proper ways of rendering your data:
How to Pass Data Between Routes in Express
Always a good idea to practice safe procedures when handling data.
first compile just one model with the schema as an argument
var adminLogin = mongoose.model('adminLogin', adminLogin);
in your code adminLogin does not exist, adminLoginModel does;
after that ,instead to
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
try this
adminLogin.find(function (err, adminLogins) {
if (err) return console.error(err);
console.log(adminLogins);
is important the "s" because mongo use the plural of the model to name the collection, sorry for my english...

Mongoose findByIdAndUpdate not working

I have a fairly straight forward method below to update a document based on its ObjectId. It does not return an error but it fails to make the required updates to the document. I think it is failing because, according to my research, findByIdAndUpdate() takes only plain Javascript whereas job._id is an ObjectId from the document that I want to update. Can someone tell me how to make this work correctly?
function handleEncoderResponse(xmlResponse, job) {
var r = et.parse(xmlResponse);
var mediaID = r.findtext('./MediaID');
var message = r.findtext('./message');
EncodingJob = mongoose.model('EncodingJob');
EncodingJob.findByIdAndUpdate( job._id, {
"MediaID": mediaID,
"Status": message
}, function(err, result) {
if (err) console.log(err);
console.log(result);
});
}
Edit: Per this question Mongoose update document Fail with findByIdAndUpdate
I also tried the following code to no avail.
job.MediaID = mediaID;
job.Status = message;
job.save(function(err, res) {
if(err) console.log(err);
});
This approach yields the issue. It does not update the document and it does not return an error.
As it turns out, my mistake was forgetting to define MediaID and Status in the Schema as follows:
var encodingJobSchema = new mongoose.Schema({
...
MediaID: String,
Status: String
});

How to access a preexisting collection with Mongoose?

I have a large collection of 300 question objects in a database test. I can interact with this collection easily through MongoDB's interactive shell; however, when I try to get the collection through Mongoose in an express.js application I get an empty array.
My question is, how can I access this already existing dataset instead of recreating it in express? Here's some code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
mongoose.model('question', new Schema({ url: String, text: String, id: Number }));
var questions = mongoose.model('question');
questions.find({}, function(err, data) { console.log(err, data, data.length); });
This outputs:
null [] 0
Mongoose added the ability to specify the collection name under the schema, or as the third argument when declaring the model. Otherwise it will use the pluralized version given by the name you map to the model.
Try something like the following, either schema-mapped:
new Schema({ url: String, text: String, id: Number},
{ collection : 'question' }); // collection name
or model mapped:
mongoose.model('Question',
new Schema({ url: String, text: String, id: Number}),
'question'); // collection name
Here's an abstraction of Will Nathan's answer if anyone just wants an easy copy-paste add-in function:
function find (name, query, cb) {
mongoose.connection.db.collection(name, function (err, collection) {
collection.find(query).toArray(cb);
});
}
simply do find(collection_name, query, callback); to be given the result.
for example, if I have a document { a : 1 } in a collection 'foo' and I want to list its properties, I do this:
find('foo', {a : 1}, function (err, docs) {
console.dir(docs);
});
//output: [ { _id: 4e22118fb83406f66a159da5, a: 1 } ]
You can do something like this, than you you'll access the native mongodb functions inside mongoose:
var mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/local');
var connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', function () {
connection.db.collection("YourCollectionName", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
})
});
});
Update 2022
If you get an MongoInvalidArgumentError: The callback form of this helper has been removed. error message, here's the new syntax using async/await:
const mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/productsDB');
const connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', async function () {
const collection = connection.db.collection("Products");
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
});
});
I had the same problem and was able to run a schema-less query using an existing Mongoose connection with the code below. I've added a simple constraint 'a=b' to show where you would add such a constraint:
var action = function (err, collection) {
// Locate all the entries using find
collection.find({'a':'b'}).toArray(function(err, results) {
/* whatever you want to do with the results in node such as the following
res.render('home', {
'title': 'MyTitle',
'data': results
});
*/
});
};
mongoose.connection.db.collection('question', action);
Are you sure you've connected to the db? (I ask because I don't see a port specified)
try:
mongoose.connection.on("open", function(){
console.log("mongodb is connected!!");
});
Also, you can do a "show collections" in mongo shell to see the collections within your db - maybe try adding a record via mongoose and see where it ends up?
From the look of your connection string, you should see the record in the "test" db.
Hope it helps!
Something else that was not obvious, to me at least, was that the when using Mongoose's third parameter to avoid replacing the actual collection with a new one with the same name, the new Schema(...) is actually only a placeholder, and doesn't interfere with the exisitng schema so
var User = mongoose.model('User', new Schema({ url: String, text: String, id: Number}, { collection : 'users' })); // collection name;
User.find({}, function(err, data) { console.log(err, data, data.length);});
works fine and returns all fields - even if the actual (remote) Schema contains none of these fields. Mongoose will still want it as new Schema(...), and a variable almost certainly won't hack it.
Go to MongoDB website, Login > Connect > Connect Application > Copy > Paste in 'database_url' > Collections > Copy/Paste in 'collection' .
var mongoose = require("mongoose");
mongoose.connect(' database_url ');
var conn = mongoose.connection;
conn.on('error', console.error.bind(console, 'connection error:'));
conn.once('open', function () {
conn.db.collection(" collection ", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // data printed in console
})
});
});
I tried all the answers but nothing worked out, finally got the answer hoe to do it.
var mongoose = require('mongoose');
mongoose.connect('mongodb://0.0.0.0:27017/local');
// let model = require('./test1');
setTimeout(async () => {
let coll = mongoose.connection.db.collection(<Your collection name in plural form>);
// let data = await coll.find({}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {projection:{player:1, _id:0}}).toArray();
let data = await coll.find({}, {limit:3, sort:{name:-1}}).toArray();
console.log(data);
}, 2000);
I have also mentioned some of the criteria to filter out. Delete and update can also be done by this.
Thanks.
Make sure you're connecting to the right database as well as the right collection within the database.
You can include the name of the database in the connection string.
notice databasename in the following connection string:
var mongoose = require('mongoose');
const connectionString = 'mongodb+srv://username:password#hosturl.net/databasename';
mongoose.connect(connectionString);