Mongoose findOne inside loop - javascript

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);
});
}

Related

Why is the 'find' query returning a Query object in MongoDB?

I have already looked at many answers here at SO, but none of them solved my problem.
I'm currently creating a project using node, express, and mongodb. I started with a seeder file to put some data into mongodb:
var products = [
new ProductModel(data),
new ProductModel(data),
new ProductModel(data),
new ProductModel(data),
];
// Connecting to mongodb using mongoose.
mongoose.connect(config.mongodb.dsn, { useNewUrlParser: true })
.then(() => {
console.log("Successfully connected to MongoDB through Mongoose.");
})
.catch((error) => {
console.log("Error when connecting to MongoDB through Mongoose: " + error);
});
// Saving product instances to the product document in mongodb
var done = 0;
for (var i = 0; i < products.length; i++) {
products[i].save((error, result) => {
done++;
if (done == products.length) {
// Disconnecting...
console.log("All products have been loaded into mongodb. Disconnecting now...");
mongoose.disconnect();
}
});
}
Then, I am trying to retrieve this data in my routes file, using:
router.get('/', function(req, res, next) {
const products = ProductModel.find();
console.log(products);
res.render('shop/index', { products });
});
However, I am console.logging the result on my terminal, and the only result that comes up is a query object that looks like this:
Query {
_mongooseOptions: {},
_transforms: [],
_hooks: Kareem { _pres: Map {}, _posts: Map {} },
_executionCount: 0,
mongooseCollection:
NativeCollection {
(...)
_traceFunction: undefined,
'$useProjection': true }
I needed to retrieve the products in my db. What am I doing wrong?
Everything is executed as it should. find() returns a Query object as per the docs: https://mongoosejs.com/docs/api.html#model_Model.find
If you want it to return the value, just turn it to an await and call exec to get a promise:
router.get('/', async function(req, res, next) {
const products = await ProductModel.find().exec();
console.log(products);
res.render('shop/index', { products });
});

My .find function is not calling my database

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 });

express/nodejs: stop duplicates in post request

