How to implement Page-based pagination - javascript

Im trying to implement Page-based pagination on this particular endpoint but the problem Im faced is that the response body is not printing out the page, page_size and total results.
The URL is http://localhost:3006/events/tickets?sort_by=name&page=1&page_size=2
How do I get to return a Json body like this ?
Ideal Response Im trying to achieve
{
content: [], // respponse items
page: 1, // current page
results_per_page: 10, // results per page
total_pages: 10, // total number of pages
total_results: 100 // number of all results
}
const getTickets = (tickets) => {
return new Promise(async (resolve, reject) => {
try {
let query = `SELECT id, name, description, ticket_price, ticket_type
FROM tickets`;
let sort_by = tickets.sort_by;
if(sort_by ?? false) {
query += ` ORDER BY ${sort_by}`;
if (sort_by) {
query += (sort_by == 'desc' ? ` DESC` : ` ASC`);
}
}
let limit = tickets.page_size;
let replacements = { limit: limit };
if (limit) {
query += ` LIMIT :limit`;
replacements.limit = limit;
}
let offset = limit ? ((tickets.page - 1) * limit) : 0;
if(offset) {
query += ` OFFSET :offset `;
replacements.offset = offset;
};
const ticket = await db.sequelize.query(
query,
{
replacements: replacements,
type: QueryTypes.SELECT,
}
);
return resolve(ticket);
} catch (err) {
return reject(err)
}
})
}
[
{
"id": 1,
"name": "Tennis Finals",
"description": "Finals: Tennis Team 1 vs Tennis Team 2",
"ticket_price": 25,
"ticket_type": 1
},
{
"id": 5,
"name": "Testing Tickets",
"description": "Description....",
"ticket_price": 1000,
"ticket_type": 30
}
]

Related

API call to youtube.videos.list failed with error

When I run the following JavaScript through Google Apps script with more then 100 keywords.
function youTubeSearchResults() {
// 1. Retrieve values from column "A".
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const values = sheet.getRange("A2:A" + sheet.getLastRow()).getDisplayValues().filter(([a]) => a);
// 2. Retrieve your current values.
const modifyResults = values.flatMap(([keywords]) => {
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
const fSearchResults = searchResults.items.filter(function (sr) { return sr.id.kind === "youtube#video" });
return fSearchResults.map(function (sr) { return [keywords, sr.id.videoId, `https://www.youtube.com/watch?v=${sr.id.videoId}`, sr.snippet.title, sr.snippet.publishedAt, sr.snippet.channelTitle, sr.snippet.channelId, `https://www.youtube.com/channel/${sr.snippet.channelId}`, sr.snippet.thumbnails.high.url] });
});
// 3. Retrieve viewCounts and subscriberCounts.
const { videoIds, channelIds } = modifyResults.reduce((o, r) => {
o.videoIds.push(r[1]);
o.channelIds.push(r[6]);
return o;
}, { videoIds: [], channelIds: [] });
const limit = 50;
const { viewCounts, subscriberCounts } = [...Array(Math.ceil(videoIds.length / limit))].reduce((obj, _) => {
const vIds = videoIds.splice(0, limit);
const cIds = channelIds.splice(0, limit);
const res1 = YouTube.Videos.list(["statistics"], { id: vIds, maxResults: limit }).items.map(({ statistics: { viewCount } }) => viewCount);
const obj2 = YouTube.Channels.list(["statistics"], { id: cIds, maxResults: limit }).items.reduce((o, { id, statistics: { subscriberCount } }) => (o[id] = subscriberCount, o), {});
const res2 = cIds.map(e => obj2[e] || null);
obj.viewCounts = [...obj.viewCounts, ...res1];
obj.subscriberCounts = [...obj.subscriberCounts, ...res2];
return obj;
}, { viewCounts: [], subscriberCounts: [] });
const ar = [viewCounts, subscriberCounts];
const rr = ar[0].map((_, c) => ar.map(r => r[c]));
// 4. Merge data.
const res = modifyResults.map((r, i) => [...r, ...rr[i]]);
// 5. Put values on Spreadsheet.
sheet.getRange(2, 2, res.length, res[0].length).setValues(res);
}
it gives me that error
GoogleJsonResponseException: API call to youtube.videos.list failed with error:
The request cannot be completed because you have exceeded your quota.
reduce.viewCounts #code.gs:23
youTubeSearchResults #code.gs:20
I know YouTube have data call limits for example you can call the results of not more then 50 video ids at one time but if you have 1000 video ids in your sheet you can run then loop for first 50 then next so on. Is it anything like that I can do with search results too.
Please help me understand how can I fix this issue.
Note that the endpoint the most expensive in your script is the Search: list one which costs 100 of your 10,000 quota (you can have a look to other endpoint costs here).
You may be interested in the standalone quota-free solution that consists in reverse-engineering the YouTube UI search feature.
Otherwise a temporary solution to Google audit consists in using my no-key service.
With my no-key service:
const searchResults = YouTube.Search.list("id, snippet", { q: keywords, maxResults: 10, type: "video", order: "viewCount", videoDuration: "short", order: "date" });
would become:
const searchResults = JSON.parse(UrlFetchApp.fetch(`https://yt.lemnoslife.com/noKey/search?part=snippet&q=${keywords}&maxResults=10&type=video&order=viewCount&videoDuration=short`).getContentText())
As part=id doesn't add more data to the response and AFAIK using two order isn't supported by YouTube Data API v3.

