I'm using Firebase for an admin panel which has multiple users. Whenever a new question is added, everyone in the panel gets alerted immediately.
It's working fine, but the way i implemented it doesn't feel right. Here's what i'm doing:
dataRef.auth(token, function(error, result) {
if (error) {
console.log("Login Failed!", error);
} else {
var started = new Date();
dataRef.on('child_added', function(snapshot) {
var message = snapshot.val();
var t = message.date.split(/[- :]/);
var lastChildDate = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
if(lastChildDate > started) {
// alert the user
}
});
}
});
And this is the data structure. It only has one field:
{date: "2013-06-14 16:45:10"}
This brings all the records in that base. A child_added event gets fired for all records. I then check if it's created after the page is loaded, and alert the user accordingly. But the clients only need to be alerted for children that are added from now on. How can i get rid of that unnecessary load? I've checked the documentation, but couldn't find an answer.
There's a few ways to solve this issue.
1) Encode the timestamp at which the event occurred in the priority of the new child, and then use "startAt" to prune older elements.
Make sure your encoding of time sorts lexigraphically in time order.
For example:
ref.startAt("2013-06-14 10:11:13").on(...);
When generating the timestamped items, you'll want to use the Firebase server time so everyone is working off of the same clock. See serverTimeOffset:
https://www.firebase.com/docs/managing-presence.html
2) Have a different event queue for every user, and have users' clients remove elements from the queue once they've seen them.
This solution is best in cases where each user might receive different notifications, such as in a social network.
Related
Note:
The asynchronous nature of firebase has been discussed thousands of times here, but my low reputation number does not allow for a comment on an existing question. That's why I have asked this question.
I am a noob, so please help me understand the implementation in an easy to understand manner.
Steps to implement:
User enters a value in the HTML input box
Search the input value in the firebase db (showMessage() gets called)
Display an appropriate result based on the search result in step 2
Problem faced:
The message displayed in step 3 takes almost an average of 1.75 seconds to display. This experience is not user-friendly. I want to display the message as soon as possible i.e. want to reduce the fetch time.
Probable root causes:
Either my way of fetching the data from firebase dB is incorrect (I still don't understand how to keep a promise :()
Or The mechanism of search and display is not right
var full_name;
function showMessage(){
extractData();
}
function extractData(){
test(function(returnValue) {
custom_message = searchMessage(returnValue);
var container = document.querySelector('#placeholder');
var para = document.createElement('p');
var custom_message = "Happy happy, buds!";
para.innerHTML = custom_message;
para.className = "message";
container.appendChild(para);
});
}
function test(callback) {
var ref = firebase.database().ref();
ref.on('value', function(snapshot) {
var data = snapshot.val();
callback(data);
}, function (error) {
console.log("Error: " + error.code);
});
}
function searchMessage(data){
for(var i = 0; i < data.length; i++)
{
name_f_data = data[i].firstName.concat(" ", data[i].lastName);
if(full_name.toLowerCase() == name_f_data.toLowerCase())
{
console.log(name_f_data.toLowerCase());
console.log(full_name.toLowerCase());
return data[i].message;
}
}
}
The time a read operation takes depends on:
The latency of your connection to Firebase's servers
The amount of data you are reading
The bandwidth of your connection
The time it takes Firebase to process the request
In most cases, the time Firebase takes is only a very small portion of the total time, and most of your time actually goes to the data transfer, which depends purely on the bandwidth and amount of data. If this is the first time you're reading data from Firebase in the page, the latency also matters more, as Firebase has to establish a connection, which takes a few roundtrips.
Your current code is downloading all data from the database, and then searching in the JavaScript code for a child node that matches a certain value. The best way to reduce the time that takes (apart from upgrading to a fast connection) is to transfer less data, which you can do by using Firebase's query mechanism to do the filtering on the server.
You can get pretty close with:
var ref = firebase.database().ref();
var query = ref.orderByChild().startAt(firstName).endAt(firstName+"~");
query.once('value', function(snapshot) {
var data = snapshot.val();
callback(data);
This will significantly reduce the amount of data transferred. A few notes though:
The query returns just the people that have the first name you're looking for. It does not yet filter on the last name, so you'll still need to filter that in the client-side code.
To further optimize this, store the full name (which you now compose in the client-side code) in the database so that you can query on that and reduce data transfer even more.
Firebase queries are case sensitive, so the query only returns data where the case matches exactly. If you want to query case-indifferent, consider storing a toLowerCase() value in the database.
Be sure to define an index on firstName, as otherwise the Firebase database will still send all data to the client, and the SDK will perform the filtering client-side.
Have created a chat application and we use firebase for realtime communication.
Sometimes i noticed that push() method shuffle the list data. We can see in the below image :
If we see in above image i am just trying to communicate with someone i said hello in reply user said hey, again i said i need some help then user said That's what I'm here for. What can I assist you with? in the reply but if we see in the image users reply is appearing first.
It happens intermittently that's why i didn't figure out this problem. So please someone help me out what shall i doing wrong.
var pushMessageToFB = function(){
var chatMsgRef = db.child("chatMessages").child("gr1").child("ch_usr1_usr2");
var message = {
type: "chat",
content: data.messageText,
timestamp: Date.now(),
by: user.id
};
chatMsgRef.push(message, function(err){
if (err){
console.log('error occurred while pushing message to fb : err ' + JSON.stringify(err));
}
});};
var loadChatMessages = function(){
var chatMsgRef = db.child('chatMessages').child("gr1").child("ch_usr1_usr2");
$scope.chatMessages = chatMsgRef.orderByKey().limitToLast(50);
};
Don't use Date for remote data. It will be slightly offset on every machine. Use ServerValue.TIMESTAMP which Firebase will convert to the server's Unix epoch time when the data is written. This ensures consistency of order across clients.
You didn't provide any code for how you're reading or displaying the chat messages, but since you're trying to show them in chronological order I assume your query uses orderBy("timestamp"). If you use ServerValue.TIMESTAMP when you write the message it will be guaranteed to sort in the order that it was written to the database.
I have a real time database with firebase and i'm using the following code to connect to the database and get some data from it.
window.onload = function(){
var databaseWebsites = firebase.database().ref('/websites').orderByChild('votes');
console.log(databaseWebsites);
databaseWebsites.on('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var webTemp = document.getElementById(childSnapshot.val().id);
webTemp.style.order = order;
var webText = webTemp.getElementsByClassName('likeText');
webText[0].innerHTML = childSnapshot.val().votes;
order--;
});
order = 0;
});
It gets all the data, in order and uses it correctly.
The problem is, I don't want the data on the front end to update until the user refreshes the page. The system is a voting system that is ordered by votes value, if it was constantly updating it would be a bad user experience.
Any help will be appreciated.
Thanks
Change the on to once, Firebase on listens for changes in your database node and sends a response.
databaseWebsites.on('value', function(snapshot) {
to
databaseWebsites.once('value', function(snapshot) {
An excerpt from Firebase doc
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.
Visit this url to read more
The accepted response is not correct (maybe outdated?) because once() requires to add a then()
It's actually
databaseWebsites.once('value').then(function(snapshot) {}
that replaces
databaseWebsites.on('value', function(snapshot) {}
I'm currently running the following Parse cloud code job which iterates through every user in the database, and I'm starting to hit the 15 minute time limit. How can I set this to run in batches of 500 users at a time instead of all users at once? I would need it to run through the users in order, so user 1-500 for the first batch, then 500-1000 for the 2nd batch, and so on, that way it doesn't repeat anyone.
Parse.Cloud.job("MCBackground", function(request, status) {
// ... other code to setup usersQuery ...
Parse.Cloud.useMasterKey();
var usersQuery = new Parse.Query(Parse.User);
return usersQuery.each(function(user) {
return processUser(user)
.then(function(eBayResults) {
return mcComparison(user, eBayResults);
});
})
.then(function() {
// Set the job's success status
status.success("MCBackground completed successfully.");
}, function(error) {
// Set the job's error status
status.error("Got an error " + JSON.stringify(error));
});
});
Well, for starters, from your comment I see you have a constraint you can add: only query for user objects that has a matchCenterItem:
query.exist("matchCenterItem");
I don't know how many percent of your users would not have this field, but maybe you already now will have reduced the number of Users to fetch.
I have no idea how often this job is run or what you are comparing with, but how likely is it that this data will change? Do you always need to run it on ALL users, or can you set a flag so that you don't run it on the same user again? At all, or until some time has passed?
I have been trying to automate Lotus Notes mail fillup from a browser interface.
After refering to Richard Schwartz's answer, i came up with this piece of code using the Lotus.NotesSession class.
function SendScriptMail() {
var mToMail = document.getElementById('txtMailId').value
var mSub = document.getElementById('txtSubject').value
var mMsg = document.getElementById('txtContent').value
var Password = "yyy"
alert("1");
var MailFileServer = "xxx.com"
var MailFile = "C:\Program Files\IBM\Lotus\Notes\mail\user.nsf"
alert("2")
var Session;
var Maildb;
var UI;
var NewMail;
var From = "user#xxx.com"
try {
alert("3")
// Create the Activex object for NotesSession
Session = new ActiveXObject("Lotus.NotesSession");
alert("4")
if (Session == null) {
throw ("NoSession");
} else {
Session.Initialize(Password);
// Get mail database
Maildb = Session.GetDatabase(MailFileServer, MailFile);
alert("5")
if (Maildb == null) {
throw ("NoMaildb");
} else {
NewMail = MailDB.CreateDocument();
if (MailDoc == null) {
throw ('NoMailDoc');
} else {
// Populate the fields
NewMail.AppendItemValue("Form", "Memo")
NewMail.AppendItemValue("SendTo", mToMail)
NewMail.AppendItemValue("From", From)
NewMail.AppendItemValue("Subject", mSub)
NewMail.AppendItemValue("Body", mMsg)
NewMail.Save(True, False)
NewMail.Send(False)
}
}
}
} catch (err) {
// feel free to improve error handling...
alert('Error while sending mail');
}
}
But now, alerts 1,2,3 are being trigerrd, and then the counter moves to the catch block. The lotus notes session is not being started.
In a powershell script that I was previously looking at there was a code regsvr32 "$NotesInstallDir\nlsxbe.dll" /s that was used before the Session = new ActiveXObject("Lotus.NotesSession");. Is there something similar in javascript too, if so how do i invoke that dll.
I think I've realised where I am going wrong. According to me, upto alert("5") things are good. But since Lotus.NotesSession doesn't have a CreateDocument() method, it is throwing the error. I am not sure how to create the document and populate the values though.
Since you've chosen to use the Notes.NotesUIWorkspace class, you are working with the Notes client front-end. It's running, and your users see what's happening on the screen. Are you aware that there's a set of back-end classes (rooted in Lotus.NotesSession) instead of Notes.NotesSession and Notes.NotesUIWorkspace) that work directly with Notes database data, without causing the Notes client to grab focus and display everything that you're doing?
Working with the front-end means that in some cases (depending on the version of Notes that you are working with) you're not going to be working directly with the field names that are standard in Notes messages as stored and as seen in the back-end. You're going to be working with names used as temporary inputs in the form that is used to view and edit the message. You can see these names by using Domino Designer to view the Memo form.
Instead of using 'SendTo', try using:
MailDoc.Fieldsettext('EnterSendTo', mToMail)
Regarding the Body field, there's no temporary field involved, however you haven't really explained the difficulty you are having. Do you not know how to display the interface that you want in the browser? Do you not know how to combine different inputs into a single FieldSetText call? Or are you just dissatisfied with the fact that FieldSetText can't do any fancy formatting? In the latter case, to get more formatting capability you may want to switch to using the back-end classes, which give you access to the NotesRichTextItem class, which has more formatting capabilities.