Firestore查询:如何获取拥有至少一个Items子集合文档的Project文档
Great question! Unfortunately, the query you tried (where("Items.size", ">", 0)) won't work directly because Firestore doesn't automatically track the size of subcollections in parent documents. Subcollections are separate entities, so their count isn't stored as a field in the Project document by default.
But don't worry—there are two reliable ways to achieve what you want:
1. Maintain an Item Count Field in the Project Document (Recommended)
The most efficient approach is to manually keep a counter of items in each Project document. Every time you add or delete an Item, you update this counter. Then you can query against it easily.
Example Code:
First, when adding an Item to a Project:
// Assuming you have a projectRef pointing to the specific Project document const projectRef = doc(getDB(), "Projects", "projectId"); const itemRef = doc(collection(projectRef, "Items")); // Use a transaction to ensure the count is updated correctly await runTransaction(getDB(), async (transaction) => { const projectDoc = await transaction.get(projectRef); if (!projectDoc.exists()) { throw new Error("Project does not exist!"); } // Increment the itemCount (default to 0 if it doesn't exist) const newCount = (projectDoc.data().itemCount || 0) + 1; transaction.update(projectRef, { itemCount: newCount }); // Add the new Item transaction.set(itemRef, { /* your item data */ }); });
Then, to query all Projects with at least one Item:
const querySnapshot = await getDocs( collection(getDB(), "Projects"), where("itemCount", ">", 0) ); // Process the results querySnapshot.forEach((doc) => { console.log("Project with items:", doc.id, doc.data()); });
This method is fast and scalable because it leverages Firestore's indexed queries. Just make sure to handle deletions too—decrement the itemCount when removing an Item.
2. Use a Collection Group Query (Alternative)
If you don't want to maintain a counter, you can use a collection group query to fetch all Items, then get their parent Project documents. However, you'll need to deduplicate results since multiple Items belong to the same Project.
Example Code:
// Get all Items across all Projects const itemsQuery = query(collectionGroup(getDB(), "Items")); const itemsSnapshot = await getDocs(itemsQuery); // Collect unique Project IDs const projectIds = new Set(); itemsSnapshot.forEach((itemDoc) => { // Get the parent Project document ID from the item's path const projectId = itemDoc.ref.parent.parent.id; projectIds.add(projectId); }); // Fetch each unique Project const projectsWithItems = []; for (const projectId of projectIds) { const projectDoc = await getDoc(doc(getDB(), "Projects", projectId)); if (projectDoc.exists()) { projectsWithItems.push({ id: projectDoc.id, ...projectDoc.data() }); } } console.log("Projects with items:", projectsWithItems);
Note that this approach can be less efficient if you have a large number of Items, as it first fetches all Items before getting the Projects. It's best suited for smaller datasets.
内容的提问来源于stack exchange,提问作者Obiwahn

