Iterating through multiple documents - javascript

I'm running into some difficulties iterating through a list of documents. I'm working on an app for distributing season tickets amongst friends and I've created a document store in mongodb. When I post the teamId and number of tickets (groups) it creates a document with the below structure.
{
draftOwner: '',
draftState: 'active',
draftCreate: 1659240148635,
draftGroups: 2,
homeTeam: '',
homeId: 17,
draftInvites: '',
homeSchedule: [
{
date: '2022-10-14T23:30:00Z',
home_id: 17,
home_name: 'Detroit Red Wings',
away_id: 8,
away_name: 'Montréal Canadiens',
ticketName_0: '',
ticketOwner_0: '',
ticketName_1: '',
ticketOwner_1: ''
},
{
date: '2022-10-17T23:30:00Z',
home_id: 17,
home_name: 'Detroit Red Wings',
away_id: 26,
away_name: 'Los Angeles Kings',
ticketName_0: '',
ticketOwner_0: '',
ticketName_1: '',
ticketOwner_1: ''
},
{
date: '2022-10-23T21:00:00Z',
home_id: 17,
home_name: 'Detroit Red Wings',
away_id: 24,
away_name: 'Anaheim Ducks',
ticketName_0: '',
ticketOwner_0: '',
ticketName_1: '',
ticketOwner_1: ''
}, ... ]
Example of my post method that creates documents for reference.
const createDraft = (req, res, next) => {
const teamId = parseInt(req.query.id);
const draftGroups = parseInt(req.query.groups);
const url = nhlScheduleAPI + teamId.toString() + season;
let settings = { method: "Get"};
fetch(url, settings)
.then(res => res.json())
.then((json) => {
let games = json['dates'];
let draftSchedule = [];
for (let i = 0; i < games.length; i++) {
let row = {};
if (games[i]['games'][0]['teams']['home']['team']['id'] === teamId) {
Object.assign(row, {date: games[i]['games'][0]['gameDate']});
Object.assign(row, {home_id: games[i]['games'][0]['teams']['home']['team']['id']});
Object.assign(row, {home_name: games[i]['games'][0]['teams']['home']['team']['name']});
Object.assign(row, {away_id: games[i]['games'][0]['teams']['away']['team']['id']});
Object.assign(row, {away_name: games[i]['games'][0]['teams']['away']['team']['name']});
for (let n = 0; n < draftGroups; n++) {
let ticketName = "ticketName_" + n.toString();
let ticketOwner = "ticketOwner_" + n.toString();
Object.assign(row, {[ticketName]: ""})
Object.assign(row, {[ticketOwner]: ""})
}
draftSchedule.push(row);
}
}
let newDraftObj = new Object({ draftOwner: "", draftState: "active", draftCreate: Date.now(),
draftGroups: draftGroups, homeTeam: "", homeId: teamId, draftInvites: "", homeSchedule: draftSchedule });
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("ticketdrafterDB");
const drafts = database.collection("drafts");
const result = await drafts.insertOne(newDraftObj);
console.log(result);
console.log(newDraftObj);
} finally {
await client.close();
}
}
run().catch(console.dir);
res.send(newDraftObj)
})
};
So now what I am running into issues with is iterating through the list of the currently {draftState: active} drafts. I'm trying to show a page that just has a table output of each object showing [draftCreate, homeTeam, draftOwner]. Here is what I have created so far and I'm getting console output, so the objects are being retrieved but I just can't display them to the client for some reason.
controller/home.js
const homeView = (req, res, next) => {
const client = new MongoClient(uri);
async function run() {
try {
const database = client.db("ticketdrafterDB");
const collection = database.collection("drafts");
const query = { draftState: "active" };
const options = { projection: { homeSchedule: 1 }};
const drafts = collection.find(query, options);
if ((await drafts.count()) === 0) {
console.log("No documents found!")
}
res.render('index', {drafts: drafts});
} finally {
await client.close();
}
}
run().catch(console.dir);
index.pug
each draft in drafts
li.list-group-item
p #{draft.draftState}
Would really appreciate any guidance here, thanks in advance!

try turning the drafts cursor into an array with method toArray:
const results = await drafts.toArray();
res.render('index', {drafts: results});

Related

Calling recursive function in loop with async/await and Promise.all

I have a use case where I'm trying to loop through an array of objects, where I need to make some GraphQL requests that may have some pagination for a given object in the array. I'm trying to speed up performance by pushing the recursive function to an array of promises, and then use Promse.all to resolve all of those.
I'm running into an issue though where I'm getting an undefined response from Promise.all - The end goal is to have the following response for each unique object in the array:
[{
account: test1,
id: 1,
high: 2039,
critical: 4059
},
{
account: test2,
id: 2,
high: 395,
critical: 203
}]
...where I'm only returning anAccount object after recursion is done paginating/making all requests for a given account object.
Here is the sample code:
const fetch = require('isomorphic-fetch');
const API_KEY = '<key>';
async function main() {
let promises = [];
let accounts = [{'name': 'test1', 'id': 1}, {'name': 'test2' , 'id': 2}];
for (const a of accounts) {
let cursor = null;
let anAccountsResults = [];
promises.push(getCounts(a, anAccountsResults, cursor));
}
let allResults = await Promise.all(promises);
console.log(allResults);
}
async function getCounts(acct, results, c) {
var q = ``;
if (c == null) {
q = `{
actor {
account(id: ${acct.id}) {
aiIssues {
issues(filter: {states: ACTIVATED}) {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`
} else {
q = `{
actor {
account(id: ${acct.id}) {
aiIssues {
issues(filter: {states: ACTIVATED}, cursor: "${c}") {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`
}
const resp = await fetch('https://my.api.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': API_KEY
},
body: JSON.stringify({
query: q,
variables: ''}),
});
let json_resp = await resp.json();
let aSingleResult = json_resp.data.actor.account.aiIssues.issues.issues;
let nextCursor = json_resp.data.actor.account.aiIssues.issues.nextCursor;
console.log(nextCursor);
if (nextCursor == null) {
results = results.concat(aSingleResult);
} else {
results = results.concat(aSingleResult);
await getCounts(acct, results, nextCursor);
}
let criticalCount = results.filter(i => i.priority == 'CRITICAL').length;
let highCount = results.filter(i => i.priority == 'HIGH').length;
let anAccount = {
account: acct.name,
id: acct.id,
high: highCount,
critical: criticalCount
};
return anAccount;
}
main();
logging anAccount in function getCounts has the correct detail, but when returning it, logging the output of Promise.all(promises) yields undefined. Is there a better way to handle this in a way where I can still asynchronously run multiple recursive functions in parallel within the loop with Promise.all?
Your main problem appears to be that results = results.concat(aSingleResult); does not mutate the array you passed, but only reassigns the local variable results inside the function, so the anAccount only will use the aSingleResult from the current call.
Instead of collecting things into a results array that you pass an a parameter, better have every call return a new array. Then in the recursive await getCounts(acct, results, nextCursor) call, do not ignore the return value.
async function main() {
let promises = [];
const accounts = [{'name': 'test1', 'id': 1}, {'name': 'test2' , 'id': 2}];
const promises = accounts.map(async acct => {
const results = await getIssues(acct);
const criticalCount = results.filter(i => i.priority == 'CRITICAL').length;
const highCount = results.filter(i => i.priority == 'HIGH').length;
return {
account: acct.name,
id: acct.id,
high: highCount,
critical: criticalCount
};
});
const allResults = await Promise.all(promises);
console.log(allResults);
}
const query = `query ($accountId: ID!, $cursor: IssuesCursor) {
actor {
account(id: $accountId) {
aiIssues {
issues(filter: {states: ACTIVATED}, cursor: $cursor) {
issues {
issueId
priority
}
nextCursor
}
}
}
}
}`;
async function getIssues(acct, cursor) {
const resp = await fetch('https://my.api.com/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'API-Key': API_KEY
},
body: JSON.stringify({
query: q,
variables: {
accountId: acct.id,
cursor,
}
}),
});
if (!resp.ok) throw new Error(resp.statusText);
const { data, error } = await resp.json();
if (error) throw new Error('GraphQL error', {cause: error});
const { nextCursor, issues } = data.actor.account.aiIssues.issues;
if (nextCursor == null) {
return issues;
} else {
return issues.concat(await getIssues(acct, nextCursor));
}
}

