How to write a static method in Mongoose that gets all documents? - node.js

I'm new to Mongoose. I wrote a statics method and a instance method for a Mongoose schema named 'questionSchema' and exported it like so:
var questionSchema = new Schema({
...
})
questionSchema.methods.createQuestion = function(){
return this.save(function(err){
if(err){
return err
};
return 'Saved the question';
});
};
questionSchema.statics.getAllQ = function(){
return this.find({}, function(err, res){
if(err){
return err
};
return res;
});
}
module.exports = mongoose.model('Question', questionSchema)
Then in a route in my Node/Express server, I imported the it as a model, and tried calling the static method, which should return all the documents under the Question model:
const Question = require('../models/question.js');
...
router.post('/qcrud/submit', (req, res) => {
let reqBody = req.body;
var newQuestion = new Question({reqBody});
newQuestion.createQuestion();
})
router.get('/qcrud/getAll',(req, res) => {
let qArr = Question.getAllQ()
res.send(qArr);
});
However, it returns a Query object, not an array like I expected. I looked around and saw on MDN that
'If you don't specify a callback then the API will return a variable
of type Query.'
I did specify a callback, but still got the Query object. First of all, am I using my static and instance methods right? Are the documents even saving? And how do I access the array documents saved?

If you're using Node 8.x you can utilize async/await
This way your code will look more synchronous:
questionSchema.statics.getAllQ = async () => {
return await this.find({});
}
router.get('/qcrud/getAll',async (req, res) => {
let qArr = await Question.getAllQ();
res.send(qArr);
});
You can find a really nice article that is explaining how to use Mongoose with async/await here.

Related

await doesn't wait for function to end

