firebase read and write - in one event - android

i have a real time firebase database ...
1 - i need to create a child when there is no child exist in database in the name "book"
book structure:
"book":[{slno:1,
"name":"harry potter",
"author":"J. K. Rowling"
}]
2- if database have the child named "book" then i need to create new child for "book"
but this time i want the "slno" , last added child's "slno + 1"
{ slno:"//last added child's slno + 1",
"name":"new book",
"author":"sample author"
}
tell me a best way to write a code on above situation ...
i am new to firebase...
[platform : Android]
i already tried this[and it's working]
but i don't know its good solution when happen database write operation
from multiple divices at same time [i want , never repeat slno]
... any one have better solution
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("book")) {
// run some code
secondtime();
}else {
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference();
BookModelClass bookModel = new BookModelClass(1,"harry potter","J. K. Rowling");
databaseP2.child("book").push().setValue(bookModel);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
private void secondtime(){
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference();
Query lastQuery = databaseP2.child("book").orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int slno = 0 ;
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
slno =postSnapshot.child("slno").getValue(Integer.class);
}
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference("book");
BookModelClass bookModel = new BookModelClass(slno+1,"new book","sample author");
databaseP2.push().setValue(bookModel);
}
#Override
public void onCancelled(DatabaseError databaseError) {
//Handle possible errors.
}
});
}

Since the new value of the node depends on the existing value (or its existence at all), you'll want to use a Firebase Database transaction for this operation.
This means you'll need to read the entire book node, but your current solution also does that, so it's the same in read performance. It'll be something along these lines:
rootRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.hasChildren()) {
int slno = 0 ;
for (MutableData postData: mutableData.getChildren()) {
slno = postData.child("slno").getValue(Integer.class);
}
BookModelClass bookModel = new BookModelClass(slno+1,"new book","sample author");
String newKey = rootRef.push().getKey();
// Set value and report transaction success
mutableData.child(newKey).setValue(bookModel);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "bookTransaction:onComplete:" + databaseError);
}
});

Related

Comments not in correct order by their names and timestamp firebase database