Firestore : why using serverTimestamp gives different results?

I am having a hard time understanding serverTimestamp in firestore.
When I save a document in database in a firebase function using Fieldvalue.serverTimestamp() or in a javascript client code using serverTimestamp() it sometimes doesn't save the same thing in the database.
See screenshots below :
Sometime I get an object with {nanoseconds: xxx, seconds: xxx} and sometimes I get a timestamp formatted date...
The problem is when I try to query my orders using query(collectionRefOrders, orderBy('createdAt', 'desc'), limit(10)).
The orders with the object appears before the others ones even if they are created after...
Any clue why this happens ? What am I doing wrong ?
Thanks a lot.
EDIT :
Here is the code I use to add documents in the my firebase function (it is a request function I call in a website) :
const { getFirestore, FieldValue } = require('firebase-admin/firestore');
const firebaseDB = getFirestore();
exports.createOrderFromTunnel = functions.region('europe-west3')
.runWith({
timeoutSeconds: 10,
memory: "4GB",
})
.https
.onRequest(async (req, res) => {
cors(req, res, async () => {
try {
const { apiKey } = req.body;
const project = await getProjectFromApiKey(apiKey);
if (!project) {
return res.json({
success: false,
error: 'Unauthorized: invalid or missing api key'
});
}
const contactData = {
address: {},
createdAt: FieldValue.serverTimestamp()
};
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: FieldValue.serverTimestamp(),
status: 'NEW',
};
const refProject = firebaseDB
.collection('projects')
.doc(project.id);
const colOrder = firebaseDB.collection(`projects/${project.id}/orders`)
const refOrder = colOrder.doc();
const colContact = firebaseDB.collection(`projects/${project.id}/contacts`)
const refContact = colContact.doc();
await firebaseDB.runTransaction(async transaction => {
const snapProject = await transaction.get(refProject);
const dataProject = snapProject.data();
const sequenceContact = dataProject.sequenceContact;
const sequenceOrder = dataProject.sequenceOrder;
contactData.sequence = sequenceContact;
orderData.sequenceNumber = sequenceOrder;
await transaction.set(refContact, contactData);
orderData.customer.id = refContact.id;
orderData.customer.sequence = sequenceContact;
await transaction.set(refOrder, orderData);
await transaction.update(refProject, {
sequenceContact: sequenceContact + 1,
sequenceOrder: sequenceOrder + 1,
totalContacts: dataProject.totalContacts + 1,
totalOrders: dataProject.totalOrders + 1,
});
return refOrder.id;
});
return res.json({
success: true
});
} catch (err) {
functions.logger.error(err);
return res.json({
success: false,
err
});
}
});
});
Here is the code I use to add documents in my client code (it is a web app in javascript) :
const createOrder = async (projectId) => {
try {
const orderData = {
accounting: {
totalHT: 0,
totalTTC: 0,
totalTVA: 0,
},
createdAt: serverTimestamp(),
status: 'NEW',
surface: 0,
};
const refProject = doc(firebaseDB, 'projects', projectId);
const colOrder = collection(firebaseDB, `projects/${projectId}/orders`)
const refOrder = doc(colOrder);
return await runTransaction(firebaseDB, async (transaction) => {
const snapProject = await transaction.get(refProject);
if (!snapProject.exists()) {
throw "Document does not exist!";
}
const dataProject = snapProject.data();
const sequence = dataProject.sequenceOrder;
orderData.sequenceNumber = sequence;
transaction.set(refOrder, orderData);
transaction.update(refProject, { sequenceOrder: sequence + 1, totalOrders: dataProject.totalOrders + 1 });
return refOrder.id;
});
} catch (e) {
console.error(e);
return null;
}
};

