Mongojs fetching data from two collections and merging the result - javascript

I am a mongodb newbie and would greatly appreciate help on this problem described below.
I have two collections "users" and "bags". The user collections scheme has {username, firstname, lastname} and the bag collection schema has {username, bagname, bagimage}.
While fetching users bags, I also want to display the firstname and lastname. My problem is that I cant seem to be able to frame a query correctly. I am using nodejs and mongojs driver. Below is my query for fetching all bags
thmConfig.db.bags.find({status: "1"}).sort({$natural:-1}, function(err, data)
{
var bagList = '{"bags":[';
if( err || !data) res.send('[{"status": "0"}]');
else data.forEach( function(innerData) {
console.log(innerData.username);
bagList += JSON.stringify(innerData)+",";
/*
This is where I would lke to also append the firstname from the
users collection
*/
});
console.log(bagList.slice(0,1));
res.write(magList.slice(0,-1));
res.end(']}');
});
I would greatly appreciate any help or pointers about this. I dont have a choice about changing the driver, so I specifically want to implement this using mongojs for now.
Thanks and regards,
Titash

I don't think that you have much choice other than reading from the users collection and doing this "join" operation programmatically.
You can either read the user document per each bag (inside your loop), or read the entire users collection into an object in advance, and do lookups by username

You could use the $in operator for that.
Pseudo-code(ish):
// get an array of bags matching your query
db.bags.find({status: "1"}).sort({$natural:-1}, function(err, bags) {
// get a list of usernames from the bags:
var usernames = bags.map(function(bag) { return bag.username; });
// perform query on user table: find all users for which we have a bag
db.users.find({ username : { $in : usernames } }, function(err, users) {
// create a mapping of username -> first name for easy lookup
var usernames = {};
users.forEach(function(user) {
usernames[user.username] = user.firstname;
});
// map first names to bags
bags.forEach(function(bag) {
bag.firstname = usernames[bag.username];
});
// done: return it as JSON (no need to build a JSON string ourselves)
res.send({ bags : bags });
});
});

Related

Create MongoDB field with a values from populated document

