How to store data in MongoDb using mongoose and async waterfall model - json

Hello i am new to node and i am trying to save data in mongoose. The problem is that there are 3 collections Units, Building and Section. The Schema of Building is :
var buildingsSchema=new Schema({
buildingname:String,
status:String
});
Schema of Section is :
var sectionsSchema=new Schema({
section_type:String,
buildings:{ type: Schema.Types.ObjectId, ref: 'buildings' },
status:String
});
Schema of Units is :
var unitsSchema=new Schema({
unit_type:String,
unit_num:String,
unit_ac_num:Number,
buildings:{ type: Schema.Types.ObjectId, ref: 'buildings' },
sections:{ type: Schema.Types.ObjectId, ref: 'sections' },
shares:Number
});
in Section there is an Id of Building and in Units there is Id's of both Building & Section
now i have used xlsx plugin to convert uploaded excel file to json by :-
var wb = XLSX.readFile("uploads/xls/" + req.file.filename);
data = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], {header:1});
and to map it into json object and saving i am using Async Waterfall Model
for (var index = 1; index < data.length - 1 ; index++) {
var ele= data[index];
// console.log(ele);
var d = { // this is an object which will hold data
record_id: ele[0],
residenceone_unit_id : ele[1],
official_unit_id: ele[2],
building: ele[3],
unit_type : ele[4],
section: ele[5],
shares: ele[6],
unit_style : ele[7]
}
// console.log(d);
var unt = new Units(); // to save units
unt = {
unit_type: d.unit_type,
unit_num: d.residenceone_unit_id,
unit_ac_num: d.official_unit_id,
buildings: '', // empty because need to put id of that particular building
sections: '', // empty because need to put id of that particular sections
shares:d.shares
}
async.waterfall([
function (callback) {
// find building first
Buildings.findOne({buildingname : ele.building})
.exec(function (err,doc) {
if (err) {
return callback(err);
}
// if no building then save one
else if (!doc) {
// console.log("Building is going to save")
var build = new Buildings();
build.buildingname = d.building;
build.status = "active";
build.save(function (err,dc) {
if (err) {
return callback(err);
}
else {
// assign id of building to unit
unt.buildings = dc._id ;
callback(null);
}
})
}
else {
// if building is there then no need to save assign id of it to units
// console.log("Building already exists;")
unt.buildings = doc._id ;
// execute second function
callback(null);
}
// callback(null);
})
},
function (callback) {
// same as building find section of that building first with building Id and section type
Sections.findOne({buildings : unt.buildings,section_type: d.section})
.exec(function (err,doc) {
if (err) {
return callback(err);
}
if (!doc) {
// if no section of that building is there then save one
// console.log("Section needs to be save")
var sect = new Sections();
sect.section_type = d.section;
sect.buildings = unt.buildings;
sect.status = "active";
sect.save(function (err,dc) {
if (err) {
return callback(err);
}
else {
// assign its id to unit
// console.log("Section is saved")
unt.sections = dc._id;
// execute third function
callback(null);
}
})
}
else {
// if there is section of that building id is available than assign id to units
// console.log("Section already exists");
unt.sections = doc._id;
// execute third function
callback(null);
}
})
},
function (callback) {
// there is no need to seaarch just need to save unit
// console.log("Units is going to save")
// console.log(unt);
unt.save(function (err, doc) {
if (err) {
} else if (doc){
// console.log("Unit Saved");
// console.log(doc);
}
})
}
])
}
}
its working but every time instead of searching of data in mongodb it save everytime. Duplication is the main problem if any other way to save units in mongodb will help me a lot.