Cannot read property 'count' of undefined Express API

When i call Create Option api it is working fine but when i call list api get error: Cannot read property 'count' of undefined Express (Node + MongoDB) API.here is my Option Controller File code.
i have Log DB.ProductDoption ,getting result but count function not working.
const _ = require('lodash');
const Joi = require('joi');
exports.create = async (req, res, next) => {
try {
const validateSchema = Joi.object().keys({
name: Joi.string().required(),
key: Joi.string().required(),
description: Joi.string().allow(['', null]).optional(),
options: Joi.array().items(Joi.object().keys({
key: Joi.string().required(),
displayText: Joi.string().required()
})).required()
});
const validate = Joi.validate(req.body, validateSchema);
if (validate.error) {
return next(PopulateResponse.validationError(validate.error));
}
const key = Helper.String.createAlias(req.body.key);
console.log(DB.ProductDoption);
const count = await DB.ProductDoption.count({ key });
if (count || validate.value.key === '_custom') {
return next(PopulateResponse.error({
message: 'Please add unique name for key'
}));
}
const option = new DB.ProductDoption(validate.value);
await option.save();
res.locals.option = option;
return next();
} catch (e) {
return next(e);
}
};
exports.list = async (req, res, next) => {
const page = Math.max(0, req.query.page - 1) || 0; // using a zero-based page index for use with skip()
const take = parseInt(req.query.take, 10) || 10;
try {
const query = Helper.App.populateDbQuery(req.query, {
text: ['name', 'key', 'description']
});
const sort = Helper.App.populateDBSort(req.query);
const count = await DB.ProductDoption.count(query);
const items = await DB.ProductDoption.find(query)
.collation({ locale: 'en' })
.sort(sort).skip(page * take)
.limit(take)
.exec();
res.locals.optionList = {
count,
items
};
next();
} catch (e) {
next(e);
}
};
collection.count is deprecated, and will be removed in a future version. Use Collection.countDocuments or Collection.estimatedDocumentCount instead

Pushing data from socket.io into array

