添加新字段或更改所有Firestore文档的结构

b-f*_*-fg 10 java database android google-cloud-firestore

考虑一下users.集合中的每个文档都有nameemail作为字段.

{
  "users": {
    "uid1": {
      "name": "Alex Saveau",
      "email": "saveau.alexandre@gmail.com"
    },
    "uid2": { ... },
    "uid3": { ... }
  }
}
Run Code Online (Sandbox Code Playgroud)

现在考虑一下,通过这个有效的Cloud Firestore数据库结构,我启动了我的第一个版本的移动应用程序.然后,在某些时候,我意识到我想要包括另一个领域,如last_login.

在代码中,使用Java从Firestore DB读取所有用户文档将完成

FirebaseFirestore.getInstance().collection("users").get()
        .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
            @Override
            public void onComplete(@NonNull Task<QuerySnapshot> task) {
                if (task.isSuccessful()) {
                    for (DocumentSnapshot document : task.getResult()) {
                        mUsers.add(document.toObject(User.class));
                    }
                }
            }
        });
Run Code Online (Sandbox Code Playgroud)

该类User现在包含的地方name,emaillast_login.

由于新的Userfield(last_login)不包含在DB中存储的旧用户中,因此应用程序崩溃,因为新User类需要一个由该方法last_login返回的字段.nullget()

包含last_loginUserDB的所有现有文档中的最佳做法是什么,而不会丢失新版本应用程序的数据?我应该只运行一次片段来执行此任务,还是有更好的方法来解决问题?

b-f*_*-fg 8

当我发布问题时,我写了一些例程来帮助自动化这个过程。我没有发布它们,因为它们有点初级,我希望有一个优雅的基于 Firestore 的解决方案。因为这样的解决方案还没有,这里是我写的函数。

简而言之,我们有重命名字段、添加字段或删除字段的函数。要重命名字段,根据数据类型使用不同的函数。也许有人可以更好地概括这一点?下面的功能是:

  • add_field:在集合的所有文档中添加一个字段。
  • delete_field:删除集合的所有文档中的字段。
  • rename_*_field:重命名集合的所有文档中包含特定数据类型 (*) 的字段。在这里,我包含了字符串、整数和日期的示例。

添加字段

public void add_field (final String key, final Object value, final String collection_ref) {
    FirebaseFirestore.getInstance().collection(collection_ref).get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {
                        WriteBatch batch = db.batch();

                        for (DocumentSnapshot document : task.getResult()) {
                            DocumentReference docRef = document.getReference();
                            Map<String, Object> new_map = new HashMap<>();
                            new_map.put(key, value);
                            batch.update(docRef, new_map);
                        }
                        batch.commit();
                    } else {
                        // ... "Error adding field -> " + task.getException()
                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // ... "Failure getting documents -> " + e
                }
            });
}
Run Code Online (Sandbox Code Playgroud)

删除字段

public void delete_field (final String key, final String collection_ref) {
    FirebaseFirestore.getInstance().collection(collection_ref).get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {

                        WriteBatch batch = db.batch();

                        for (DocumentSnapshot document : task.getResult()) {
                            DocumentReference docRef = document.getReference();
                            Map<String, Object> delete_field = new HashMap<>();
                            delete_field.put(key, FieldValue.delete());
                            batch.update(docRef, delete_field);
                        }
                        // Commit the batch
                        batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
                            @Override
                            public void onComplete(@NonNull Task<Void> task) {
                                // ...
                            }
                        });

                    } else {
                        // ... "Error updating field -> " + task.getException()
                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // ... "Failure getting notices -> " + e
                }
            });
}
Run Code Online (Sandbox Code Playgroud)

重命名字段

