Nesting data in an express app with postgres - javascript

So I'm new to express and have been playing around with a program where you can create events and invite other users. I know that you can use join tables to get the data, but I was hoping that there is a way to have the data nest itself using POSTGRESQL to show the events that the user is hosting, attending, and invited to based on the id of an event.
Here is what I have so far:
app.get('/api/users', function(req, res) {
var list = [];
User.findAll().then(function(users) {
for( var i in users) {
var item = {
id: users[i].dataValues['id'],
firstName: users[i].dataValues['firstname'],
lastName: users[i].dataValues['lastname'],
email: users[i].dataValues['email'],
phone: users[i].dataValues['phone'],
image: users[i].dataValues['image'],
confirmed: users[i].dataValues['confirmed'],
events: []
}
Event.findAll({ where: { hosting_id: user.id }}).then(function(events) {
for( var i in events) {
item.events.push({
hosting: event[i].dataValues['id'],
attending: event[i].dataValues['id'],
invites: event[i].dataValues['id']
})
}
})
list.push(item)
}
res.json(list)
})
});
I've been able to have it so that it loops and can find all the events that are associated with the user through id, but I'm still struggling with how to have it connect with each one so that it can display the event id, like the following example:
{
"id": "4",
"firstName": "Jon",
"lastName": "Doe",
"email": "jdoe#test.com",
"phone": "1234567890",
"image": "",
"events": {
"hosting": [
"1"],
"attending": [],
"invites": [
"3", "4"]
},
"confirmed": true
}
Besides having the info displayed, I'm also trying to figure out if it will need to be nested within a POSTGRES database, or if I can just use something like a join table but still have the info display without having another API call.
Any help would be greatly appreciated! Thank you in advance!

Event.findAll is a Promise and run asynchronously.
You need to make sure that you format your response only after that call.
You can try something like this and see if it works:
Promise.all(
users.map(function(user) {
return Event.findaAll({ where: { hosting_id: user.id }})
.then(function(events) {
var formatEvents = events.map(function(event) {
return {
hosting: event.id,
attending: event.id,
invites: event.id
};
})
return Promise.resolve({
id: users[i].dataValues['id'],
firstName: users[i].dataValues['firstname'],
lastName: users[i].dataValues['lastname'],
email: users[i].dataValues['email'],
phone: users[i].dataValues['phone'],
image: users[i].dataValues['image'],
confirmed: users[i].dataValues['confirmed'],
events: formatEvents
});
})
})
)
.then(function(list) {
res.json(list)
})
Or you can also add a relationship between Users and Events and when get users include the events model.

Related

Simple $lookup "left join" of the ObjectId of two collections not working in Mongo DB

I am using Mongo DB Atlas with node.js and oboe (oboe streams the results to html). I am new to this all, just learning all these technologies, so explaining in simpler terms will be appreaciated.
The goal is to do a $lookup between two collections, in this case, the collection of 'review' to 'place'. I've researched similar answers, but the either didn't work or were using strings, not ObjectIds.
It's quite simple, just connect the ObjectIds of both of the collections, but I am not able to pull the data out from the "left joined" collection of 'places' when using oboe (see oboe code at bottom, FWIW).
Here is a look at a document from both collections, then the code. What am I doing wrong? I have tried converting them to strings and joining with .toString() and .str, plus put 'place_id.ObjectId' and '_id.ObjectId' for localField and foreignField. Another thing too, is how can I see what is in the cursor to know what I am getting? debug(cursor.ToArray()) didn't work. Thanks in advance.
review
({
"_id": { "$oid": "5fd27fd9647f7bb815c4c946" },
"place_id": { "$oid": "5fbc37c4fc13ae680b00002b" }, // connect this...
"user_id": { "$oid": "5fbc10ecfc13ae232d000068" },
"title": "Black Forest -- unforgettable!",
"description": "The forest was great.",
"score": { "$numberInt": "5" }
}
place
{
"_id": { "$oid": "5fbc37c4fc13ae680b00002b" }, // connected to _id above
"name": "Black Forest (Schwarzwald)",
"category": "activity",
"city": "Freiburg",
"country": "Germany",
"description": "The Black Forest (German: Schwarzwald [ˈʃvaʁtsvalt] (About this soundlisten)) is a large forested mountain range.]",
"image": { "filename": "1607629020164_black_forest.jpg", "mime": "image/jpeg" },
"state": ""
})
router.get('/', async (req, res, next) => {
debug('get all reviews api');
try {
const q = req.query.q;
const collation = { locale: 'en_US', strength: 1 };
const matchStage = {};
if (q) {
matchStage.$text = { $search: q };
}
const pipeline = [
{
$match: matchStage,
},
{
$lookup: {
from: 'place',
localField: 'place_id',
foreignField: '_id',
as: 'place',
},
},
];
const connection = await db.connect();
const cursor = connection.collection('review').aggregate(pipeline, { collation: collation });
// write the JSON file
res.type('application/json');
res.write('[\n');
for await (const doc of cursor) {
res.write(JSON.stringify(doc));
res.write(',\n');
}
res.end('null]');
} catch (err) {
sendError(err, res);
}
});
The cursor goes to oboe and becomes an 'item'. I would expect to use a template string such as {item.place.name} to get the data when putting this into html. That's how I would access it, right?
const performSearch = () => {
seen = 0;
$('stream-data-spinner').removeClass('d-none');
$('#search-results').html('');
const formData = $('#search-place-form').serialize();
oboe('/api/review?' + formData)
.node('![*]', (item) => {
if (item) {
chunk.push(item);
if (chunk.length >= 1000) {
showChunk(chunk);
}
}
return oboe.drop;
})
.done((_) => {
// show the last chunk
showChunk(chunk);
// hide the spinner
outputSpinner.classList.add('d-none');
})
.fail((res) => {
// show the error
outputSeen.textContent = `ERROR: network error`;
outputSeen.classList.add('text-danger');
outputSpinner.classList.add('text-danger');
});
};
From your MongoDB aggregation query, your place field is an array. You may want to $unwind it to flatten it into object for your oboe code to access it.

