Firebase(Swift)中数据过滤的最优JSON结构与分类存储方案咨询
Great question! Let's break this down clearly for your Swift + Firebase setup.
First, should you create separate child nodes for each category? Absolutely, especially since your categories are fixed (Business, Entertainment, Other). This approach is way more efficient than storing all questions in a single node and filtering on the category field later—Firebase is optimized for direct node access, so querying a dedicated businessQuestions node will be faster and use less bandwidth than filtering a giant list.
How to structure your Firebase Realtime Database
Your structure would look something like this (JSON representation):
{ "businessQuestions": { "-Nabc123xyz": { "questionText": "How do I register a small business?", "timestamp": 1699999999, "userId": "user_123" }, "-Ndef456uvw": { "questionText": "What's the best accounting software for startups?", "timestamp": 1700000000, "userId": "user_456" } }, "entertainmentQuestions": { "-Nghi789rst": { "questionText": "What's the best new movie streaming service?", "timestamp": 1700000001, "userId": "user_789" } }, "otherQuestions": { "-Njkl012opq": { "questionText": "How do I fix my broken laptop screen?", "timestamp": 1700000002, "userId": "user_012" } } }
Getting all questions across categories
If you need to fetch every question from all three categories, you'll need to perform three separate reads and combine the results. Here's how you can do that in Swift using DispatchGroup to wait for all requests to complete:
import FirebaseDatabase // Assume you have a Question model struct to map your data struct Question { let id: String let text: String let timestamp: TimeInterval let userId: String init?(id: String, data: [String: Any]) { guard let text = data["questionText"] as? String, let timestamp = data["timestamp"] as? TimeInterval, let userId = data["userId"] as? String else { return nil } self.id = id self.text = text self.timestamp = timestamp self.userId = userId } } let ref = Database.database().reference() let dispatchGroup = DispatchGroup() var allQuestions: [Question] = [] // Fetch Business questions dispatchGroup.enter() ref.child("businessQuestions").observeSingleEvent(of: .value) { snapshot in guard let businessData = snapshot.value as? [String: [String: Any]] else { dispatchGroup.leave() return } let businessQuestions = businessData.compactMap { Question(id: $0.key, data: $0.value) } allQuestions.append(contentsOf: businessQuestions) dispatchGroup.leave() } // Fetch Entertainment questions dispatchGroup.enter() ref.child("entertainmentQuestions").observeSingleEvent(of: .value) { snapshot in guard let entertainmentData = snapshot.value as? [String: [String: Any]] else { dispatchGroup.leave() return } let entertainmentQuestions = entertainmentData.compactMap { Question(id: $0.key, data: $0.value) } allQuestions.append(contentsOf: entertainmentQuestions) dispatchGroup.leave() } // Fetch Other questions dispatchGroup.enter() ref.child("otherQuestions").observeSingleEvent(of: .value) { snapshot in guard let otherData = snapshot.value as? [String: [String: Any]] else { dispatchGroup.leave() return } let otherQuestions = otherData.compactMap { Question(id: $0.key, data: $0.value) } allQuestions.append(contentsOf: otherQuestions) dispatchGroup.leave() } // Once all fetches are done, update your UI dispatchGroup.notify(queue: .main) { print("All questions loaded: \(allQuestions.count)") // Use allQuestions to populate your UI here }
Fetching only Business category questions
This is straightforward—just read directly from the businessQuestions node. You can use .observe(.value) to listen for real-time updates, or .observeSingleEvent(of: .value) for a one-time fetch:
// Real-time listener for Business questions ref.child("businessQuestions").observe(.value) { snapshot in guard let businessData = snapshot.value as? [String: [String: Any]] else { // Handle empty or invalid data return } let businessQuestions = businessData.compactMap { Question(id: $0.key, data: $0.value) } print("Business questions loaded: \(businessQuestions.count)") // Update your UI with the filtered questions } // One-time fetch for Business questions ref.child("businessQuestions").observeSingleEvent(of: .value) { snapshot in // Same processing as above }
A quick note on alternatives
If you ever think you might add more categories later, or need to filter questions by other fields (like user ID, timestamp) alongside category, you could store all questions in a single questions node with a category field, then use a query:
// Example with a single node and category filter ref.child("questions") .queryOrdered(byChild: "category") .queryEqual(toValue: "Business") .observe(.value) { snapshot in guard let questionData = snapshot.value as? [String: [String: Any]] else { return } let filteredQuestions = questionData.compactMap { Question(id: $0.key, data: $0.value) } // Update UI with filteredQuestions }
But for your fixed three categories, separate nodes are still better for performance, especially as your dataset grows.
内容的提问来源于stack exchange,提问作者ezera.olsen

