2

I actually have 2 questions both similar to each other. A brief overview of my issue is as follows:

The Library App I am developing has a one to many relationship with books and authors meaning one book can have many authors. A sample author and a book is attached as images.

a sample author

a sample book

Q1. How can I search for a specific author? For a book that has only 1 author, it will always have the zeroth index. How about a book which has multiple authors? If so the same author can exist in other indexes as well. I want to get all the books from a specific author and I am wondering what is the best way to do it?

Q2. How to find out whether the author has books under their name. I can actually get all the books and iterate through it. However, it will take a whole lot of bandwidth just to do an "EXISTS" operation. Is there an easy way to do it?

Thanks in advance!

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • 1
    I would suggest using Firestore ,because it has better query features. Refer this [link](https://firebase.google.com/docs/firestore/query-data/queries) – Tupio Aug 04 '18 at 05:49
  • Is there an easy way to migrate all my existing data to Cloud Firestore? – Charitha De Silva Aug 04 '18 at 05:54
  • 1
    These [Stackoverflow](https://stackoverflow.com/questions/46572567/migrate-firebase-realtime-database-to-firestore),[Official](https://firebase.google.com/docs/firestore/firestore-for-rtdb) posts might help. (BONUS)[Here](https://medium.freecodecamp.org/rtdb-to-firestore-fd8da8149877) is information packed post I found online. – Tupio Aug 04 '18 at 06:02
  • Thanks a lot for the links and heads up. I have studied a lot about how to move my data and I think I can do it with a script. – Charitha De Silva Aug 04 '18 at 13:32
  • 1
    Great that you found it helpful ,give a upvote so others can find it.Cheers! – Tupio Aug 04 '18 at 13:36

1 Answers1

2

You can migrate to Cloud Firestore but if you are already using the Firebase real-time database you can achieve this, but not without some changes in your database.

I want to get all the books from a specific author and I am wondering what is the best way to do it?

Unfortunately, you cannot achieve what you you using your actual database structure. To get all the books from a specific author, you should consider augmenting your data structure to allow a reverse lookup. That implies you to create a new top level collection named books in which you should store as book objects all the books of a particular author. You database schema should look like this:

Firebase-root
  |
  --- books
        |
        --- authorIdOne
              |
              --- bookIdOne
              |     |
              |     --- id: "bookIdOne"
              |     |
              |     --- nameInEnglish: "nameInEnglish"
              |     |
              |     --- nameInSinhalese: "nameInSinhalese"
              |
              --- bookIdTwo
                    |
                    --- id: "bookIdTwo"
                    |
                    --- nameInEnglish: "nameInEnglish"
                    |
                    --- nameInSinhalese: "nameInSinhalese"

To get all the books from a specific author you need to attach a listener on its id (authorIdOne) and loop through its children.

DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference yourRef = authorIdOneRef.child("books").child(authorIdOne);
authorIdOneRef.addListenerForSingleValueEvent(/* ... */);

How to find out whether the author has books under their name?

To solve this, you need to use exists() method on the DataSnapshot object. Assuming you are using a model class named Book, please see the following lines of code:

ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if(dataSnapshot.exists()) {
            for(DataSnapshot ds : dataSnapshot.getChildren()) {
                Book book = ds.getValue(Book.class);
                Log.d("TAG", book.getNameInEnglish());
            }
        }
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {}
};
authorIdOneRef.addListenerForSingleValueEvent(valueEventListener);

But the same thing can be achieved using the String class.

ValueEventListener valueEventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        if(dataSnapshot.exists()) {
            for(DataSnapshot ds : dataSnapshot.getChildren()) {
                String nameInEnglish = ds.child("nameInEnglish").getValue(String.class);
                Log.d("TAG", nameInEnglish);
            }
        }           
    }

    @Override
    public void onCancelled(@NonNull DatabaseError databaseError) {}
};
authorIdOneRef.addListenerForSingleValueEvent(valueEventListener);

And note, the Firebase real-time database doesn't store arrays. What you are actually having is an object. See here more details.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thanks a lot for the detailed answer. As you mentioned and as I feared my database structure seems to be the issue here. Hence I think of moving to Cloud Firestore at this point might be the best option considering the fact that it will ensure less hassle in future functionalities when I implement them. – Charitha De Silva Aug 04 '18 at 13:30
  • 1
    In this case, good luck with that. It'a a very nice product and easy to learn. – Alex Mamo Aug 04 '18 at 13:41
  • Just did! Before I couldn't vote up since I didn't have enough rep. – Charitha De Silva Aug 04 '18 at 13:57