My .find function is not working. It is not calling my database and returning an error of:
TypeError: beerSchema.findOne is not a function
My other pages display fine, it is just the page, I am trying to load the database on.
app.get("/beers", function(req, res){
//Get all beers
beerSchema.find({}, function(err, beers){
if (err) {
console.log(err);
} else {
res.render("beers", {beers: beers});
}
});
});
BeerSchema:
var beerSchema = new mongoose.Schema({
name:String,
abv: Number,
type:String,
Brewery:String,
Image:String
});
var beer = mongoose.model("Beer", beerSchema );
I get the error that .find is not a function.
You need the model not the schema. Wherever you're making your schema, change this:
module.exports = beerSchema;
To this:
module.exports = mongoose.model("Beer", beerSchema);
Then import this in your app file:
const { Beer } = require("./beer.js");
And use Beer:
Beer.find({}, function(err, beers) {...});
Side note: ES6 shorthand property notation means instead of this:
res.render("beers", { beers: beers });
You can just do:
res.render("beers", { beers });
Related
I have some code that pulls all documents from a collection and puts it onto a webpage. a simplified version looks like this:
var mongodb = require("mongodb"),
express = require("express"),
mongoServer = new mongodb.Server('localhost', 27017),
dbConnector = new mongodb.Db('systemMonitor', mongoServer),
db;
var app = new express();
app.get('/drives', function(req, res) {
db.collection('driveInfo', function(err, collection) {
if (err) throw err;
collection.find({}, function(err, documents) {
res.send(documents);
});
});
});
dbConnector.open(function(err, opendb) {
if (err) throw err;
db = opendb;
app.listen(80);
});
I have a driveInfo collection which contains a long list of documents. Each document contains nested objects. What I would like to do, is whenever someone visits /drives in their browser, to print the entire collection as a json object so that I can grab everything with jquery later (beginnings of an api)
However, I get an error saying "TypeError: Converting circular structure to JSON". The error on the page points to this line of code:
collection.find({}, function(err, documents) {
res.send(documents);
});
I'm unsure what the problem is, or where the self-reference is. Am I not querying the collection properly?
Not sure what version of the API you are using, but i think that your syntax might be wrong looking at the API spec:
http://docs.mongodb.org/manual/reference/method/db.collection.find/
This is the declaration:
db.collection.find(<criteria>, <projection>)
And you are definitely misusing the projection parameter. Passing a callback like you are doing seems to return the db object in the result, which is causing the circular error during JSON serialization in express.
The correct code for the find all operation should be something like:
collection.find({}).toArray(function(error, documents) {
if (err) throw error;
res.send(documents);
});
In my case I was getting the error because I was querying(using mongoose find method) without doing an await. Please see below
Query that gave the error (as I haven't executed this query using await) :
const tours = Tour.find({
startLocation: {
$geoWithin: { $centerSphere: [[longitude, latitude], radius] }
}
});
Error that I got on postman due to this :
"message": "Converting circular structure to JSON\n --> starting at object with constructor 'NativeTopology'\n | property 's' -> object with constructor 'Object'\n | property 'sessionPool' -> object with constructor 'ServerSessionPool'\n --- property 'topology' closes the circle"
How I got rid of the above error (added await) :
const tours = await Tour.find({
startLocation: {
$geoWithin: { $centerSphere: [[longitude, latitude], radius] }
}
});
callback option is from Mongoose not from MongoDB see docs.
// Mongoose Docs : callback option
MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
// Example
app.get( '/api/users' , (req,res,done)=>{
let getUsers = NewUser.find({},(err,data)=>{
if(err) return done(err);
res.json(data)
});
});
Look that the response is into callback that in your case it would be
YourModel.find({}, function(err, documents) {
if(err) return done(err);
res.send(documents); // <-- here
});
// <-- not here
In Mongo there is a cursor method to access the documents next() see docs :
var myCursor = db.bios.find( );
var myDocument = myCursor.hasNext() ? myCursor.next() : null;
if (myDocument) {
var myName = myDocument.name;
print (tojson(myName));
}
You can find CRUD operations in mongo docs at manual/crud. In Query Documents you will see db.inventory.find( {} ) : To select all documents in the collection, pass an empty document as the query filter parameter to the find method.
Async/Await function solution : Mongo Docs
app.get( '/api/users' , async (req,res)=>{
const getUsers = await NewUser.find({});
res.json( getUsers );
})
< callback > solution : Mongoose Docs.
app.get( '/api/users' , (req,res,done)=>{
let getUsers = NewUser.find({},(err,data)=>{
if(err) return done(err);
res.json(data)
});
});
const res1 = await db.collection("some-db").find()
Here, res1 will contain a "cursor" which has a circular structure, hence the given error is thrown.
Try adding const res2 = await res1.toArray() to the code.
Here, res2 will now contain an array of documents, pointed by cursor res1, which is the documents you were querying for.
I have 2 collections setup as below, Dates and Streets.
What I would like to achieve is to, query Streets by a param StreetName and look that up to find it's unique ID and then query the other collection by that ID to pull back all the dates that match.
My route is set up to /wasteDate/:StreetName. Here's what I have:
model.js
var DateSchema = new Schema({
date: {
type: Date
},
street_id: {
type: String,
}
});
var StreetSchema = new Schema({
name: {
type: String
}
});
routes.js
module.exports = function(app) {
var wasteCollections = require('../controllers/wasteController');
app.route('/wasteDate/:streetName')
.get(wasteCollections.get_dates_by_street_name);
};
controller.js
var mongoose = require('mongoose'),
ColDate = mongoose.model('Dates'),
that = this,
Street = mongoose.model('Streets');
(...)
exports.manual_get_dates_by_street = function (id) {
var wasteDates = ColDate.find({ street_id: id }).lean();
return wasteDates;
};
exports.get_dates_by_street_name = function (req, res) {
Street.find({
name: req.params.streetName
}, function(err, street) {
var query;
var theStreetId = street[0].id;
if (err) res.send(err);
query = that.manual_get_dates_by_street(theStreetId);
res.json(query);
});
};
at the moment i'm getting a circular reference error on the JSON.
I don't think I'm doing it the right way and think I may need to amend my schema?
Any help appreciated
You can either use (1) find twice or (2) aggregation.
Here's the first way:
exports.manual_get_dates_by_street = function (id, callback) {
// you are dealing with asynchronous operations, so you have to wait for the callback
// to execute before you can get the data
ColDate.find({ street_id: id }).lean().exec(callback);
};
exports.get_dates_by_street_name = function (req, res) {
// you are expecting one result, so use findOne instead of find
Street.findOne({ name: req.params.streetName }, function (err, street) {
// make sure you handle errors properly such as stopping execution of
// the next lines or else you may get unexpected errors
if (err)
return res.send(err);
// we pass a callback that will be executed once results (or an error) are found
that.manual_get_dates_by_street(street._id, function (err, dates) {
res.json({ dates: dates });
});
});
};
I never used it but I think mongoose-models may resolve your problem. https://github.com/SportZing/mongoose-models
Another possible approach is to put the second query function as a callback of the first.
I have a document Users with storage property defined as an array of ObjectId referenced to other document called Storage. I'm trying to get a specific user and then return the storage information inside an array.
This is my code:
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = [];
data.storages.forEach(function(record){
Storage.findOne({_id: record}, function(err, storage){
storageArray.push(storage);
});
});
return Promise.all(storageArray);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
}
But after execution, storageList is an empty array.
I'm a newbie to node.js and please let me know if I need to provide more information.
So the main point here is that you need to .exec() in order to return the Promise. This is why your attempt failed. But there are better approaches syntactically as well.
Instead use .map() and call .exec() to return the Promise
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = data.storages.map(function(id) {
return Storage.findOne({_id: id }).exec()
});
return Promise.all(storageArray);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
Or instead use $in with .find() where the method returns an array and the operator allows you to specify an array of things to match:
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
return Storage.find({ "_id": { "$in": data.storages } }).exec();
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
It also basically looks like you could instead simply use .populate()
User.findOne({'userId': req.user.userId}, 'storages')
.populate('storages')
.then(function(data) {
res.render('storage_template', {
storage: data.storages
});
console.log(data.storages);
});
But it's not clear in your question if these are actually defined as references to the Storage model or not.
Also see Queries are not Promises in the mongoose documentation.
If you are using mongoose then you can use .populate() to get your storages information details from Storage collection.
can try this one
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages')
.populate('storages')
.exec(function (err, storageList) {
if (err) return handleError(err);
console.log(storageList);
res.render('storage_template', {
storage: storageList
});
});
}
for using this .populate() ensure first in your User model you added ref filed for storages
like:
storages: [{ type: Schema.Types.ObjectId, ref: 'Storage' }]
//do npm install lodash
const _ = require('lodash');
module.exports.storageDetail = function(req, res) {
User.findOne({'userId': req.user.userId}, 'storages').then(function(data){
var storageArray = [];
let queries = _.map(data.storages, (record)=>{
return Storage.findOne({_id: record}, function(err, storage){
storageArray.push(storage);
});
})
return Promise.all(queries);
}).then(function(storageList){
res.render('storage_template', {
storage: storageList
});
console.log(storageList);
});
}
I have some code that pulls all documents from a collection and puts it onto a webpage. a simplified version looks like this:
var mongodb = require("mongodb"),
express = require("express"),
mongoServer = new mongodb.Server('localhost', 27017),
dbConnector = new mongodb.Db('systemMonitor', mongoServer),
db;
var app = new express();
app.get('/drives', function(req, res) {
db.collection('driveInfo', function(err, collection) {
if (err) throw err;
collection.find({}, function(err, documents) {
res.send(documents);
});
});
});
dbConnector.open(function(err, opendb) {
if (err) throw err;
db = opendb;
app.listen(80);
});
I have a driveInfo collection which contains a long list of documents. Each document contains nested objects. What I would like to do, is whenever someone visits /drives in their browser, to print the entire collection as a json object so that I can grab everything with jquery later (beginnings of an api)
However, I get an error saying "TypeError: Converting circular structure to JSON". The error on the page points to this line of code:
collection.find({}, function(err, documents) {
res.send(documents);
});
I'm unsure what the problem is, or where the self-reference is. Am I not querying the collection properly?
Not sure what version of the API you are using, but i think that your syntax might be wrong looking at the API spec:
http://docs.mongodb.org/manual/reference/method/db.collection.find/
This is the declaration:
db.collection.find(<criteria>, <projection>)
And you are definitely misusing the projection parameter. Passing a callback like you are doing seems to return the db object in the result, which is causing the circular error during JSON serialization in express.
The correct code for the find all operation should be something like:
collection.find({}).toArray(function(error, documents) {
if (err) throw error;
res.send(documents);
});
In my case I was getting the error because I was querying(using mongoose find method) without doing an await. Please see below
Query that gave the error (as I haven't executed this query using await) :
const tours = Tour.find({
startLocation: {
$geoWithin: { $centerSphere: [[longitude, latitude], radius] }
}
});
Error that I got on postman due to this :
"message": "Converting circular structure to JSON\n --> starting at object with constructor 'NativeTopology'\n | property 's' -> object with constructor 'Object'\n | property 'sessionPool' -> object with constructor 'ServerSessionPool'\n --- property 'topology' closes the circle"
How I got rid of the above error (added await) :
const tours = await Tour.find({
startLocation: {
$geoWithin: { $centerSphere: [[longitude, latitude], radius] }
}
});
callback option is from Mongoose not from MongoDB see docs.
// Mongoose Docs : callback option
MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
// Example
app.get( '/api/users' , (req,res,done)=>{
let getUsers = NewUser.find({},(err,data)=>{
if(err) return done(err);
res.json(data)
});
});
Look that the response is into callback that in your case it would be
YourModel.find({}, function(err, documents) {
if(err) return done(err);
res.send(documents); // <-- here
});
// <-- not here
In Mongo there is a cursor method to access the documents next() see docs :
var myCursor = db.bios.find( );
var myDocument = myCursor.hasNext() ? myCursor.next() : null;
if (myDocument) {
var myName = myDocument.name;
print (tojson(myName));
}
You can find CRUD operations in mongo docs at manual/crud. In Query Documents you will see db.inventory.find( {} ) : To select all documents in the collection, pass an empty document as the query filter parameter to the find method.
Async/Await function solution : Mongo Docs
app.get( '/api/users' , async (req,res)=>{
const getUsers = await NewUser.find({});
res.json( getUsers );
})
< callback > solution : Mongoose Docs.
app.get( '/api/users' , (req,res,done)=>{
let getUsers = NewUser.find({},(err,data)=>{
if(err) return done(err);
res.json(data)
});
});
const res1 = await db.collection("some-db").find()
Here, res1 will contain a "cursor" which has a circular structure, hence the given error is thrown.
Try adding const res2 = await res1.toArray() to the code.
Here, res2 will now contain an array of documents, pointed by cursor res1, which is the documents you were querying for.
My query is:
db.Room.find({}, function (err, rooms) {
if (!err) {
async.each(rooms, function (room, done) {
db.User.find({}, function (err, users) {
if (err) {
done(err)
} else {
room.users = users
room._name = room.name
done()
}
})
}, function (err) {
if (!err) {
socket.emit('read:rooms', rooms)
}
})
}
})
and schemas are:
room:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Room = new Schema({
name: String
});
module.exports = Room
user:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var User = new Schema({
email: String
});
module.exports = User
but in front-end:
socket.on('read:rooms', function (rooms) {
$scope.rooms = rooms
})
but rooms has no users property, help me, please
It's because the Rooms schema doesn't have a users property.
So, there are a few ways to fix it. Since it looks like though you want the users property to be something that really isn't part of the schema, and is a client-side join rather than work that is done on the database (which is a good!), I'd suggest you convert the data to be just plain old JavaScript objects when you send it over the socket (this would have happened anyway, you're just doing it a bit earlier).
So, when the find returns, it is actually returning a fully-realized MongooseJS model object. While you can set dynamic properties on the object instance, they aren't part of the "data" of the model, so that when it is serialized later to the client, only the properties that are documented will be available.
So, here is an example of what I'd suggest:
db.Room.find({}, function (err, rooms) {
if (!err) {
var oRooms = [];
async.each(rooms, function (room, done) {
/* etc. your code */
} else {
var oRoom = room.toObject();
oRoom.users = users
oRoom._name = room.name
oRooms.push(oRoom);
done()
}
This technique would use the toObject functionality of a Model to return a JavaScript object (not a Mongoose model). That way, you can do what you'd like to the object, including adding new properties dynamically.
Later, of course, make sure you send the new array of rooms to the client:
if (!err) {
socket.emit('read:rooms', oRooms)
}