【问题标题】:How to get an array from Firestore?如何从 Firestore 获取数组?
【发布时间】:2018-10-18 09:15:41
【问题描述】:

我将下图所示的数据结构存储在 Cloud Firestore 中。我想保存 dungeon_group,它是存储在 Firestore 中的字符串数组。

我难以获取数据并将其存储为数组。我只能得到一个奇怪的字符串,但是有什么方法可以存储为字符串数组吗?下面是我使用的代码。

我可以在 Swift 中按以下方式实现这一点,但不确定如何在 Android 中做到这一点。

斯威夫特:

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 安卓:

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, 紧急, 任务挑战, 下降, 协作、日常、技术、常规]}

【问题讨论】:

    标签: java android firebase google-cloud-platform google-cloud-firestore


    【解决方案1】:

    当您调用 DocumentSnapshot.getData() 时,它会返回一个 Map。您只是在该地图上调用 toString(),这将为您提供文档中所有数据的转储,这并不是特别有用。您需要按名称访问dungeon_group 字段:

    DocumentSnapshot document = task.getResult();
    List<String> group = (List<String>) document.get("dungeon_group");
    
    • 编辑: 类型转换中的语法错误

    【讨论】:

      【解决方案2】:

      如果您想获取整个 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
      }
      

      使用此结构,您还可以根据 dungeon_group 映射中存在的属性来查询它,否则就像在 official documentation 中一样:

      虽然 Cloud Firestore 可以存储数组,但 it does not support 查询数组成员或更新单个数组元素。

      2021 年 1 月 13 日编辑:

      如果您有一个对象数组而不是字符串值数组,那么您可以将该对象数组映射到自定义对象列表,如下文所述:

      2018 年 8 月 13 日编辑:

      根据关于array membership 的更新文档,现在可以使用whereArrayContains() 方法根据数组值过滤数据。一个简单的例子是:

      CollectionReference citiesRef = db.collection("cities");
      citiesRef.whereArrayContains("regions", "west_coast");
      

      此查询返回每个城市文档,其中区域字段是包含 west_coast 的数组。如果数组有多个您查询的值的实例,则该文档仅包含在结果中一次。

      【讨论】:

        【解决方案3】:

        您的问题有两种解决方案,一种,您可以通过以下方式从文档中转换值:

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

        希望对您有所帮助! 编码愉快!

        【讨论】:

        • 很好的解决方案,但我认为你应该为新手扩展。 Dungeon dungeon = document.toObject(Dungeon.class); List&lt;String&gt; dungeonGroup = dungeon.getDungeon();
        【解决方案4】:

        因为当您将文档转换为字符串时,您的文档看起来像这样"dongeon_group=[SP, urgent, missinon challenge,...],比如通过String.valueOf(document.getData())

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

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

        说明

        document 来自 documentSnapshot.getDocuments(),它返回一个包含您的文档的列表。

        由于来自 firestore 的文档似乎是嵌套的,因此调用 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(","); //do something with the unpacked doc

        }

        【讨论】:

          【解决方案5】:

          我如何在我的 Production 应用中存档。

          全局声明

          private final FirebaseFirestore FIRE_STORE_DB;
          

          参考

          this.FIRE_STORE_DB = FirebaseFirestore.getInstance();
          

          采集方法

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

          我如何在我的活动中获得它

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

          下面 - 我怎么称呼它

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-08-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-01-25
            相关资源
            最近更新 更多