I know this has been asked a lot but I can't seem to use the existing answers to get my code to work. I am trying to use mongojs to make a single query, then put the results in a global (relative to the scope) variable to avoid nesting multiple callbacks. However, I can't seem to get the code to wait for the query to end before continuing.
async function taskmaster() {
const db = mongojs(mongoUri.tasker);
let currentDoc;
const getTask = async function() {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
console.log(doc);
currentDoc = doc;
});
}
await getTask();
console.log(currentDoc);
// Code to process currentDoc below
}
No matter what I do, console.log(doc) shows a valid MongoDB document, yet console.log(currentDoc) shows "undefined". Thanks in advance!
Inside your async function, you use findOne method() in callback style, so it's totally normal that console.log(currentDoc) shows undefined, because it executes before
currentDoc = doc;
You can promisify the findOne method, to use it with async/await keyword.
I found a tutorial to promisfy a callback style function here, hope it help : https://flaviocopes.com/node-promisify/
--- EDIT ---
I rewrite your code when promising the findOne method, as suggested by O.Jones
async function taskmaster() {
const getTask = async (taskId) => {
return new Promise((resolve, reject) => {
db.tasks.findOne({'task_id': {'$in': [taskId, null]}}, function(err, doc) {
if(err) {
console.log("problem when retrieve data");
reject(err);
} else {
resolve(doc);
}
});
})
const db = mongojs(mongoUri.tasker);
const currentDoc = await getTask(taskId);
console.log(currentDoc);
}

`find()` Not Working Like `findOne()` After Connecting to MongoDB Via Node

I am familiar with how to get documents from the Mongo shell, but am having difficulty getting documents using find() when connecting via Node.
What I'm getting right now looks like a lot of cursor info, but not the actual documents.
What do I need to change with the following code so that I get the actual documents logged to the console for 'results'?
const config = require('./../../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);
module.exports = async function getSchedules() {
let results;
return new Promise((resolve, reject) => {
client.connect(async function (err) {
if (err) return reject(err);
try {
const db = await client.db(dbName);
results = await db.collection('schedules').find();
} catch (error) {
return reject(error);
}
return resolve(results);
});
});
};
... And here's where I actually try and get the documents:
async function getSchedulesFromDB() {
await getSchedules().then((schedules => {
console.log('schedules: ', schedules); // expect result here
return schedules;
}));
}
When I used this same kind of code structure on a findOne(), it worked. But here when using a find() it is not. What am I missing? Does find() work fundamentally differently than findOne()?
Yes. find() returns a cursor over which you must iterate. findOne() returns a single doc, not a cursor. If you want an array of results, you must "build it yourself" by iterating the cursor, something like:
results = [];
db.collection('schedules').find().forEach(function(d) { results.push(d); });

how to export array created in a promise

I'm trying to follow a project from mozilla developer local library tutorial
. It includes learning how to use nodejs, express, mongoose and mongoDB. I am swapping out mongoose and mongoDB with firestore and using firebase functions to eventually host with firebase hosting.
The project uses a model, view, controller structure. I really want to stick to this structure to help break everything down into manageable chunks.
The first problem I’m trying to resolve is to return an array that I created in a promise to be exported as a function that is called from booksController.js.
A concise view of the structure I have is:
index.js
const booksRouter = require('./routes/books');
app.use('/books', booksRouter);
routes/books.js
const books_controller = require('../controllers/booksController');
router.get('/', books_controller.index);
controllers/booksController.js
var Books = require('..models/books');
exports.index = function(req, res){
var books = Books.AllBooks();
res.render('books',{books: books});
};
models/books.js
var booksRef = db.collection(books);
var allBooks = booksRef.get()
.then(snapshot => {
var books = [];
Snapshot.forEach(doc => {
books.push({doc.data: doc.data()});
});
});
.catch(err =>{
console.log('Error getting documents', err);
});
exports.AllBooks = function(req, res){
return books;
}
I've tried wrapping the promise in a function and returning the array to export it but I get undefined when console logging in booksController.js.
Can someone please enlighten me and complete the puzzle of how to return the array so it can be exported.
What you're trying to do, can't be done using require, since require is synchronous.
Instead what you can do is export a function that will fetch the books asynchronously, and use that function in your controller.
models/books.js
const booksRef = db.collection(books);
async function getBooks() {
// You can implement a cache and fetch the books only once.
const snapshot = await booksRef.get()
const books = [];
snapshot.forEach(doc => {
books.push({
doc.data: doc.data()
});
});
return books;
}
module.exports = getBooks;
controllers/booksController.js
const getBooks = require('./../models/books');
exports.index = async function(req, res){
const books = await getBooks(); // Handle exception
res.render('books',{books: books});
};

Stuck with Promise Pending [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
Using NodeJS and Mongoose I'm trying to create a database file that allows me to query the database and return the result to be able to work with without using Mongoose.
In my database file I have this
db.js
module.exports = {
getUser: function (name) {
var u = User.findOne({username: name}).exec().then(function(user) {
console.log(user);
return user;
});
return u;
}
};
and in my other file calling it I have
var db = require('./db')
var user_info = db.getUser("tom");
console.log(user_info);
When the getUser function is called it correctly prints the information I'm looking for in the db.js file with the following line of code.
console.log(user);
However I can't seem to find a way of getting the database to return the value into user_info, I just get
Promise { <pending> }
I've looked into .then and callbacks but can't seem to figure out how to get it to work I just always end up in an endless cycle of Promise pending.
Thanks
var db = require('./db')
var user_info = db.getUser("tom");
console.log(user_info);
user_info.then(user => {
const username = user.username;
console.log(username);
});
Promises execute. But, you get access to the result on after a .then.
you must call .then on the promise to capture the results:
check here
Promise pending
You need to call .then for getUser.
// db.js
module.exports = {
getUser: function (name) {
return User.findOne({
username: name
}).exec();
}
};
var db = require('./db');
db.getUser("tom").then((user) => {
console.log(user);
}).catch((err) => {
console.log(err);
});

Unit testing / Mocking mongoose models with sinon

so I have a node module like:
let mongoose = require('mongoose'),
User = mongoose.model('User');
module.exports = (req, res, next) => {
User.findById(req.user)
.then(user => {
req.body.user = user._id;
req.body.company = user.company;
next();
}, err => {
req.send(400, err);
});
};
So, in this case, I want to ensure the proper things are attached to the req.body. So, how would I go about mocking the User function? I have to load the model first so this code doesn't throw an error before calling mongoose.model so maybe something to do with actually stubbing the global require? Thanks for any advice!
So, I just discovered proxyquire https://www.npmjs.com/package/proxyquire and it was like the heavens opened and shined a light down upon me for the most glorious "ah ha" moment. No more having to load models in mongoose before trying to use them!
The code to mock mongoose looks something like:
const proxyquire = require('proxyquire'),
expect = require('chai').expect,
Promise = require('bluebird'),
_ = require('lodash'),
USERID = 'abc123',
COMPANY = 'Bitwise',
mongooseStub = {
mongoose: {
model: (name, schema) => ({
findById: () => {
return new Promise((resolve, reject) => {
resolve({_id: USERID, company: COMPANY});
});
}
})
}
},
attachUser = proxyquire('../lib/users/attach-user', mongooseStub);
That will effectively load the attach-user module, while stubbing out the mongoose.model function to return something I can control. Then the rest of my test just calls out to the module function and can make assertions as expected.

Resources