Implementing Infinite Scrolling with Firebase? - javascript

var self = this;
var firebaseRef = new Firebase(baseUrl + '/sparks');
firebaseRef.limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
My code currently loads the last 5 posts and will load any new posts. However, I'd also like to be able to load older posts as well. I have a button that when clicked will call a function (that I'm unsure of how to implement) that loads older posts. How do I retrieve these older posts?
(The arrow just signifies that I want to retrieve posts starting from the bottom and working my way up to the top)

You need to think a bit backwards to do this. When you get the results for your query for the first page, remember the first item in the results:
firebaseRef.endAt().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
self.addChild(childSnapshot); // adds post to a <div>
});
While you cannot access child items by index with Firebase, you can store the key of an item and use that to start a next query.
var firstKnownKey;
firebaseRef.orderByKey().limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Now you have a variable firstKnownKey that has the first key you've ever seen. To get the previous batch of children, you pass that value in to endAt() when you fire your next query:
firebaseRef.orderByKey().endAt(firstKnownKey).limitToLast(5).on('child_added', function(childSnapshot, prevChildKey) {
if (!firstKnownKey) {
firstKnownKey = childSnapshot.key;
}
self.addChild(childSnapshot); // adds post to a <div>
});
Answers to similar questions of the past few days:
Can I get the nth item of a firebase "query"?
Firebase results range using startAt and endAt

Since endAt() is inclusive, the last item gets repeated every time I do the infinite scroll, so I did a little modification to frank van puffen 's answer.
I initiate a list childrenVal to store all the values, another list childrenKey to store all the keys and a var firstKnownKey, as frank van puffen sugests.
var childrenVal=[];
var childrenKey=[];
var firstKnownKey = "";
For the first time you make the query, you get the last 5 elements:
getFirst(){
firebaseRef.orderByKey().limitToLast(5).once('value')
.then((snap)=>{
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length-1];
});
}
In your next query, you won't want your firstKnownKey to get repeated, so I did the following function:
exclude(key){
return key.substring(0, key.length - 1) + String.fromCharCode(key.charCodeAt(key.length - 1) - 1)
}
and for the query itself, the following function:
getNext() {
firebaseRef.orderByKey().endAt(exclude(firstKnownKey)).limitToLast(5).once('value')
.then((snap) => {
snap.forEach(childSnap => {
childrenVal.unshift(childSnap.val());
childrenKey.unshift(childSnap.key);
});
firstKnownKey = childrenKey[childrenKey.length - 1];
});
}

Related

Pass value function

