Move from firebase to firestore - android

I'm working on an app that contains USERs and EVENTs.
Each event has several users and each users has several events.
In Firebase, both events and users as "details" key(see json below), and those details are duplicate so all the event users has the details of the event, so in my main activity I have a recyclerview that shows those events easly (and the same way for users list inside event activity.
To sync all those duplicates I use firebase functions.
Now I want to move ti firestore but I'm not sure how should it be.
From what I see I can have list of users IDs in event document and then do a query for all events with user.id = current_user_id.
The issue is that I have more fields per user (expenses, and I want to add more) So either I have also subcollection for each user, or have more complicate list.
Can I query the events according to their subcollection id?
I will appreciate any help with that.
The current Json:
{
"events" : {
"-L7v0K***" : {
"average" : 110,
"details" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"items" : {},
"require_transactions" : [ {}],
"totalexpenses" : 220,
"users" : {
"ARKuwgrDHcNnXHoPlCgIBXOObjD3" : {
"details" : {
"uid" : "ARKuw***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"expenses" : 20
},
"pDFtk***" : {}
},
"-L84Gg***" : {}
}
},
"users" : {
"ARKuw***" : {
"details" : {
"uid" : "ARKuwg***",
"userEmail" : "r***g#walla.com",
"username" : "R** G***"
},
"events" : {
"-L7v0Kx***" : {
"date" : {
"date" : 1520354933426
},
"location" : "Tel Aviv",
"name" : "test"
},
"-L97_3***" : { }
},
"TAJK6***" : {}
}
}
}

Firebase is pretty close to firestore, i also started everything with firebase realtime and moved to firestore, to have more complex queries.
So, as i understood, you will have two primary collections, you can create them by:
firebase.firestore().collection('users')
firebase.firestore().collection('events')
inside users you will have a subcollection called events to trigger all events a specific user have, so it will be like this:
firebase.firestore().collection('users').doc(userId).collection('events')
To make queries, for example, to get all events that a specific user is attending you can type:
firebase.firestore().collection('users').doc(userId).collection('events').get()
(here you can do filters like limiting, 'where', byId... by 'field equals to'... all of this is the documentation)
this will return all eventsId that are in the collection he attended.
Remember to work thinking on scale to get lower cost.
If you can explain more the task you need i can work in a solution.
Documentation of firestore is very easy.

Related

Getting filtered list values from all users in my firebase Realtime-Database where the list is a child of user

This is how my firebase real-time database looks like.
{
"users" : {
"PDFZ0QOoTxYbCyruOrbiA2y1n5O2" : {
"email" : "mark#outlook.com",
"name" : "Mark Evans",
"paymentMethods" : {
"-ML5oMsCnXgfBRCh7DdO" : {
"paymentProvider" : "Google Pay",
"phone" : "+915555555555",
"userName" : "Mark Evans"
},
"-ML5oRHklHHIK33NQowD" : {
"paymentProvider" : "BHIM",
"phone" : "+911111111111",
"userName" : "Chris Evans"
}
},
"uid" : "PDFZ0QOoTxYbCyruOrbiA2y1n5O2"
},
"epyBsLU0fYOT8uc0Bo698f5SRcO2" : {
"email" : "axle#gmail.com",
"name" : "Axle Blaze",
"paymentMethods" : {
"-ML5o5Zv_3ZixCIXWBqg" : {
"paymentProvider" : "Google Pay",
"phone" : "+918888888888",
"userName" : "Axle Blaze"
},
"-ML5o9pMNucaacdU0G3P" : {
"paymentProvider" : "BHIM",
"phone" : "+911111111111",
"userName" : "Bunny Blaze"
}
},
"uid" : "epyBsLU0fYOT8uc0Bo698f5SRcO2"
}
}
}
I need to get those payment options where phone number = "+91XXXXXXXXXX" no matter from which user payment option belongs to.
For example: If i need all payment options where phone == +911111111111
Result should be :
"-ML5oRHklHHIK33NQowD" : {
"paymentProvider" : "BHIM",
"phone" : "+911111111111",
"userName" : "Chris Evans"
},
"-ML5o9pMNucaacdU0G3P" : {
"paymentProvider" : "BHIM",
"phone" : "+911111111111",
"userName" : "Bunny Blaze"
}
I have to do this in Android but since querying should be similar for any platform so I need help with how the query should be structured.
Any kind of help is appreciated. Thanks in advance.
Firebase queries work on a flat list. The value you order/filter on must be in a fixed location under each direct child node of users.
In your current structure, you can:
Search across all users for direct properties of that user, such as their email or name.
Search across the payment methods of a specific users.
The query you want is not possible on your current data structure. If you want to allow a query across all payment methods of all users, you'll need to change (or augment) your data structure to (also) have a flat list of payment methods across all users.
Also see:
Firebase Query Double Nested
Firebase query if child of child contains a value

Firebase multiple where clauses using equalsTo

I have a list of all the keys I want to download from Firebase. For this I'm using this code:
Query newUidsQuery = FirebaseDatabaseHelper.getUsersReference();
newUidsQuery.orderByChild(DATABASE_NODE_USER_UID);
for (String uid : uidList) {
newUidsQuery.equalTo(uid);
}
newUidsQuery.addListenerForSingleValueEvent();
This is my current structure:
{
"users" : {
"5mvsiNKz2hO4rmcDDNskv855dkB3" : {
"contacts" : [ "GG8JeRNOIhb1qloZb4oCAb7Jd593", "gc0ci7Jgu2QpVYFbeiMJfVy1WHP2" ],
"contactsHash" : -224276455,
"email" : "lung.razvan#yahoo.com",
"name" : "Razvan Cristian Lung",
"photoUrl" : "https://lh5.googleusercontent.com/-bItm3-ieAtU/AAAAAAAAAAI/AAAAAAAALZo/mtPyAMohOvg/s96-c/photo.jpg",
"uid" : "5mvsiNKz2hO4rmcDDNskv855dkB3"
},
"GG8JeRNOIhb1qloZb4oCAb7Jd593" : {
"contactsHash" : 1,
"email" : "andralung#yahoo.com",
"name" : "Andra Florina Lung",
"photoUrl" : "https://lh4.googleusercontent.com/-po2yelyi3mY/AAAAAAAAAAI/AAAAAAAAQ5s/ROefxP6Q1oA/s96-c/photo.jpg",
"uid" : "GG8JeRNOIhb1qloZb4oCAb7Jd593"
},
"gc0ci7Jgu2QpVYFbeiMJfVy1WHP2" : {
"contactsHash" : 1,
"email" : "lung_razvan2100#yahoo.com",
"name" : "Lung Razvan",
"photoUrl" : "https://scontent.xx.fbcdn.net/v/t1.0-1/p100x100/15390976_1192204140865562_3397773349261436244_n.jpg?oh=d61795a8df67d3e9c5ddf60557e9e60c&oe=59270863",
"uid" : "gc0ci7Jgu2QpVYFbeiMJfVy1WHP2"
}
}
}
The problem is that when I try to get only the entries with the specific key I also get the other entries that have that key as a value in the "contacts" field.
When you call orderBy... or other methods, it returns a new query. So you're now creating a new query for each uid that you then don't use. To keep it, you'd need newUidsQuery = newUidsQuery.equalTo(uid).
But that won't solve the problem yet, because a query can only order/filter on a single property and value. See Query based on multiple where clauses in firebase.
Better news is that this doesn't matter much here, since you can just retrieve the items one at a time. Unlike what you might expect, that's not significantly slower than retrieving them in one go. See Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly or watch this episode of #AskFirebase: https://youtu.be/66lDSYtyils?t=1m49s

Firebase, query specific keys from a large set of data

Any help is much appreciated. Thank you so much for your time.
Let us say, there is a large set of articles under Articles node.
"Articles" : {
"article1Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
},
"article2Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
},
...
"article10Key" : {
"articleAuthor" : "Author",
"articleFavByNo" : 21,
"articleKey" : "Key",
"articleName" : "Name",
"articlePostedOn" : "21/07/11",
"articleTopic" : "Topic"
}
The articles are posted by some authors. Registered user can browse through the articles, and like them. The liked articles keys are stored in Users profile under favArticles node. Since, the article contains huge amount of data under it, only the key and name of the article is stored under User profile.
"Users" : {
"ZtlIQ2d1qJT1XpmHuGxwFSwaiEy2" : {
"emailId" : "vs#gmail.com",
"favArticles" : {
"article1Key" : {
"name" : "article1"
},
"article7Key" : {
"name" : "article7"
},
"article4Key" : {
"name" : "article4"
}
},
"firstName" : "Vimala",
"image" : "default",
"lastName" : "Sridhar"
}
}
Let us say that the user has liked some 50 articles out of 1000. Now if I want to display the User favorite articles in a RecyclerView, how should I write my query to pick the specific articles from the article list?
Since you already keep a list of the favorites for each user, you'd just:
load that list of favorites
iterate over it
load the references article for each
If you're worried about the performance of this loop-and-load: Firebase loads all the articles in step 3 over the same connection, so the requests are pipelined. For reasonable numbers of articles, this is actually quite fast. See my answer here for more details: Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly

What is the fastest when it comes to reading in Firebase real time database

In my quest to understand Firebase Realtime Database I want to know what is the fastest when it comes to reading. I don´t know if asking the question like this is not suitable for Firebase, but anyway I have to start somewhere right.
In what way is this list of nods or an array och keys faster and why. Is the speed dependent on how the question is asked, if so how to ask the question to get speed? This question is about speed only and reading only.
Is the speed dependent on the length of the key or if the key is a Pushed time stamp. Is the speed dependent on the nr of keys?
(this is not a broad question and please don´t downvote)
Firebase data structure 1-billions of keys: - no comment
"Stars_in_the_sky": {
"star_1": "-wjkhwe872hjwekj",
"star_2": "-sds768787sada6s",
"star_3": "-sd9876sad8asd7d",
...,
...,
"star_127169118922": "-334d78sddisd798sd98sds9d8sds"
}
Firebase data structure 1-billions of keys: - using push ids
"Stars_in_the_sky": {
"-Kjkhwe872hjwekj": {
"name" : "star_1"
},
"-Kds768787sada6s": {
"name" : "star_2"
},
"-Kd9876sad8asd7d": {
"name" : "star_3"
},
...,
...,
"-K334d78sddisd798sd98sds9d8sds": {
"name" : "star_127169118922"
},
}
Firebase data structure 1-billions of keys - using custom keys
"Stars_in_the_sky": {
"aaa-bbb-ccc-ddd-eee": {
"name" : "star_1"
},
"aaa-bbb-ccc-ddd-eee-fff": {
"name" : "star_2"
},
"aaa-bbb-ccc-ddd-eee-fff-ggg": {
"name" : "star_3"
},
...,
...,
"aaa-bbb-ccc-ddd-eee-fff-ggg-hhh..........................": {
"name" : "star_127169118922"
},
}

How to properly denormalize data in Firebase

I am very new to NoSQL and denormalization. However, I wish to allow the actions at SignUp within my app defined as:
If a username is already taken, then a user is not allowed to use it
If a phone number is already taken, then a user is not allowed to use it
Allow a new user to "sync" their phone number contacts with the server to determine who are presently users, and retrieve their respective uid's
I have the schema outlined as below given the quick need to check if a username/phone number is already present at user sign up, as well as the needed search and compare given if the new users contacts phone numbers are link to users already present within the app:
{
"presentUsersByPhoneNumber" : {
"1614#######" : {
"uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
},
"1614#######" : {
"uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"1614#######" : {
"uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
},
"1614#######" : {
"uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
}
},
"presentUsersByUsername" : {
"ak" : {
"uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
},
"ak2" : {
"uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"ak3" : {
"uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
},
"kja" : {
"uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
}
},
"users" : {
"1783917f-00e4-47a0-b2cd-987bdf185129" : {
"phoneNumber" : "614#######",
"username" : "ak3"
},
"99e4989b-a046-4c5f-9478-5ebd8bdc3ded" : {
"phoneNumber" : "1614#######",
"username" : "ak2"
},
"a96da7b1-7c4e-44bc-b82e-fc75bed52bcd" : {
"phoneNumber" : "1614#######",
"username" : "ak1"
},
"fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de" : {
"phoneNumber" : "1614#######",
"username" : "kja"
}
}
}
Is this approach going too fair in the act of denormalizaiton?
In NoSQL you should model the data for how your application needs to access it. Read this article on NoSQL data modeling for more information.
So if you need an efficient way to check whether a phone number or username is already taken, it makes sense to store mappings for those. The only thing I would probably do different there is to store them as simple types:
"phoneNumberToUid" : {
"1614#######" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
"1614#######" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"usernameToUid" : {
"ak" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
"ak2" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
}
One thing I noticed in your sample data is that you have a key ak in presentUsersByUsername, but there is no corresponding child in users with that name. This typically happens because your code either aborts half-way through or because you made a mistake at some point during development.
You can prevent many of these problems by:
using multi-location updates, so that all writes are sent to Firebase as a single command
ref.update({
'/users/a96da7b1-7c4e-44bc-b82e-fc75bed52bcd/username': 'ak1',
'/usernameToUid/ak': null,
'/usernameToUid/ak1': 'a96da7b1-7c4e-44bc-b82e-fc75bed52bcd'
});
This update is safest way to change the name from the user from ak to ak1, wiping the old mapping and adding a new one.
using validation rules ensure that a user for each name exists
"usernameToUid": {
"$username": {
".validate": "newData.parent().parent().child(newData.va()).child('username').val() == $username"
}
}

Categories

Resources