0

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);
Jackie Degl'Innocenti
  • 1,251
  • 1
  • 14
  • 34

2 Answers2

0

Try arranging your data this way:

users: {
   one: {
      name: Mark
   },
   two: {
      name: Carl
   },
   three: {
      name: Gustav
   }
},
friends : {
   Mark : {
      two : true, 
      three : true
}
}
F0r3v3r-A-N00b
  • 2,903
  • 4
  • 26
  • 36
  • This not solve my problem about concurrent data retrieving. Moreover, what are the benefits of your model? In my model I can easily manage db permission to secure my data and guarantee access just to the data that belongs to the logged user. – Jackie Degl'Innocenti Aug 09 '16 at 22:34
  • See [Firebase Best practices for data structure](https://firebase.google.com/docs/database/android/structure-data). – F0r3v3r-A-N00b Aug 10 '16 at 00:20
  • I see that your model is better when the app goes to download an user node and it doesn't need to download the additional information about its friendships. Anyway your model require and additional security policy rule in firebase to enable an user to read just its node on the friends branch. – Jackie Degl'Innocenti Aug 10 '16 at 11:54
  • Yes you have to set it this way "rules": { "friends": { "$me": { ".read": "auth.uid === $me", ".write": "auth.uid === $me" } } } – F0r3v3r-A-N00b Aug 11 '16 at 01:13
  • Ok, but what do I have to do when: a friend (A) of the currentUser (B) un-friends him and I have to delete the friend relationship on both nodes (A, B) ? The security rule doesn't allow (A) to access (B) node. – Jackie Degl'Innocenti Aug 11 '16 at 11:53
0

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();
                            }
                        }
                    });
        }
    }
};
Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807