duplicate player name should not be in the same array

I am making a matchmaking system where 2 players of the same level will be matched.
My target is how can I remove a duplicate player name in the same
array? After removing that duplicate entry, it should be stored on
another array(waiting list).
I have provided an example and a screenshot below. Any help will be appreciated. Thank you.
script for matching: (Matching the players with the same level)
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
script ajax: (Fetching data from db)
let ajaxResult = []; // the pushed data will be saved here
let save_method;
let table;
let base_url = "<?php echo base_url();?>";
let result = [];
var html = "";
$(document).ready(function() {
//datatables
table = $("#entry_list1").DataTable({
processing: false,
serverSide: true,
order: [],
searching: false,
paging: false,
info: false,
ajax: {
url: "<?php echo site_url('controller/fetch')?>",
type: "POST",
async: true,
dataType: "json",
success: function(data) {
result = combine(data.data2);
console.log(result)
var keys = Object.keys(result)
for (var i = 0; i < keys.length; i++) {
result[keys[i]].forEach(function(val) {
val.forEach(function(value, index) {
var entryIDs = index == 0 ? "entryIDM[]" : "entryIDW[]"
var players = index == 0 ? "playerM[]" : "playerW[]"
var levels = index == 0 ? "levelM[]" : "levelW[]"
html += `<input type="text" name="${entryIDs}" value="${value.entryID}">
<input type="text" name="${players}" value="${value.player}">
<input type="text" name="${levels}" value="${value.level}">
`
})
})
}
document.getElementById("result").innerHTML = html //add html to div
},
},
"columnDefs": [{
"targets": [0], //first column
"orderable": false, //set not orderable
},
{
"targets": [-1], //last column
"orderable": false, //set not orderable
},
],
});
});
I would suggest removing duplicate players before passing them to the combine function. This way you shouldn't see duplicate players matched.
I would create a removeDuplicates function to remove duplicate players from the player list.
In this example, we have 18 players, with each player duplicated once.
We run these through the removeDuplicates function, now we have 9 unique players.
We then pass the unique players to the combine function and get 3 groups of 2 matched players and 3 unmatched players.
const players = Array.from({ length: 18 }, (v,k) => ( { level: Math.floor(k / 6) + 1, player: `test-${Math.floor(k/2)+1}` }));
function removeDuplicates(players) {
return Object.values(players.reduce((acc, curr) => {
acc[curr.player] = acc[curr.player] || curr;
return acc;
}, {}))
}
const combine = (source) => {
return source.reduce((acc, curr) => {
if (acc[curr.level]) {
const levelArr = acc[curr.level];
const last = levelArr[levelArr.length - 1];
if (last.length === 2) {
levelArr.push([curr])
} else {
last.push(curr)
}
} else {
acc[curr.level] = [
[curr]
];
}
return acc;
}, {})
};
console.log("All players:", players);
const uniquePlayers = removeDuplicates(players);
console.log("Unique players:", uniquePlayers);
const matched = Object.values(combine(uniquePlayers)).flatMap(a => a.filter(x => x.length === 2));
const unmatched = Object.values(combine(uniquePlayers)).flatMap(a => a.filter(x => x.length === 1));
console.log("Matched players:", matched);
console.log("Unmatched players:", unmatched);

