update firebase entries on an interval - javascript

I want to be able to update certain values in a firebase entry on a minute interval. I am not sure how to do this but have tried the following:
var interval = setInterval(function(){
database.ref().on('value',function(snapshot){
snapshot.forEach(function(childSnapshot) {
var item = childSnapshot.val();
item.key = childSnapshot.key;
database.ref(item.key).update({ item.trainTimeLeft: moment(item.trainFrequency).subtract(moment(moment(Date.now())).diff(item.trainTimeStamp,"minutes"),"minutes") });
});
});
},60000);
I am using moment.js for time differences (I am trying to make an updating train schedule). All of the entries are on the main branch of firebase.

Use setInterval() to trigger your function every X mile seconds you want.
Use firebase.database.ref() to access your database node you want to update.
Use .update() to update the database node value.
Use firebase.database.ServerValue.TIMESTAMP to get server time. Using client timestamp is not safe due different timezones.
With this you can write your function. It should look something like this
const serverTime = firebase.database.ServerValue.TIMESTAMP;
setInterval(() => {
database.ref('myNode').once('value',(snapshot) => {
const updatedDate = snapshot.val() && snapshot.val().storageTime ? snapshot.val().storageTime - serverTime : serverTime;
snapshot.ref().update({ storageTime: updatedDate })
});
}, 5000);
Consider this code just as an example, not a copy/paste solution.
Hope it helps.

There is no sense in polling the database, rather, tell it you want to listen for value (change) events.
From Read and Write Data on the Web
Important: The value event is called every time data is changed at the
specified database reference, including changes to children. To limit
the size of your snapshots, attach only at the lowest level needed for
watching changes. For example, attaching a listener to the root of
your database is not recommended. The following example demonstrates a
social blogging application retrieving the star count of a post from
the database:
var starCountRef = firebase.database().ref('posts/' + postId + '/starCount');
starCountRef.on('value', function(snapshot) {
updateStarCount(postElement, snapshot.val());
});

Related

Firestore getCountFromServer not real time updating?

I am using the getCountFromServer() for my React App to display the number of docs in a collection.
However, when the user deletes a doc on the front end or creates one, the count is not updated in real-time.
Is it possible to fix this or must the user always reload the page to see the latest data?
The documentation says, emphasis mine:
count() aggregation queries are currently only supported via direct server response. Queries are served only by the Cloud Firestore backend, skipping the local cache and any buffered updates. This behavior is identical to operations performed inside Cloud Firestore transactions. You cannot currently use count() queries with real-time listeners and offline queries.
So, no, you can't get realtime count updates. You will have to make the request again to get an update. Whether or not you require a page refresh is up to you.
As #DougStevenson mentioned in his comment, there is no way you can get real-time updates when you're using count(). It's true you can call count() every time it is needed, but there is however a workaround.
To solve this, you should consider storing a counter in a document at a known path, and then increment its value every time a new document is added or decrement the value every time a document is deleted. To get real-time updates, simply attach a persistent listener on that document and you'll be up to date with the number of documents in the collection.
Besides that, if your collection grows in size (> 10,000,000 documents), then the above solution might be the only solution you have. For that, please check #FrankvanPuffelen's answer from the following post:
How fast is counting documents in Cloud Firestore?
If you are using getCountFromServer() to load the initial count and then want to decrement it for all active users when a document is deleted, you can use a listener on the same collection and update the state on frontend like this:
import { collection, query, where, onSnapshot } from "firebase/firestore";
// initial state loaded by getCountFromServer()
const [count, setCount] = useState(10);
const q = query(collection(db, "collectionName"));
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docChanges().forEach((change) => {
if (change.type === "removed") {
console.log("Removed document: ", change.doc.data());
// TODO: Decrement the count in state
// e.g. setCount(count - 1)
}
});
});
This way you won't have to calculate the count again or maintain the count anywhere.

How to get a Firestore document ID based on its content?