I have a users collection and Companies collection. Right now the output looks like this:
"_id":"5ce543a5390d87567f523153",
"updatedAt":"2019-10-18T15:01:53.812Z",
"createdAt":"2019-05-22T12:42:13.046Z",
"associatedCompany":{
"_id":"5ce543a4390d87567f523148",
"companyName":"Company Two"},
"accountStatus":1,
"roleInfo":{"roleType":1},
"role":2,
"personalInfo":{
"fullName":"SomeName",
"firstName":"Name",
"lastName":"Last",
"email":"email#email.com",
"phone":{"countryCode":"SE","number":"9876543210"}}}
Where company field is populated from Company collection.
Is there a way to display just a company name right away with firstName and lastName field without "personalInfo" field so the output will look like:
{"data":[{
"_id":"5ce543a5390d87567f523153",
"updatedAt":"2019-10-18T15:01:53.812Z",
"createdAt":"2019-05-22T12:42:13.046Z",
"companyName":"Company Two",
"accountStatus":1,
"roleInfo":{"roleType":1},
"role":2,
"firstName":"Name",
"lastName":"Last",
"email":"email#email.com"
Query I am using
const listUsers = (skip, limit = 10) => {
let config = {
populate: {
'path': 'associatedCompany',
'select': 'companyName'
},
limit: Number(limit),
skip: Number(skip),
};
I have tried to do it with aggregation and lookup but without success.
Thanks is advance.
Use projection to filter the fields that you want to be displayed in your output.
Refer:
mongoDB projection

Node JavaScript express JavaScript format data get from the sequlize query

I am using sequlize in express JavaScript framework.
const data = await db.Tour.findAll();
res.status(200).json(data)
If I do this I can nicely retrieve the data in the front-end vue JavaScript spa like this
{
tour_name:"Bali",
activities:[
{name:"Swimming"},
{name:"Beach vollyball"},
]
}
Above one is for retrieve the data for front-end.
If I need to get the data and make some changes in the controller before send them, I will raw: true
then I can get the same output in my controller. But the problem is raw: true is not going well with the
joins, so that point getting the data from the controller and make some changes to it is very hard.
I have to access so many nested objects to find the data I want. Is there a smarter way (there should be) to get the above format
from the controller without using the raw: true.
I hope there must be a nice way to pass that data object to some thing and convert to the format.
How do I achieve this?
In below code I had to retrieve a shops products images and its ids along with some other data like ratings, count etc.
In below code I had to retrieve a shop products images and its ids.
exports.filterShopListings = async (data) => {
return Db.sequelize.query("SELECT productImages, S.shopId, S.currency, S.mainImage, S.mainImageThumb, S.coverImage, S.coverImageThumb, S.name, S.location, S.approved, S.locationLatitude, CONCAT('"+process.env.QR_URL+"',S.qrCode) as qrCode, S.locationLongitude, COALESCE(productCount,0) as productCount, COALESCE(ratingCount,0) as ratingCount, COALESCE(ROUND(UR.ratingAvg,1) ,0) as ratings, COALESCE(shopFollowing,0) as followingCount FROM shops as S JOIN users U ON (U.userId=S.userId AND U.blocked='0') LEFT JOIN ( SELECT COUNT(*) as productCount, shopId, GROUP_CONCAT(mainImageThumb,'--',shopProductId) as productImages FROM shopProducts WHERE shopProducts.deleted='0' AND shopProducts.blocked='0' GROUP BY shopId) SP ON (SP.shopId=S.shopId) LEFT JOIN ( SELECT COUNT(*) as ratingCount, AVG(ratings) as ratingAvg, shopId FROM userRatings WHERE userRatings.blocked='0' AND userRatings.deleted='0' GROUP BY shopId ) UR ON (UR.shopId=S.shopId) LEFT JOIN ( SELECT COUNT(*) as shopFollowing, shopId FROM shopFollowings GROUP BY shopId) SF ON (SF.shopId=S.shopId) WHERE "+data.whereString+" HAVING "+data.havingString+" ORDER BY "+data.orderingParam+" "+data.orderingSort+" LIMIT "+data.skip+", "+data.take+" ",{ type: Sequelize.QueryTypes.SELECT})
.then( (shops) => {
shops = JSON.parse(JSON.stringify(shops));
shops.forEach( (shop) => {
//shop.productImagesTemp = shop.productImages;
shop.productImages = shopImagesFunc(shop.productImages);
});
return shops;
});
};
And The shopImagesFunc Code -
var shopImagesFunc = (productImages) => {
if(productImages ==null)
return [];
var images = (productImages.split(",").filter(Boolean));
var newImages = [];
images.forEach(image => {
let temp = image.split("--").filter(Boolean);
newImages.push({
shopProductId: parseInt(temp[1]),
mainImage: temp[0],
});
});
return newImages;
};
SQL Query is little complicated but creating a common function to format into required output would be very useful.

Find index of an object in a nested array with mongodb, node js and express

Im trying to find the index of an object inside an array and return that index so that I can update my collection with the returned index. Consider this structure in mongoDb:
{
"_id" : ObjectId("571e32d44990e27409df4bb5”),
"association" : ”f",
"events" : [
{
"event" : ”eventtest”,
"students" : [
{
"session" : "beP--kELak7gcFQLN6-o_AdTkJA8eYcp",
"name" : ”Student",
"email" : ”email#example.com”,
"checkedIn" : false
},
{
"name" : "newstudent",
"email" : "newemail",
"checkedIn" : false
}
]
}
}
When I enter the students email in a form I want to set its "checkedIn" value to true. Using mongodb, express and node js this is what I've managed to do so far:
MongoClient.connect(url, function(err, db){
if (err){
console.log("Unable to connect to server");
} else {
console.log('Connected to server');
var collection = db.collection('associations');
//indexes should be set dynamically where the emails match
var eventIndex = 0;
var studentIndex = 1;
var setModifier = { $set: {} };
setModifier.$set['events.' + eventIndex + '.students.' + studentIndex + '.checkedIn'] = true;
collection.update({
association: assoc,
"events.event": event,
"events.students.email": req.body.email
}, setModifier, function(err, user){
if(err) {
console.log(err);
} else{
res.redirect("checkin");
}
db.close();
});
}
});
This works just fine, I need to query the student with, for example, the email of "newemail" and set the studentIndex to 1, and the same to return a value of 0 for the event: "eventtest".
I've found that you can use mapReduce or aggregation to find an items index in an array but I'm not sure that this would work for objects and I have no idea where to start. Someone suggested processing this on the client side as mapReduce wasn't recommended in the first place, but this seems like quite a lot of hassle.
Here are some links that might shed to some light over what I'm trying to accomplish.
Retrieve only the queried element in an object array in MongoDB collection
Retrieve index of item in array in MongoDB
Using MongoDB's positional operator $ in a deeply nested document query
function to return index of an object in a deeply nested array
EDIT:
I found this article aswell, Mongodb sort inner array
Maybe it could work if I aggregate the inner array of students and sort the student objects so that the corresponding student that matches the email is sorted at the top of the list while still keeping the rest of the structure in the whole document the same. This way the student that should be checked in always has an index of 0? Is this bad practice? It seems to me that this is a a lot of hassle for a rather simple task.

Javascript Query Builder with MongoDB "$in" operator

So I have set up a query builder that builds a query based on the users interaction with the data filtration area on the front end which contains a lot of radio buttons and dropdown boxes etc. Similar to what eBays data filtration function provided on their website.
My Query Builder so far:
app.post('/user/test',function(req, res) {
var query = {};
if (req.body.region){
query.region = req.body.region
console.log(query.region)
}
if(req.body.sector){
query.sector = req.body.sector
console.log(query.sector)
}
if(req.body.client){
query.client = req.body.client
console.log(query.client)
}
Project.find(query, function(err, project){
if (err){
res.send(err);
}
console.log(project);
res.json(project);
});
});
Now the above works very well. I can send filtration options in any scenario and it will bring back the required result. For example I can only send the region name and it will give me all the data that belongs to that region or I can send region name, sector name and it will further filter down the data that matches region and sector name sent and so on.
The Issue:
Now my database contains an array of data like:
words: ["book", "table", "pen"]
Each object in the database will have this array. So if there are 100 objects in the database each has one of these will have the "words" array with different or similar values.
I want to be able to send multiple options like "table" , "pen" to my database and get all the objects that contains the those two options within the data array.
To achieve that I did the following:
if (req.body.sol){
var arr = [];
arr.push(req.body.sol)
query.words = {words: {$in: arr}}
}
The above Did not work.
But if I make the following changes to this line:
From
query.words = {words: {$in: arr}}
to
query = {words: {$in: arr}}
Making the above change does work but then it does not build the remaining queries. It only builds the "$in" query.
Any idea how I can fix this?
you can simply write the query like
query.words = {$in: arr}
This way you would be able to build rest of the query.
the reason why query.words = {words: {$in: arr}} fails is that the query becomes{words:{words: {$in: arr}}}, which is not what you want, since its trying to find words inside words.
instead using query.words = {$in: arr} will make your query {words: {$in: arr}
You can use the bracket notation to add the $in operator in your query properties:
if (req.body.sol){
var arr = [],
obj = {};
arr.push(req.body.sol);
obj["$in"] = arr;
query.words = obj;
}

How can I make Parse.Query.AND?

I need to connect 2 queries in Parse.com with an and, my code is:
var queryDeseo1 = new Parse.Query(DeseosModel);
queryDeseo1.equalTo("User", Parse.User.current());
queryDeseo1.equalTo("Deseo", artist);
queryDeseo1.find({...
The result of the .find is all the objects with User = Parse.User.current()) and all the objects with Deseo = artist but I want the objects with the two queries together:
User = Parse.User.current()) and Deseo = artist
You've actually got it setup correctly to do an AND query. The problem (assuming that your data structure is setup properly) is that your User field is a Pointer to the User table. Therefore, you need to query for a User equal to the pointer, as opposed to a User equal to Parse.User.current() which will return a string. Something like the following:
var userPointer = {
__type: 'Pointer',
className: 'User',
objectId: Parse.User.current().id
}
queryDeseo1.equalTo('User', userPointer);

Categories