Taking the Average of a Data Set in Firebase Database

I'm in the process of designing an app but coming a bit unstuck with Javascript. So far I have a Firebase Realtime Database with the following structure.[!
What I'd like to do is for each time an area in green is added / updated, take a value(red) from that area in green get the average from all the values that are held within each green object and place it into a brown object at the bottom.
Would anyone have any idea on how to complete this using Javascript / Firebase functions?
JSON Export:
{
"5Rz8DpU34PeXAcnriD6vEiPu7jk2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
},
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"average" : 0
},
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"Fi43uP2LcbVLi2uFwUyCAp2uvSH2" : {
"rating1" : 5,
"rating2" : 5
},
"asdas" : {
"rating1" : 2
},
"average" : 0
},
"gov4hRpDgDVhyVgsQrYJnn1rfeW2" : {
"UiWK7RkdeCbUte8g7naB9qp42qu1" : {
"rating1" : 5
},
"average" : 0
}
}
The following Cloud Function code should do the trick:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.database();
exports.average = functions.database
.ref('/ratings/{blueId}/{greenId}')
.onWrite((change, context) => {
const blueId = context.params.blueId;
const blueRef = db.ref('ratings/' + blueId);
const blueAverageRef = db.ref('ratings/' + blueId + '/average');
let totalSum = 0;
let nbrOfElem = 0;
return blueRef
.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
if (childSnapshot.val().val) {
//console.log(childSnapshot.val());
totalSum += childSnapshot.val().val;
nbrOfElem++;
}
});
})
.then(() => {
//console.log('totalSum: ' + totalSum);
//console.log('nbrOfElem: ' + nbrOfElem);
return blueAverageRef.transaction(function(average) {
if (nbrOfElem > 0) {
return { val: totalSum / nbrOfElem };
} else {
return 0;
}
});
})
.catch(error => {
console.log(error);
});
});
Note that it uses a Transaction, see https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
The database shall be structured as:
-ratings
-blueNode1
-greenNode11
-val:2 // <- red node in your picture
-greenNode12
-val:10
-average // <- red node in your picture
-val:6
-blueNode2
-greenNode21
-val:5
-greenNode22
-val:3
-greenNode23
-val:1
-average
-val:5
It would be something like this:
exports.detectGreenChanges = functions.database.ref('/requests/{blueId}/{greenId}').onWrite((change, context) => {
const greenIdData = change.after.val();
const blueId = context.params.blueId;
const greenId = context.params.greenId;
// Do whatever you want with the data and set it to where ever you want
});
Take a look at this docs, it'll help you set the value back to where you want.
Good luck!

How to grab the previous and next id from an array

