I'm using localstorage to track domain visits and their timestamp.
Here's how I'm setting it:
localStorage.setItem(`${domain[0]}`, JSON.stringify(Date.now()));
What code would I use to delete all rows with a timestamp that's older than 30 days so users don't end up with too much extra data that probably won't be used?
I've seen a lot of examples about deleting individual rows based on the key. But I can't find anything related to bulk deleting based upon the values.
Enumerate all key/value pairs explicitly:
const cutOff = Date.now() - 30 * 24 * 3600 * 1000;
Object.entries(localStorage).forEach(([k, v]) => {
const time = JSON.parse(v);
if (time < cutOff) {
delete localStorage[k];
}
});
Related
Is it possible to somehow do it with startAt() and endAt()?
db.ref('/items')
.orderByChild('createdAt')
.endAt(new Date().getTime() + 180 * 60 * 1000) // Updated
.on('value', () => {/* ... */})
Edit:
What I'm trying to achieve is getting all the items that are currently in the database and listening for the newly added items from the last n hours.
Using a static number like endAt(1669743560642), lets say user is there for a few minutes, the number 1669743560642 will not be updated to the new time that has gone by, it's important to somehow that value gets somehow updated in real time.
So from (user's/server time) up to n hours.
Exports from Realtime Database:
{
"items": {
"-NI3TAUjGNRlAUQ3Yc8K": {
"createdBy": "5gQ5cfGN15WCSa9RZVvAYXhKTyc2",
"createdAt": 1669743560642
},
"-NI3i-jMEyx_inQCaALr": {
"createdBy": "lc4BcHOls7cBuTVmbNo9LmtDdlf2",
"createdAt": 1669747710748
},
"-NI8-Jb_Qvd8gA3nG9cv": {
"createdBy": "5gQ5cfGN15WCSa9RZVvAYXhKTyc2",
"createdAt": 1669819620439
}
}
}
It sounds like you want to get nodes with a createdAt value of N hours ago or newer. For that I see two problems in your code:
Since Firebase Realtime Database always orders the value in ascending order, that's a startAt operation, not an endAt operation.
Since you want N hours in the past, you need to subtract from the current timestamp, not add to it.
So something like this:
db.ref('/items')
.orderByChild('createdAt')
.startAt(Date.now() - 8 * 60 * 60 * 1000) // 8 hours ago
.on('value', () => {/* ... */})
There is no way to make that startAt value dynamic though. Once you create a query, it is immutable. So if you want the window of data to move, you'll have create another query for that.
In most cases where I need this, I actually end up post-filtering the nodes in my application code. So I create the query with the startAt value as above, and then add additional logic to expire items locally too. When you have a limit on the query too, that won't work or be harder, so I try to avoid needing that combination.
I am trying to sort entries in my firebase by the most recent entry showing up as the first entry. I keep running into the issue that the most recent entry shows up last.
I am trying to sort by time as well as this is how it is set up in my databases:
I am also doing this in my code
completed.child(user.uid).orderByValue("time")
I was wondering where I was going wrong
This is how it shows up in my app:
There are quite a few problems with your code, so I'll try to address them in turn.
To order by a child property, use orderByChild
You're calling orderByValue("time"), but if you check the documentation you'll note that orderByValue() takes no parameters. If you want to order on the value of a child property, you should call orderByChild("time").
Use a time format that allows sorting them
Your current time format is not suited for sorting it. The problem is that your lexicographical sort order doesn't match with the chronological sort order you want. More simply put, this is the sort order you'll get (but not want):
"6:26AM"
"6:26PM"
"6:27AM"
To get the result you want, use a string format that allows the sorting. For example, use 24 hour/military notation with 0-padding:
"06:26"
"06:27"
"18:26"
You could also store a numerical value, for example: the number of minutes since midnight:
(( 6 * 60) + 26) = 386
(( 6 * 60) + 27) = 387
((18 * 60) + 26) = 1106
Firebase always returns in ascending order
Firebase always returns results in ascending order. I'll recommend reading a few relevant questions, but you have two options here:
Reverse the results client-side. This is often the simplest, as you're usually dealing with relatively a small result set.
Store an inverted value in the database for sorting.
This last one is typically easiest if you use a numerical value, like the number of minutes since midnight that we used above. If we store -1 * this value, we can then sort on that to get the results in reverse.
-1 * (( 6 * 60) + 26) = -386
-1 * (( 6 * 60) + 27) = -387
-1 * ((18 * 60) + 26) = -1106
I have a database and I want to sort all posts by their score (an integer), I thought it would be super easy because I'm currently doing orderByChild('created') and it works perfectly, but for some reason orderByChild('score') doesn't work.
Here is my database structure:
score is an integer that can be negative, positive or 0.
The JavaScript I'm using:
this.subscription = this.database.database.ref('/posts/'+this.userData.location)
.orderByChild('score')
.startAt(new Date().getTime() - (24 * 3600 * 1000)) //last 24 hrs
.limitToFirst(10);
this.subscription.on('child_added', (snapshot) => {
this.postFeed.push(snapshot.val())
console.log(this.postFeed)
});
The weird thing is, the console.log isn't even firing so it's not even getting to that stage for some reason, but if I change .orderByChild('score') to .orderByChild('created') it works exactly as expected...
Any ideas what I'm doing wrong? Thank you!
I believe it might be something to do with this line
.startAt(new Date().getTime() - (24 * 3600 * 1000))
This will create a startAt query which would currently be 1519944842592
This means that it's trying to start at that specific number. So it's not looking for -1, it's starting the query at 1519944842592.
You would need to incorporate a createdAt object within your schema and query that first.
The reason your .orderByChild('created') works is because it's a date object which is before the startAt query so that's why it works correctly :)
I'm writing a system to handle messaging.
One of the options of a user is DND (Do Not Disturb) where a user can define a time range where he should not be alerted (such as 22:00 - 08:00).
When I perform a query on users, I wish to filter out the users which currently are on DND.
In each user's document, I define a field to hold the time in seconds:
{dnd: {start: 22*3600, end: 8*3600}}
I'm not sure how to perform the query itself (the following sample will not work due to overlapping midnight)
var current; // holds current time in seconds
db.user.find({$or:[{"dnd.start":{$gt:current}},{"dnd.end":{$lt:current}}]});
A different approach to this might be to store a "blacklist" of times as an array and query that the current time is not one of those elements. It depends on how granular you need this to be. Hours or half hours may be reasonable, as far out as per minute is not too bad.
So to generate the "blacklist" per minute you could do, as JavaScript code but the principles are easy to convert:
var start = 22 * 60;
var end = 8 * 60;
var dnd = [start];
do {
start++;
if ( start == 1440 )
start = 0;
dnd.push( start );
} while ( start != end );
db.user.update({},{ "$set": { "dnd": dnd } },true)
Essentially setting the "blacklist" against the user record.
Then when you query for say 1:30 in the morning, or the 90th minute you would do:
db.user.find({ "dnd": { "$ne": 90 })
And since the "blacklist" contains that value then you do not return the user records where that value would be in the list. But for a time beyond the "dnd" range it would return:
db.user.find({ "dnd": { "$ne": 481 })
Clearly you would generally want to exclude the array element when calling up details for the user record, but that is a simple matter of projection.
I need to store the user preferences in a cookie.
I have three multiple select drop downs & date.
User is allowed to select all the values in dropdowns.
If I store all the unique (8 characters) values in a cookie, then it will get overloaded. I need a mechanism to do some mapping so that minimal value is stored in cookie.
My requirement matches this question, but only difference is that I have large data set (100 items per select column).
Two possibilities:
A) Make your values numeric and powers of 2 (1,2,4,8,16,etc) and store a single value in the cookie as a bitmap. (Or in the server-side code, map the distinct values to powers of 2.)
B) Store the values in the cookie as a delimited list.
Updated: 6:40PM
Seeing that you have the possibility of a large number of selections, neither of my original solutions is going to work. To deal with that large an amount of data, I think you're going to need to create a "session id" of some sort, save the actual settings in a database or other persistent storage, and store the session id in the cookie.
(You'll of course want to make the cookie only persist for the duration of the browser session and delete the stored settings after you can be reasonably certain the browser session has ended -- perhaps a week or so.)
That's a good question. Given your requirements, I don't see much choices left on how to proceed. As ThatBlairGuy suggested, a bit map seems to be the way to go. Your 300 choices translate to 300 bits. Now I personally would stick to use only 6 bits per byte, to make a quick and easy translation to base64, for storage purpose. 300 / 6 = 50 characters for the values coming from the drop-down.
The value of any preference has to be hardcoded for good though, you can't remove or add any in the middle: Any new preference will have to be added at the end of the bit map, or use a bit that is no longer used somewhere in the map.
Edit: Given the comments re. bit map, I will flesh out what I had in mind for the implementation. Assuming a bit, each corresponding to a off-on switch of a particular option (an entry in the drop-box):
this.bitmap = [];
this.customBase64code="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
...
// index=index of bit within bit map, on=state of the bit
this.setOption=function(index,on) {
var islot = Math.floor(index / 6);
var ibit = index % 6;
if ( this.bitmap[islot] === undefined ) {
while ( this.bitmap.length <= islot ) {
this.bitmap.push(0);
}
}
if (on) {
this.bitmap[islot] |= (1 << ibit);
}
else {
this.bitmap[islot] &= ~(1 << ibit);
}
};
this.getOption=function(index) {
var islot = Math.floor(index / 6);
if ( this.bitmap[islot] === undefined ) {
return 0;
}
return this.bitmap[islot] & (1 << (index % 6));
};
this.getStringFromOptions=function() {
var bitmap64 = [];
var encoder64 = customBase64code.split('');
var n = this.bitmap.length;
for ( var i = 0; i < n; i++ ) {
bitmap64.push(encoder64[this.bitmap[i]]);
}
return bitmap64.join('');
};
this.getOptionsFromString=function(s) {
var bitmap64 = s.split('');
var decoder64 = this.customBase64code;
var n = bitmap64.length;
for ( var i = 0; i < n; i++ ) {
this.bitmap.push(decoder64.indexOf(bitmap64[i]));
}
};
Disclaimer: I didn't test any of the above.
You can store the indexes of the selected items. For example, if the user has selected item 1 and 3 from list A, item 4 from list B and the first 10 items of list C, you'd get something like this:
A[0,2],B[3],C[0-9]
You'd have to write some simple parsing code to read out the values though.
In the worst case scenario, when the user has selected all the odd or even items, you'll need about 150 characters for a list containing a total of 100 items. If the user has selected all 100 items, you'll only need A[0-99].