I have an application that populates the array continuously until it is stopped and it does two things:
If you click Stop button, it writes values in the DB.
Every 1000sec it checks the size of array and if it is > 2000 write the values in the db.
Now I have a problem:
I use the first element of the array to do some calculations, before writing the data to the db.
So if the array exceeds the size of 2000, it performs a splice and passes the array to another page, taking the first element as the basis for the calculation that will be performed on the next page.
At this point, if the user clicks the stop key as the basis for the operations, the last element of the array previously passed must be used.
For example:
array = [0, 20, 40, ......, 2000,..]
array.length > 2000
arrayBase = 0 // I use it for operations.
// Do a splice
array = [2020, 2040, ...... ]
array.length < 2000
//User click stop Button
//I should pass as arrayBase the last value of array (2000)
I hope at least I have explained myself by example.
This is my code:
//this function populate array until I click stop
populateArray(){
this.arrayTimestamp.push(`${buf.readInt16LE(0)}`);
this.firstElementTimestamp = this.arrayTimestamp[0];
//.....
}
//This function check the size and write in the DB if > 2000
checkSize(){
that.timeout = setInterval(function() {
if( (that.arrayTimestamp.length > 2000 ){
that.arrayTimestampCopy = that.arrayTimestamp.splice( 0, 2000 );
//this is a function in other page where I do some operations
scrittura.write({
counterTimestamp: that.firstElementTimestamp,
//...
})
.then(response => {
//...
})
// I have tried something like this:
that.firstElementTimestamp = that.arrayTimestamp[2000] //obviously it is undefined as the array with the splice has been emptied
}, 1000);
}
//this is the function when the Stop button is clicked.
stopConnection(){
Actions.Activity({
counterTimestamp: this.firstElementTimestamp,
//...
})
}
So my goal is to find a way to always use the same base in the calculations, without it being updated.
How can I do?
I think you should use array reduce or Promise All (based on which one you need, parallel or not)
arr.reduce((prom, item) => {
return prom.then(() => {
return scrittura.write(item).then((result) => ... );
});
}, Promise.resolve()).then(function() {
// all done here
}).catch(function(err) {
// error here
});
or use Promise All for parallel
You can see another example here
Synchronous loop in Promise all

Return most recent time in series of creationTimes

Not entirely sure how I word the question but my problem is Im doing an api call that returns a bunch of messages that have a creation time, now what I want to do is only return the latest creationTime for the messages with the same date so say If I have 30 messages on the 15/03/2018 I want to grab the latest time, and discard the rest.. and do that for each set of messages with the same date
So what Ive done so far is..
using lodash I have gotten all the messages, filtered out all the ones with a certain type, and I have ordered them by creationTime so the latest being at the top and going down.. now my question is how can I then make an array of the latest times for each date??
this._activityServiceProxy.getAllItems(start, end).subscribe(result => {
// this.messages = result;
// console.log(result);
let loginUnfiltered = _.filter(result, {'title': 'LOGIN'});
let loginFiltered = _.orderBy(loginUnfiltered, {'creationTime': 'desc'});
console.log(loginFiltered);
});
any help would be appreciated!
Thanks
Use .map(...) to get at array of only the latest creationTime:
this._activityServiceProxy.getAllItems(start, end).subscribe(result => {
// this.messages = result;
// console.log(result);
let loginUnfiltered = _.filter(result, {'title': 'LOGIN'});
let loginFiltered = _.orderBy(loginUnfiltered, {'creationTime': 'desc'});
const creationTimes = loginFiltered.map(l => l.creationTime);
console.log(creationTimes);
const latestTime = creationTimes[0];
console.log(latestTime);
});
You can use Underscore's groupBy function to achieve this:
const groups = _.groupBy(loginFiltered, (login) => {
const asDate = new Date(login.creationTime);
asDate.setHours(0, 0, 0, 0);
return asDate;
});
Object.keys(groups).forEach((key) => {
console.log(groups[key][0]);
});
You group by the creationDate property but remove the time component so all days get grouped together. You then loop through the result and just take the first entry per day.
Note that this assumes your creationTime property is a string, as it came from an API. If it's already a date, you don't need the new Date line.

How to remove any firebase data using JavaScript?

I am building a web application using JavaScript and firebase.
everytime I click on the "remove" paragraph tag, I can only remove the most recent added item, but cannot remove the rest.
Example: if I have 9 items, and I just added a 10th item, I can only remove the 10th item, but not the other 9 items. I basically can only remove items in a backwards order, as opposed to being able to remove items in any order of my choice.
Here is my code:
function displayFav(){
const dbRef = firebase.database().ref();
dbRef.on("value", (firebaseData) => {
// empty out element before adding new updated firebase data so there are no repeated data
document.getElementById("displayUsername").innerHTML = "";
let accounts = [];
const accountData = firebaseData.val();
for (let itemKey in accountData) {
accountData[itemKey].key = itemKey;
accounts.push(accountData[itemKey])
const key = accountData[itemKey]["key"];
const password = accountData[itemKey]["password"];
let user = accountData[itemKey]["username"];
// code where I try to render results from page
document.getElementById('displayUsername').innerHTML += `
<li> Username: ${user} Password: ${password}</li>
<p id=${key}>Remove</p>
`;
// code where I try to remove the item
document.getElementById(key).addEventListener("click", function(){
removeItem(key)
})
}
});
}
This is my function to remove the firebase data:
function removeItem(itemToRemove){
const dbRef = firebase.database().ref(`${itemToRemove}`);
dbRef.remove();
};
What can I change or add to my code that will allow me to remove items in any order I want, instead of letting me delete only the most recent items?
Not trying to be rude, this is just a tip: Please indent/format your code well so that people can understand it without having to format it themselves. Usually when people see large code that's not well formatted, it throws them off (sometimes including me), and therefore they wouldn't want to continue to read or help you with the question.
Suppose you have a table called posts, and the structure looks like this:
that is:
{
<postId>: {
date: 1518925839059,
message: 'some message'
},
<postId>: {
date: 151892583967,
message: 'some message'
},
...
...
}
Suppose you want to delete the extra property of the first post, as circled in the picture, you must know the full path of the data:
firebase.database().ref('posts/-L5b1EZ1L_FycluzTIn8/extra').remove();
If you want to delete everything in post 1:
firebase.database().ref('posts/-L5b1EZ1L_FycluzTIn8').remove();
Or, if you want to delete every single post:
firebase.database().ref('posts').remove();

Proper way to manage paging and search/filter on a REST API made with Node and Express

I am playing around with Node and Express. My current problem is not "how to do things", as I have my paging and filtering/searching working in my "mini API" that I have made from scratch and that I am playing with. My question is more about "good practices" and "proper way" of doing things.
I will put some snippets of code below, which I am sure will bring some critics. The API is memory based, no database involved. I have an array of hard-coded users that I am pulling data from and pushing data in.
Below is my code (as you can see, I have also implemented basic authentication using passport):
//This array contains all my user data...
var users = [
{
"id": "1",
"firstName": "john",
"lastName": "doe"
}
];
//This is the route I have configured in order to retrieve all users.
//I am retrieving the users with the getUsers() function and then returning it.
//in the response object.
router.get('/users', passport.authenticate('basic', { session: false }),
function(req, res, next) {
var result = users.getUsers(req);
res.status(200).json({ users: result });
});
//This method will get the page and items parameters and will try to parse
//them. After that, it will call the search function that will filter the data
//Finally, I am passing the result array, page param and items param to the
//sliceUsers() function that will take care of slicing the result array depending
//on the values of page and items.
exports.getUsers = function(req) {
console.log(req.query);
var page = req.query.page;
items = req.query.items;
page = page !== 'undefined' ? parseInt(page, 10) : undefined;
items = items !== 'undefined' ? parseInt(items, 10) : undefined;
//The search method will filter the data
var searchResults = exports.search(req.query);
//Then, I call sliceUsers(), passing the filtered data, page and items parameters
return exports.sliceUsers(searchResults , page, items);
}
//This method will slice the array to return the page and # of items specified
//The "data" array that is passed as the first parameters is the array that contains
//the data that have already been filtered.
exports.sliceUsers= function(data, page, items) {
page = (page < 1 ? 1 : page) || 1;
items = (items < 1 ? 5 : items) || 5;
console.log('page', page, 'items', items);
var indexStart, indexEnd;
indexStart = (page - 1) * items;
indexEnd = indexStart + items;
return data.slice(indexStart, indexEnd);
};
//Those 2 methods take care of filtering
exports.search = function(query) {
return users.filter(search(query));
}
function search(query) {
console.log('search function');
return function(element) {
for(var i in query) {
//Please note here how I am checking the the parameter I am currently
//checking is NOT 'page' nor 'items'
if(query[i] != element[i] && i !== 'page' && i !== 'items') {
return false;
}
}
return true;
}
}
A few questions arise here:
Is the way I am dealing with filter/search and THEN, dealing with paging the right way?
In the search() function, when I am looping over req.query, I know that the way I check if the current param is different than 'page' or 'items' is not very efficient, but I don't know I could do that differently.
My goal here is to learn node and express and get better at javascript, what would you advice me to do next in order to pursue that goal? Any resources greatly appreciated, as the only stuff I found on APIs are basic operations that don't really deal with search/filtering. When I do find those, it's never in addition to paging for example. I have never found a complete example.
I have heard that underscore could help me do the filtering, but once again, did not really find any good example, any snippets somewhere?
Any critic GREATLY appreciated.
P.S: I apologize in advance for any grammatical error in this question.
There's a standard called OData for thing like filtering, searching, selecting and of course REST. There are few options for using it at the moment. For node backend part it's node-odata. For more, see here: http://www.odata.org/libraries/

Selecting n elements belonging to a user in a MapReduce for CouchDB

Please bear with me, I'm pretty new to the whole CouchDb stuff.
The db looks like:
** item ** count ** user **
A 20 bob
B 30 bob
C 10 bob
D 15 john
I want to write a MapReduce that selects all the items belonging to bob and only return the top 2, sorted. so it should return [{item:"B",count:"30"},{item:"A",count:"20}]
I'm not sure how this can be done? Seems like I have to emit(doc.item, doc.count), but how do I know if the user owns the doc? How do I run another MapReduce to select the top elements?
One solution would be to write your view to use a complex key, such as:
function (doc) {
emit([doc.user, doc.count], doc.item);
}
If you add descending=true to your query string, that would give you a view result like:
{"total_rows":4,"offset":0,"rows":[
{"id":"53f359b7cd360da296dd9aab3d0029bd","key":["john",15],"value":"D"},
{"id":"53f359b7cd360da296dd9aab3d001a0e","key":["bob",30],"value":"B"},
{"id":"53f359b7cd360da296dd9aab3d000fec","key":["bob",20],"value":"A"},
{"id":"53f359b7cd360da296dd9aab3d002668","key":["bob",10],"value":"C"}
]}
It's sorted already by user, then count. (with the item type as the value)
Then you can use a _list function to do the rest. The code below basically loops through the view, and returns the top 2 results for each user. If you specify user=bob in the query string, you'll only get the results for bob.
function (head, req) {
// specify that we're sending JSON as our response
provides('json', function () {
var results = [],
result, user, count, row;
while (row = getRow()) {
// if the user doesn't match the last iteration, reset our counter
if (user != row.key[0]) {
user = row.key[0];
count = 0;
}
// we only need the top 2
if (count++ >= 2) {
continue;
}
// start building a result object
result = {
item: row.value,
count: row.key[1]
};
// if we provide user=?
if (req.query.user) {
// check to see if it matches the current user
if (req.query.user === user) {
// if so, add it to the results
results.push(result);
}
// by default, we'll return the top 2 for every user
} else {
// add the user key to the result object
result.user = row.key[0];
// and add it to the result set
results.push(result);
}
}
// send outside the loop, since it needs to be sent as valid JSON
send(JSON.stringify(results));
});
}
If you put user and count in the key of the view, you can use startkey=["bob",""] and endkey=["bob"] to select the user, and descending=true and limit=2 to get the top two items.
I tried the following map function:
function(doc) {
if(doc.user && doc.count && doc.item) {
emit([doc.user, doc.count], doc);
}
}
with the query string ?startkey=["bob",""]&endkey=["bob"]&descending=true&limit=2 it returns:
{"total_rows":4,"offset":1,"rows":[
{"id":"item_B_bob","key":["bob",30],"value":{"_id":"item_B_bob","_rev":"1-b23bd22fb719c7d59b045bce0932df8c","item":"B","count":30,"user":"bob"}},
{"id":"item_A_bob","key":["bob",20],"value":{"_id":"item_A_bob","_rev":"2-515bca46eab383cfeaaa2a101d180291","item":"A","count":20,"user":"bob"}}
]}
Please note:
startkey and endkey are reversed because descending=true.
["bob",""] is a key greater then ["bob", ANY NUMBER] as specified in view collation.

Categories

Resources