Firebase Firestore查询指定子集合文档返回数量为0的问题求助
你遇到的问题其实是Firestore一个容易踩的特性坑:当你只在某个文档下创建子集合,但没有给这个文档本身设置任何字段时,这个文档不会出现在集合的get()查询结果里。
问题根源
看你的addFolder代码,你直接在Starred Messages集合下的folderName文档里创建了子集合KEY_STARRED_MESSAGE_FOLDERS,但这个folderName文档本身没有任何数据(没有设置任何键值对)。Firestore的集合查询(get()方法)只会返回那些包含至少一个字段的文档,空文档(只有子集合没有自身数据)会被自动忽略,所以你的Toast才会显示文档数量为0。
解决方案
这里有两种可行的解决方法,你可以根据需求选择:
方法1:创建文件夹时给父文档添加至少一个字段
这是更推荐的方案,因为给文件夹文档添加元数据(比如创建时间)对后续功能扩展很有用。修改你的addFolder方法,先给folderName文档设置数据,再创建子集合:
private void addFolder(String folderName){ // 给文件夹文档本身添加元数据(比如服务器时间戳) HashMap<String, Object> folderMetadata = new HashMap<>(); folderMetadata.put("createdAt", FieldValue.serverTimestamp()); FirebaseFirestore db = FirebaseFirestore.getInstance(); String userId = preferenceManager.getString(Constants.KEY_USER_ID); // 先创建/更新文件夹文档 db.collection(Constants.KEY_COLLECTION_USERS) .document(userId) .collection(Constants.KEY_COLLECTION_STARRED_MESSAGE) .document(folderName) .set(folderMetadata) .addOnSuccessListener(aVoid -> { // 文件夹文档创建成功后,再添加子集合的示例数据 HashMap<String, String> map = new HashMap<>(); map.put("sampleValue","sampleValue"); db.collection(Constants.KEY_COLLECTION_USERS) .document(userId) .collection(Constants.KEY_COLLECTION_STARRED_MESSAGE) .document(folderName) .collection(Constants.KEY_STARRED_MESSAGE_FOLDERS) .document("sampleValueFolder2358") .set(map) .addOnCompleteListener(task -> { if (task.isSuccessful()) Log.d("folderCreation","done"); else Log.d("folderCreation","fail"); }); }) .addOnFailureListener(e -> Log.d("folderCreation", "Failed to create folder: " + e.getMessage())); }
修改后,你的getFolders方法不需要改动,get()查询就能正常获取到文件夹文档了。
方法2:使用listDocuments()获取所有文档(包括空文档)
如果你不想给文件夹文档添加字段,可以用listDocuments()方法替代get(),这个方法会列出集合下所有的文档引用,不管文档有没有数据:
private void getFolders(){ FirebaseFirestore.getInstance().collection(Constants.KEY_COLLECTION_USERS) .document(preferenceManager.getString(Constants.KEY_USER_ID)) .collection(Constants.KEY_COLLECTION_STARRED_MESSAGE) .listDocuments() .addOnCompleteListener(task -> { if (task.isSuccessful()) { List<DocumentReference> folderDocRefs = task.getResult(); Toast.makeText(this, "" + folderDocRefs.size(), Toast.LENGTH_SHORT).show(); folders.clear(); for (DocumentReference docRef : folderDocRefs) { Folder folder = new Folder(); folder.name = docRef.getId(); folders.add(folder); } adapter.notifyDataSetChanged(); binding.refreshLayout.setRefreshing(false); } else { Log.e("getFolders", "Failed to load folders", task.getException()); binding.refreshLayout.setRefreshing(false); } }); }
注意:listDocuments()是客户端API,无法通过Firestore安全规则进行过滤,不过你的场景是获取当前用户自己的文件夹,所以这个限制影响不大。
总结
优先选择方法1,因为额外的元数据能让你后续更容易扩展功能(比如按创建时间排序文件夹)。如果只是临时需求,方法2可以快速解决问题。
内容的提问来源于stack exchange,提问作者Sambhav Khandelwal

