add extra parameters using mongoose transform on aggregate - node.js

I am new to node.js. I am using mongoose, node.
var schema = new Schema({
name: String,
});
schema.options.toObject.transform = function (doc, ret, options) {
tmp = ret;
tmp['abcd'] = 'abcd';
return tmp;
}
module.exports = mongoose.model('modelName', schema);
//my route.js
modelName.find({}, function (err, docs) {
docs.forEach(function(doc) {
console.log(doc);
console.log("doc['abcd'] is available");
});
});
modelName.aggregate({"$match":{"_id":{"$ne":0}}} function (err, docs) {
docs.forEach(function(doc) {
console.log(doc);
console.log("doc['abcd'] is not available");
});
});
I am using above code to add extra parameters while rendering. But when i use aggregate, it is not working. Is there any way to add extra parameters while rendering even on aggregate also? Thanks in advance.

Related

mongoose creating null document after using module export in express

I was recently using a function to upload files to a mongodb database successfully. However after moving those functions into a file (to clean up) and exporting that file then requiring it in my route, the database now creates documents with only null values like so:
_id:ObjectId("xxxxxxxxxxxxxxx")
name:null,
value:null,
image:null,
desc:null
I don't know what might be causing this, I am logging the argument object that i'm trying to insert from inside the function 'insertProducts' and the item.name is not null. Sorry I'm new to mongodb :/
maybe someone can point me in the right direction?
CRUD.js
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;
const removeProducts = function(req,res){
MongoClient.connect('mongodb://localhost', (err, client) => {
if (err) {
throw err;
}
let db = client.db('account-app');
let products = db.collection('products');
let users = db.collection('users');
try{
products.remove({ _id: req.body.id }, function(err) {
if (!err) {
console.log('removed item')
}
});
}
catch(err){
console.log('Error while inserting', err)
}
client.close()
res.redirect('/addItems')
})
}
const insertProducts = function(item,res){
console.log("item name",item.name)
MongoClient.connect('mongodb://localhost', (err, client) => {
if (err) {
throw err;
}
let db = client.db('account-app');
let products = db.collection('products');
try{
products.insertOne(item)
console.log('item inserted')
}
catch(err){
console.log('Error while inserting', err)
}
client.close()
res.redirect('/admin/addItems')
})
}
module.exports={removeProducts: removeProducts, insertProducts: insertProducts}
my admin route that requires the crud functions
const crudOps = require('../utils/admin/CRUD') //require CRUD functions
// Adding new items
// --------------------------------------------------
router.post('/addNewItems', (req, res, next) => {
console.log(req.body.name)
let item = {
name:req.body.name,
file: binary(req.files.image.data),
value: req.body.value,
desc: req.body.desc
}
crudOps.insertProducts(item, res)
});
That connection URL looks wrong. Generally, it has the format:
mongodb://localhost:27017/mydatabase
Try replacing the connection string with the appropriate one for your database and see if that works. Also, the docs normally have insertOne statements like this so maybe that is the issue?
products.insertOne(item, function(err, r) {
console.log('item inserted')
res.redirect('/admin/addItems')
db.close();
});

nodejs- not able to save data returned by an asynchronous function

I'm getting undefined when I trying to print a value to console which is returned by an asynchronous function.
This is happening in my controller function (dumyController.js) which calls a function written in helper (DBHelper.js), for DRY approach, which
asynchronously fetches data from a model function in DBHelperModel.js
dumyController.js
var dbHelpers = require('../helpers/helpers');
exports.dumyControllerFunc = function (req, res) {
var result= dbHelpers.dumyHelperFunc(165);
console.log(result);
};
DBHelper.js
var dbHelp = require('../models/DBHelperModel');
module.exports = {
dumyHelperFunc: function (userId) {
dbHelp.fetchDataFromDB(userId, function (err, rows) {
var res;
if (err) {
return null;
}
else {
res.send(rows.member_code);
}
});
}
};
DBHelperModel.js
var db = require('../db');
var DBHelpers = {
fetchDataFromDB: function (userId, callback) {
var query = `SELECT member_code FROM members where id=?`;
db.query(query, userId, callback);
},
};
module.exports = DBHelpers;
db.js
var mysql = require('mysql');
var connection = mysql.createPool({
host: '127.0.01',
user: 'root',
password: '',
database: 'dumyDB'
});
module.exports = connection;
I know I'm not getting value because of the asynchronous nature of the function but can anyone tell me how to fetch the value with an architecture like given above. I'm new to nodeJS. Thanks!
I think you want a solution about the asynchronous in nodejs.
The promise,that is a universal and popular solution
Use a callback,That is a approach in nodejs's way,anyway,that is native nodejs's asynchronous solution .
Other asysn: Generate and async, that's es6 feature,and the latter is support by nodejs7+
The promise is most recommend,it has a good looks structure if has a asycn chain, The callback looks bad on that.
This is probably what you trying to achieve:
https://repl.it/MA0Z/0
var DBHelpers = {
fetchDataFromDB: function (userId, callback) {
callback(null, 'hello fetchDataFromDB');
},
};
var dbhelp = {
dumyHelperFunc: function (userId, callback) {
DBHelpers.fetchDataFromDB(userId, function (err, rows) {
if (err) {
callback('error bro');
} else {
callback(null, rows);
}
});
}
}
dbhelp.dumyHelperFunc(165, function (error, result) {
console.log(result);
});

