I'm trying to add chat feature to an Android App with Firebase Realtime DB as storage.
My schema is something like this,
conversation: {
conversation_id_generated_by_firebase: {
last_message: {
message: "hehe"
type: "text"
attachments: {0: "url", 1: "url"}
likes: {0: "uid1", 1: "uid2"}
created_at: 1245789898
},
messages: {
m1: {
message: "hehe"
type: "text"
attachments: {0: "url", 1: "url"}
likes: {0: "uid1", 1: "uid2"}
created_at: 1245789898
}, m2: {}, m3: {}, m4: {}
},
read_till: {
uid1: 14567894229
uid2: 14567894228
},
created_at: 1456789329
updated_at: 1456789429
},
}
Now, How can I fetch multiple conversations with ids in one query/call?
There is no Firebase equivalent to SQL's SELECT * WHERE id IN (1,2,3). In Firebase you'll retrieve each item separately.
This is not nearly as slow as most developers think, since Firebase can efficiently pipeline such requests over a single connection. See my answer here for a longer explanation (and some ASCII art): Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly
By retrieving ID do you mean you want to retrieve "conversation_id_generated_by_firebase", If yes, i have implemented somewhat similar thing, have modified code as per your DB details. If you could provide some code from what you have tried i can help you further.
ArrayList<String> keyList = new ArrayList<>();
// i am assuming you have Chat Object for each conversation
ArrayList<Chat> chatList = new ArrayList<>();
myRef = database.getReference().child("conversation");
ValueEventListener chatListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//will be called every time a new conversation is added
if(dataSnapshot != null){
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
String key = postSnapshot.getKey();
//store key(your id) in array list
keyList.add(key);
//now i am assuming you have an object of chat message
Chat chat = postSnapshot.getValue(Chat.class);
//add all conversation to chatList ArrayList
chatList.add(chat);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
//handle error incase something fails
}
};
myRef.addValueEventListener(chatListener);
Related
that's my database
{
"users" : {
"-MdgDcU3Zb-TiLlTwfwU" : {
"email" : "t1#email.com",
"fname" : "test",
"lname" : "1",
"password" : "t1",
"phoneno" : "304*******"
},
"-MdgNDN23XCTyB4DsF4W" : {
"email" : "t2#email.com",
"fname" : "test",
"lname" : "2",
"password" : "t2",
"phoneno" : "333*******"
}
}
}
that's the login activity's code
DatabaseReference reference= FirebaseDatabase.getInstance().getReference("users");
Query checkUser = reference.orderByChild("phoneno").equalTo(userphoneno);
checkUser.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if(snapshot.exists()){
//editTextphoneno.setError(null);
//editTextphoneno.setErrorEnabled(false);
String passwordFromDB =snapshot.child(userphoneno).child("password").getValue(String.class);
if(passwordFromDB.equals(userpassword)){
//editTextphoneno.setError(null);
//editTextphoneno.setErrorEnabled(false);
Intent i = new Intent(Login_page.this, Bottomnav.class);
startActivity(i);
finish();
}else{
editTextpassword.setError("Invalid Password");
editTextpassword.requestFocus();
}
}else{
editTextphoneno.setError("Invalid Phone Number");
editTextphoneno.requestFocus();
}
}
I want the user to login through his phone number and password but every time, after entering phone no and password I click on login, the app crashes. What I have understood is that may be the user's phone number and password is not being retrieved successfully.
When you are using the following query:
DatabaseReference reference= FirebaseDatabase.getInstance().getReference("users");
Query checkUser = reference.orderByChild("phoneno").equalTo(userphoneno);
It means that you are searching the "users" node, for all users who have the "phoneno" equal to "userphoneno". This also means that such a query may potentially return multiple results. Even if the query returns a single result, you'll need to loop through the DataSnapshot object using getChildren() method, as in the following lines of code:
checkUser.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
for (DataSnapshot ds : task.getResult().getChildren()) {
String passwordFromDB = ds.child("password").getValue(String.class);
Log.d("TAG", passwordFromDB);
//Add your logic
}
} else {
Log.d("TAG", task.getException().getMessage()); //Don't ignore potential errors!
}
}
});
Please see that inside onComplete, in order to get the password, there is no need for a .child(userphoneno) call, as the "password" is already a child of the DataSnapshot object.
More important, never store sensitive data as passwords in plain text. Malicious users might take advantage of that. Use Firebase Authentication for that and secure the database using Firestore Security Rules.
I want to retrieve only that post where postCategory matches the user interest.
How can I achieve this type of query. Any Help??
Below is my json file of database..
"Posts" : {
"-MAKobGjdhMfJFwZ1kol" : {
"postCategory" : "Animals",
"postDesc" : "still",
"postImage" : "Image Link",
"postPersonName" : "Name",
"postPersonUid" : "CLQA22uZpLS1ErIrnoBTrC3xqBw2",
"postTitle" : "ek"
},
"Users" : {
"CLQA22uZpLS1ErIrnoBTrC3xqBw2" : {
"email" : "email",
"interest" : {
"-MA14zFcNfs1JAjux129" : {
"name" : "Developer"
},
"-MA14zFgzhL-MOBB7D6h" : {
"name" : "Shopping"
}
},
"name" : "Name",
"uid" : "CLQA22uZpLS1ErIrnoBTrC3xqBw2"
}
In firebase database you cannot have such queries.
You should use Firestore (https://firebase.google.com/docs/firestore) for such advanced queries.
firestore/query-data
firestore
To get all posts with postCategory equal to Animals, you'd do:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Posts");
Query query = ref.orderBy("postCategory").equalTo("Animals");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
Log.i(TAG, postSnapshot.getKey()+": "+postSnapshot.child("postTitle").getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
If you want to retrieve posts for multiple categories, you'll have to perform a separate query for each of those and merge the results on your client.
I'd also recommend changing how you store the categories for a user. The idiomatic way is:
"Users" : {
"CLQA22uZpLS1ErIrnoBTrC3xqBw2" : {
"email" : "email",
"interest" : {
"Developer": true,
"Shopping": true
}
},
The reasons this is more common is that it automatically ensures that each value can occur only once in the interest object, since property names are by definition unique.
Why is the list I retrieve from the Firebase Database not ordered with orderByKey()?
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
transactions = mDatabase.child("balance").child("users").child(userid).child("log").orderByKey();
transactions.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
Map<String, Object> td = (HashMap<String,Object>) snapshot.getValue();
ArrayList<Object> values = new ArrayList<Object>();
values.addAll(td.values());
Log.d("Transactions", values.toString());
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
The ArrayList "values" is unordered.
This is an example of the structure in json format:
"balance" : {
"users" : {
"log" : {
"-Kp0plO6zBeulHQ8S1Fj" : {
"In" : 100
},
"-Kp0qHM73Mcwbli1gaK6" : {
"Out" : -100
},
"-Kp0qgkORJx5sTi2rUZf" : {
"In" : 100
},
"-Kp1OCdSpUA7SEZhVoPR" : {
"In" : 100
},
"-Kp1OdtuuF6z_zlNj2St" : {
"In" : 100
}
}
}
}
When you retrieve a collection from Firebase, it receives three pieces of information for each child node:
the key of the child
the value of the child
the relative order of the child
When you call snapshot.getValue() in your current code, you tell Firebase to convert this into a Map<String, Object>. Since a Map is unordered, the Firebase client keeps the keys and values but loses the ordering of the children.
To ensure you keep the order, you must use DataSnapshot.getChildren() to process the result:
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
transactions = mDatabase.child("balance").child("users").child(userid).child("log").orderByKey();
transactions.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
ArrayList<Object> values = new ArrayList<Object>();
for (DataSnapshot child: snapshot.getChildren)) {
values.addAll(child.getValue());
}
}
To achieve what you want you need to change this line of code:
transactions = mDatabase.child("balance").child("users").child(userid).child("log").orderByKey();
with
transactions = mDatabase.child("balance").child("users").child(userid).child("log").orderByChild("In");
I'm building a sport manager app that let the users create their own competitions and update the match results to be consults for other participants.
I read a lot about how flat the database (I come from SQL :-) and saw many video tutorials from the Firebase Team.
At this point I have this structure:
{sport-manager :
...
groups : {
"groupId1" : {
competitionId : "competitionId1"
name : "Group A",
type : "LEAGUE"
},
"groupId2" : {
competitionId : "competitionId1"
name : "West playoofs",
type : "PLAYOFFS"
},
...
},
players : {
"playerId1" : {
birthday : 351408600000,
firsName : "Jhon",
lastName : "Doe",
shirtNumber : 0,
teamId : "teamId1"
},
...
},
teams : {
"teamId1" : {
competitionId : "competitionId1"
name : "Team 1",
},
...
},
teamGroupStats : {
"groupId1" : {
"teamId1" : {
goalsAgainst: 0,
goalsFavor: 0,
matchesDraw: 0,
matchesLoss: 0,
matchesPlayed: 0,
matchesWin: 0,
points: 0,
teamName: "Team 1",
},
"teamId2" : {
...
},
...
},
"groupId2" : {
"teamId2" : {
...
},
...
}
...
}
}
This structure let me make queries to populate a ListView adapter with the data of all the teams that are part of a group in this way:
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Query groupQuery = mDatabase.child("teamGroupStats").child("groupId1")
Or when I have to get the player from determinated team:
Query playersQuery = mDatabase.child("players").orderByChild("teamId").equalTo("teamId1");
So far, so good. My problem come when I have to delete a team from this structure. For avoid integrity problems I use multipath update to delete all.
A team can have one or more players
A team can be part of one or more groups.
With that in mind, I can delete any teams with all his players.
Map<String, Object> deleteMap = new HashMap<>();
deleteMap.put("teams/teamId1", null);
Query playersQuery = mDatabase.child("players").orderByChild("teamId").equalTo("teamId1");
playersQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot playerSnapshot : dataSnapshot.getChildren()){
deleteMap.put("players/" + playerSnapshot.getKey(), null);
}
// Delete all in one call!
mDatabase.updateChildren(deleteMap);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
How I can make a query to get all the paths with "teamId1" has secundary key in the structure "teamGroupStats"?
His this structure good for the use I need?
IMHO your database is very well structured. Regarding your question, you cannot get all paths as you say. What can you do, is to delete data in the same way as you add it. So in order to delete all data i recomand you using this method:
private static void deleteTeam(String teamId) {
Map<String, Object> map = new HashMap<>();
map.put("/teams/" + teamId, null);
map.put("/teamGroupStats/groupId1/" + teamId, null);
map.put("/players/playerId1/teamId/", null);
databaseReference.updateChildren(map);
}
A more generic method will be:
private static void deleteTeam(String reference, String teamId) {
Map<String, Object> map = new HashMap<>();
map.put(reference, null);
databaseReference.updateChildren(map);
}
If you need to query the database to get additional data, use getRef() method to get the reference and pass those values to the second method.
All your data will be deleted at once atomically.
Hope it helps.
Finally I found one solution. I modified my database to store all the groups Id's where the team belong inside the Team structure.
teams : {
"teamId1" : {
competitionId : "competitionId1"
groups : {
"groupId1" : true,
"groupId6" : true,
...
},
name : "Team 1",
},
...
With that information, I can add an inner ValueEventListener in the playersQuery's listener and make the update/delete.
public static void deleteTeam(final String teamId){
Map<String, Object> deleteMap = new HashMap<>();
deleteMap.put("teams/" + teamId, null);
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();
Query playersQuery = mDatabase.child("players").orderByChild("teamId").equalTo(teamId);
playersQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot playerSnapshot : dataSnapshot.getChildren()){
String playerId = playerSnapshot.getKey();
deleteMap.put("players/" + playerId, null);
}
mDatabase.child("teams").child(teamId).child("groups")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot groupSnapshot : dataSnapshot.getChildren()){
String groupId = groupSnapshot.getKey();
deleteMap.put("teamGroupStats/" + groupId + "/" + teamId, null);
}
// Delete all in one call!
mDatabase.updateChildren(deleteMap);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("The read failed: " + databaseError.getCode());
}
});
}
I wasn't very sure about the inner listener, and I had to made a little change to my database structure, but this works fine.
If anybody knows a better way to do this, I'm open to read it. Thanks!
This question already has an answer here:
Firebase query if child of child contains a value
(1 answer)
Closed 6 years ago.
What is the proper way to query for every Comment (POJO) attached to a Photo (POJO) in Android. In Firebase, I have the following structure for a photo collection:
"photos" : {
"-KaeuYf9jXcy4oqpRqbi" : {f
"entities" : {
"type" : {
"image" : {
"bucket" : "bucket",
"prefix" : "https://s3-us-west-2.amazonaws.com/",
"suffix" : "image.jpg"
}
}
},
"stats" : {
"liked" : 0
},
"text" : "Person Detected!",
"timestamp" : 1484631194257
}
}
and for comments collection:
"comments" : {
"-KdE6Hwua8d6sBQimPos" : {
"attachphoto" : {
"-KaeuYf9jXcy4oqpRqbi" : true
},
"attachuser" : {
"-KaeuYHjkdf9okflslf" : true
},
"displayName" : "Gary",
"text" : "ok",
"timestamp" : 1487385995844
},
"-KdE6IPc-NL-6zGkwXq3" : {
"attachphoto" : {
"-KaeuYf9jXcy4oqpRqbi" : true
},
"attachuser" : {
"-KaeuYHjkdf9okflslf" : true
},
"displayName" : "Thomas",
"text" : "ok",
"timestamp" : 1487385997735
}
}
In Android I'm using a FirebaseAdapter, but I am having trouble defining a proper Query that would only retrieve comments attached to a specific photo. I have the key of the Photo
FirebaseListAdapter adapter = new CommentAdapter(this, Comment.class, R.layout.item_comment, QUERY){
#Override
protected void populateView(View v, Comment model, int position) {
if(model.getDisplayName()!=null) {
String[] name = model.getDisplayName().split(" ");
((TextView) v.findViewById(R.id.id)).setText(name[0]);
}
((TextView) v.findViewById(R.id.content)).setText(model.getText());
}
};
I thought I could define something like:
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference commentsRef = database.getReference("comments");
Query query = commentsRef.orderByChild("attachphoto").equalTo()
But I'm having some disconnect on how to finish this. First project using Firebase, so any help/tips would be much appreciated!
Firebase is rather noSQL, therefore there are limits which one has to work around... especially when designing the data-structure. for example, in this case it might make sense to add the ids of the related users & comments (or even the related comments altogether) into a nested node of the structure, which represents the photo. there are other ways to get there - while one always needs to consider how to look up the details later on (because the structure dictates which queries are possible and which not).