The comment section of my application is not working properly, the childs are mixing up by their names and the timestamp, the content inside of them it is in correct order, here are all of my code inside Pastebin links (if necessary to see the whole code), also I provided the chunks I think that the problem is in.
1) CommentAdapter.java Activity (https://pastebin.com/m7LHDUDF)
Take note of this block of code:
private void getHandleName(final ViewHolder viewHolder) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Log.d(TAG, "getHandleName: checking comment userID" + viewHolder.comment.getUser_id());
Query query;
query = reference
.child("data")
.child("-Kxzyb5JsUPhsMQAb84X")
.child("users")
.orderByChild("user_id")
.equalTo(viewHolder.comment.getUser_id());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) {
viewHolder.handleName.setText(singleSnapshot.getValue(User.class).getHandlename());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
2) CommentRecylerViewAdapter (https://pastebin.com/Tb7L9EVD)
private void getHandleName(final CommentViewHolder viewHolder, Comment comment) {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Log.d(TAG, "getHandleName: checking comment userID" + comment.getUser_id());
Query query = reference
.child("data")
.child("-Kxzyb5JsUPhsMQAb84X")
.child("users")
.orderByChild("user_id")
.equalTo(comment.getUser_id());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) {
viewHolder.handleName.setText(singleSnapshot.getValue(User.class).getHandlename());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
3)Chunk of code for adding the comment:
` private void addComment() {
if (commentText.getText().toString().isEmpty()) {
Toast.makeText(ViewPostActivity.this, "Please enter your comment", Toast.LENGTH_SHORT).show();
} else {
String currentUserID = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
String commentID = reference.push().getKey();
Comment comment = new Comment();
comment.setCaption(commentText.getText().toString());
comment.setDate_created(System.currentTimeMillis());
comment.setUser_id(currentUserID);
reference.child("data").child("-Kxzyb5JsUPhsMQAb84X").child("comments").child(postID).child(commentID).setValue(comment);
setNumComment();
setNumPointCurrentUser();
setNumPointUser();
setNumPointPost();
}
}`
4) **And Finally, the comment model Activity** which I think is ok(https://pastebin.com/VaYV3Gv3)
Here is a screenshot of my database:
-Kxzyb5JsUPhsMQAb84X is the root of my database
comments is where all the comments are store per post
-LSv6lXZml-Cf0GX3i5q randomly generated child that stores the other content such as Timestamps,user id and content
Here is the screenshot of my phone showing the incorrect order of the comments:

Android Firebase, Get position of key in node

I want to get data according to user's ranking. First order by user's points and get users position in node. But I can't think of the way to implement this.
First This is my firebase 'users' node
Maybe It will be like 'databaseRef.child("users").orderByChild("points")...'
I don't know from here.
ps. It's not get nth item. It's 'where is the item' ( item's nth )
Thank you!
This can be done by adding a ctr in you dataSnapshot loop
myRef = FirebaseDatabase.getInstance().getReference().child("users");
final Query query = myRef.orderByChild("points");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int total = dataSnapshot.getChildrenCount();
int i = 0;
// loop through dataSnapshot
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
String nickName = childSnapshot.child("nickName").getValue(String.class);
if (nickName.equals(mUser.getNickName()) {
//when nickName would match
int userPlace = total - i;
int points = childSnapshot.child("points").getValue(Interger.class);
//do something here
break;
} else {
i++;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Kindly read
Retrieving Data.
How Data is Ordered
The callback function receives a DataSnapshot, which is a snapshot of
the data. A snapshot is a picture of the data at a particular database
reference at a single point in time. Calling val() / getValue() on a
snapshot returns the a language-specific object representation of the
data. If no data exists at the reference's location, the snapshot's
value is null.
If you want to get all points then check this LOGIC.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("products");
ref.addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ArrayList<Integer> points = new ArrayList<>();
for (Map.Entry<String, Object> entry : products.entrySet()){
Map singleUser = (Map) entry.getValue();
points.add((Integer) singleUser.get("points"));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
//handle databaseError
}
});
I solve this problem. Android_K.Doe's answer is right. I add some code in his code. Because Firebase database does not give descending query.
final Query query = databaseReference.child("users").orderByChild("points");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int i = 0;
int all = (int) dataSnapshot.getChildrenCount();
Log.d(TAG, "all: " + all);
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String nickName = ds.child("nickName").getValue(String.class);
if (nickName.equals(user.getNickName())) {
Log.d(TAG, "nickName: " + user.getNickName());
int userPlace = i;
Log.d(TAG, "position: " + userPlace);
myInfoRankingNumTxt.setText(Integer.toString(all - (i)) + "위");
break;
} else {
i++;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

firebase get least time taken returning null value

Hey I am trying to find the least time taken by a user by the following code but for some reason it says that the value of timeTaken is null.
When I logged in it shows the incorrect user's time taken so what could be the probable solution.
private void leastTimeTaken() {
final DatabaseReference databaseReference1 = firebaseUrl.child("users");
final Query query = databaseReference1.orderByChild("timeTaken").limitToFirst(1);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot userAnswerSnapshot : dataSnapshot.getChildren()) {
if (userAnswerSnapshot.child("questions").child(imagename).hasChild("timeTaken")) {
long a = userAnswerSnapshot.child("questions").child(imagename).child("timeTaken").getValue(Long.class);
DatabaseReference userPuzzleDetails = FirebaseDatabase.getInstance().getReference().child("users").child(uid).child("questions").child(imagename);
userPuzzleDetails.child("leastTime").setValue(a);
}
query.removeEventListener(this);
databaseReference1.removeEventListener(this);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Firebase query n items and listen to new only

I am trying to query last 5 items and then listen only to new childs. However,
when I add new child, childeventlistener returns the last child from thos 5 i queried earlier.
query = myRef.orderByKey().limitToLast(5);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// dataSnapshot is the "issue" node with all children with id 0
int oldSize = mData.size();
List<ChatMessageEntity> data = new ArrayList<>();
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual "issues"
ChatMessageEntity chat = issue.getValue(ChatMessageEntity.class);
data.add(0,chat);
}
mData.addAll(data);
adapter.notifyItemRangeInserted(oldSize, mData.size());
}
query.addChildEventListener(newMessageListener);
}
#Override public void onCancelled(DatabaseError databaseError) {
}
});
With this type of scenario you're better off using a ChildEventListener. This has a method onChildAdded that will be called for (up to) five children initially and then for each new child added later.
query = myRef.orderByKey().limitToLast(5);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
ChatMessageEntity chat = issue.getValue(ChatMessageEntity.class);
data.add(0,chat);
adapter.notifyItemInserted(mData.size()-1);
}
...
#Override public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore these
}
});
The above code works, but is missing handling of previousChildName (as children that arrive later might not be added to the end of the list), onChildChanged, onChildMoved and onChildRemoved. Adding handling for all of these is a fun exercise, but quite a bit of work and a bit tricky to get right. If you want to complete the work, I recommend taking a look at the RecyclerViewAdapter in FirebaseUI and its work horse FirebaseArray - which are nicely battle tested.
For more information see the Firebase documentation on listening for child event.
The best i make up is to load required number of items, save key of the newst one. Remove item with that key from list. Create new query which starts from key of the newest and add childEventListener to it.
query = myRef.orderByKey().limitToLast(20);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// dataSnapshot is the "issue" node with all children with id 0
int oldSize = mData.size();
//List<ChatMessageEntity> data = new ArrayList<>();
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual "issues"
ChatMessageEntity chat = issue.getValue(ChatMessageEntity.class);
chat.key = issue.getKey();
mData.add(chat);
//data.add(0, chat);
}
//mData.addAll(data);
}
Log.e("mcheck", "onDataChange: " + mData.get(0).message);
Query query;
if (mData.size() > 0) {
query = myRef.orderByKey().startAt(mData.get(mData.size() - 1).key);
olderstKey = mData.get(0).key;
mData.remove(mData.size() - 1);
adapter.notifyItemRemoved(mData.size()-1);
adapter.notifyItemRangeInserted(0, mData.size());
//adapter.notifyDataSetChanged();
query.addChildEventListener(newMessageListener);
} else {
myRef.addChildEventListener(newMessageListener);
}
}
#Override public void onCancelled(DatabaseError databaseError) {
}
});