first i have save Buildings and section :
async.every(uniqueSection,function (uS,callback) {
if (uS != undefined) {
console.log(uS);
Buildings.findOne({buildingname:uS.building}, function (err,doc) {
if (doc) {
// console.log(doc);
Sections.findOne({buildings:doc._id,section_type:uS.section},function (er,sd) {
if (sd) {
// then dont save
console.log(sd);
}
if (er) {
console.log(er);
}
if (!sd) {
// then save
var sect = new Sections();
sect.buildings = doc._id;
sect.section_type = uS.section;
sect.status = 'active';
sect.save(function (err,st) {
if(err) console.log(err);
console.log(st);
})
}
})
}
if (!doc) {
if (uS.building != undefined) {
var building = new Buildings();
building.buildingname = uS.building;
building.status = "active";
building.save(function (er,dc) {
if (dc) {
// console.log(dc);
Sections.findOne({buildings:dc._id,section_type:uS.section},function (er,sd) {
if (sd) {
// then dont save
console.log(sd);
}
if (er) {
console.log(er);
}
if (!sd) {
// then save
var sect = new Sections();
sect.buildings = dc._id;
sect.section_type = uS.section;
sect.status = 'active';
sect.save(function (err,st) {
if(err) console.log(err);
console.log(st);
})
}
})
}
if (er) {
console.log(er);
}
})
}
}
if (err) {
console.log(err);
}
})
}
})
then i have saved Units by :
async.waterfall([
function(callback) {
Buildings.findOne({buildingname:d.building}, function (err,doc) {
if (doc) {
buildingId = doc._id;
callback(null, doc);
}
if (err) {
console.log(err);
}
})
},
function(doc,callback) {
Sections.findOne({buildings: buildingId,section_type:d.section},function (er,sd) {
if (sd) {
sectionId = sd._id;
callback(null,doc,sd);
}
if (er) {
console.log(er);
}
})
},
function (bld,st,callback) {
var s = d.shares.replace(",","");
var unit = {
unit_type: d.unit_type,
unit_num: d.residenceone_unit_id,
unit_ac_num: d.official_unit_id,
buildings: bld._id,
sections: st._id,
shares: s
}
Units.findOne(unit,function (err,unt) {
if (err) {
console.log(err);
}
if(unt) {
console.log(unt)
}
if (!unt) {
var units = new Units();
units.unit_type = d.unit_type;
units.unit_num = d.residenceone_unit_id;
units.unit_ac_num = d.official_unit_id;
units.buildings = bld._id;
units.sections = st._id;
units.shares = s;
units.save(function (er,doc) {
if (er) console.log(er);
console.log(doc);
})
}
})
}
], function(err) {
console.log(err)
});

Related

strapi version 4 flatten complex response structure

Use this function to flatten the response returned from strapi on version 4. Helps you get rid of data and attributes properties
This will give you the same response structure as version 3 of strapi. This would help you migrate to version 4 from version 3 easily.
How to use it?
import the file.
const flattnedData = flattenObj({...data})
NOTE: The data here is the response returned from strapi version 4.
export const flattenObj = (data) => {
const isObject = (data) =>
Object.prototype.toString.call(data) === "[object Object]";
const isArray = (data) =>
Object.prototype.toString.call(data) === "[object Array]";
const flatten = (data) => {
if (!data.attributes) return data;
return {
id: data.id,
...data.attributes,
};
};
if (isArray(data)) {
return data.map((item) => flattenObj(item));
}
if (isObject(data)) {
if (isArray(data.data)) {
data = [...data.data];
} else if (isObject(data.data)) {
data = flatten({ ...data.data });
} else if (data.data === null) {
data = null;
} else {
data = flatten(data);
}
for (const key in data) {
data[key] = flattenObj(data[key]);
}
return data;
}
return data;
};
In my case I created a new middleware "flatten-response.js" in "middlewares" folder.
function flattenArray(obj) {
return obj.map(e => flatten(e));
}
function flattenData(obj) {
return flatten(obj.data);
}
function flattenAttrs(obj) {
let attrs = {};
for (var key in obj.attributes) {
attrs[key] = flatten(obj.attributes[key]);
}
return {
id: obj.id,
...attrs
};
}
function flatten(obj) {
if(Array.isArray(obj)) {
return flattenArray(obj);
}
if(obj && obj.data) {
return flattenData(obj);
}
if(obj && obj.attributes) {
return flattenAttrs(obj);
}
for (var k in obj) {
if(typeof obj[k] == "object") {
obj[k] = flatten(obj[k]);
}
}
return obj;
}
async function respond(ctx, next) {
await next();
if (!ctx.url.startsWith('/api')) {
return;
}
ctx.response.body = flatten(ctx.response.body.data)
}
module.exports = () => respond;
And I called it in "config/middlewares.js"
module.exports = [
/* ... Strapi middlewares */
'global::flatten-response' // <- your middleware,
'strapi::favicon',
'strapi::public',
];

Mocking/Stubbing/unit testing mysql streaming query rows node js

I have a following function which uses streaming-query-rows of mysql node js module. How can i unit test the below function and also i want to mock the database behavior instead of connecting to database while unit test.
'processRow' and ''wirteCsvFile'' function both are synchronous task.
function executeTask(sql_connection,sql_query) {
let query = sql_connection.query(sql_query);
let showInfo = {};
let showids = [];
query
.on('error', (error) => {
console.error(`error executing query --> ${error}`);
})
.on('result', function (row) {
sql_connection.pause();
processRow(row, showInfo, showids, function () {
sql_connection.resume();
});
})
.on('end', function () {
showids.forEach(showid => {
if (showInfo[showid].faults.length === 0) {
delete showInfo[showid];
}
});
wirteCsvFile(showInfo, (error, done) => {
if (error) {
console.error(error);
} else {
console.log("done");
process.exit();
}
})
});
}
You can stub the query function to return whatever you want instead of making request to database:
sinon.stub(connection, "query").callsFake(() => /* whatever you want here */);
You should also break executeTask into smaller functions, for ex:
function errorHandler(error) {
console.error(`error executing query --> ${error}`);
}
function resultHandler(data, row) {
sql_connection.pause();
processRow(row, data.showInfo, data.showids, function() {
sql_connection.resume();
});
}
function endHandler(data) {
data.showids.forEach(showid => {
if (data.showInfo[showid].faults.length === 0) {
delete data.showInfo[showid];
}
});
wirteCsvFile(data.showInfo, (error, done) => {
if (error) {
console.error(error);
} else {
console.log("done");
process.exit();
}
})
}
function executeTask(sql_connection, sql_query) {
let query = sql_connection.query(sql_query);
let data = {
showInfo: {},
showids: [],
};
query.on('error', errorHandler)
.on('result', resultHandler.bind(null, data))
.on('end', endHandler.bind(null, data));
}
Now you can test errorHandler, resultHandler, endHandler separately
What I'm thinking is we can mock the sql_connection with a class of Event Emitter.
const sinon = require("sinon");
const assert = require('assert');
const EventEmitter = require('events');
const src = require('....'); // your source file that contain `executeTask`
// Create mock emitter
class QueryEmitter extends EventEmitter {}
describe('test execute task', function() {
const queryEmitter = new QueryEmitter();
// we build mock connection that contains all methods used as in `sql_connection`
const conn = {
query: sinon.stub().returns(queryEmitter),
pause: sinon.spy(),
resume: sinon.spy()
};
const query = 'SELECT *';
before(function() {
src.executeTask(conn, query);
});
it('calls query', function() {
assert(conn.query.calledWith(query));
});
it('on result', function() {
queryEmitter.emit('result');
assert(conn.pause.called);
// assert if processRow is called with correct arguments
// assert if conn.resume is called
});
it('on end', function() {
queryEmitter.emit('end');
// assert if writeCsvFile is called
});
// probably is not needed since you only call console.log here
it('on error', function() {
queryEmitter.emit('error');
});
});
Hope it helps

How to set nodejs to work synchronously?

var job = new cronJob('* * * * * *', function () {
Draft.find().then(data => {
var finalData = data;
finalData.forEach(function(item2) {
if (item2.scheduledTime === 'now') {
finalData.forEach(function (item) {
var psms = {
phoneno: item.senderdata,
sender: item.senderName,
message: item.message
}
var obj = psms;
var finalpostsms = obj.phoneno.split("\n").map(s => ({ ...obj,
phoneno: +s
}));
Profsms.bulkCreate(finalpostsms).then(function (data) {
if (data) {
console.log("successfully moved in profsms mysql");
} else {
console.log("failed");
}
})
});
} else {
console.log('Better you be in drafts..manual input');
}
//delete from draft
if (item2.scheduledTime === 'now') {
Draft.findOneAndRemove({
_id: item2._id
}, function (err, employee) {
if (err)
console.log('err');
console.log('Successfully deleted from draft');
});
} else {
console.log('You cant delete from drafts hahaha because no sendnow statement');
}
});
});
}, function () {
console.log('DelCron Job finished.');
}, true, 'Asia/Calcutta');
This above code, working as asynchronously.
I want the above code to be work as synchronous, need some answers. I am a newbie for JS development
Is it possible to do with async await? i dont know how to write async await code.
Your callback should be
async function(){
let data = await Draft.find();
...process data;
}
This is an async await sample code, you can modify based on your need. But first you need to use Node that support async syntax. I believe node 8 LTS is already have async/await feature.
function get() {
// your db code block
return Promise.resolve(7);
}
async function main() {
const r = await get();
console.log(r);
}
main();

.populate() issue with references

var mongoose = require('mongoose')
mongoose.connect('mongodb://127.0.0.1/DocTest');
var patientsSchema = mongoose.Schema({
//This is the value I wanna populate in the rdvs collection.
ssn: String
//But I can't see to have it working.
});
var patients = mongoose.model('patients', patientsSchema);
var Max = new patients({
ssn: "okokok"
});
Max.save(function (err) {
if (err) {
console.log(err);
}
else {
console.log('wink')
}
});
var rdvsSchema = mongoose.Schema({
Heure: {
type: Date
},
patient: {
type: mongoose.Schema.Types.ObjectId, ref: 'patients'
}
});
var rdvs = mongoose.model('rdvs', rdvsSchema);
var rdvs1 = new rdvs({
Heure: 14
}
);
rdvs1.save(function (err) {
if (err) {
console.log(err);
}
else {
console.log('wonk')
}
});
This is the request I'm trying to have working :
rdvs.findOne().populate('patient').exec(function(err, rdvs){
console.log (rdvs.patient.ssn)
});
I'm struggling here, the issue here is I wanna add ssn's value from patients to my rdvs collection.
Mongoose operations are asynchronous. You need to wait for the operation is done before doing other things. In your case, the second query depends on the response of your first query -- therefore you need to nest your operations.
// setup schemas
var patientSchema = mongoose.Schema({
ssn: String
});
var rdvSchema = mongoose.Schema({
Heure: Number, // changed: you're expecting a number
patient: { type: mongoose.Schema.Types.ObjectId, ref: 'Patient' }
});
var Patient = mongoose.model('Patient', patientSchema);
var Rdv = mongoose.model('Rdv', rdvSchema);
// create a new patient
Patient.create({ ssn: "okokok" }, function (err, patient) {
if (err) {
console.log(err);
return;
}
if (!patient) {
console.log('patient could not be created');
return;
}
// you can only create a RDV once a patient has been created
Rdv.create({ Heure: 14, patient: patient._id }, function (err, rdv) {
if (err) {
console.log(err);
return;
}
if (!rdv) {
console.log('rdv could not be created');
return;
}
// send response
});
});
Aside: get into the habit of naming your variables properly. Usually, models are singular and capitalized e.g. Patient and documents are lowercase e.g. patient.

Convert directory structure in the filesystem to Json object

I Know how to convert the directory structure into JSON object, from here
But I want all the files in an array, and the folders in object with the object key as the folder name. I have been trying for a long time but just cannot get it done.
Thi is what I have tried so far:
var diretoryTreeToObj = function (dir, done) {
var results = {};
var _contents = [];
fs.readdir(dir, function (err, list) {
if (err) {
return done(err);
}
var pending = list.length;
if (!pending) {
return done(null, {name: path.basename(dir), type: 'folder', children: results});
}
list.forEach(function (file, index) {
file = path.resolve(dir, file);
fs.stat(file, function (err, stat) {
if (stat && stat.isDirectory()) {
diretoryTreeToObj(file, function (err, res) {
results[path.basename(file)] = {
name: path.basename(file),
type: 'folder',
path: path.dirname(file),
_contents: [res]
};
if (!--pending) {
done(null, results);
}
});
} else {
results['_contents'] = [{
name: path.basename(file),
path: file
}];
if (!--pending) {
done(null, results);
}
}
});
});
});
};
Thanks in advance. :)
Finally I figured out the solution, here it is if anybody needs it:
var diretoryTreeToObj = function (dir, done) {
var results = {};
var _contents = [];
var files = [];
fs.readdir(dir, function (err, list) {
if (err) {
return done(err);
}
var pending = list.length;
if (!pending) {
return done(null, {name: path.basename(dir), type: 'folder', children: results});
}
list.forEach(function (file, index) {
file = path.resolve(dir, file);
fs.stat(file, function (err, stat) {
results['_contents'] = files;
if (stat && stat.isDirectory()) {
diretoryTreeToObj(file, function (err, res) {
results[path.basename(file)] = res;
if (!--pending) {
done(null, results);
}
});
} else {
files.push({
name: path.basename(file),
path: file
});
results['_contents'] = files;
if (!--pending) {
done(null, results);
}
}
});
});
});
};