I am trying to get a way to delete firestore document by a click event.
here is what i have so far. x is the document id i get from firestore. But how to get the ID change dynamically based on the content show in the page? SO that user can delete the content they wanted.
var deleteContent = document.getElementById('delete');
deleteContent.addEventListener('click', function(e) {
// get current document ID
x = 'poOjcQce2iiKzp2FaFVA'
var getId = db.collection("checkin").doc(x);
getId.delete().then(function() {
console.log(" successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
Thanks
Firebase Cloud Firestore allows you to perform simple and compounded queries. Essentially, you can provide a condition in the db.collection(<COLLECTION>).where() clause which will filter all documents that match that certain condition.
For illustrative purposes, suppose documents in the a collection follow the following structure: { id, '123', name: 'test', color: 'red' }. If you want to get all documents with the color red, you can simply call db.collection('a').where('color', '==', 'red'), which you can then iterate into to process each document that matches that condition.
Moreover, in your specific case, suppose your document structure is { id, content }you can try something like this:
var deleteContent = document.getElementById('delete');
deleteContent.addEventListener('click', function(e) {
// get current document ID
let docToDelete = db.collection("checkin").where('content', '==', 'CONTENT_GOES_HERE')
x = docToDelete.id;
var getId = db.collection("checkin").doc(x);
getId.delete().then(function() {
console.log(" successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
However, a much simpler way would be to store the document id somewhere within your application whenever a specific document is read, local storage or session storage for instance. As such, you can refer to the document id whenever you need. For example, suppose that you fetch all of a user's documents in an onLoad event handler --- suppose that you have HTML elements with the id 'content1', 'content2', 'content3', etc. and that it is where the contents of your documents are shown. You can try something like this:
let userId = 123;
[...].onload = () => {
// Get all of the documents belonging to this user
let i = 0;
let docs = [];
let documents = db.collection('posts').where('userId', '==', userId);
documents.forEach(doc => {
document.getElementById(`content${i}`).innerHTML = JSON.stringify(doc.data()); // Just stringifying to be certain
/**
* Extra step to add the document id in the sessionStorage so that we can refer to it later
*/
htmlId = 'content' + i;
sessionStorage.setItem(htmlID, doc.id );
})
}
In doing so, you can simply refer to the document id like such sessionStorage.getItem('content2').
Note that in any of the examples I listed, there is heavy preparation that needs to be done in order for this to be as smooth sailing as possible. However, wouldn't you agree that a programmer merely is a person that spends hours on things that takes minutes for the sake of making it easier in subsequent uses? :)

Check and Increment the Version Number of an IndexedDB.

I have an IndexedDB that is storing a large amount of dynamic data. (The static data is already cached by a Service Worker)
My problem is as this data is dynamic, I need the IndexedDB to be cleared and it to be restored each time the application is opened. For this, I need the version number to be incremented so that the onupgradeneeded event is fired. I can't think of any logical way to do this, and even using the following call in the onupgradeneeded event I get an undefined answer.
e.target.result.oldversion
My IndexedDB code is as follows, with the parameteres being:
Key - The name of the JSON object to store in the database.
Value - The JSON object itself.
function dbInit(key, value) {
// Open (or create) the database
var open = indexedDB.open("MyDatabase", 1);
console.log(value);
// Create the schema
open.onupgradeneeded = function(e) {
console.log("Old Version: " + e.target.result.oldversion); //Undefined
console.log("New Version: " + e.target.result.newversion); //Undefined
var db = open.result;
var store = db.createObjectStore("Inspections", {keyPath: "id", autoIncrement: true});
var index = store.createIndex(key, key);
};
open.onsuccess = function() {
// Start a new transaction
var db = open.result;
var tx = db.transaction("Inspections", "readwrite");
var store = tx.objectStore("Inspections");
var index = store.index(key);
store.add(value);
// Close the db when the transaction is done
tx.oncomplete = function() {
db.close();
};
}
}
As this method is called several times for several 'Key' objects, I will need to work out a way to increment this Version number once per opening of the page, and then move the 'add' call to outside of the onupgradeneeded method - but for the moment the priority is making sure it runs through once - incrementing the version number, firing the onupgradeneeded, deleting the current data, storing the new data.
Thanks in advance!
The oldVersion and newVersion properties (note the capitalization) are on the IDBVersionChangeEvent, not on the IDBDatabase. In other words, this:
console.log("Old Version: " + e.target.result.oldversion); //Undefined
console.log("New Version: " + e.target.result.newversion); //Undefined
should be:
console.log("Old Version: " + e.oldVersion);
console.log("New Version: " + e.newVersion);
Now that said... you're using the schema versioning in a somewhat atypical way. If you really want to start with a fresh database each time the page is opened, just delete before opening:
indexedDB.deleteDatabase("MyDatabase");
var open = indexedDB.open("MyDatabase", 1);
// An open/delete requests are always processed in the order they
// are made, so the open will wait for the delete to run.
Note that the queued operations (delete and open) would then be blocked if another tab was holding an open connection and didn't respond to a versionchange event sent out in response to the delete request. Maybe that's a good thing in your case - it would prevent two tabs from partying on the database simultaneously.
A more typical usage pattern would be to only change the version when the web app is upgraded and the database schema is different. If you did need to wipe the data across sessions you'd do that on open, rather than on upgrade, and use things like clear() on the object store. But now we're getting into the design of your app, which it sounds like you've got a good handle on.

How to only get new data without existing data from a Firebase?

I have a node in Firebase getting continually updated with information from a logfile. The node is lines/ and each child of lines/ is from a post() so it has a unique ID.
When a client first loads, I want to be able to grab the last X number of entries. I expect I'll do this with once(). From then on, however, I want to use an on() with child_added so that I get all new data. However, child_added gets all data stored in the Firebase and, after the initial setup, only want the new stuff.
I see that I can add a limitToLast() on the on(), but, if I say limitToLast(1) and a flood of entries come in, will my app still get all the new entries? Is there some other way to do this?
You need to include a timestamp property and run a query.
// Get the current timestamp
var now = new Date().getTime();
// Create a query that orders by the timestamp
var query = ref.orderByChild('timestamp').startAt(now);
// Listen for the new children added from that point in time
query.on('child_added', function (snap) {
console.log(snap.val()
});
// When you add this new item it will fire off the query above
ref.push({
title: "hello",
timestamp: Firebase.ServerValue.TIMESTAMP
});
The Firebase SDK has methods for ordering, orderByChild() and methods for creating a range startAt(). When you combine the two you can limit what comes back from Firebase.
I think there is a problem in #David East's solution. He is using the local timestamp which may cause problem if the time is not accurate in client device. Here is my suggested solution (iOS Swift):
Using observeSingleEvent to get the complete data set
Then returned it in reversed order by reversed()
Get the last timestamp by for example data[0].timestamp
Using queryStarting for timestamp
self._dbref.queryOrdered(byChild: "timestamp").queryStarting(atValue: timestamp+1)
.observe(.childAdded, with: {
snapshot in
print(snapshot.value)
})
You have the right idea. child_added should be called only for the new nodes. Without source code it's hard to tell why you get all the data in your child_added event.
You can check the chat demo app to see how they load new chat messages. The use case sounds similar.
https://github.com/firebase/firechat/blob/master/src/js/firechat.js#L347
Here's temporary but quick solution:
// define a boolean
var bool = false;
// fetch the last child nodes from firebase database
ref.limitToLast(1).on("child_added", function(snap) {
if (bool) {
// all the existing child nodes are restricted to enter this area
doSomething(snap.val())
} else {
// set the boolean true to doSomething with newly added child nodes
bool = true;
}
});
Disadvantage: It will load all the child nodes.
Advantage: It will not process existing child nodes but just the newly added child nodes.
limitToLast(1) will do the work.

Self-triggered perpetually running Firebase process using NodeJS

I have a set of records that I would like to update sequentially in perpetuity. Basically:
Get least recently updated record
Update record
Set date of record to now (aka. send it to the back of the list)
Back to step 1
Here is what I was thinking using Firebase:
// update record function
var updateRecord = function() {
// get least recently updated record
firebaseOOO.limit(1).once('value', function(snapshot) {
key = _.keys(snapshot.val())[0];
/*
* do 1-5 seconds of non-Firebase processing here
*/
snapshot.ref().child(key).transaction(
// update record
function(data) {
return updatedData;
},
// update priority after commit (would like to do it in transaction)
function(error, committed, snap2) {
snap2.ref().setPriority(snap2.dateUpdated);
}
);
});
};
// listen whenever priority changes (aka. new item needs processing)
firebaseOOO.on('child_moved', function(snapshot) {
updateRecord();
});
// kick off the whole thing
updateRecord();
Is this a reasonable thing to do?
In general, this type of daemon is precisely what was envisioned for use with the Firebase NodeJS client. So, the approach looks good.
However, in the on() call it looks like you're dropping the snapshot that's being passed in on the floor. This might be application specific to what you're doing, but it would be more efficient to consume that snapshot in relation to the once() that happens in the updateRecord().

Categories

Resources