如何从Firestore获取数组?

31
我在Cloud Firestore中存储了如下数据结构。我想要保存存储在Firestore中的字符串数组dungeon_group。
我很难获取数据并将其存储为数组。我只能获得奇怪的字符串,有任何方法可以将其存储为字符串数组吗?以下是我使用的代码。
我能够在Swift中实现此操作,但不确定如何在Android中实现相同的操作。

enter image description here

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);
                     }
                 });

控制台输出如下:

{dungeon_group=[3P,紧急,任务挑战,下降,合作,每日,技术,正常]}

5个回答

41
当您调用DocumentSnapshot.getData()时,它将返回一个Map。 您只是在该映射上调用toString(),这将为您提供文档中所有数据的转储,这并不特别有帮助。 您需要按名称访问dungeon_group字段:
DocumentSnapshot document = task.getResult();
List<String> group = (List<String>) document.get("dungeon_group");
  • 编辑: 强制类型转换中的语法错误

11

您的问题有两种解决方案,一种是可以按照以下方式将文档中的值转换:

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

或者,我建议您使用此解决方案,因为在开发应用程序时,模型始终有可能发生更改。即使它们只有一个参数,此解决方案也可以将所有内容建模为Firebase POJO:

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;
    }
}

记住,您可以使用注释@PropertyName来避免以与数据库中值相同的方式调用变量。 这样做最终您只需要执行以下操作:

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

希望这能对你有所帮助!
祝你编码愉快!


4
很好的解决方案,但我认为你应该为新手扩展一下。Dungeon dungeon = document.toObject(Dungeon.class); List<String> dungeonGroup = dungeon.getDungeon(); 翻译:不错的解决方案,不过我认为你应该为新手提供更详细的说明。Dungeon dungeon = document.toObject(Dungeon.class); List<String> dungeonGroup = dungeon.getDungeon(); - King Of The Jungle

8

如果您想获取整个dungeon_group数组,需要按照以下方式遍历Map:

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());
    }
}

但请注意,即使dungeon_group对象以数组形式存储在数据库中,entry.getValue()返回的是一个ArrayList,而不是数组。

对于您来说,更好的方法是考虑这种备选的数据库结构,其中每个groupMap中的键,所有值都设置为布尔值true

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

使用这种结构,您还可以基于存在于映射中的属性查询它,否则与官方文档中一样:
尽管Cloud Firestore可以存储数组,但不支持查询数组成员或更新单个数组元素。 2021年1月13日编辑: 如果您拥有的不是字符串值数组,而是对象数组,则可以将该对象数组映射到自定义对象列表中,如下文所述:
- 如何将Cloud Firestore中的对象数组映射到对象列表中? 2018年8月13日编辑: 根据有关数组成员资格的更新文档,现在可以使用whereArrayContains()方法基于数组值过滤数据。一个简单的例子是:
CollectionReference citiesRef = db.collection("cities");
citiesRef.whereArrayContains("regions", "west_coast");

此查询返回包含数组regions字段的所有城市文档,该数组包含west_coast。如果数组中有多个与您查询的值相同的实例,则该文档仅包括在结果中一次。

3

当你通过 String.valueOf(document.getData()) 把它转化为字符串时,你的文档看起来像这样 "dongeon_group=[SP, urgent, missinon challenge,...]

我认为另一种简单实现这个功能的方法是立即将文档解压缩成字符串数组,如下所示:

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

说明

从Firestore中获取的document来自于 documentSnapshot.getDocuments() 返回包含你的文档的列表。

由于你的文档似乎是嵌套的,调用document.getData()会返回单个元素的列表(当然只有一个元素)。

document.getData().entrySet() 将文档准备好,以便转换为包含单个元素的数组(仅使用getData()无法完成此操作)。

访问单个元素 document.getData().entrySet().toArray()[0] ,然后将其转换为字符串 document.getData().entrySet().toArray()[0].toString(),将留下一个字符串,然后您可以使用该字符串分割(使用字符串中找到的=)并且然后取第二部分。 第二部分也可以拆分为包含文档值的数组。

由于此解决方案每次只转换一个元素,因此您可以将其包装在循环中,以便可以转换所有可用文档。

例如:

for(DocumentSnapshot document :documentSnapshot.getDocuments()){ String[] unpackedDoc = document.getData().entrySet().toArray()[0].toString().split("=")[1].split(","); //对解压缩的文档做一些操作

}


0

我在我的生产应用程序中如何实现这个功能。

全局声明

private final FirebaseFirestore FIRE_STORE_DB;

参考资料

this.FIRE_STORE_DB = FirebaseFirestore.getInstance();

Collection Method

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

我的需求是获取数组的最后一个索引时间,如果它相等于当前时间,则返回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");
        }
    });
}

我该如何在我的Activity中获取它?
public interface OnCheckIn {
    void todayCheckIn(boolean check);
}

以下 - 我的称呼
dbHelper.dailyCheck(CURRENT_USER, check -> {
        if (!check) {
            // todo
        } else {
            // 
        }
    });

注 - 这个dbHelper是一个类名 ->如果您对此答案有任何疑问,请在下面评论


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接