How to save the result from collection.findone() - javascript

i have a simple question and i have read a lot of same issues here, but these are not exact the same or doesn't work for me :-(
I have a REST function called "addevent". The function gets a json input (req) and iterate through the json array to get some IDs to store them in an extra Array. That works perfect!
After that, the function should search in a mongodb for every single id and store some extra informations from this ID (e.g. the stored URL of this ID). With "console.log(result.link)" it works again perfect. But my problem is that, that i need to store this link in an extra Array (urlArray).
So how can i save the result of collection.findone(). I read something about, that findone() doesn't return a document, but a cursor? what does that mean? How do i have to handle that in my case?
That's the code:
exports.addevent = function(req, res) {
var ids = req.body;
var pArray = new Array();
var urlArray = new Array();
var eventName = ids.name;
for(var i in ids.photos) {
photoArray.push(ids.photos[i]);
var id = ids.photos[i]._id;
var collection = db.get().collection('photos');
collection.findOne({'_id':new mongo.ObjectID(id)},function(err, result) {
console.log(result.link);
}
)
}
Many thanks!
-------------------- Update --------------------
Ok, i think that has something to do with the asynch Callbacks. I found an article, but i don't know how to implement it in my case.
http://tobyho.com/2011/11/02/callbacks-in-loops/
And something about "promises" in javascript.

You can save the result of your search doing something like:
var foundPhoto = collection.find({_id':new mongo.ObjectID(id)}, function(err, photo){
if(!err){
return photo;
} else {
console.log(err)
return null;
}
});
This way you get the return statement of your query in the "photo" variable.

Related

Elegant way to make Array from xml (string)

i need to make Array for data grid.
Input is XML(string). With lots of unnecessary data, i need only array "a:Client"
Here is my code which works, but I think it's not too clean to set a way like this.
parse(XML){
var parseString = require('react-native-xml2js').parseString;
var xml = XML;
parseString(xml, (err, result) => {
this.setState({rows: result["s:Envelope"]["s:Body"][0].GetClientsResponse[0].GetClientsResult[0]["a:Client"]});
});
}
Here is XML String
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetClientsResponse xmlns="http://tempuri.org/"><GetClientsResult xmlns:a="http://schemas.datacontract.org/2004/07/tt4t.Dispatching.ApplicationServer.Data.CommonData" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:Client><a:ClientID>0</a:ClientID><a:Name/></a:Client><a:Client><a:ClientID>12</a:ClientID><a:Name>Magistrát města Liberec</a:Name></a:Client><a:Client><a:ClientID>30</a:ClientID><a:Name>Krajský úřad Libereckého kraje</a:Name></a:Client><a:Client><a:ClientID>31</a:ClientID><a:Name>OC Nisa</a:Name></a:Client><a:Client><a:ClientID>32</a:ClientID><a:Name>Globus</a:Name></a:Client><a:Client><a:ClientID>33</a:ClientID><a:Name>Die Länderbahn GmbH DLB</a:Name></a:Client><a:Client><a:ClientID>34</a:ClientID><a:Name>Magistrát města Jablonec nad Nisou</a:Name></a:Client><a:Client><a:ClientID>35</a:ClientID><a:Name>Dopravní podnik měst Liberce a Jablonce n.N.</a:Name></a:Client><a:Client><a:ClientID>36</a:ClientID><a:Name>Liplastec</a:Name></a:Client><a:Client><a:ClientID>37</a:ClientID><a:Name>CBRE Česká republika</a:Name></a:Client><a:Client><a:ClientID>38</a:ClientID><a:Name>Cinestar</a:Name></a:Client><a:Client><a:ClientID>39</a:ClientID><a:Name>České dráhy a.s.</a:Name></a:Client><a:Client><a:ClientID>40</a:ClientID><a:Name>DENSO MANUFACTURING CZECH s.r.o.</a:Name></a:Client><a:Client><a:ClientID>41</a:ClientID><a:Name>Hasiči</a:Name></a:Client><a:Client><a:ClientID>42</a:ClientID><a:Name>MŠ Lísteček ((N.Ruda)</a:Name></a:Client><a:Client><a:ClientID>43</a:ClientID><a:Name>MŠ Tanvaldská (vč. pobočky Poštovní)</a:Name></a:Client><a:Client><a:ClientID>44</a:ClientID><a:Name>Policie ČR</a:Name></a:Client><a:Client><a:ClientID>45</a:ClientID><a:Name>SOŠ Kateřinky</a:Name></a:Client><a:Client><a:ClientID>46</a:ClientID><a:Name>Sportkids</a:Name></a:Client><a:Client><a:ClientID>47</a:ClientID><a:Name>SŽDC, s.o.</a:Name></a:Client><a:Client><a:ClientID>48</a:ClientID><a:Name>Záchranná služba</a:Name></a:Client><a:Client><a:ClientID>49</a:ClientID><a:Name>ZŠ Nad Školou</a:Name></a:Client><a:Client><a:ClientID>50</a:ClientID><a:Name>SKI KLUB Jizerska 50</a:Name></a:Client><a:Client><a:ClientID>51</a:ClientID><a:Name>TJ Dukla Liberec, z.s.</a:Name></a:Client><a:Client><a:ClientID>52</a:ClientID><a:Name>Jiný, viz poznámka</a:Name></a:Client><a:Client><a:ClientID>53</a:ClientID><a:Name>KORID LK, spol. s r.o.</a:Name></a:Client><a:Client><a:ClientID>54</a:ClientID><a:Name>STUDENT AGENCY k.s.</a:Name></a:Client><a:Client><a:ClientID>55</a:ClientID><a:Name>Boveraclub z.s.</a:Name></a:Client><a:Client><a:ClientID>56</a:ClientID><a:Name>Archa 13</a:Name></a:Client><a:Client><a:ClientID>57</a:ClientID><a:Name>Pekárny</a:Name></a:Client><a:Client><a:ClientID>58</a:ClientID><a:Name>MŠ Sídliště - Skloněná</a:Name></a:Client><a:Client><a:ClientID>59</a:ClientID><a:Name>Vratislavice</a:Name></a:Client><a:Client><a:ClientID>60</a:ClientID><a:Name>První festivalová s.r.o.</a:Name></a:Client><a:Client><a:ClientID>61</a:ClientID><a:Name>Městský obvod Liberec - Vratislavice nad Nisou</a:Name></a:Client><a:Client><a:ClientID>62</a:ClientID><a:Name>Preciosa</a:Name></a:Client><a:Client><a:ClientID>63</a:ClientID><a:Name>Central Europe Spartan Race ESR Enterprises Czech</a:Name></a:Client><a:Client><a:ClientID>64</a:ClientID><a:Name>Kümpers Textil, s.r.o.</a:Name></a:Client><a:Client><a:ClientID>65</a:ClientID><a:Name>Základní škola a Mateřská škola, Stráž n.N.</a:Name></a:Client><a:Client><a:ClientID>66</a:ClientID><a:Name>Základní škola a mateřská škola logopedická, LBC</a:Name></a:Client><a:Client><a:ClientID>165</a:ClientID><a:Name>Zájezd stř. 704</a:Name></a:Client><a:Client><a:ClientID>166</a:ClientID><a:Name>DPMLJ X11</a:Name></a:Client><a:Client><a:ClientID>167</a:ClientID><a:Name>DPMLJ 2 a 3</a:Name></a:Client><a:Client><a:ClientID>168</a:ClientID><a:Name>DPMLJ 5 a 11</a:Name></a:Client></GetClientsResult></GetClientsResponse></s:Body></s:Envelope>
Your code works, but I think that it have two things that can be done better....
1) It's not readable, if you'll read this code in a year it'll be difficult to understand what you are looking for, so I would wrap that:
{rows: result["s:Envelope"]["s:Body"][0].GetClientsResponse[0].GetClientsResult[0]["a:Client"]}
in a function like:
getClient(result)
2) It's not reusable, if the XML change or you need to find some other data it'll break, so try to use a function with a parameter that finds exactly what you need recursively if necessary
(You can give a look here, I know that you have array, but you can search recursively also for your arrays)
The only thing I worry about the above code is that it missed error checking. I am sure how confident that you are about your API and the schema will behave as expected. I feel it is good to have error checking when you are accessing a nested property of a nested object.
afterParsing(err, result){
if(err){
// Handle error
return [];
}
try {
const res = result["s:Envelope"]["s:Body"][0].GetClientsResponse[0].GetClientsResult[0]["a:Client"];
return res;
} catch (error) {
return [];
}
}
parse(XML){
var parseString = require('react-native-xml2js').parseString;
var xml = XML;
parseString(xml, (err, result) => {
this.setState({rows: this.afterParsing(err, result)});// if afterParsing and this function are in the class
});
}
If this piece of code is going to be run in the browser you can also try DOMParser.
const stringContainingXMLSource = `<your xml string>`;
var parser = new DOMParser();
var doc = parser.parseFromString(stringContainingXMLSource, "application/xml");
console.log(Array.from(doc.querySelectorAll('Envelope > Body > GetClientsResponse > GetClientsResult > Client')).map(n => n.textContent));