nedb: how to get docs out of find() function?

I encapsulated the nedb module in self-defined object:
var Record = function() {
var Datastore = require('nedb');
this.db = new Datastore({filename: 'record'});
this.db.loadDatabase();
};
And I want to define my own function to get all the objects in the database:
Record.prototype.getItems = function() {
var items = null;
this.db.find({}, function(err, docs) {
items = docs;
});
return items;
};
However, the variable "items" can't be assigned to variable "docs" and is always "null". I realize that this is caused by the asynchronous nature of JavaScript.But how can I get the variable "docs" out?
You can use promises and async/await syntax to omit the callback
Record.prototype.getItems = () => {
return new Promise((resolve, reject) => {
this.db.find({}, (err, doc) => {
err ? reject(err) : resolve(doc);
});
});
}
And then retrieve the data like so
async function whatever(){
const data = await Record.getItems();
}
I would also recommend using this wrapper module for nedb, called NeDB-promises ,which includes convenient features like built-in support for promises and events
You need a callback function that retrieves the data when it's ready, like this:
Record.prototype.getItems = function(callback) {
this.db.find({}, callback);
};
Then use it:
const record = new Record();
record.getItems((err, docs) => {
if (err) return handleError(err);
console.log(docs);
});

mocha test function with mongoose call

