I have a simple comments app which enables the user to enter a comment into the system via a form and these are then logged onto a list on the bottom of the page.
I wanted to modify it so that a user could click a comment once it is created and it would load up the associated content that goes with that comment.
My schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
title: String,
content: String,
created: Date
});
module.exports = mongoose.model('Comment', CommentSchema);
My app.js routes:
app.use('/', routes);
app.use('/create', create);
app.use('/:title', show);
My show route:
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Comment = mongoose.model('Comment', Comment);
router.get('/', function(req, res) {
Comment.findOne(function(err, comment){
console.log(comment.content)
});
});
module.exports = router;
I have three comments in my system and saved in my database, each with unique contents, But whenever I click on a comment, no matter what it is. I am only getting the content that is associated with the first comment.
Why is this?
You'll have to provide a condition for .findOne() to retrieve a specific document:
Model.findOne(conditions, [fields], [options], [callback])
Without one, an empty condition is implied that matches every document in the collection:
Comment.findOne({}, function ...);
And, .findOne() simply retrieves the 1st of those that are matched.
With the :title parameter in the route for show and title property in the Schema, one possible condition would be:
Comment.findOne({ title: req.params.title }, function ...);
Though, if the titles aren't unique in order to find the "right" one, you'll have make the condition more specific. The _id or id would be the most distinct.
app.use('/:id', show);
Comment.findOne({ id: req.params.id }, function ...);
// or
Comment.findById(req.params.id, function ...);
Also adjusting any links and res.redirect()s to fill pass the id for :id.
Related
Hey guys could really use some help with a DELETE route. I'm working with RESTful routing, trying to follow the conventions and when adding the delete route I get the error:
CastError: Cast to ObjectId failed for value " X" at path "_id" for model "Blog"
I've searched for the issue on stackoverflow and the best I could come up with was the version of mongoose had a bug. I rolled it back to V 4.10.0 and am still getting the issue. My code follows:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
methodOverride = require("method-override");
// APP CONFIG
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(methodOverride("_method"));
mongoose.connect("mongodb://localhost/restful_blog_app");
// Create a new Blog Schema defines what the object will be
var Blog = new mongoose.Schema ({
title: String,
image: String, //{type: String, default: "placeholder.jpg"} for a default image
body: String,
created: {type: Date, default: Date.now}
});
// Mongoose compiles the Schema into a model, useable object
var Blog = mongoose.model("Blog", Blog);
// DELETE ROUTE
app.delete("/blogs/:id", function(req, res){
Blog.findByIdAndRemove(req.params.id, function(err){
if(err){
console.log(err)
}
})
})
I think the id parameter that you get is not a valid ObjectId string, so it can't be casted to a ObjectId Object.
Here you have have some information about ObjectId, check you are passing a valid one.
Found the error, it was in my ejs file. I noticed that the error message had a space between the first quotation and the actual id itself so " 92384759" rather than "92384759". I removed the space and it works perfectly. The second fault on my part is for editing the error message in my original question (I should have just copied and pasted the objectid rather than abbreviate with " X"). – Andy 9 hours ago
I am trying to find a way to modify the query results of mongoose.
Below is the self contained model with the post hook
'use strict';
// load the things we need
var mongoose = require('mongoose');
var invoice_db = mongoose.createConnection(config.mongo.url + '/invoiceDB'); //connect to buyer DB
var path = require('path');
// define the schema for our invoice details model
var invoicedetailSchema = new Schema({
//SCHEMA INFO
});
invoicedetailSchema.post('find', function(results){
console.log('POST FIRED')
results = results.filter(function(doc){
return doc.tags.length;
})
})
var InvoiceModel = invoice_db.model('InvoiceDetail', invoicedetailSchema);
// create the model for seller and expose it to our app
promise.promisifyAll(InvoiceModel);
promise.promisifyAll(InvoiceModel.prototype);
module.exports = InvoiceModel;
The find query is working fine and the post is firing but the results are not filtered per the post hook.
How do i go about editing the results before the results are returned.
I have a simple comments app which enables the user to enter a comment into the system via a form and these are then logged onto a list on the bottom of the page.
I wanted to modify it so that a user could click a comments title once it is created and it would load up the associated content that goes with that comment.
I have modified my route in the app.js to lookup the :id:
And have also modified my main Show.js route to use the id as an argument in a findOne command to locate the comment.
I have also put in a console.log() command to log out the req.params.id
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Comment = mongoose.model('Comment', Comment);
router.get('/', function(req, res) {
Comment.findOne({ id: req.params.id }, function(err, comment){
console.log(req.params.id)
});
});
module.exports = router;
However, all I am getting back is an undefined message in my terminal.
If I place the console.log() directly in my app.js, I get the id logged as intended.
app.use('/:id', function(req,res) {
console.log(req.params.id)
});
Am I missing something in my route that is stopping my from getting the id parameter?
You need to specify the :id in your get route, like this:
// GET /1 -> print 1
router.get('/:id', function(req, res) {
console.log(req.params.id);
});
// GET /foo/1/bar/2 -> print 1, print 2
router.get('/foo/:id1/bar/:id2', function(req, res) {
console.log(req.params.id1);
console.log(req.params.id2);
});
You need to pass {mergeParams: true} when calling your router in the router page
var router = express.router({mergeParams: true})
this will pass the parent parameters to the child.
This is my first time writing a MVC app in Node/Express/Mongoose so I could really use some help. My .find() command just doesn't find anything! :(
Structure is that I have a an /app folder in the root. /app folder contains /models (schemas), /controllers and /views in it. And I have app.js outside in the root.
Somewhere in app.js:
// all necessary config/setup stuff..
var mongoose = require('mongoose');
mongoose.connect(config.db);
var app = express();
require('./config/routes')(app)
In my routes.js file:
var skills = require('../app/controllers/skills');
app.get('/', skills.showall);
My controller skills.js contains:
var Skill = require('../models/skill');
exports.showall = function(req, res) {
Skill.find({}, function(err, docs){
if (!err) {
res.render('index', {title: 'Skilldom', skills: docs});
}
else {
throw err;
}
});
}
Finally my Model skill.js contains:
var mongoose = require('mongoose');
//Skill schema definition
var skillSchema = new mongoose.Schema({
name: String,
length: String,
});
var Skill = mongoose.model('Skill', skillSchema);
module.exports = Skill;
My index view renders, so I see the content from my index.jade template, but for some reason the find command in the model is not fetching anything. I can confirm that my database (in MongoHQ) has real data.
Any thoughts?
Change your Skill.js for this
var mongoose = require('mongoose');
mongoose.set('debug', true);
//Skill schema definition
var skillSchema = new mongoose.Schema({
name: String,
length: String,
});
var Skill = mongoose.model('Skill', skillSchema);
module.exports = Skill;
After that, you can see at the console if mongoose is doing your queries.
I was in the same situation as you describe and it turns out I didn't understand the magic of mongoose collection naming, in your code it will try to load the "skills" and if that's not what it's named in your mongo nothing will be returned. Should really toss a "so such collection" error instead imho.
This below method gives an alternate name for your collection
var skillSchema = new mongoose.Schema({
name: String,
length: String,
},{collection : 'Skill'});
or
var Skill = mongoose.model('Skill', skillSchema,''Skill);
This is not a concrete app/code question, it's just about common app architecture.
I'm trying to understand proper way to organize my mongoose application. As I'm new to mongoose, that's how I do it now:
core/settings.js
var mongoose = require('mongoose');
exports.mongoose = mongoose;
mongoose.connect('mongodb://localhost/blog');
exports.db = mongoose.connection;
core/models.js
settings = require("./settings");
// post schema
var postSchema = settings.mongoose.Schema({
header: String,
author: String,
text: String
})
//compiling our schema into a Model
exports.post = settings.mongoose.model('post', postSchema)
core/db-layer.js
settings = require("./core/settings");
models = require("./core/models");
exports.function = createAndWriteNewPost(function(callback) {
settings.db.on('error', console.error.bind(console, 'connection error:'));
settings.db.once('open', function callback() {
new models.post({
header: 'header',
author: "author",
text: "Hello"
}).save(function(err, post) {
callback('ok');
});
});
});
routes/post.js
db = reqiure("../core/db.js")
exports.get = function(req, res) {
db.createAndWriteNewPost(function(status){
res.render('add_material', {
//blah blah blah
});
});
};
app.js
var post = require ('routes/post.js')
...
app.get('/post', post.get);
So, this code was extremely simplified (even not tested) just to show my current architecture thoughts. It's not a concrete app, just something like creating an abstract blog post. So thats how it works:
app.js --> routes/post.js <--> core/db-layer.js
|
v
core/models.js <--> core/settings.js
It seems a bit over superfluous for me. Could you suggest more optimal app structure? Thanks.
When I first got into Node.js, Express and Mongoose I struggled with scaling my code.
The intention of my answer is to help someone who's working on more than just a simple blog, but to help with an even larger scalable project.
I am always connected to the database, I do not open and close connections when needed
I use index.js as the root file of a folder, just like we'd do in other languages
models are kept in their own documents, and require()d into the models/index.js file.
routes are similar to models, each route level has a folder, which has an index.js file in turn. So it's easy to arrange something like http://example.com/api/documents/:id. It also makes more sense when one goes through the file structure.
Here's the structure of what I use:
-- app.js
-- models/
---- index.js
---- blog.js
-- mongoose/
---- index.js
-- routes/
---- index.js
---- blog/index.js
-- public/
-- views/
---- index.{your layout engine} => I use Jade.lang
-- methods/
---- index.js => use if you'd rather write all your functions here
---- blog.js => can store more complex logic here
app.js
var db = require('./mongoose'),
express = require('express');
// note that I'm leaving out the other things like 'http' or 'path'
var app = express();
// get the routes
require('./routes')(app);
// I just require routes, without naming it as a var, & that I pass (app)
mongoose/index.js
// Mongoose connect is called once by the app.js & connection established
// No need to include it elsewhere
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');
// I have just connected, and I'm not exporting anything from here
models/index.js
// Logic here is to keep a good reference of what's used
// models
Blog = require('./blog');
// User = require('./user');
// exports
exports.blogModel = Blog.blogModel;
// exports.userModel = User.userModel;
models/blog.js
So for every model that you work on you create a model.js document, and add it in the models/index.js above. As an example I've added a User model but commented it out.
// set up mongoose
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var BlogSchema = Schema({
header: {type: String },
author: {type: String },
text: {type: String },
_id: { type: ObjectId } // not necessary, showing use of ObjectId
});
Blog = mongoose.model('Blog', BlogSchema);
// the above is necessary as you might have embedded schemas which you don't export
exports.blogModel = Blog;
routes/index.js
module.exports = function(app) {
app.get('/', function(req, res) {
// do stuff
});
require('./blog')(app);
// other routes entered here as require(route)(app);
// we basically pass 'app' around to each route
}
routes/blog/index.js
module.exports = function(app) {
app.get('/blog', function(req, res) {
// do stuff
});
require('./nested')(app);
// this is for things like http://example.com/blog/nested
// you would follow the same logic as in 'routes/index.js' at a nested level
}
suggested use
models: for creating the logic that deals with the documents, i.e. creating, updating, deleting, and searching.
routes: minimal coding, only where I need to parse http data, create instances of models, and then I send queries to the relevant model.
methods: for the more complex logic that doesn't directly involve models. As an example, I have an algorithms/ folder where I store all the algorithms that I use in my app.
Hope this provides more clarity. This structure is working wonders for me as I find it easy to follow.
That's pretty much how I go about it, with a few differences:
I don't think you can have the open listener inside your function in the db-layer. What I generally do when using a persistent connection like yours is start the application itself in the db open handler. If you don't want to use persistent connections, use createConnection in the db layer function, and make sure you close it before calling the callback. I am not sure if I am making myself clear. Let me know if you want a code example.
This is more of a general node.js tip, but I keep my database connection string and other configuration in a json file and require it wherever it is needed. You probably won't need another settings.js file after that.
You can also use schema functions (http://mongoosejs.com/docs/api.html#schema_Schema-method) to code some app functionality into your models itself.