简体   繁体   中英

How to get an array from Firestore?

I have the data structure illustrated below stored in Cloud Firestore. I want to save the dungeon_group which is an array of strings stored in Firestore.

I have difficulty in getting the data and stored as an array. I am just able to get a weird string but any method to store as a string array? Below is the code I used.

I am able to achieve this in Swift as follow, but not sure how to do the same in Android.

在此处输入图片说明

Swift:

Firestore.firestore().collection("dungeon").document("room_en").getDocument { 
    (document, error) in
    if let document = document {
        let group_array = document["dungeon_group"] as? Array ?? [""]
        print(group_array)
    }    
}

Java Android:

FirebaseFirestore.getInstance().collection("dungeon")
                 .document("room_en").get()
                 .addOnCompleteListener(new 
                     OnCompleteListener<DocumentSnapshot>() {
                     @Override
                     public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                         DocumentSnapshot document = task.getResult();
                         String group_string= document.getData().toString();
                         String[] group_array = ????
                         Log.d("myTag", group_string);
                     }
                 });

Console output as the follow:

{dungeon_group=[3P, Urgent, Mission Challenge, Descended, Collaboration, Daily, Technical, Normal]}

When you call DocumentSnapshot.getData() , it returns a Map. You're just calling toString() on that map, which is going to give you a dump of all the data in the document, and that's not particularly helpful. You need to access the dungeon_group field by name:

DocumentSnapshot document = task.getResult();
List<String> group = (List<String>) document.get("dungeon_group");
  • edit: syntax error in typecasting

There are two solutions for your problem, one, you can cast the value from your document in the next way:

DocumentSnapshot document = task.getResult();
List<String> dungeonGroup = (List<String>) document.get("dungeon_group");

Or, and I would recommend you this solution because there is always a possibility that your model will change when you are developing your app . This solution is just model everything in Firebase POJO's even if they have just one parameter:

public class Dungeon {

    @PropertyName("dungeon_group")
    private List<String> dungeonGroup;

    public Dungeon() {
        // Must have a public no-argument constructor
    }

    // Initialize all fields of a dungeon
    public Dungeon(List<String> dungeonGroup) {
        this.dungeonGroup = dungeonGroup;
    }

    @PropertyName("dungeon_group")
    public List<String> getDungeonGroup() {
        return dungeonGroup;
    }

    @PropertyName("dungeon_group")
    public void setDungeonGroup(List<String> dungeonGroup) {
        this.dungeonGroup = dungeonGroup;
    }
}

Remember that you can use the Annotation @PropertyName to avoid call your variables in the same way that your value in the database. Doing it in this way finally you can just do:

DocumentSnapshot document = task.getResult();
Dungeon dungeon= toObject(Dungeon.class);

Hope that it will help you! Happy coding!

If you want to get the entire dungeon_group array you need to iterate over a Map like this:

Map<String, Object> map = documentSnapshot.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
    if (entry.getKey().equals("dungeon_group")) {
        Log.d("TAG", entry.getValue().toString());
    }
}

But note, even if the dungeon_group object is stored in the database as an array, entry.getValue() returns an ArrayList and not an array.

A better approach for you would be if you consider this alternative database structure, in which each group is the key in a Map and all values are set to the boolean true :

dungeon_group: {
    3P: true,
    Urgent: true,
    Mission Chalange: true
    //and so on
}

Using this structure you'll also be able to query it based on the property that exists within the dungeon_group map, otherwise as in the official documentation :

Although Cloud Firestore can store arrays, it does not support querying array members or updating single array elements.

Edit 13 Jan 2021:

If instead of an array of String values you would have had an array of objects, then you can map that array of objects to a List of custom objects, as explained in the following article:

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.

Since Your document looks like this "dongeon_group=[SP, urgent, missinon challenge,...] when you convert it to string, say via String.valueOf(document.getData())

I think another way to simply achieve this is by unpacking the document right away into a string of array as follows:

String[] unpackedDoc = document.getData().entrySet().toArray()[0].toString().split("=")[1].split(",");

Explanation

The document is gotten from documentSnapshot.getDocuments() which return a list containing you documents.

Since your documents, from the firestore, seem to be nested, calling the document.getData() will return the list, a list with single element of course, of your document. document.getData().entrySet() will make the document ready to be converted to array (you can't do that with getData() alone) also containing single element. Accessing the single element document.getData().entrySet().toArray()[0] and then converting it to string, document.getData().entrySet().toArray()[0].toString() will leave with a string that you can then split (using the = found in the string) and then take the second part. The second part can also be split into an array containing the values of your document.

Since this solution convert one element at a time, you can wrap it within a loop so you can convert all the documents available.

For example:

for(DocumentSnapshot document :documentSnapshot.getDocuments()){ String[] unpackedDoc = document.getData().entrySet().toArray()[0].toString().split("=")[1].split(","); //do something with the unpacked doc

}

How I archive this in my Production app.

Global Declare

private final FirebaseFirestore FIRE_STORE_DB;

Reference

this.FIRE_STORE_DB = FirebaseFirestore.getInstance();

Collection Method

 public CollectionReference getCOLLECTION_REF() {
    return FIRE_STORE_DB.collection(Global.COLLECTION_USER);
}

My recuirment is fetch last index of arrays time to current time if it equal return false.

public void dailyCheck(String UID, OnCheckIn onCheckIn) {
    getCOLLECTION_REF().document(UID).get().addOnSuccessListener(documentSnapshot -> {
        if (documentSnapshot.exists()) {
            List< String > dateList = (List< String >) documentSnapshot.get(FIELD_DAILY_CHECK_IN);
            if (dateList != null) {
                String lastDate = dateList.get(dateList.size() - 1);
                if (!lastDate.equals(getCurrentTimeStamp())) {
                    onCheckIn.todayCheckIn(false);
                } else {
                    Log.e(TAG, "LAST DATE EQUAL ---------------> RETURN");
                    onCheckIn.todayCheckIn(true);
                }
            } else {
                Log.e(TAG, "DATE LIST ---------------> NULL RETURN");
            }

        } else {
            Log.e(TAG, "LOGIN DATE NOT EXIST ---------------> checkInDate");
        }
    });
}

How I get it in my Activity

public interface OnCheckIn {
    void todayCheckIn(boolean check);
}

Below - How I call it

dbHelper.dailyCheck(CURRENT_USER, check -> {
        if (!check) {
            // todo
        } else {
            // 
        }
    });

Notes - This dbHelper is a class name ->If you have any question about this answer please comment below

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM