Store mongoose field values into a var and interate through - javascript

Been making great waves getting deeper into node.js and mongo but I've hit a bit of a wall.
I've got my data being pushing from a form into a collection with mongoose and I can see it being pulled into a JS file and calling the document with console.log below.
var fields = { 'cheap': 1, 'number': 1 };
var query = userModel.find({}).select(fields);
query.exec(function (err, docs) {
if (err) return handleError(err);
console.log(docs + 'first');
request(url, function(error, response, html) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(html);
//var $el = $("a:contains('Xbox')");
var $el = $('a:contains(' + docs + ')');
console.log(docs + 'second')
if ($el.length) {
client.messages.create({
to: "+ '+ cheap.number +'",
from: "+61418739508",
body: $el.text()
}, function(err, message) {
if(err){
console.log(err);
} else {
console.log(message.sid);
}
});
console.log($el.text());
} else {
console.log('hey');
}
}
});
});
Output:
[ { _id: 561650245b3d0d57ad7f3c72,
cheap: 'menus',
number: '0000000000' } ]
However, now I would like to take the values of the 'cheap' and 'number' fields and store them as a var that I can iterate through on the below function.
Can anyone point me in the right direction for what I need to work on? It very much seems like an elusive missing piece of a puzzle.

Found what I was looking for. Needed a for.. in construct to iterate through the properties of the object. From there I had to call the object and property type and it works.
Magic that fixed it:
var docs;
for (var key in docs) {
var value = docs[key];
console.log(key + ", " + value);
}
Full code:
var query = userModel.find({});
query.exec(function (err, docs) {
if (err) return handleError(err);
//console.log(docs);
var docs;
for (var key in docs) {
var value = docs[key];
console.log(key + ", " + value);
}
request(url, function(error, response, html) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(html);
//var $el = $("a:contains('Xbox')");
var $el = $('a:contains(' + value.cheap + ')');
//console.log($el);
if ($el.length) {
client.messages.create({
to: value.number,
from: "+61418739508",
body: $el.text()
}, function(err, message) {
if(err){
console.log(err);
} else {
console.log(message.sid);
}
});
console.log($el.text());
} else {
console.log('hey');
}
}
});
});

I think the mongoose is async, you should move the request function to exec something like this
` query.exec(function (err, cheap, number) {
if (err) return handleError(err);
console.log(cheap);
request(url, function(error, response, html) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(html);
async.each(cheap, function(iterateCheap, callback){
var $el = $("a:contains(" + cheap + ")");
}, function(err){
if(err) {
console.log(err);
} else {
console.log('We successfully iterate on all cheap')
}
})
......`
UPDATE1:
Or you should use async.js module and waterfall method for this situation

Related

Node.js query INSERT callback not working as expected

Small problem when using POST and adding an INSERT. Works as below, but want to use a callball after the data has been inserted. At the moment the database is being updated. (good) but can't use the callback - I would expect this to be just below the throw error. So you could use result.insertId. Any thoughts welcome?
router.post('/group/:id', function(req, res) {
var idToken = req.params.id;
admin.auth().verifyIdToken(idToken).then(function(decodedToken) {
var userID = decodedToken.uid;
var name = encrypt(req.body.group);
getID(userID, function(result){
var ID = result;
var post = {ID:ID, name:name};
db.query('INSERT INTO cu_groups SET ?', post, function (error, results, fields) {
if (error)throw error;
//*** when I add response here get 502 bad gateway error.
});
res.sendStatus(200);
}); // depends on getID
// admin.auth cat
}).catch(function(error) {
res.sendStatus(error);
});
});
try this way :
router.post('/group/:id', function(req, res) {
var idToken = req.params.id;
admin.auth().verifyIdToken(idToken).then(function(decodedToken) {
var userID = decodedToken.uid;
var name = encrypt(req.body.group);
getID(userID, function(result){
var ID = result;
var post = {ID:ID, name:name};
db.query('INSERT INTO cu_groups SET ?', post, function (error, results, fields) {
if(error){
return res.status(500).send(error);
}
if(!error && results){
return res.status(200).send(results);
}
});
});
}).catch(function(error) {
return res.status(500).send(error);
});
});
if you want to use callback then ,create a separate function like :
var insertData = function(query,data,callback){
db.query(query, data, function (error, results, fields) {
if(error){callback(error,null);}
if(!error && results){callback(null,results);}
});
});
and call this way inside getID :
getID(userID, function(result){
var ID = result;
var post = {ID:ID, name:name};
insertData('INSERT INTO cu_groups SET ?', post, function (error,data){
if(error){
return res.status(500).send(error);
}
if(data){
return res.status(200).send(data);
}
});
});
Working code below many thanks to Saurabh Mistry. I removed the SET post and added the table fields and values explicity.
router.post('/group/:id', function(req, res) {
var idToken = req.params.id;
admin.auth().verifyIdToken(idToken).then(function(decodedToken) {
var userID = decodedToken.uid;
var name = encrypt(req.body.group);
getID(userID, function(result){
var ID = result;
// query
let query = "INSERT INTO cu_groups (ID, name) VALUES('" + ID + "','" + name + "')";
// execute query
db.query(query, (error, result) => {
if(error){
return res.status(500).send(error);
}
if(!error && result ){
return res.send(result);
}
});
}); // depends on getID
// admin.auth cat
}).catch(function(error) {
return res.status(500).send(error);
});
});

Unable to initiate the route from the beginning after else statement deadend

Firstly, I'm sorry if this has been posted before. I searched but couldn't find any credible solution.
So I'm working on this route in nodejs where I make an API call for a piece of information and then using that info in an if statement to check if it's the correct info(the server sometimes sends wrong info).
If I get the correct info then I use that in another API to get more info about it and render it into my template. Everything works fine.
But I want the first API call to take place again if the info doesn't match or it's wrong. How can I initiate the API call again from the start(like a loop) and it will break only if the info is correct. Please check the "comment" in the code below. That is where I don't know what to put. Your help would be highly appreciated.
PS. I am a beginner in nodejs and javascript.
Route
router.get("/check", (req, res) => {
if(req.query.search) {
var input = req.query.search;
var url = "http://firstapi.com/json/" + input + "?fields=query";
request(url, function(error, response, body) {
if(!error && response.statusCode === 200) {
var data = JSON.parse(body);
if(data.query.match(/((^|\.)((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]?\d))){4}$/)){
var url = "https://secondapi.com/" + data.query + "?key=something";
request(url, function(error, response, body) {
if(!error && response.statusCode === 200) {
var Data = JSON.parse(body);
res.render("index", {data: Data});
}
});
}else{
//want to use the input at the top and check the firstapi again. All the code above should run again until its the correct one which I will use in my template.
}
}
});
}else{
res.render("index", {data: null});
}
});
I would probably do it this way:
router.get('/check', (req, res) => {
if (req.query.search) {
var input = req.query.search;
// Put this logic away in a `checkData` function
checkData(input)
.then(data => {
res.render('index', { data }); // Short version of {data: data}
})
.catch(err => {
console.error(err);
res.render('index', { data: null });
});
} else {
res.render('index', { data: null });
}
});
// Here, we have a `retries` parameter, set to 0 initially
function checkData (input, retries = 0) {
const maxRetries = 3;
// Return a promise (you could also use callbacks)
return new Promise((resolve, reject) => {
// Define a retry method
const retry = () => {
if (retries < maxRetries) {
// Increment the retries count and go for another try
checkData(input, retries + 1).then(resolve).catch(reject);
} else {
reject(`Could not get the data after ${retries} retries.`);
}
};
var url = `http://firstapi.com/json/${input}?fields=query`;
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var data = JSON.parse(body);
if (data.query.match(/((^|\.)((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]?\d))){4}$/)) {
var url = 'https://secondapi.com/' + data.query + '?key=something';
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var Data = JSON.parse(body);
// If it worked, resolve with the data
resolve(Data);
} else {
retry();
}
});
} else {
retry();
}
}
});
});
}

Mongo deleting a document using an object as a value

Hello I am having issues deleting a document from MongoDb using an object
here is what I mean
const deleteTrimByName = function (db, callback) {
// Get the documents collection
const collection = db.collection(documentName)
// Insert some documents
console.log(trimNameToDelete)
collection.deleteOne({Video_trim: trimNameToDelete}, function (err, result) {
assert.equal(err, null)
assert.equal(1, result.result.n)
console.log('Removed the document')
callback(result)
})
fs.unlink('./public/videos/cut-videos/' + documentName + '/' + trimNameToDelete.trimName + '.mp4', (err) => {
if (err) {
console.log('failed to delete local image:' + err)
} else {
console.log('successfully deleted local image')
}
})
}
where trimNameToDelete evaluates to
{"trimName":"cut2","startTime":"00:00:05","endTime":"00:00:15"}
and the mongo document appears as this in the shell
{ "_id" : ObjectId("5abe67897a9b9e0933c64acd"), "Video_trim" : { "trimName" : "cut2", "startTime" : "00:00:05", "endTime" : "00:00:15" } }
the error I get is
AssertionError [ERR_ASSERTION]: 1 == 0
For technical reasons use the Id cannot be used for deleting.
Ah I found the issue, I needed to parse the trimNametoDelete, stupid mistake, I was sending the trimNametoDelete as a string.
const deleteTrimByName = function (db, callback) {
// Get the documents collection
const collection = db.collection(documentName)
console.log('>>>>>' + trimNameToDelete)
collection.deleteOne({Video_trim: JSON.parse(trimNameToDelete)}, function (err, result) {
assert.equal(err, null)
assert.equal(1, result.result.n)
console.log('Removed the document')
callback(result)
})
fs.unlink('./public/videos/cut-videos/' + documentName + '/' + JSON.parse(trimNameToDelete).trimName + '.mp4', (err) => {
if (err) {
console.log('failed to delete local image:' + err)
} else {
console.log('successfully deleted local image')
}
})
}

Change async workflow to Promise (Bluebird)

I have been trying to wrap my head around Promises. For basic concepts I understand, but once it gets nested, I am a little bit confused. Any feedback is appreciated
Here is the code that I am trying to refactor into Promises (bluebird)
var getIndividualData = function(url, doneGetIndividualData) {
var $, data;
request(url, function(err, res, body) {
if (!err && res.statusCode === 200) {
$ = cheerio.load(body);
data = {
title: $("#itemTitle").children()["0"].next.data,
condition: $("#vi-itm-cond").text(),
price: $("#prcIsum_bidPrice").text(),
imgUrl: $("#icImg")[0].attribs.src,
createdAt: chance.date(),
likes: chance.integer({min: 0, max: 1000})
};
doneGetIndividualData(null, data);
} else {
doneGetIndividualData(err);
}
});
};
var getListing = function(url, doneGetListing) {
var $;
var links = [];
request(url, function(err, res, body) {
if (!err && res.statusCode === 200) {
$ = cheerio.load(body);
$('.vip').each(function(i, el) {
if (i < 15) {
links.push(el.attribs.href);
}
});
async
.concat(links, getIndividualData, function(err, result) {
return doneGetListing(null, result);
});
} else {
doneGetListing(err);
}
});
};
var putToMongo = function(err, result) {
if (devConfig.seedDB) {
mongoose.connect(devConfig.mongo.uri);
Item.find({}).remove(function(err, items) {
Item.create(result, function(err, items) {
console.log('done');
process.kill();
});
});
}
};
async
.concat(urls, getListing, putToMongo);
The first thing to do is wrap request in something that returns a promise. Many promise libraries have utilities for "promisifying" async functions, but I don't think that'll work here because request passes two success values into its callback:
var requestAsync = function(url) {
return new Promise(function (resolve, reject) {
request(function (err, res, body) {
if (err) {
reject(err);
}
resolve({ res: res, body: body});
});
});
};
Once that's done, it gets a lot easier:
var getIndividualData = function(url) {
return requestAsync(url).then(function (result) {
if (result.res.statusCode === 200) {
var $ = cheerio.load(result.body);
return {
title: $("#itemTitle").children()["0"].next.data,
condition: $("#vi-itm-cond").text(),
price: $("#prcIsum_bidPrice").text(),
imgUrl: $("#icImg")[0].attribs.src,
createdAt: chance.date(),
likes: chance.integer({min: 0, max: 1000})
};
}
throw new Error("Individual data status code: " + result.res.statusCode);
});
};
var getListing = function(url, doneGetListing) {
return requestAsync(url).then(function (result) {
if (result.res.statusCode === 200) {
var $ = cheerio.load(result.body),
promises = $('.vip').filter(function (i) {
return i < 15;
}).map(function (i, el) {
return getIndividualData(el.attribs.href);
});
return Promise.all(promises);
}
throw new Error("Listing status code: " + result.res.statusCode);
});
};
var putToMongo = function(result) {
if (devConfig.seedDB) {
mongoose.connect(devConfig.mongo.uri);
Item.find({}).remove(function(err, items) {
Item.create(result, function(err, items) {
console.log('done');
process.kill();
});
});
}
};
Promise.all(urls.map(getListing))
.then(putToMongo)
.catch(function (err) {
// handle error
});

Problems with multiple Node.js callbacks

I'm trying to solve a problem i've been having with a nodejs cronjob of mine. So basically, This request grabs my tracks from soundcloud, I loop through the results and put the data in a mongodb collection. This all works great, but now i'm adding another section to the site, so I need to grab some additional info from another collection.
I have a tracks collection, and an inventory collection. The track id is in both collections to relate the additional track data to the newly pulled tracks. So my question is how can I get this additional track data in? Below i have tried to loop through it and inject the data using mongoose's query.find() but the loops do not work together. The callback of the inventory query will run all in one for loop it seems... I'm not exactly sure whats going on there.
I'm pretty sure you can also inject a document from another collection by referencing it in the schema... but i'm unsure of how to get this working. This would obviously be a better solution as it won't require more code like this.
if anybody has any suggestions for me that would be great!
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
var o = 1;
for(i=0; i < body.tracks.length; i++){
var last = (i + 1);
var track = body.tracks[i];
if( track.sharing == 'public'){
var invData;
var obj;
db.model('inventory').find({id:track.id}).exec(function(err,item){
//console.log(item[0]);
invData = item[0];
});
console.log(invData, obj);
obj = new TracksModel({
id: track.id,
sharing:track.sharing,
uri:track.uri,
description:track.description,
created_at:track.created_at,
duration:track.duration,
title:track.title,
description:track.description,
order: o,
inventory_data: invData
});
o++;
obj.save(function (err) {
if (!err) {
console.log('Track inserted successfully');
} else {
throw err;
}
});
if(last == body.length){
setTimeout(function(){
console.log("Automatically closing database connection after 5 seconds");
db.close();
}, 5000);
}
}
}
} else {
console.log('An error has occurred: ', error);
}
});
The way you are treating the query callback is wrong. You are assuming that the code starting from "console.log(invData, obj);" will be executed immediately after the db.model.find. That not the correct notion of Callback. How must put that code inside the exec callback function. You may have to use a closure.
Something like:
if (!error && response.statusCode === 200) {
var o = 1;
for(i=0; i < body.tracks.length; i++){
var last = (i + 1);
var track = body.tracks[i];
if( track.sharing == 'public'){
(function(track,last,o){
var invData;
var obj;
db.model('inventory').find({id:track.id}).exec(function(err,item){
//console.log(item[0]);
invData = item[0];
console.log(invData, obj);
obj = new TracksModel({
id: track.id,
sharing:track.sharing,
uri:track.uri,
description:track.description,
created_at:track.created_at,
duration:track.duration,
title:track.title,
description:track.description,
order: o,
inventory_data: invData
});
obj.save(function (err) {
if (!err) {
console.log('Track inserted successfully');
} else {
throw err;
}
});
if(last == body.length){
setTimeout(function(){
console.log("Automatically closing database connection after 5 seconds");
db.close();
}, 5000);
}
});
}(track,last,o);
o++;
}
}
}
Try this out:
var utils = require('restberry-utils');
var Inventory = mongoose.model('Inventory');
var Track = mongoose.model('Track');
request({
url: url,
json: true
}, function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('An error has occurred: ', error);
return;
}
utils.forEachAndDone(body.track, function(track, iter) {
if (track.sharing !== 'public') {
iter();
return;
}
Inventory.findOne({ id: track.id }, function(err, item) {
new Track({
id: track.id,
sharing: track.sharing,
uri: track.uri,
description: track.description,
created_at: track.created_at,
duration: track.duration,
title: track.title,
description: track.description,
order: o,
inventory_data: item,
}).save(function(err) {
if (err) {
throw err;
} else {
console.log('Track inserted successfully');
iter();
}
})
});
}, function() {
console.log('Done!');
setTimeout(function() {
console.log("Automatically closing database connection after 5 seconds");
db.close();
}, 5000);
})
});

Categories