public void rename_string_field (final String old_key, final String new_key, final String collection_ref) {
    FirebaseFirestore.getInstance().collection(collection_ref).get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {

                        WriteBatch batch = db.batch();

                        for (DocumentSnapshot document : task.getResult()) {
                            DocumentReference docRef = document.getReference();
                            String old_value = document.getString(old_key);

                            if (old_value != null) {
                                Map<String, Object> new_map = new HashMap<>();
                                new_map.put(new_key, old_value);

                                Map<String, Object> delete_old = new HashMap<>();
                                delete_old.put(old_key, FieldValue.delete());

                                batch.update(docRef, new_map);
                                batch.update(docRef, delete_old);
                            }
                        }
                        // Commit the batch
                        batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
                            @Override
                            public void onComplete(@NonNull Task<Void> task) {
                                // ...
                            }
                        });

                    } else {
                        // ... "Error updating field -> " + task.getException()
                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // ... "Failure getting notices ->" + e
                }
            });
}

public void rename_integer_field (final String old_key, final String new_key, final String collection_ref) {
    FirebaseFirestore.getInstance().collection(collection_ref).get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {

                        WriteBatch batch = db.batch();

                        for (DocumentSnapshot document : task.getResult()) {
                            DocumentReference docRef = document.getReference();
                            int old_value = document.getDouble(old_key).intValue();
                            Integer ov = old_value;
                            if (ov != null) {
                                Map<String, Object> new_map = new HashMap<>();
                                new_map.put(new_key, old_value);

                                Map<String, Object> delete_old = new HashMap<>();
                                delete_old.put(old_key, FieldValue.delete());

                                batch.update(docRef, new_map);
                                batch.update(docRef, delete_old);
                            }
                        }
                        // Commit the batch
                        batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
                            @Override
                            public void onComplete(@NonNull Task<Void> task) {
                                // ...
                            }
                        });

                    } else {
                        // ... "Error updating field -> " + task.getException()
                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // ... "Failure getting notices -> " + e
                }
            });
}

public void rename_date_field (final String old_key, final String new_key, final String collection_ref) {
    FirebaseFirestore.getInstance().collection(collection_ref).get()
            .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
                @Override
                public void onComplete(@NonNull Task<QuerySnapshot> task) {
                    if (task.isSuccessful()) {

                        WriteBatch batch = db.batch();

                        for (DocumentSnapshot document : task.getResult()) {
                            DocumentReference docRef = document.getReference();
                            Date old_value = document.getDate(old_key);
                            if (old_value != null) {
                                Map<String, Object> new_map = new HashMap<>();
                                new_map.put(new_key, old_value);

                                Map<String, Object> delete_old = new HashMap<>();
                                delete_old.put(old_key, FieldValue.delete());

                                batch.update(docRef, new_map);
                                batch.update(docRef, delete_old);
                            }
                        }
                        // Commit the batch
                        batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
                            @Override
                            public void onComplete(@NonNull Task<Void> task) {
                                // ...
                            }
                        });

                    } else {
                        // ... "Error updating field -> " + task.getException()
                    }
                }
            })
            .addOnFailureListener(new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    // ... "Failure getting notices -> " + e
                }
            });
}
Run Code Online (Sandbox Code Playgroud)


小智 7

只是想分享,因为我读到您希望有一个基于 Firestore 的解决方案。

这对我有用。forEach 将查询集合中的每个文档,您可以根据需要进行操作。

    db.collection("collectionName").get().then(function(querySnapshot) {
        querySnapshot.forEach(async function(doc) {
            await db.collection("collectionName").doc(doc.id).set({newField: value}, {merge: true});
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        });
    });
Run Code Online (Sandbox Code Playgroud)


Tim*_*kle 6

您陷入了NOSQL数据库的空白:面向文档的数据库不保证数据的结构完整性(如RDBMS那样)

这笔交易是:

  • RDBMS中,所有存储的数据在任何给定时间(在同一实例或集群内)具有相同的结构.更改结构(ER图)时,您必须迁移所有现有记录的数据,这些记录需要花费时间和费用.

    结果,您可以针对当前版本的数据结构优化应用程序.

  • 面向文档的数据库中,每个记录都是一个独立的"页面",具有自己独立的结构.如果您更改了敲击,它只适用于文件.因此,您无需迁移现有数据.

    结果,您的应用程序必须能够处理您在当前数据库中使用过的所有数据结构版本.

我不详细了解firebase,但一般来说,你永远不会更新 NOSQL数据库中的文档.您只能创建该文档的新版本.因此,即使您更新所有文档,您的应用程序也必须准备好处理"旧"数据结构...