I'm trying to create next and previous buttons for my blog page. My blogs posts are stored within a table in my MySQL database. At the moment I'm getting the following result.
So I can get the current id and current title, but I'm not sure how to go about displaying the previous and next one on a page.
JavaScript code:
router.get('/posts/:permalinkSlug', async(req, res, next) => {
try {
var blogPostArray = []
var results = await _db.rawSql('SELECT id, permalink_slug FROM blog_posts')
blogPostArray.push(results)
const permalinkSlug = req.params.permalinkSlug
const post = await postTools.getPostByPermalinkSlug(permalinkSlug)
res.locals.current_id = post.id
console.log(res.locals.current_id)
console.log(permalinkSlug)
for (i = 0; i < blogPostArray.length; i++) {
console.log(blogPostArray[i])
}
if (post) {
res.render('post/post', {
post: post,
page: await _db.findOne('posts', {})
})
} else next()
} catch (err) {
next(err)
}
})
New code:
var results = await _db.rawSql('SELECT id FROM blog_posts')
console.log(results)
Result:
[
RowDataPacket { id: 12 },
RowDataPacket { id: 13 },
RowDataPacket { id: 14 },
RowDataPacket { id: 15 }
]
If res.locals.current_id is giving a value then following will do the trick.
Replace this code like this:
blogPostArray.push(JSON.parse(JSON.stringify(results)));
This is a bug as methioned here.
var blogPostArray = [{
id: 12,
permalink_slug: 'title1'
},
{
id: 13,
permalink_slug: 'title2'
},
{
id: 14,
permalink_slug: 'title3'
},
{
id: 15,
permalink_slug: 'title4'
}
];
var res = {
locals: {
current_id: 14
}
};
var index = blogPostArray.findIndex(x => Number(x.id) == Number(res.locals.current_id));
var next = getNext(index);
var prev = getPrev(index);
console.log(prev, next);
function getNext(sr) {
sr = Number(sr);
if (sr + 1 == blogPostArray.length) {
return {}
}
return blogPostArray[sr + 1];//if not working try using blogPostArray[0][sr + 1] or blogPostArray[1][sr + 1]
}
function getPrev(sr) {
sr = Number(sr);
if (sr - 1 == -1) {
return {}
}
return blogPostArray[sr - 1];//if not working try using blogPostArray[0][sr - 1] or blogPostArray[1][sr - 1]
}
inside the loop you can use the code as follows:
for (i = 0; i < blogPostArray.length; i++) {
console.log(getNext(i))
console.log(getPrev(i))
}

Trouble filtering data in firebase database

I'm trying to filter some data from firebase database in a cloud function.
That data looks like this :
"items": {
"id1": {
"status": {
"creation": timestampValue,
"status": "initialized"
},
"data" : data1
}
"id2": {
"status": {
"status": "loaded"
},
"data" : data2
},
"id2": {
"status": {
"creation": timestampValue,
"status": "loaded"
},
"data" : data
},
"id3": {
"status": {
"creation": timestampValue,
"status": "ended"
},
"data" : data3
}
}
I want to use a filter based on the creation field.
The field is not always present.
My code is inspired by this one :
https://github.com/firebase/functions-samples/blob/master/delete-old-child-nodes/functions/index.js
here's the code I wrote :
const CUT_OFF_TIME = 24 * 60 * 60 * 1000; // 24 Hours in milliseconds.
exports.cleanAfter24h = functions.database.ref('/items/{itemId}').onUpdate((change, event) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const cutoff = now - CUT_OFF_TIME;
const oldItemsQuery = ref.orderByChild('creation').endAt(cutoff);
return oldItemsQuery.once('value').then((snapshot) => {
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
let childData = child.val();
if (childData.status.creation) {
let elapsed = childData.status.creation - cutoff;
if (elapsed <= 0) {
updates[child.key] = null;
}
} else {
console.log(child.key + ' does not have a creation date');
}
});
// execute all updates in one go and return the result to end the function
return ref.update(updates);
});
});
When the code is run, all items are retrieved, even those that have a timestamp smaller than the cutoff and those that do not have a creation field.
Any suggestions how to fix that?
I tried removing the items that have no creation field by adding a startAt("") before the endAt as suggested here :
Firebase, query with "a child exists" as a condition?
const oldItemsQuery = ref.orderByChild('creation')startAt("").endAt(cutoff);
Doing this I have no results in the query response.
Change this:
const oldItemsQuery = ref.orderByChild('creation').endAt(cutoff);
into this:
const oldItemsQuery = ref.orderByChild('creation').equalTo(cutoff);
equalTo
Creates a Query that includes children that match the specified value.
Using startAt(), endAt(), and equalTo() allows us to choose arbitrary starting and ending points for our queries.

Categories

Resources