Creating # pages like Twitter with Firebase - android

I have a question to the Firebase community.
How to structure the data for # pages like Twitter? (e.g:#funnyVideos so that any one can see that link and add some stuff into it) in Firebase?

It sounds like you're trying to store hash tags.
But that enough is not enough to determine your data model.
In Firebase (as in most NoSQL databases) you model your data for the way you app consumes it. So you'll have to determine what screens there are in your app and what information they show.
For example, if you want to show the hash tags per page, you'd store a list of hash tags per page:
hashtagsPerPage
pageId1
hashTag1: true
hashTag2: true
pageId2
hashTag1: true
hashTag3: true
If you also want a list of pages for a given hash tag, you'd also store:
pagesPerHashtag
hashTag1
pageId1: true
pageId2: true
hashTag2
pageId1: true
hashTag3
pageId2: true
And of course you'd then have the list of pages itself:
pages
pageId1
title: "Creating # pages like Twitter with Firebase"
url: "http://stackoverflow.com/questions/39947270/creating-pages-like-twitter-with-firebase"
pageId2
title: "Structure Your Database"
url: "https://firebase.google.com/docs/database/web/structure-data"
I highly recommend that you read both the Firebase documentation on data structuring (the first two lists are the structure documented in the section "create data that scales").
In addition there's this great primer on NoSQL data modeling.

Related

How to reference one Node value in Other Node in firebase Android [duplicate]