How to get a list of nodes by their key

I'm developing an application for Android that uses firebase.
The application has Users and each user has Friends.
users: {
one: {
name: Mark,
friends: {
two: true,
three: true
},
two: {
name: Carl
},
three: {
name: Gustav
}
}
In this example, Mark has two friends (Carl and Gustav), the other two don't have friends.
I want to get a Mark's Friends List.
String userId = "one";
DatabaseReference friendsDb = db.getReference("users").child(userId).child("friends");
final DatabaseReference usersDb = db.getReference("users");
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
GenericTypeIndicator<LinkedHashMap<String,Boolean>> t = new GenericTypeIndicator<LinkedHashMap<String,Boolean>>() {};
LinkedHashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
// how to return the user???
// users.add(user);
}
});
}
mAdapter.notifyDataSetChanged();
}
};
friendsDb.addValueEventListener(friendsListener);
Am I using a correct approach about data modeling and indexes?
How is it supposed to give me back the user list that I need?
I understand that listening to a resource it is an async operation, is there a way to get the values that I need in one shot?
Any help will be appreciated! Thanks!
EDIT
Solution proposed by Frank van Puffelen it's correct in the concept but it's not correctly implemented. The concept is to call the mAdapter.notifyDataSetChanged(); when all the children has been retrieved from firebase db. But it have to check the dimension of the first snapshot, intead of the second, as below.
DatabaseReference friendsDb = db.getReference("users").child(userId).child("friends");
final DatabaseReference usersDb = db.getReference("users");
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
GenericTypeIndicator<HashMap<String,Boolean>> t = new GenericTypeIndicator<HashMap<String,Boolean>>() {};
HashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
final int usersSize = tDataset.size();
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
users.add(user);
if (users.size() == usersSize) {
mAdapter.notifyDataSetChanged();
}
}
});
}
}
};
friendsDb.orderByKey().addValueEventListener(friendsListener);
There is no Firebase equivalent of SQLs WHERE id IN (1,2,3). Performance-wise that is not needed, since Firebase pipelines the requests.
You code looks fine to me, except that you're not adding the user to the list. I expect that you're having trouble defining the "exit condition" for that loop, which is:
ValueEventListener friendsListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
GenericTypeIndicator<LinkedHashMap<String,Boolean>> t = new GenericTypeIndicator<LinkedHashMap<String,Boolean>>() {};
LinkedHashMap<String,Boolean> tDataset = dataSnapshot.getValue(t);
users.clear();
for( String userId : tDataset.keySet() ) {
usersDb.child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
users.add(user);
if (users.size() == dataSnapshot.getChildrenCount()) {
mAdapter.notifyDataSetChanged();
}
}
});
}
}
};
Try arranging your data this way:
users: {
one: {
name: Mark
},
two: {
name: Carl
},
three: {
name: Gustav
}
},
friends : {
Mark : {
two : true,
three : true
}
}

Categories

Resources