Having some problems unit testing the below code, I'm unsure whether it is possible or not due to the way it is coded.
storeModel.js
var storeSchema = new Schema({
storeId : { type: String, index: true},
storeName : String
});
var model = mongoose.model('store', storeSchema);
var findStoresById = function(ids, callback) {
model.find({ storeId: { $in: ids }}, function (err, result) {
if (err) callback(err);
callback(err, result);
});
};
return {
findStoresById: findStoresById,
schema: storeSchema,
model: model
};}();
Which i test like so..
it('will call "findStoresById" and return matched values [storeId: 1111] ', function (done) {
storeModel.findStoresById(['1111'], function(err, store) {
assert.equal(store[0].storeId, '1111');
assert.equal(store[0].storeName, 'StoreName');
assert.equal(err, null);
done();
});
});
However the problem when i implement the following code within a separate function:
get: function (req, res) {
if (req.query.storeIds) {
var ids = req.query.storeIds.split(',');
storeModel.findStoresById(ids, function(err, stores) {
if (err) {
return res.send(err);
}
if (_.isEmpty(stores)) {
var error = {
message: "No Results",
errorKey: "XXXX"
}
return res.status(404).json(error);
}
return res.json(stores);
}); ...
How can i unit test this, i dont want to mock it as there is functionality in the "findStoreById" that needs testing, or is a refactor needed? suggestions?
I'd contest that you actually should be stubbing findStoreById because in not doing so get cannot strictly be unit tested, in that it's not isolated and could fail through no fault of its own. Seeing as the functionality you'd want to test lies within the callback of findStoreById and not the method itself, we can happily stub it and use the yields method of sinon to invoke its callback accordingly.
Note that, if you're testing routes, it's preferable to use supertest else you'll have a lot of mocking of request and response methods on your hands. Therefore, for example:
var request = require('supertest');
var express = require('express');
// stub database method
sinon.stub(storeModel, 'findStoresById');
// create a test app/route to which we direct test requests
var app = express();
app.get('/', myRouteFunction);
it('sends a 404 error when no stores are found', function(done) {
// use the second argument of `yields` to pass a result to the callback
storeModel.findStoresById.yields(null, []);
request(app).get('/').expect(404, done);
});
it('responds with any stores found', function(done) {
// pass an array of found stores to the callback
storeModel.findStoresById.yields(null, [{_id: 1}]);
request(app).get('/').end(function(err, res) {
if(err) return done(err);
assert.deepEqual(res.body, [{_id: 1}]);
done();
});
});
If what you want is test static's and method's of certain Mongoose model, I would recommend you to use sinon and sinon-mongoose.
But first, some tips for your code
var storeSchema = new Schema({
storeId : { type: String, index: true},
storeName : String
});
// 1) If you will callback with the same 'err' and 'result', pass the callback directly
function findStoresById(ids, callback) {
// Instead of this...
this.find({ storeId: { $in: ids } }, function (err, result) {
if (err) callback(err);
callback(err, result);
});
// Use this... it's the same, but shorter
this.find({ storeId: { $in: ids } }, callback);
}
// 2) Declare a static method on your model, instead of export manually (see Monggose documentation for more info)
storeSchema.static('findStoresById', function (ids, callback) {
});
// 3) Create your model after the statics were declared, and use CamelCase
var model = mongoose.model('Store', storeSchema);
// 4) Export just your model
// If you want the model -> var Store = mongoose.model('Store')
// If you want the schema -> var schema = Store.schema
// If you want to call your method -> Store.findStoresById(...)
module.exports = model;
Then, to test the method findStoresById
var sinon = require('sinon');
require('sinon-mongoose');
var Store = mongoose.model('Store');
sinon.mock(Store)
.expects('find').withArgs({ storeId: { $in: ['id1', 'id2'] } })
.yields(null, 'SUCCESS!');
Store.findStoresById(['id1', 'id2'], function (err, res) {
assert(res, 'SUCCESS!');
});
You can find working (and simple) examples on the sinon-mongoose repo.

Partial update of a subdocument with nodejs/mongoose

Is it possible to set multiple properties on a (sub)document in one go with Mongoose? An example of what I'm trying to do:
Let's say I have this schema:
var subSchema = new Schema({
someField: String,
someOtherField: String
});
var parentSchema = new Schema({
fieldOne: String,
subDocs: [subSchema]
})
Then I would like to do:
exports.updateMyDocument = function(req, res) {
var parentDoc = req.parentDoc; // The parent document. Set by parameter resolver.
var document = req.myDoc; // Sub document of parent. Set by parameter resolver.
var partialUpdate = req.body; // updated fields sent as json and parsed by body parser
// I know that the statement below doesn't work, it's just an example of what I would like to do.
// Updating only the fields supplied in "partialUpdate" on the document
document.update(partialUpdate);
parentDoc.save(function(err) {
if(err) {
res.send(500);
return;
}
res.send(204);
});
};
Normally, I could achieve this using the $set operator, but my problem is that document in this example is a subdocument (embedded schema) of parentDoc. So when I tried to do
Parent.update({_id: parentDoc._id, "subDocs._id": document._id},
{$set: {"subDocs.$" : partialUpdate}},
function(err, numAffected) {});
it replaced the subdocument instance identified by subDocs._id. Currently I have "solved" it by setting only fields manually, but I was hoping for a better way to do this.
Build up a $set object programmatically based on the fields of partialUpdate to update just those fields using dot notation:
var set = {};
for (var field in partialUpdate) {
set['subDocs.$.' + field] = partialUpdate[field];
}
Parent.update({_id: parentDoc._id, "subDocs._id": document._id},
{$set: set},
function(err, numAffected) {});
I've done different, in a REST application.
First, I have this route:
router.put('/:id/:resource/:resourceId', function(req, res, next) {
// this method is only for Array of resources.
updateSet(req.params.id, req.params.resource, req, res, next);
});
and the updateSet() method
function updateSet(id, resource, req, res, next) {
var data = req.body;
var resourceId = req.params.resourceId;
Collection.findById(id, function(err, collection) {
if (err) {
rest.response(req, res, err);
} else {
var subdoc = collection[resource].id(resourceId);
// set the data for each key
_.each(data, function(d, k) {
subdoc[k] = d;
});
collection.save(function (err, docs) {
rest.response(req, res, err, docs);
});
}
});
}
The brilliant part is mongoose will validate the data if you define the Schema for this subdocument. This code will be valid for any resource of the document that is an Array. I'm not showing all my data for simplicity, but is a good practice to check for this situations and handle the response error properly.
You can assign or extend embedded document.
Doc.findOne({ _id: docId })
.then(function (doc) {
if (null === doc) {
throw new Error('Document not found');
}
return doc.embeded.id(ObjectId(embeddedId));
})
.then(function(embeddedDoc) {
if (null === embeddedDoc) {
throw new Error('Embedded document not found');
}
Object.assign(embeddedDoc, updateData));
return embeddedDoc.parent().save();
})
.catch(function (err) {
//Do something
});
And in this case you should be shure that _id is not assigning.
I handled this in a slightly different manner without using the $set object. My approach is similar to Guilherme's but one difference is that I wrapped my method into the statics functionality so that it is easier to re-use throughout my application. Example below.
In CollectionSchema.js server model.
collectionSchema.statics.decrementsubdocScoreById = function decreasesubdoc (collectionId, subdocId, callback) {
this.findById(collectionId, function(err, collection) {
if (err) console.log("error finding collection");
else {
var subdoc = collection.subdocs.filter(function (subdoc) {
return subdoc._id.equals(subdocId);
})[0];
subdoc.score -= 1;
collection.save(callback);
}
});
};
In Server Controller
Collection.decrementsubdocScoreById(collectionId, subdocId, function (err, data) {
handleError(err);
doStuffWith(data);
});

Resources