How to run a test multiple times with different sets of data in cypress?

While automating a website, I have a requirement to run a test case(it block) multiple times with different set of testdata in cypress.
Please consider the below example :
it('example test', () => {
//first run
getOnDefaultForm.typeUserName('Name1');
getOnDefaultForm.typePassword('Pass1');
getOnDefaultForm.clickSubmit();
//second run
getOnDefaultForm.typeUserName('Name2');
getOnDefaultForm.typePassword('Pass2');
getOnDefaultForm.clickSubmit();
//third run
getOnDefaultForm.typeUserName('Name3');
getOnDefaultForm.typePassword('Pass3');
getOnDefaultForm.clickSubmit();
});
How can I achieve this in Cypress?
I think you need to have a look as this repo: https://github.com/cypress-io/cypress-example-recipes/tree/master/examples/fundamentals__dynamic-tests Or just search this site, this is not the first time someone has asked this very question.
In general, you can wrap your it in a loop. In practice, it'd look e.g. like this:
const testData = [
{
name: 'Name1',
password: 'Pass1'
},
{
name: 'Name2',
password: 'Pass2'
},
{
name: 'Name3',
password: 'Pass3'
}
]
testData.forEach((credentials) => {
it('example test', () => {
getOnDefaultForm.typeUserName(credentials.name);
getOnDefaultForm.typePassword(credentials.password);
getOnDefaultForm.clickSubmit();
})
});
It is also possible to put the data in a json file in the fixtures folder, and import it at the top of the spec file.
This methodology is still working in Cypress version 12.5.0.
fixture
[
{ "name": 'Name1', "password": 'Pass1' },
{ "name": 'Name2', "password": 'Pass2' },
{ "name": 'Name3', "password": 'Pass3' }
]
test
const testData = require('../fixtures/test-data.json')
testData.forEach((credentials) => {
it('example test for ' + credentials.name, () => {
getOnDefaultForm.typeUserName(credentials.name);
getOnDefaultForm.typePassword(credentials.password);
getOnDefaultForm.clickSubmit();
})
})

Mongoose: Updating a nested array inside of an array

