I am working on a performance issue of a function, that takes 15sec to response, which makes a request to firebase for all documents that are
"ErrorID" "==" "0"
The problem is that there are many documents and they are kind of very large objects, and I only need TWO FIELDS (Order and Amount) of each document, there are any way to request only those two fields that accomplish the condition?
Something like :
firestore.collection("purchases").where("ErrorID", "==", "0").get(Order, Amount);
The function that im talking about:
const totalEarn = async (req, res, next) => {
const DAY = 86400000;
const WEEK = 604800016;
const MONTH = 2629800000;
try {
let snap = await firestore.collection("purchases").where("ErrorID", "==", "0").get(); // CONDITION
let totalToday = 0;
let totalYesterday = 0;
let totalLastWeek = 0;
let totalLastMonth = 0;
let now = Date.now();
let Yesterday = now - 86400000;
await snap.forEach((doc) => { // THIS FOR EACH TAKES TOO MUCH TIME
let info = doc.data();
let time = info.Order.split("-")[2]; // FIRESTORE FIELD -> ORDER
let amount = info.AmountEur * 1; // FIRESTORE FIELD -> AMOUNT
if (time > now - DAY) {
totalToday = totalToday + amount;
}
if (time < now - DAY && time > Yesterday - DAY) {
totalYesterday = totalYesterday + amount;
}
if (time > now - WEEK) {
totalLastWeek = totalLastWeek + amount;
}
if (time > now - MONTH) {
totalLastMonth = totalLastMonth + amount;
}
});
res.send({
status: true,
data: {
totalToday: totalToday.toFixed(2),
totalYesterday: totalYesterday.toFixed(2),
totalLastWeek: totalLastWeek.toFixed(2),
totalLastMonth: totalLastMonth.toFixed(2),
},
});
} catch (error) {
res.status(410).send({
status: false,
error: "some error occured counting the numbers",
e: error.message,
});
}
};
The document im talking about
If you use Firestore Node.JS client or Firebase Admin SDK, then you can use select() to select fields:
import { Firestore } from "#google-cloud/firestore";
const firestore = new Firestore();
const snap = firestore
.collection("purchases")
.where("ErrorID", "==", "0")
.select("Order", "Amount")
.get();
Related
I need to join different objects depending on their conditions.
Context
The theme is to calculate the hours that a user accumulates during a full week, let's call it "tracking".
I have an array where I store the daily history of the hours that a user performs daily represented by 'data_daily'
The following array stores the current weekly information of the day that is being consulted, for example, 2022-10-28 is consulted, so its week is from 2022-10-24 to 2022-10-30, which is represented by 'data_week'
However, there are different cases of important uses to highlight:
--- If a user does not record hours on a certain day, that means there will be no record of his hours, right? It's obvious, but to know the reason why he didn't meet his hours, it is stored in 'time_off_request_query' where the reason why he didn't track is specified and if he has an excuse or not give him a minimum of hours or by default zero
So to better understand this let's see it in an example
The user with ID 1234 did not mark hours on October 30 and when consulting the daily information for that day it does not come to us, but we have to store in 'time_off_request_query' that it was missing due to an authorized permission, and a minimum number of hours will be assigned per agreement
Time Off Request
[{
user_id: 1234,
reason: 'authorized permission',
input_date: 2022-10-31,
departure_date: 2022-10-29,
min_tracked: 4
}]
Then, when consulting the weekly information, we obtain that in the week from 2022-10-24 to 2022-10-30, specifically during the days that he did attend, he did 40 hours of work plus the minimum 4 hours assigned to him by agreement for the day 2022 -10-29 gives a total of 44 hours
Weekly Information
[{
user_id: 1234,
weklyConsulting: '2022-10-24 / 2022-10-30',
tracking Weekly: 44
}]
And when consulting the daily information of 2022-10-29 we obtain the minimum hours that were assigned by agreement
[{
user_id: 1234,
date: '2022-10-30',
trackingDaily: 4
}]
So when joining or merging the objects, a result like the following is expected:
[{
user_id: 1234,
date: '2022-10-30',
trackingDaily: 4,
weklyConsulting: '2022-10-24 / 2022-10-30',
tracking Weekly: 44
}]
What are the validations then to take into account
If a user did not do hours on the day consulted but has accumulated for the week, the minimum hours added by agreement must be added to the weekly record, as must also be represented in the Daily Information of the day consulted
If a user made hours that day consulted but during the week he asked for permission and was assigned minimum hours by agreement, he must bring what he did that day plus his accumulated hours of the week where the total will be taken into account what was get from time_off_request_query
Problem
This is my current code
export const generateReporteDiarioTesteo = async (req: Request & any, res: Response, next: NextFunction) => {
try {
const dayliHoursxTimeWork: DayliHoursxTimeWork[] = [];
const dataDailyxResume: DayliHoursxResume[] = [];
const dataWeekxResume: ResumeWeek[] = [];
const hubstaffService = new HubstaffClass();
const supabaseService = new SupabaseService();
const configGenerals = new Configs();
let data_daily: DailyActivityJoin[] = [];
let data_week: DailyDB[] = [];
let generalConfigs: GeneralConfig[] = [];
let time_holiday_request: TimeHoliday[] = [];
let attendance_schedule_request: AttendanceSchedule[] = [];
let time_off_request_query: TimeOffRequest[] = [];
const promises = [
hubstaffService.getDailyActivities(req.query.start_date, req.query.end_date, req).then(dataFromHubstaff => {
let summedTrackedHubstaff = trackedDataFromApi(dataFromHubstaff);
data_daily = summedTrackedHubstaff;
}),
supabaseService.getSummaryWeek(req.query.start_date).then(dataFromDB => {
let summedTrackedWeekly = trackedData(dataFromDB as any[]);
data_week = summedTrackedWeekly;
}),
configGenerals.getConfigs().then(dataFromDB => { generalConfigs = dataFromDB as any }),
configGenerals.get_time_holiday(req.query.start_date).then(dataFromDB => { time_holiday_request = dataFromDB as any }),
configGenerals.attendance_schedule(req.query.fecha_inicio).then(dataFromDB => { attendance_schedule_request = dataFromDB as any }),
/** LIST OF USERS WHO DID NOT TRACK THAT DAY REQUESTED */
configGenerals.time_off_request(req.query.start_date).then(dataFromDB => { time_off_request_query = dataFromDB as any }),
];
await Promise.all(promises);
generalConfigs.map(config => {
dayliHoursxTimeWork.push({
dayliHourFullTime: config.fulltime_hours_daily,
dayliHourPartTime: config.parttime_hours_daily
})
});
/** WEEKLY DATA - REGISTRY OF USERS IN THE WEEK */
data_week.map(weekData => {
dataWeekxResume.push({
user_id: weekData.user_id,
tracked_weekly: weekData.tracked,
overall_weekly: weekData.overall,
keyboard_weekly: weekData.keyboard,
input_tracked_weekly: weekData.input_tracked,
mouse_Weekly: weekData.mouse,
billable_Weekly: weekData.billable,
manual_Weekly: weekData.manual,
idle_Weekly: weekData.idle,
resumed_weekly: weekData.resumed,
rolTittle: weekData.rol.tittle,
rolePriority: weekData.rol.priority,
status: weekData.status,
username: weekData.username,
daily_hours: weekData.daily_hours,
summaryStarts: weekData.summaryStarts,
time_work: weekData.user.time_work
});
});
/** DAILY DATA */
data_daily.map(dayli => {
dataDailyxResume.push({
user_id: dayli.user_id,
report_date: dayli.date,
tracked_daily: dayli.tracked,
input_tracked_daily: dayli.input_tracked,
overall_daily: dayli.overall,
mouse_daily:dayli.mouse,
billable_daily: dayli.billable,
manual_daily: dayli.manual,
idle_daily: dayli.idle,
resumed_daily: dayli.resumed
});
});
// WEEKLY UNION X DAILY
const joinWeekDailyTracked = dataDailyxResume.map(dayliData => {
const weekData = dataWeekxResume.find(item => item.user_id === dayliData.user_id);
let dataDailyOff = getTimeOff(time_off_request_query, dayliData, weekData, req.query.start_date);
let dayliWeekOff = getTimeOffWeek(time_off_request_query, dayliData, weekData, req.query.start_date);
let accWeekOff = getAccTimeOffWeek(time_off_request_query, dayliData, weekData, req.query.start_date)
return (weekData && dayliData) ? { ...dayliData, ...weekData } : (weekData && !dayliData) ? { ...weekData, ...dataDailyOff } : (!weekData && !dayliData) ? { ...dataDailyOff, dayliWeekOff } : (!dayliData && (weekData && time_off_request_query.length > 0)) ? {...dataDailyOff, ...accWeekOff} : null
});
res.json(joinWeekDailyTracked) }
catch (error) {
res.status(500).json({ title: 'API-CIT Error', message: 'Error Interno del Servidor' });
}
}
Carry out the following functions with the idea of being able to validate each use case that I exposed in the context
export function getTimeOff(time_off_request_query, dayliData, weekData, date) {
let tracked_off: number = 0;
let user_id_off: number = 0;
let username_off: string = '';
let rol_off: string = '';
let role_priority_off: string = '';
let activity_off: string = '';
// of the time_off_request_query in the week query for each element of the array
time_off_request_query.forEach(off_time => {
// if the id is different from the one that brings the daily information
if (off_time.user_id != dayliData.user_id) {
// stores the minimum number of hours assigned by agreement + extra information
tracked_off = off_time.tracked;
user_id_off = off_time.user_id;
username_off = off_time.user.name;
rol_off = off_time.rol.tittle;
rol_priority_off = off_time.rol.priority;
activity_off = off_time.activity
}
})
// Return the new object
let dataDailyOff = {
user_id: user_id_off,
report_date: date,
tracked_daily:tracked_off,
input_tracked_daily: tracked_off,
overall_daily: 0,
mouse_daily: 0,
billable_daily: 0,
manual_daily: 0,
idle_daily: 0,
resumed_daily: 0,
username: username_off
};
return dataDailyOff;
}
export function getTimeOffWeek(time_off_request_query, dayliData, weekData, date) {
let tracked_off: number = 0;
let user_id_off: number = 0;
let username_off: string = '';
let rol_off: string = '';
let role_priority_off: string = '';
let activity_off: string = '';
let role_status = true;
let dayli_hours_off: number = 0;
let time_work_off: number = 0;
// of the time_off_request_query in the week query for each element of the array
time_off_request_query.forEach(off_time => {
// If the id of the user in daily information is different from the time_off and the id of the weekly information is also different
if (off_time.user_id != dayliData.user_id && off_time.user_id != weekData.user_id) {
tracked_off = off_time.tracked;
user_id_off = off_time.user_id;
username_off = off_time.user.name;
rol_off = off_time.rol.tittle;
rol_priority_off = off_time.rol.priority;
activity_off = off_time.activity;
role_status = off_time.user.status
time_work_off = off_time.user.time_work
}
})
let dayliWeekOff = {
user_id: user_id_off,
tracked_weekly:tracked_off,
overall_weekly:tracked_off,
keyboard_weekly: 0,
input_tracked_weekly: 0,
mouse_weekly: 0,
billable_Weekly: 0,
manual_weekly: 0,
idle_Weekly: 0,
summarized_weekly: 0,
rolTitle: rol_off,
rolePriority: role_priority_off,
status: role_status,
username: username_off,
daily_hours: daily_hours_off,
summaryStarts: 0,
time_work: time_work_off
};
return dayliWeekOff;
}
export function getAccTimeOffWeek(time_off_request_query, dayliData, weekData, date) {
let tracked_off: number = 0;
let user_id_off: number = 0;
let username_off: string = '';
let rol_off: string = '';
let role_priority_off: string = '';
let activity_off: string = '';
let role_status = true;
let dayli_hours_off: number = 0;
let time_work_off: number = 0;
// of the time_off_request_query in the week query for each element of the array
time_off_request_query.forEach(off_time => {
// If the user id in daily information is equal to the time_off and the id of the weekly information is also equal to the time_off
if (off_time.user_id != dayliData.user_id && off_time.user_id === weekData.user_id) {
// fetch the information you found about the time_off of that user_id
tracked_off = off_time.tracked;
user_id_off = off_time.user_id;
username_off = off_time.user.name;
rol_off = off_time.rol.tittle;
rol_priority_off = off_time.rol.priority;
activity_off = off_time.activity;
role_status = off_time.user.status
time_work_off = off_time.user.time_work
}
})
let dayliWeekOff = {
user_id: weekData.user_id,
// And add it when returning the object
tracked_weekly: tracked_off + weekData.tracked_weekly,
overall_weekly: tracked_off + weekData.tracked_weekly,
keyboard_weekly: weekData.keyboard_weekly,
input_tracked_weekly: weekData.input_tracked_weekly,
mouse_Weekly: weekData.mouse_Weekly,
billable_Weekly: weekData.billable_Weekly,
manual_Weekly: weekData.manual_Weekly,
idle_Weekly: weekData.idle_Weekly,
resumed_weekly: weekData.resumed_weekly,
rolTittle: weekData.rolTittle,
rolPrpriority: weekData.rolPriority,
status: weekData.status,
username: weekData.username,
daily_hours: weekData.daily_hours,
summaryStarts: weekData.summaryStarts,
time_work: weekData.user.time_work
};
return dayliWeekOff;
}
In the code that I present to you, I used functions that were too extensive and even too difficult to read and understand, in addition to different bad practices that I would like to be able to do in another way.
Currently this code is not working for me, the functions that I am trying to implement according to my logic are not correct
How can I do this? Is there a way to do it with Javascript? Or better to look elsewhere?
I have a command serverinfo and it shows how many bans there are on the server. If the server has 1200 bans, the bot will show that the server has 1000 bans.
Is it possible to somehow make the bot shows how many bans really are?
In the documentation the following parameters are specified limit?| number | number of users to return (up to maximum 1000) | default 1000
How to change default 1000 to e.g. 1500?
I am using discord.js vers 13.8.0 and node.js version 16.15.1.
For a v13 discord.js solution to your problem, this is what I did.
I edited this to include the while loop.
let completeBanIdList = await (async (a = [], last = 0, limit = 1000) => {
while(limit === 1000){
let bans = await guild.bans.fetch({after: last, limit: limit});
let banlist = bans.map(user => user.user.id);
last = bans.last().user.id;
limit = banlist.length;
for(let i = 0; i < limit; i++){a.push(banlist[i]);}
}
return a;
})();
// below is just console logging to show that it worked properly...
let banIdObj = ((o = {}) => {
for(let i = 0; i < completeBanIdList.length; i++){
o[completeBanIdList[i]] = 1;
}
return o;
})();
console.log(`unique ban id count:`, completeBanIdList.length, `should equal number of id keys in object:`, Object.keys(banIdObj).length);
Although the max limit is 1000, you can use the limit and before options to fetch the previous bans, too. before is a snowflake, it tells Discord to consider only bans before this id.
So, if you have around 1200 bans, you can fetch the first 1000, grab the id of the last item in the returned collection and use its id as the before option in your second fetch:
let firstHalf = await guild.bans.fetch({ limit: 1000 });
let secondHalf = await guild.bans.fetch({ before: firstHalf.last().id, limit: 1000 });
You can also create a new function that fetches more than 1000 bans. There is an example below. This function returns a collection of GuildBans, so you can use the usual collection methods, like filter, each, etc.
async function fetchMoreBans(guild, limit = 2000) {
if (!guild || typeof guild !== 'object')
throw new Error(`Expected a guild, got "${typeof guild}"`);
if (!Number.isInteger(limit))
throw new Error(`Expected an integer, got ${typeof limit}.`);
if (limit <= 1000) return guild.bans.fetch({ limit });
let collection = new Collection();
let lastId = null;
let options = {};
let remaining = limit;
while (remaining > 0) {
options.limit = remaining > 1000 ? 1000 : remaining;
remaining = remaining > 1000 ? remaining - 1000 : 0;
if (lastId) options.before = lastId;
let bans = await guild.bans.fetch(options);
if (!bans.last()) break;
collection = collection.concat(bans);
lastId = bans.last().id;
}
return collection;
}
Example usage:
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
let banList = await fetchMore(message.guild, 1500);
let filteredBanList = banList.filter(({ user }) =>
user.username.toLowerCase().startsWith('e'),
);
console.log(`There are ${banList.size} bans fetched`);
console.log(
`Number of bans where the user's username starts with the letter "e": ${filteredBanList.size}`,
);
});
Why do all my function calls get executed in parallel here when using await? What needs to be changed in order to start a function call once the previous is done?
for (let collection of collections) {
await this.updateListings(collection)
}
private async updateListings(collection: Collection) {
try {
const startTime = Date.now();
const supply = collection.stats.total_supply;
const contract_address = collection.primary_asset_contracts[0].address;
const token_ids = [];
for (let i = 1; i <= supply; i++) {
token_ids.push(i);
}
let offset = 0;
let limit = 30;
while (offset + limit <= supply) {
this.apiKey = this.apiKeys.find(key => key !== this.apiKey); //rotate api key on each request
const ids = token_ids.splice(0, 30);
const params = new URLSearchParams();
params.set("offset", offset.toString());
params.set("limit", limit.toString());
params.set("side", "1");
params.set("sale_kind", "0");
params.set("order_by", "eth_price");
params.set("order_direction", "asc");
params.set("asset_contract_address", contract_address);
for (let i = 0; i < ids.length; i++) {
params.append("token_ids", ids[i])
}
await this.delayRequest(700);
const response = await axios.get(process.env.OPENSEA_API_BASE_URL + "/orders", {
headers: {
"X-API-KEY": this.apiKey,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
},
params: params
})
const { orders } = response.data;
if (orders.length > 0) await getRepository(Listing).save(orders);
offset += limit;
console.log(`update listings for ${collection.slug} status: ${offset} / ${supply}`)
}
const endTime = Date.now() - startTime;
console.log("fetched listings for " + collection.slug + " in " + endTime + " ms")
} catch (error) {
console.log("updating listings for " + collection.slug + " failed", error.message);
}
}
constructor() {
setTimeout(() => {
this.update();
}, 6000)
}
What I expect is that the first iteration finishes before it proceeds but its calling each iteration "updateListings(coll)" at the same time.
In fact. your code is executed sequentially. How did you check that your code was running in parallel?
You can use Array.reduce to make it sequentially:
return collections.reduce((currentPromiseChain, collection) => {
return currentPromiseChain.then(() => this.updateListings(collection));
}, Promise.resolve());
Just add an await before the condition. This will ensure each loop finishes before continuing to the next one.
Note: not fully sure on the compatibility for this, you may need check for compatibility.
for await (let collection of collections) { //notice the await in this line.
await this.updateListings(collection)
}
This is to demostrate there isn't anything wrong with the way you are calling for loop, and also how you have a while loop inside your updateListing function.
There's probably something you didn't show?
The only reason I can see, affecting your codes, is if there are errors, and your try/catch block returns the promise with error immediately, making it appears it is running in parallel.
const whileLoopFunction = async(value) => {
let limit = 0;
let supply = 10
while (limit < supply) {
await new Promise(resolve => setTimeout(resolve,100))
limit++
}
console.log('While loop complete for value ', value)
};
(async() => {
const collections = [ 1, 2, 3, 4, 5 ]
console.log("For loop works with await")
for (let collection of collections) {
await whileLoopFunction(collection)
}
})()
How would one fetch messages from text channel beginning with the latest/newest message until a specific date. For example until the date two days ago.
The desired result is having a function that will do the job and return an array of messages dating in a range: now -> end date specified as the function's argument.
This would be my approach, feel free to post your own better answers :3
async function fetchMessagesUntil(channel, endDate, lastID) {
let messages = (await channel.messages.fetch({ limit: 100, before: lastID })).array();
if (messages.length == 0) return messages;
for (let i = 0; i < messages.length; i++) {
if (messages[i].createdAt.getTime() < endDate.getTime()) {
return messages.slice(0, i);
}
}
return messages.concat(
await fetchMessagesUntil(channel, endDate, messages[messages.length - 1].id)
);
}
Example usage
let end = new Date();
end.setDate(end.getDate() - 2); // Subtract two days from now
(await fetchMessagesUntil(message.channel, end)).forEach(x => console.log(x.content));
I'm trying to calculate the amount of sign-ups of each day of the current month, and send that data
to the Statistics page, where I display that data in a chart using Chart.js.
the data initialized well insides the query's scope, however I "lose" it outside of the scope.
EDIT - I extended my code so maybe there's some information relevant to my problem.
Here is my code:
async function getSignUps() {
const query = { createAt: { $gt: date.getFirstDateOfMonth(), $lt: new Date() } };
const projection = { createAt: 1, _id: 0 }; //can be added to find()
var signUps = new Array(date.getDaysInMonth()).fill(0); //create empty array of days in current month
Contractor_Users_Collection.find(query).project(projection).toArray(function (err, result) {
if (err) throw err;
// manipulte data to create array that the index indicates the day of month
// the value indicates the amount of signups per that day of the month
for (let i = 0, d = date.getFirstDateOfMonth(); i < result.length; i++, d.setDate(d.getDate() + 1)) {
nextDate = new Date(d.getDate() + 1);
if (d <= result[i]['createAt'] <= nextDate) {
day = result[i]['createAt'].getDate() - 1;
++signUps[day];
}
}
console.log('*****');
console.log('signUps inside find : ' + signUps);
console.log('*****');
})
console.log('*****');
console.log('signUps outside find : ' + signUps);
console.log('*****');
return signUps;
};
router.get("/statistics",async (req, res) => {
const signUps = await getSignUps();
console.log('*****');
console.log('signUps :' + signUps);
console.log('*****');
res.status(200).render("statistics", { signUps: signUps });
});
Here's the output :
*****
signUps outside find : 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
*****
signUps :0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
*****
signUps inside find : 1,3,3,5,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
*****
I found the solution, hope it will help to others...
async function getSignUps() {
const query = { createAt: { $gt: date.getFirstDateOfMonth(), $lt: new Date() } };
const projection = { createAt: 1, _id: 0 }; //can be added to find()
var signUps = new Array(date.getDaysInMonth()).fill(0); //create empty array of days in current month
try {
let result = Contractor_Users_Collection.find(query).project(projection)
result = await result.toArray()
// manipulte data to create array that the index indicates the day of month
// the value indicates the amount of signups per that day of the month
for (let i = 0, d = date.getFirstDateOfMonth(); i < result.length; i++, d.setDate(d.getDate() + 1)) {
nextDate = new Date(d.getDate() + 1);
if (d <= result[i]['createAt'] <= nextDate) {
day = result[i]['createAt'].getDate() - 1;
++signUps[day];
}
}
return signUps;
} catch (error) {
console.error(error)
throw error
}
};