Getting array values from multidimensional array in JavaScript

I'm in need of some minor assistance. I'm having trouble getting an array (larray3) populated with two other array objects (larray1 and larray2) to pass both from data.js into the subsequent model.js and view.js. Data.js correctly builds the multidimensional array however when the results are received in model.js/view.js I only receive the results for larray1. Because only the first values come thru I cannot tell if both larray1 and larray2 are actually passing thru. Can someone please tell me how I should alter my syntax in either model.js or view.js to access both array values or what else I could change? Thank you in advance.
data.js.
function getCountries(done) {
var sqlite3 = require('sqlite3').verbose();
var file = 'db/locations.sqlite3';
var db = new sqlite3.Database(file);
var larray1 = [];
var larray2 = [];
var larray3 = [];
db.all('SELECT * FROM Country', function(err, rows) {
// This code only gets called when the database returns with a response.
rows.forEach(function(row) {
larray1.push(row.CountryName);
larray2.push(row.CountryCode);
})
larray3.push(larray1);
larray3.push(larray2);
return done(larray3[0], larray3[1]);
});
db.close();
}
model.js
function Countries(done) {
//Pull location values from data
return getCountries(done);
}
view.js
function viewCountries() {
var viewCou = Countries(function(results) {
// Code only gets triggered when Countries() calls return done(...);
var container = document.getElementById('country-select');
var fragment = document.createDocumentFragment();
results.forEach(function(loc, index) {
var opt = document.createElement('option');
opt.innerHTML = loc;
opt.value = loc;
fragment.appendChild(opt);
});
container.appendChild(fragment);
})
}
In data.js you send two arguments to the done callback:
return done(larray3[0], larray3[1]);
This done function is passed through in your model.js:
return getCountries(done);
And that done is passed in from view.js:
Countries(function(results) { // ...
So it is this anonymous function (function(results) {...}) that is called in data.js. But notice that this function only has one parameter, so you're doing nothing with the second argument that data.js sends. result gets the value of larray3[0], but larray3[1] is not captured anywhere.
You could solve this in different ways. Personally, I think the design with two arrays is wrong from the start. I would not separate data that belongs in pairs (name and code) into two different arrays.
Instead make an array of objects, and pass that single array around:
In data.js:
rows.forEach(function(row) {
larray1.push({
name: row.CountryName,
code: row.CountryCode
});
})
return done(larray1);
In view.js:
opt.textContent = loc.name;
opt.value = loc.code;
Side-note: .textContent is preferred over .innerHTML when assigning plain text.

Get json array from sub mongoose query

I have a sub query in mongoose need to get array out of sub query and attach to main json out put/ object.
my first query get user info which contains blocked_users array which is nothing but array of user id's.
i my second query we get profile details of blocker_users array and append to main user object in blocked_users.
var userId = ObjectID(req.body.user_id);
//Get user
newUserModel.findById(userId, function(err, user){
if(err){
utils.getResponse(res, req.url, messages.failure, "");
} else {
var userInfo = {};
var blcked_contacts;
//get users details from blocked contacts userid's array
newUserModel.find({'_id': {$in:user.blocked_contacts}}, function (err,blocked_users) {
if(blocked_users){
//blcked_contacts.push(blocked_users);
console.log(blocked_users);
return;
};
/*else{
blcked_contacts = [];
}*/
});
userInfo['blocked_contacts'].push(blocked_users);
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
//userInfo['blocked_contacts'].push(blcked_contacts);
//userInfo['blocked_contacts'] = user.blocked_contacts;
var userData = Array();
}
});
Don't really know what you're looking for. But saw a problem in your code. You've assigned the blocked_users to the blocked_contacts field outside the find method.
Since these calls are asynchronous in nature, it might happen that the assignment takes place even before the documents are fetched from MongoDB. So you should write your assignment statements inside the find methods' callback, just the way Medet did.
Noticed few mistakes in your code like trying to use .push on an object. You cant do
userInfo['blocked_contacts'].push(blocked_users); // incorrect as userInfo is an empty object and you dont have an array defined for userInfo['blocked_contacts']
You probably get cannot push into undefined error for this. So instead do
userInfo['blocked_contacts'] = blocked_users;
Also you have to do this inside the second find() as blocked_users is only available inside it. So your final query should be something like
var userId = ObjectID(req.body.user_id);
//Get user
newUserModel.findById(userId, function(err, user){
if(err){
utils.getResponse(res, req.url, messages.failure, "");
} else {
var userInfo = {};
//get users details from blocked contacts userid's array
newUserModel.find({'_id': {$in:user.blocked_contacts}}, function (err,blocked_users) {
if(blocked_users){
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
userInfo['blocked_contacts'] = blocked_users; // assign blocked_users into userInfo
console.log(userInfo) // Your required object
} else {
userInfo['user_id'] = user.id;
userInfo['country_code'] = user.country_code;
userInfo['blocked_contacts'] = []; // assign null array if no blocked users fould
}
});
var userData = Array();
}
});
The result of console.log should be an object like this
{
user_id : "..id of searched user...",
country_code : "..country code of searched user..",
blocked_contacts : [<array containing detais of all blocked users>] // null array if no users found
}

NodeJS MSSQL WHERE IN Prepared SQL Statement

I am use nodejs npm package sql
I currently have an array of product skus like so..
var skus = ['product1', 'product2', 'product3'];
My sql store in a file as follows...
SELECT *
FROM stock AS s
WHERE s.sku IN (#skus)
Then I also have my prepared statement code as follows..
var connection = new sql.Connection(config, function(err) {
var ps = new sql.PreparedStatement(connection);
//Add params
if(params != undefined){
for(var key in params){
ps.input(key, sql.VarChar(200));
}
}
ps.prepare(sqlstatement, function(err) {
ps.execute(params, function(err, data) {
callback(null, data);
ps.unprepare(function(err) {
});
});
});
});
}
skus is contained correctly within the params object as the statement works fine when I am using it for simple WHERE X = #YI am just struggling with how I need pass the array of skus to allow them to work in the prepared statement.
I am amend the string using split and join to comma seperate them etc etc but I can't get these methods to work.
I assumed that I would need the param string to look like the following 'product1','product2','product3'.
would be also useful if someone could shed some light on how to debug the completed prepared statement so I can see what is actually being queried to SQL (with params inplace)
Many thanks in advance!
It appears that the sql object (i.e. the mssql module) has no attribute to handle arrays of anything. Moreover, specifying a scalar type in the call to ps.input similarly does not work.
The next best thing is to build keys for your array of parameters into your sql statement itself:
var connection = new sql.Connection(config, function(err) {
var ps = new sql.PreparedStatement(connection);
// Construct an object of parameters, using arbitrary keys
var paramsObj = params.reduce((obj, val, idx) => {
obj[`id${idx}`] = val;
ps.input(`id${idx}`, sql.VarChar(200));
return obj;
}, {});
// Manually insert the params' arbitrary keys into the statement
var stmt = 'select * from table where id in (' + Object.keys(paramsObj).map((o) => {return '#'+o}).join(',') + ')';
ps.prepare(stmt, function(err) {
ps.execute(paramsObj, function(err, data) {
callback(null, data);
ps.unprepare(function(err) {
});
});
});
});
}

Storing db queries in nodejs (with socket.io)

I have a (possibly dumb) problem with my code. I have something like this:
io.sockets.on('connection', function(socket)
{
var list = {};
socket.on('event', function(data){
if(!list[data.field_name]){
list[data.field_name] = [];
// db and collection already defined
var collection = new mongodb.Collection(db, coll);
collection.find({
field: data.field_name,
}).toArray(function(err, results){
list[data.field_name] = results[results.length-1];
socket.emit('another-event',{
list:list[data.field_name];
});
});
} else {
socket.emit('another-event',{
list:list[data.field_name];
});
}
});
}
I'm basically trying to cache the db results in list, and send it directly (without a db query), if it has already been retrieved from the database earlier.
The problem I'm having is that the assignment list[data.field_name] = results[results.length-1] doesn't work. So if it has tried to retrieve the data before, list[data.field_name] has been initialized to [] but it doesn't contain the data that is assigned to it after.
To be clear, it does retrieve the data from the db fine, and sends it to the client properly the first time when it is retrieved from the database. It's when I try to send the cached copy that the problem arises -- there is no cached copy.
I know this is something to do with nodejs (and the mongodb query) being asynchronous, but I can't seem to figure out how to make this work. Any help will be appreciated.
You are emitting another-event before the query to the collection has completed. If the data has not yet been retrieved when another-event is emitted then list[data.field_name] will still be empty. Try something like:
io.sockets.on('connection', function(socket)
{
var list = {};
socket.on('event', function(data){
var anotherEvent = function() {
socket.emit('another-event',{
list:list[data.field_name];
});
};
if(!list[data.field_name]){
list[data.field_name] = [];
// db and collection already defined
var collection = new mongodb.Collection(db, coll);
collection.find({
field: data.field_name,
}).toArray(function(err, results){
list[data.field_name] = results[results.length-1];
anotherEvent();
});
} else {
anotherEvent();
}
});
}

Categories