I am trying to figure out how to update a nested array that is within an array with Mongoose. In my User collection, I have a customer array that contains customer info, along with a nested fleet array that holds the customer's fleet equipment. I am trying to update the fleet array via a PUT request, but am having difficulties.
I partially think it is not possible to update a nested array within an array like this, and maybe I should create a separate Schema for the customer and fleet. Anyways, here is what my User Schema looks like currently:
{
"username": "xps_maint",
"password": "0000",
"registerDate": "2018-10-24T13:37:12.093Z",
"_id": "5bd07612d63de74932734d92",
"customer": [
{
"name": "Freight Service ",
"email": "info#fsllc.com",
"dotInterval": "90 days",
"fleet": [
{
"unitType": "Box Truck",
"unitNumber": "BT-61318",
"vinNumber": "1XXXYYYUUUZZ3222",
"_id": "5bd0aef1e2abd64b12e0ab42"
},
{
"unitType": "Cargo Van",
"unitNumber": CV-78453",
"vinNumber": "4ZZYYYTTUZZ3JK2",
"_id": "5bd0aef1e2arg64b15e0ab43"
}
],
"_id": "5bd0821f79f9454b06b2c2bf"
}
],
"__v": 0
}
Here is my PUT route to update the fleet array:
router.put('/customers/fleet/:equipmentid', customer_controller.customer_update_fleet);
And finally here is the what the fleet update controller looks like:
exports.customer_update_fleet = (req, res) => {
const { body, params } = req;
const { unitType, unitNumber, vinNumber } = body;
const { equipmentid } = params;
const updatedEquipment =
{
unitType: unitType,
unitNumber: unitNumber,
vinNumber: vinNumber,
}
User.updateOne({ 'customer.$.fleet': { _id: equipmentid }}, { $set: { 'customer.$.fleet': { updatedEquipment} } }, (err) => {
if (err)
throw err;
else
res.send('Success!!');
});
}
I thought this might of worked, because I have a similar PUT route that updates just the customer array in the User Schema via Model.updateOne(). However this does not seem to work the same way when trying to go deeper into the nested fleet array within each customer.
I may be approaching this all wrong, so I am all ears on a better way to model the User Schema. I do partially think that it is not too good to have arrays nested deep in Schemas like this, they seem like a pain to update. Thanks in advance for reading!

Set data in array of array using mongoose

I've got such User object:
{
"_id": "584d91ff6c751769fab91be5",
"username": "svit",
"name": "Ilya",
"role": "user",
"authData": [
{
"fb": {
"access_token": "susdfg",
"expiration_date": 1,
"id": "1187410264678321"
}
}
],
"__v": 9,
"currentToken": "9735f44f1c4371f143747ff670b0076148053f391ab866bafab7c6eaf47d295b"
}
I am interested in updating User.authData.fb. I tried that:
var curUser = userModel.findOne({_id: user._id}, function(err, curUser){
curUser['authData'][socialType] = {
access_token: socialToken,
expiration_date: 173249700 //TODO: normal date
};
curUser.set('currentToken', tokenName);
curUser.markModified('authData');
curUser.save();
});
But that does just nothing. It modifies currentToken, but not authData.
Also, I tried curUser.set('authData.fb.expiration_date", 173249700)
That does not works too.
I really need your help!
Best regards,
Ilya
You can loop through the 'authData' array and modify whatever values you may like. I am assuming authData is a array of socialTypes and it has an attribute name to indentify.
example:
var curUser = userModel.findOne({_id: user._id}, function(err, curUser){
curUser.authData.map(socialType => {
// Select the type of socialAuth you want to modify
if(socialType.name === 'fb') {
access_token: socialToken,
expiration_date: 173249700 //TODO: normal date
}
})
curUser.set('currentToken', tokenName);
curUser.markModified('authData');
curUser.save();
});

Insert data in collection at Meteor's startup

I would like to insert data at Meteor's startup. (And after from a JSON file)
At startup, I create a new account and I would like to insert data and link it to this account once this one created.
This is the code that creates the new account at startup:
if(!Meteor.users.findOne({emails: { $elemMatch: { address: "test#test.com"}}})){
var id = Accounts.createUser({ email: "test#test.com", password: "1234", profile: { name: 'Test' } });
Meteor.users.update({_id: id }, { $set: { admin: false }});
}
And after that, I need to insert data and link it to this account with its ID. (In different collections).
So I tried to do something like that, but obviously It didn't work:
UserData = new Mongo.Collection('user_data');
if(!Meteor.users.findOne({emails: { $elemMatch: { address: "test#test.com"}}})){
var id = Accounts.createUser({ email: "test#test.com", password: "1234", profile: { name: 'Test' } });
Meteor.users.update({_id: id }, { $set: { admin: false }});
UserData.insert({
createdBy: id,
firstname: "test",
/* ... */
});
}
EDIT
Sorry for not have been clear.
The real issue is the :
UserData = new Mongo.Collection('user_data');
declaration is in another file, so I can't do like above.
As it's not in the same file, I tried to get the userId that got "test#test.com" as the email (the account's email created at startup). And once I got it, I want to use it in "createdBy: ID_HERE".
Ok, you'll want to check out Structuring your application. You'll have to make the file with the definition load earlier, or the one with the fixture later.
Normally you have your collections inside lib/ and your fixtures inside server/fixtures.js.
So if you put your insert code into server/fixtures.js it'll work.

Categories