What should I do to stop the post request from posting new object if the object already exists?
I have pasted my code and json object. I have tried next(); but it doesnt work and a duplicate object with some other id is created no matter what. I am using before beforePostTransaction function to check if the product already exists. The request contains the id and the store id of the
index.js
const Transaction = require('./transactions/TransactionModel');
const TransactionCTL = require('./transactions/TransactionController');
Transaction.before('post', TransactionCTL.beforePostTransaction);
Transaction.register(router, '/transactions');
TransactionController.js
const beforePostTransaction = (req, res, next) => {
var id = req.body.id;
Transaction.findById(
id,
(err, data)=>{
if (!data){
next();
}
else{
var store = data.store;
store = JSON.stringify(store);
store = store.replace(/\"/g, "");
if(store !== req.body.store){ //update the whole object
next();
}
else{
//do what?
}
}
});
res.sendStatus(409);
}
json object
[{
"_id": "596db06849822a13c97ba3f9",
"store": "596b088131ea400490897c50"
}]
Not sure what is your model. But if it is mongoose you can use method
.findOneAndUpdate(query,
update,
{upsert: true},
function (error, result) {
if (!error) { //if you need to do something else
}
});
Where update is your object. It will create new item if it not exist and update it if it exist.

How to update document on pre or post query hook in mongoose?

I am trying to update a field on a query hook. For example:
var mySchema = new Schema({
name: String,
queryCount: {type: Number, default:0}
});
I want to increment and update queryCount field on each find or findOne query.
mySchema.post('find', function (doc) {
// here is the magic
});
I have tried a few things but no success so far. Can I achieve this in model or do I have to do it in the controller?
What you want is a post init hook
mySchema.post('init', function (doc) {
doc.queryCount++;
doc.save();
});
Alternatively, you could use a mongoose static method which internally calls findAndUpdate()
mySchema.statics.findWithIncrement = function (query, callback) {
this.findAndUpdate(query, { $inc: { queryCount: 1 })
.exec(function(err, res) {
if (err) return callback(err);
//Handle response
});
}
And then use the method in your controllers:
MyModel.findWithIncrement({name: "someName"}, function (err, result) {
})

Get the _id of inserted document in Mongo database in NodeJS

I use NodeJS to insert documents in MongoDB. Using collection.insert I can insert a document into database like in this code:
// ...
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId; // = ???
});
// ...
How can I get the _id of inserted object?
Is there any way to get the _id without getting latest object inserted _id?
Supposing that in same time a lot of people access the database, I can't be sure that the latest id is the id of object inserted.
A shorter way than using second parameter for the callback of collection.insert would be using objectToInsert._id that returns the _id (inside of the callback function, supposing it was a successful operation).
The Mongo driver for NodeJS appends the _id field to the original object reference, so it's easy to get the inserted id using the original object:
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId = objectToInsert._id; // this will return the id of object inserted
});
There is a second parameter for the callback for collection.insert that will return the doc or docs inserted, which should have _ids.
Try:
collection.insert(objectToInsert, function(err,docsInserted){
console.log(docsInserted);
});
and check the console to see what I mean.
As ktretyak said, to get inserted document's ID best way is to use insertedId property on result object. In my case result._id didn't work so I had to use following:
db.collection("collection-name")
.insertOne(document)
.then(result => {
console.log(result.insertedId);
})
.catch(err => {
// handle error
});
It's the same thing if you use callbacks.
I actually did a console.log() for the second parameter in the callback function for insert. There is actually a lot of information returned apart from the inserted object itself. So the code below explains how you can access it's id.
collection.insert(objToInsert, function (err, result){
if(err)console.log(err);
else {
console.log(result["ops"][0]["_id"]);
// The above statement will output the id of the
// inserted object
}
});
if you want to take "_id" use simpley
result.insertedId.toString()
// toString will convert from hex
Mongo sends the complete document as a callbackobject so you can simply get it from there only.
for example
collection.save(function(err,room){
var newRoomId = room._id;
});
You could use async functions to get _id field automatically without manipulating data object:
async function save() {
const data = {
name: "John"
}
await db.collection('users').insertOne(data)
return data
}
Returns (data object):
{
    _id: '5dbff150b407cc129ab571ca',
    name: 'John',
}
Now you can use insertOne method and in promise's result.insertedId
#JSideris, sample code for getting insertedId.
db.collection(COLLECTION).insertOne(data, (err, result) => {
if (err)
return err;
else
return result.insertedId;
});
Similar to other responses, you can grab the variable using async await, es6+ features.
const insertData = async (data) => {
const { ops } = await db.collection('collection').insertOne(data)
console.log(ops[0]._id)
}
Another way to do it in async function :
const express = require('express')
const path = require('path')
const db = require(path.join(__dirname, '../database/config')).db;
const router = express.Router()
// Create.R.U.D
router.post('/new-order', async function (req, res, next) {
// security check
if (Object.keys(req.body).length === 0) {
res.status(404).send({
msg: "Error",
code: 404
});
return;
}
try {
// operations
let orderNumber = await db.collection('orders').countDocuments()
let number = orderNumber + 1
let order = {
number: number,
customer: req.body.customer,
products: req.body.products,
totalProducts: req.body.totalProducts,
totalCost: req.body.totalCost,
type: req.body.type,
time: req.body.time,
date: req.body.date,
timeStamp: Date.now(),
}
if (req.body.direction) {
order.direction = req.body.direction
}
if (req.body.specialRequests) {
order.specialRequests = req.body.specialRequests
}
// Here newOrder will store some informations in result of this process.
// You can find the inserted id and some informations there too.
let newOrder = await db.collection('orders').insertOne({...order})
if (newOrder) {
// MARK: Server response
res.status(201).send({
msg: `Order N°${number} created : id[${newOrder.insertedId}]`,
code: 201
});
} else {
// MARK: Server response
res.status(404).send({
msg: `Order N°${number} not created`,
code: 404
});
}
} catch (e) {
print(e)
return
}
})
// C.Read.U.D
// C.R.Update.D
// C.R.U.Delete
module.exports = router;

Categories