I've read the Firebase docs on Stucturing Data. Data storage is cheap, but the user's time is not. We should optimize for get operations, and write in multiple places.
So then I might store a list node and a list-index node, with some duplicated data between the two, at very least the list name.
I'm using ES6 and promises in my javascript app to handle the async flow, mainly of fetching a ref key from firebase after the first data push.
let addIndexPromise = new Promise( (resolve, reject) => {
let newRef = ref.child('list-index').push(newItem);
resolve( newRef.key()); // ignore reject() for brevity
});
addIndexPromise.then( key => {
ref.child('list').child(key).set(newItem);
});
How do I make sure the data stays in sync in all places, knowing my app runs only on the client?
For sanity check, I set a setTimeout in my promise and shut my browser before it resolved, and indeed my database was no longer consistent, with an extra index saved without a corresponding list.
Any advice?
Great question. I know of three approaches to this, which I'll list below.
I'll take a slightly different example for this, mostly because it allows me to use more concrete terms in the explanation.
Say we have a chat application, where we store two entities: messages and users. In the screen where we show the messages, we also show the name of the user. So to minimize the number of reads, we store the name of the user with each chat message too.
users
so:209103
name: "Frank van Puffelen"
location: "San Francisco, CA"
questionCount: 12
so:3648524
name: "legolandbridge"
location: "London, Prague, Barcelona"
questionCount: 4
messages
-Jabhsay3487
message: "How to write denormalized data in Firebase"
user: so:3648524
username: "legolandbridge"
-Jabhsay3591
message: "Great question."
user: so:209103
username: "Frank van Puffelen"
-Jabhsay3595
message: "I know of three approaches, which I'll list below."
user: so:209103
username: "Frank van Puffelen"
So we store the primary copy of the user's profile in the users node. In the message we store the uid (so:209103 and so:3648524) so that we can look up the user. But we also store the user's name in the messages, so that we don't have to look this up for each user when we want to display a list of messages.
So now what happens when I go to the Profile page on the chat service and change my name from "Frank van Puffelen" to just "puf".
Transactional update
Performing a transactional update is the one that probably pops to mind of most developers initially. We always want the username in messages to match the name in the corresponding profile.
Using multipath writes (added on 20150925)
Since Firebase 2.3 (for JavaScript) and 2.4 (for Android and iOS), you can achieve atomic updates quite easily by using a single multi-path update:
function renameUser(ref, uid, name) {
var updates = {}; // all paths to be updated and their new values
updates['users/'+uid+'/name'] = name;
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
updates['messages/'+messageSnapshot.key()+'/username'] = name;
})
ref.update(updates);
});
}
This will send a single update command to Firebase that updates the user's name in their profile and in each message.
Previous atomic approach
So when the user change's the name in their profile:
var ref = new Firebase('https://mychat.firebaseio.com/');
var uid = "so:209103";
var nameInProfileRef = ref.child('users').child(uid).child('name');
nameInProfileRef.transaction(function(currentName) {
return "puf";
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('Transaction aborted by our code.');
} else {
console.log('Name updated in profile, now update it in the messages');
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.on('child_added', function(messageSnapshot) {
messageSnapshot.ref().update({ username: "puf" });
});
}
console.log("Wilma's data: ", snapshot.val());
}, false /* don't apply the change locally */);
Pretty involved and the astute reader will notice that I cheat in the handling of the messages. First cheat is that I never call off for the listener, but I also don't use a transaction.
If we want to securely do this type of operation from the client, we'd need:
security rules that ensure the names in both places match. But the rules need to allow enough flexibility for them to temporarily be different while we're changing the name. So this turns into a pretty painful two-phase commit scheme.
change all username fields for messages by so:209103 to null (some magic value)
change the name of user so:209103 to 'puf'
change the username in every message by so:209103 that is null to puf.
that query requires an and of two conditions, which Firebase queries don't support. So we'll end up with an extra property uid_plus_name (with value so:209103_puf) that we can query on.
client-side code that handles all these transitions transactionally.
This type of approach makes my head hurt. And usually that means that I'm doing something wrong. But even if it's the right approach, with a head that hurts I'm way more likely to make coding mistakes. So I prefer to look for a simpler solution.
Eventual consistency
Update (20150925): Firebase released a feature to allow atomic writes to multiple paths. This works similar to approach below, but with a single command. See the updated section above to read how this works.
The second approach depends on splitting the user action ("I want to change my name to 'puf'") from the implications of that action ("We need to update the name in profile so:209103 and in every message that has user = so:209103).
I'd handle the rename in a script that we run on a server. The main method would be something like this:
function renameUser(ref, uid, name) {
ref.child('users').child(uid).update({ name: name });
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
messageSnapshot.update({ username: name });
})
});
}
Once again I take a few shortcuts here, such as using once('value' (which is in general a bad idea for optimal performance with Firebase). But overall the approach is simpler, at the cost of not having all data completely updated at the same time. But eventually the messages will all be updated to match the new value.
Not caring
The third approach is the simplest of all: in many cases you don't really have to update the duplicated data at all. In the example we've used here, you could say that each message recorded the name as I used it at that time. I didn't change my name until just now, so it makes sense that older messages show the name I used at that time. This applies in many cases where the secondary data is transactional in nature. It doesn't apply everywhere of course, but where it applies "not caring" is the simplest approach of all.
Summary
While the above are just broad descriptions of how you could solve this problem and they are definitely not complete, I find that each time I need to fan out duplicate data it comes back to one of these basic approaches.
To add to Franks great reply, I implemented the eventual consistency approach with a set of Firebase Cloud Functions. The functions get triggered whenever a primary value (eg. users name) gets changed, and then propagate the changes to the denormalized fields.
It is not as fast as a transaction, but for many cases it does not need to be.

emoji look up table and algorithm

I am trying to build an android app that the user can enter a string, and a list emoji related to that string would show up. (Just like Venmo app) For example:
case 1: User enters "pizz", and in the list there would be "πŸ•", note that the users enter "pizz", not pizza!
case 2: User enters "rabb", and in the list there would be "πŸ‡" and "🐰", note that the users enter "rabb", not rabbit!
What would be a good data structure and algorithm for this problem?
A trie is what your looking for. From Wikipedia
A trie, also called digital tree and sometimes radix tree or prefix tree (as they can be searched by prefixes), is a kind of search treeβ€”an ordered tree data structure ...
A trie is similar to a HashMap<K,V>, you can perform a lookup with keys and get a value. The difference is that you can also search by prefix. Given a prefix, it will find all the key-value pairs in the structure that have that prefix. It's basically the data structure for generating search suggestions.
General Idea:
Trie<String, String> t = new Trie<String, String>();
t.insert("pizza", "πŸ•");
t.insert("rabbit1", "πŸ‡");
t.insert("rabbit2", "🐰");
// then later...
t.findByPrefix("rabb"); // [πŸ‡,🐰]
Unfortunately, tries are too generic and are not present in any popular data structure libraries (like Java Collections Framework or Google Guava, for example). You'd have to implement one yourself or find an existing implementation and modify it.
I'd recommend:
Learning the theory. Watch this video. There are many more on YouTube that will teach you the basics. You can also search google for "N-way trie" and read notes about it.
Taking this class TrieST and modifying it. It's very similar (or already perfect) for what you need: http://algs4.cs.princeton.edu/52trie/TrieST.java.html see specifically thekeysWithPrefix method.

How to edit Google spreadsheets in Kinetise's mobile application?

I develop app in kinetise tool and I found it is possible to easly add list with Google spreadsheet content. But I don't know whether it is possible to edit sheets from user account (when it is public or user sign up with Google). Let's say add records for specific position (A1) in specific sheet (SheetName). How to achieve this?
Yes, you can both view your spreadsheet (read) and edit it (write) in Kinetise.
Firstly, publish your sheet and get public URL address (File -> Publish to web).
Now in Kinetise editor add List widget and select "From Google Sheets" in Online source view. Paste your URL address and data from sheet should be displayed.
What about write, well you need to Sign-In with Google first. So please add Google login method to your application. You can do this attaching Google login widget to your Splash Screen.
Then in screen where you want to edit sheet please add Form widget. Select "To RESTful API" as form send destination. Paste below url:
https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/{range}:append?insertDataOption=INSERT_ROWS&valueInputOption=USER_ENTERED&access_token=##GetGoogleUserAccessToken##
where
{spreadsheetId} - yours spreadsheet ID (same as in List widget's URL);
{range} - columns range: A1:B1 (depends on number of columns in your table).
The last thing you have to do is to edit send request body. Please click Settings button on the right of URL address. In Body tab and Request body transform section please paste:
{ "values": [
[ .form.field1, .form.field2 ]
]
}
field1 and field2 are names of form fields, so if you have another names or more columns please change it. And that's all, now you should be able to edit spreadsheet.

Firebase data flattening

I have a quick question about the best practices for data structure in a firebase database.
I want users of my app to be able to maintain a friends list. The firebase documentation recommends creating a schema (not sure if thats the proper word in this context) that is as flat as possible. Because of this I thought it would be a good idea to separate the friends section from the player section in the database like so:
{
"players":{
"player1id":{
"username":"john",...
},
"player2id": ...,
"player3id": ...
}
"friends": {
"player1id"{
"friends":{
"friend1Id":true,
"friend2Id":true
}
},
}
"player2id"{
"friends":{
"friend1Id":true,
"friend2Id":true
}
},
}
}
So my questions are as follows:
Is this a good design for my schema?
When pulling a friends list for one player, will the friends lists of EVERY player be pulled? and if so, can this be avoided?
Also, what would be the best way to then pull in additional information about the friends once the app has all of their IDs. e.g. getting all of their user names which will be stored as a string in their player profile.
Is this a good design for my schema?
You're already thinking in the right direction. However the "friends" node can be simplified to:
"friends": {
"player1id": {
"friend1Id":true,
"friend2Id":true
}
}
Remember that Firebase node names cannot use the character dot (.). So if your IDs are integer such as 1, 2, and 3 everything is OK, but if the IDs are username be careful (for example "super123" is OK but "super.duper" is not)
When pulling a friends list for one player, will the friends lists of EVERY player be pulled? and if so, can this be avoided?
No. If you pull /friends/1 it obviously won't pull /friends/2 etc.
Also, what would be the best way to then pull in additional information about the friends once the app has all of their IDs. e.g. getting all of their user names which will be stored as a string in their player profile.
Loop through the IDs and fetch the respective nodes from Firebase again. For example if user 1 has friends 2, 3, and 4, then using a for loop fetch /players/2, /players/3, and /players/4
Since firebase pull works asynchronously, you might need to use a counter or some other mechanism so that when the last data is pulled you can continue running the completion code.

Firebase structuring relationship

I started developing simple app to learn Firebase, I followed cool blog post: https://firebase.googleblog.com/2013/04/denormalizing-your-data-is-normal.html
In my app, I want to store user profiles (its extension of firebase user) and relationship between users (something like friendship)
I came up with this data structure idea:
profile
profile1:
userName:"User 1",
userDescription: "User 1 description"
profile2:
userName:"User 2",
userDescription: "User 2 description"
profile3:
userName:"User 3",
userDescription: "User 3 description"
profileFriends:
profile1:
profile2: true
profile2:
profile1: true
profile3: true
profile3:
profile2: true
Of course instead of profile1 I use pushed keys.
I wonder I its okay for such a use case - I want to display all friends of profile2.
I have to get Database reference to
"profileFriends/profile2"
And then iterating childs gives me keys: profile1 and profile3 which I can then listen using reference
"profile/profile1"
"profile/profile3"
Since Im working in Android, I can wrap all this code and use Observable that emits profiles.
Question: Do I get this right? I have some SQL background and standard request-response api experience, Im just little worried if my user have 100 friends Ill need to make total 101 listeners - is it similar to make 101 requests? Is there any smarter way to solve "join" in non-joinable no-sql database?
I guess another solution is denormalization, but I'm not a big fan of updating many places to change for example profile description

Categories

Resources