I have the following problem that I cant seem to work out. I'm trying to get an array of arrays from my socket.io emitter that is structured like follows:
[ [{...},{...},{...}] , [{...},{...}] , [{...}] ]
Instead I get this:
I need all the arrays in one master array so that I can render bootstrap cards for each sub array.
Client side code:
const socket = io("http://localhost:5000");
socket.on('data', (dta) => {
handleData(dta.data);
})
function handleData(data) {
const masterArray= [];
masterArray.push(data);
console.log(masterArray);
}
Server side code:
for(let i = 0 ; i < alarmpanels.length ; i++) {
const ElkClient = elkClient.ElkClient;
let client = new ElkClient({
connection: {
name: alarmpanels[i].name,
host: alarmpanels[i].host,
port: alarmpanels[i].port,
secure: alarmpanels[i].secure,
zones: alarmpanels[i].zones
}
});
connectClient(client);
}
async function connectClient(client) {
await client.connect();
const zonesArray = client.options.connection.zones;
const arr = [];
try {
const clhost = client.options.connection.host;
const clport = client.options.connection.port;
const clsecure = client.options.connection.secure;
let data = await client.getArmingStatus();
for (i = 0 ; i < zonesArray.length ; i ++) {
const armUpState = await data.areas[i].armUpState;
const clName = client.options.connection.name;
const zoneName = zonesArray[i].name;
const siteName = zonesArray[i].site;
const clzone = zonesArray[i].zone;
const totalPanels = zonesArray[i].length;
const info = new PanelStatus(clhost, clport ,clsecure, clzone, siteName, clName, zoneName, armUpState, totalPanels);
arr.push(info);
}
io.on('connection', (socket, req) => {
socket.emit('data', {data: arr});
})
}
catch (err) {
console.log("Connection Lost!");
}
}
Your client code need slight changes
1) keep the masterArray declaration outside of handleData
2) When pushing to masterArray, use ... spread operator.
const masterArray = [];
/*
const socket = io("http://localhost:5000");
socket.on("data", dta => {
handleData(dta.data);
});
*/
function handleData(data) {
masterArray.push(...data);
}
handleData([{ a: 4 }, { b: 5 }]);
handleData([{ z: 4 }]);
handleData([{ p: 4 }, { q: 5 }, { r: 5 }]);
console.log(masterArray);
If you can use es2020, you can use Array.flat()
let startingArray = [
[{
entry1: 1,
entry2: 2,
entry3: 3
}, {
entry4: 4,
entry5: 5,
entry6: 6
}, {
entry7: 7,
entry8: 8,
entry9: 9
}],
[{
entry10: 10,
entry11: 11,
entry12: 12
}, {
entry13: 13,
entry14: 14,
entry15: 15
}],
[{
entry16: 16,
entry17: 17,
entry18: 18
}]
]
const flattened = startingArray.flat()
console.log('startingArray', startingArray)
console.log('flattened', flattened)

Update fields in object with mongoose in mongodb

I have a simple collection in mongodb.
I use mongoose.
I have users model with one field type object.
And I want change this object dynamically. But this code doesn't work, I used findByIdAndUpdate(), findById, findOne(), findOneAndUpdate().
const UsersSchema = mongoose.Schema({
likes: {}
},
{ collection: 'users' });
const Users = mongoose.model('Users', UsersSchema);
const id ="5b4c540f14f353a4b9875af4";
const thems = ['foo', 'bar'];
Users.findById(id, (err, res) => {
thems.map(item => {
if (res.like[item]) {
res.like[item] = res.like[item] + 1;
} else {
res.like[item] = 1;
}
});
res.save();
});
I believe that, for solve this problem you need to add more fields in your schema:
I created one example with this data:
const UsersSchema = new mongoose.Schema({
likes :[
{
thema:{
type: String
},
likes_amount:{
type: Number
},
_id:false
}]
});
module.exports = mongoose.model('Users', UsersSchema);
I added one user:
var newUser = new UserModel({
likes:[{
thema:'foo',
likes_amount:1
}]
});
newUser.save();
Here the code that increment the likes per thema:
const thems = ['foo', 'bar'];
const userId = "5b4d0b1a1ce6ac3153850b6a";
UserModel.findOne({_id:userId})
.then((result) => {
var userThemas = result.likes.map(item => {
return item.thema;
});
for (var i = 0; i < thems.length; i++) {
//if exists it will increment 1 like
if (userThemas.includes(thems[i])) {
UserModel.update({_id: result._id, "likes.thema" : thems[i]}, {$inc: {"likes.$.likes_amount": 1}})
.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err)
});
} else {
//if doesn't exist it will create a thema with 1 like
UserModel.update({_id: result._id},
{
$addToSet: {
likes: {
$each: [{thema: thems[i], likes_amount: 1}]
}
}})
.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err)
});
}
}
}).catch((err) => {
console.log(err)
});
Database result of this increment:
I hope that it can help you.

Categories

Resources