I am trying to get data of a specific user when they log in, currently this is working well using the .child(userId) & .get() methods for a small amount of data/records. However, I created over 50,000+ test users & it ended up crashing on the android app.
My simple code for retrieving user data:
realtimeDatabase.getReference("users")
.child(userId)
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
DataSnapshot result = task.getResult();
if (result != null && result.exists()) {
Map<String, Object> mainMap = (Map<String, Object>) result.getValue();
//I should equate the result to a POJO instead of a Map, but I'll sort it out later :)
UserList userList = new UserList();
userList.setPoints((Long) mainMap.get("points"));
if (userList != null) {
long currentNoOfPoints = userList.getPoints();
...
}
}
}
});
I've checked out similar posts on SO & the common solution is using .limitToFirst(...) & equivalent methods... however in my case, I'm not showing the data on a list but simply want to retrieve the data for the user. In addition, correct me if I'm wrong getting the data using the .child(...) method is the same as creating a index since the whole node isn't retrieved.
The error trace I'm getting is:
E/RunLoop: Firebase Database encountered an OutOfMemoryError. You may need to reduce the amount of data you are syncing to the client (e.g. by using queries or syncing a deeper path). See https://firebase.google.com/docs/database/ios/structure-data#best_practices_for_data_structure and https://firebase.google.com/docs/database/android/retrieve-data#filtering_data
java.lang.OutOfMemoryError: Failed to allocate a 524304 byte allocation with 434072 free bytes and 423KB until OOM, target footprint 201326592, growth limit 201326592
at java.util.HashMap.resize(HashMap.java:703)
at java.util.HashMap.putVal(HashMap.java:628)
at java.util.HashMap.put(HashMap.java:611)
How can I solve this issue?
Edit
The exported 'users' node data looks like this
"users": {
"-N8sU8jQ0zE9klo8ukKC": {
"account_status": "active",
"displayName": "w9R5uBiYoQ",
"points": 39188,
"profilePic": "-"
},
"-N8sU8j__vlLa_UCCgvc": {
"account_status": "active",
"displayName": "LqO7LE9P95",
"points": 35743,
"profilePic": "-"
},
...
Related
Hi I have 2 arrays like
phonenumber[1,2,3]
amount[10,20,30]
I need to store the data in a single document like
field1: 1
field2: 10
The code I tried is
I took the object like
**
public class Pojo {
String invitee;
Map<Integer,Integer>phoneAmount;
public Pojo(String invitee, Map<Integer, Integer> phoneAmount)
{
this.invitee = invitee;
this.phoneAmount=phoneAmount;
}
}**
And in another class
Map<int[], int[]> phoneAmount = new HashMap<>();
phoneAmount.put(phonenumber,amount);
db.collection("Split").document("invitees").set(phoneAmount).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully written!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error writing document", e);
}
});
It shows error like
java.lang.RuntimeException: Could not serialize object. Maps with non-string keys are not supported
at com.google.android.gms.internal.zzejw.zza(Unknown Source)
at com.google.android.gms.internal.zzejw.zza(Unknown Source)
at com.google.android.gms.internal.zzejw.zzbp(Unknown Source)
From the official documentation:
Although Cloud Firestore can store arrays, it does not support querying array members or updating single array elements.
This approach is one that I personally don't recommend it to be used. One of the many reasons also Firebase recommends against using arrays is that it makes the security rules impossible to write.
However, you can still model this kind of data by leveraging the other capabilities of Cloud Firestore.
Let's take an example. Suppose you have a database which look like this:
{
title: "My Book",
categories: [
"science",
"computer science",
"technology"
]
}
You need to know that if you want to query for all books that are part of the "science" category, using this data structure, there is no way to perform this query.
To solve this, you can consider an alternative data structure, where each category is the key in a map and all values are true.
{
title: "My Book",
categories: {
"science": true,
"computer science": true,
"technology": true
}
}
And to query your database you can use this query:
db.collection("books")
.whereEqualTo("categories.science", true)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {/* ... */});
For your particular case, you can use a database structure that looks like this:
{
title: "phonenumber",
categories: {
"12345": true,
"67890": true,
"43215": true
}
}
To query, please use the following code:
db.collection("phonenumbers")
.whereEqualTo("categories.12345", true)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {/* ... */});
Edit 13 Aug 2018:
According to the updated documentation regarding array membership, now it is possible to filter data based on array values using whereArrayContains() method. A simple example would be:
CollectionReference citiesRef = db.collection("cities");
citiesRef.whereArrayContains("regions", "west_coast");
This query returns every city document where the regions field is an array that contains west_coast. If the array has multiple instances of the value you query on, the document is included in the results only once.
I am trying to query data form Google Spreadsheet available to be read by anyone with the Read Only link.
I implemented this Quickstart solution but here is what I need:
Access data just with URL, no authentication needed
Query item in column A and get value in column B
No need for updating any data
I tried constructing queries like:
http://spreadsheets.google.com/tq?tq=SELECT%20*%20WHERE%20A=C298732300456446&key=2aEqgR1CDJF5Luib-uTL0yKLuDjcTm0pOIZeCf9Sr0wAL0yK
But all I get is:
/*O_o*/
google.visualization.Query.setResponse(
{
"version": "0.6",
"reqId": "0",
"status": "error",
"errors": [
{
"reason": "invalid_query",
"message": "INVALID_QUERY",
"detailed_message": "Invalid query: NO_COLUMN: C298732300456446"
}
]
}
This comes when the data is actually present in the sheet in column A with value C298732300456446.
What can I do for getting the data, without any authentication from my spreadsheet?
I am not sure if this can be done. If fine, I can suggest an alternative solution. You can try writing a Google App script like:
function doGet(e) { return getInfo(e); }
function doPost(e) { return getInfo(e); }
function getInfo(request) {
var someValueFromUrl = request.parameter.value;
var requiredValue = "";
var sheet = SpreadsheetApp.openById("spreadsheet_id");
var data = sheet.getDataRange().getValues();
for (var i = 0; i < data.length; i++) {
Logger.log("Reading row num: " + i);
if(data[i][0] == someValueFromUrl) {
requiredValue = data[i][1];
break;
}
}
Logger.log(requiredValue);
return ContentService.createTextOutput(JSON.stringify(requiredValue));
}
This way, you can publish this script as web app and call it using an URL which will be obtained when you publish this script.
Call the script like:
https://script.google.com/macros/s/obtained_url/exec?value=1234
If key is found, you will get the String response as:
"value"
I hope this helps.
C298732300456446 needs to be put within single quotes.
So you need to enclose C298732300456446 as %27C298732300456446%27
Your'e modified query would be
http://spreadsheets.google.com/tq?tq=SELECT%20*%20WHERE%20A=%27C298732300456446%27&key=2aEqgR1CDJF5Luib-uTL0yKLuDjcTm0pOIZeCf9Sr0wAL0yK
I'm unable to test this though - looks like you've removed/removed access from this spreadsheet.
I'm using this query to fetch the posts by user A and sort by timestamp.
This below query fetches the posts by user A but it doesn't sort by date.
mDatabase = FirebaseDatabase.getInstance().getReference("posts");
String UID = "userA";
Query myTopPostsQuery = mDatabase.equalTo(UID).orderByChild("UID");
I tried using below query, but it returns an empty set.
Query myTopPostsQuery = mDatabase.equalTo(UID).orderByChild("date");
What is the right way to achieve my result?
This is my Data Structure:
posts
-KlgYXK01ezPjk
UID: "YiXgM3qgqcsd"
date: 1496428200000
post: "This is a Test Post by user A"
,
-KlgYXK01ezPpl
UID: "YiXgM3qgqcsd"
date: 1496428220022
post: "This is another Test Post by user A"
,
-KlgYXK01ezKjk
UID: "YiXCWsdj712"
date: 1496428203000
post: "This is a Test Post by user B"
,
Well this may not be the exact answer you are expecting but it helps when your app scales up.
I recommend you use a fan out data structure.
By this create a separate node user-posts where you store all the posts by individual users like below:
user-posts
-YiXgM3qgqcsd //this us A's UID
KlgYXK01ezPjket4ery62
post: "This is a Test Post by user A"
date: 1496428200000
author:'A'
KlgYXK01ezPjket4ery62
post: "This is a 2nd Test Post by user A"
date: 1496428500000
author:'A'
KlgYXK01ezPjket4ery62
post: "This is a 3rd Test Post by user A"
date: 1496428600000
author:'A'
-YiXCWsdj712 //this us B's UID
KlgYXK01ezPjket4ery62
post: "This is a Test Post by user B"
date: 1496428200000
author:'B'
Now you can query for A's posts lik this:
mDatabase = FirebaseDatabase.getInstance().getReference("user-posts");
String UID = "userA";
Query myTopPostsQuery = mDatabase.child(UID).limitToLast(10);
Since pushing data into a node creates a unique key which are by default in chronological order you don't have to worry about sorting by timeline as you are using limitToLast() which gives you posts from the bottom i.e latest
So its better you push data to different nodes posts and user-posts whenever a user creates a post. This is better as writing data is cheap in firebase as compared to reading data
Now you can just pull out data from ref "user-posts/UID" instead of firebase querying data fromposts filtering all the posts by user A then again ordering by timeline which will be expensive and slow if you have many number of posts
When it comes to pushing data to different nodes i.e posts and user-posts this could be cheap and you can use updateChildren() method like below:
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
// Generate a new push ID for the new post
Firebase newPostRef = ref.child("posts").push();
String newPostKey = newPostRef.getKey();
// Create the data we want to update
Map newPost = new HashMap();
newPost.put("title", "New Post");
newPost.put("content", "Here is my new post!");
Map updatedUserData = new HashMap();
updatedUserData.put("user-posts/" + newPostKey, true);
updatedUserData.put("posts/" + newPostKey, newPost);
// Do a deep-path update
ref.updateChildren(updatedUserData, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Error updating data: " + firebaseError.getMessage());
}
}
});
You could refer to the firebase blog post here
I am new with Firebase. I want to implement ondelete cascade in Firebase.
Here is the problem for which I want solution
I have two table "users" and "groups".
{ "users":{
"user1":{
"username":"john",
"full_name":"John Vincent",
"created_at":"9th Feb 2015",
"groups":{
"group1":true,
"group3":true
}
"last_logins":...
},
"user2": ...,
"user3": ...
}"groups": {
"group1"{
"group_name":"Administrators",
"group_description":"Users who can do anything!",
"no_of_users":2,
"members":{
"user1":true,
"user3":true
}
},
"group2"{
"group_name":"Moderators",
"group_description":"Users who can only moderate!",
"no_of_users":1,
"members":{
"user2":true
}
}
}
}
Please pardon me for above code indentation.
Now if I removed user1 from users table then how it should be automatically removed from groups table using Firebase.
This can easily done using SQL but I don't know how to do this in Firebase. One way to do this in Firebase is to remove user1 from users and then makes group1 and group3 to null and then in groups table make user1 to null under group1/member but this need 2-3 calls. So is there any another best way to do this.
Please help me I am stuck here.
The Firebase Database has no knowledge of relations between values in its JSON tree. In SQL/relations terms: it doesn't have the concept of foreign keys. This means that it also doesn't have an option to delete related objects with a cascading delete. You will need to delete each value separately.
But you can combine all those deletes into a single call by using multi-location updates. If you write null to each of the locations for the user, you can delete all of them with one call to updateChildren():
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
Map<String,Object updates = new HashMap<String,Object>();
updates.put("users/user1", null);
updates.put("groups/group1/members/user", null);
// Do a deep-path update
ref.updateChildren(updatedUserData, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Error updating data: " + firebaseError.getMessage());
}
}
})
With this in place, you could then write security rules that validate that members of a group must also exist under the /users node:
{
"rules": {
"groups":
"$groupid": {
"users": {
"$uid": {
".validate": "newData.parent().parent().parent().parent().child('users').hasChild($uid)"
}
}
}
}
}
}
The validation rule is a bit easier to understand if you read the multiple .parent() calls as newRoot (which unfortunately doesn't exist). So in pseudo-code it is:
newRoot.child('users').hasChild($uid)
In words: a UID can only be a member of a group if it also exists under /users.
I want to add some certain data to a Firebase as arrays. Example:
groups : ['a','b','c']
How can I add and read data in Firebase from Android?
When you have a structure like that, you actually shouldn't be using an array to model it. It seems much more like a set in my eyes.
In the Firebase Database sets are best modeled as keys, since that automatically guarantees that items are unique. So your structure then becomes:
groups: {
"a": true,
"b": true,
"c": true
}
The true values are just markers, since Firebase won't allow you to store keys without a value.
Now to add a group to this, you'd use Firebase's setValue() function:
DatabaseReference root = FirebaseDatabase.getInstance().reference();
DatabaseReference groupsRef = root.child("groups");
groupsRef.child("d").setValue(true);
From the documentation:
setValue() - Record or change exists values
If you want to only append datas, you can to use updateChildren().
In Java, if we know that the data is array-like, it can be cast as a List:
Firebase julieRef = new Firebase("https://SampleChat.firebaseIO-demo.com/users/julie/");
julieRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<String>> t = new GenericTypeIndicator?<List<String>>() {};
List messages = snapshot.getValue(t);
if( messages === null ) {
System.out.println('No messages');
}
else {
System.out.println("The first message is: " + messages.get(0) );
}
}
// onCancelled...
});
Check this best practices post from the Firebase Blog.