Reputation: 413
As of now, my data base is structured like this - which works for me. But here is the mishap i am having - These two nodes separated by auto ID's are actually the same post.
If you see the previous was my DB was structured (using an array), you will see that each post could have multiple images, dates and plans. which worked well, but gave me nightmares when trying to access indices and such. So i switched to this more advisable way. But now i am having issues grouping the posts together. For instance, these two below should be apart of the exact same post.
How would i be able to assure that when i read data from my DB - i group each auto ID node together with their appropriate posts? ie: These two nodes are actually the same post - as you can tell by them each having the same title.
Planit
-LETR-XJvQsZCOpG-T1N
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "Jun 9, 2018 at 10:00 AM"
image: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c..
plan: "Bike Rodeo, Safety Presentation and Riding Tour o"
title: "This weekend Plans?"
-LETR-XKqXf7NY1gh6Jm
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "Jun 11, 2018 at 10:00 AM"
image: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c..
plan: "Fun night out at the Parlor"
title: "This Weekend Plans?""
My DB was previously structured in this way- which worked perfect on a functional level, but was wrong by Firebase programmatic principals. and i am only putting this in so you can tell the structure that i actually need, these posts need to be grouped in someway so that when i read the from FB and load them into a table view, the appropriate data is stored together - For example - 1 post can have 2 images, 2 plans, and 2 dates, but only 1 TITLE (of post).
Plant:
-LETYTJh1WEHGgGcVCEp
dates
0: " "
1: " "
images
0: " "
1: " "
plans
0: " "
1: " "
senderId
0: " "
title: close
Below is the function i use to push the data to Firebase - i looped through an array so that each iteration would push the images, dates, plans, title as one "bunch".
for planits in chosenPlanArray {
DataService.instance.uploadPlanitData(withTitleOfPlan: [planits.nameOfEvent!],
withDate: [planits.eventStartsAt!],
withImage: [planits.imageForPlan!],
withTitle: nameOfPlanit.text!,
forUID: (Auth.auth().currentUser?.uid)!,
sendComplete: { (isGood) in
if isGood {
}
})
}
And here is my actual implementation in the Data Service Class
func uploadPlanitData(withTitleOfPlan plan: [String], withDate: [String], withImage: [String], withTitle: String, forUID uid: String, sendComplete: @escaping (_ status: Bool) -> ()) {
_REF_PLANITS.childByAutoId().updateChildValues(["plan" : plan, "date" : withDate, "image": withImage, "title": withTitle, "Uid": uid])
sendComplete(true)
}
Upvotes: 1
Views: 143
Reputation: 35648
I think I understand the question and there are a few ways to go about it.
1) Group within the post
PlanIt
plans
-LETR-XJvQsZCOpG-T1N
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "20180609"
images:
image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
plans
plan_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
plan_1: "Bike Rodeo, Safety Presentation and Riding Tour o"
title: "This weekend Plans?"
-Yjijiioimoifijjiuee
Uid: "asdaasdas"
etc
The keys of image_0, image_0, plan_0, plan_1 etc would be created with childByAutoId.
With this, you can load a post once and you get all of the needed child data with it.
Note that while this structure appears 'deep' which is usually discouraged in Firebase NoSQL, you won't be performing any queries on the deep data and that data is specific to the post so it's fine in this case.
2) Denormalize/Flatten the structure
PlanIt
plans
-LETR-XJvQsZCOpG-T1N
Uid: "ZjtJdkjzuxc0mZn4u9TfWsXa9jh2"
date: "20180609"
title: "This weekend Plans?"
-UYijs09as9jiosdijfi
Uid: "some uid"
date: "20180609"
title: "some title"
all_images:
-LETR-XJvQsZCOpG-T1N
image_0: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
image_1: "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.c.."
all_plan_details
-LETR-XJvQsZCOpG-T1N
detail_0: "Bike Rodeo, Safety Presentation and Riding Tour o"
detail_1: "Bike Rodeo, Safety Presentation and Riding Tour o"
the second option would require an initial loading of the post node to obtain it's key and then one call to the all_images node to load the images and a call to all_plans to get it's plans. Note that I am using the posts key to reference the all_images and all_plans child node.
The idea here is that we use the key of the PlanIt child as a reference to that node within the all_images and all_plans node. image_0, image_1, plan_0, plan_1 are keys generated with .childByAutoId
The second option is kind cool because for example, say you have a tableview with a list of just titles and dates and then when the user taps it, you can load images or plans into another view... This option would be loading far less data at a time which makes it a bit more manageable memory wise.
EDIT
To clarify the process, here's the code to read in the plan, images and plan details and print them to the console based on the structure above.
let mainRef = self.ref.child("PlanIt")
let plansRef = mainRef.child("plans")
let allImagesRef = mainRef.child("all_images")
let allPlanDetailsRef = mainRef.child("all_plan_details")
plansRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let childSnap = child as! DataSnapshot
let dict = childSnap.value as! [String: Any]
let planKey = childSnap.key
let title = dict["title"] as! String
let thisPlansImagesRef = allImagesRef.child(planKey)
let thisPlansDetailsRef = allPlanDetailsRef.child(planKey)
thisPlansImagesRef.observeSingleEvent(of: .value, with: { imageSnapshot in
thisPlansDetailsRef.observeSingleEvent(of: .value, with: { planDetailSnapshot in
//printing is done here because firebase is asynchronous and all data
// will be valid for each node ONLY at this point
print("plan: \(planKey) \(title)")
for imageChild in imageSnapshot.children {
let imageChildSnap = imageChild as! DataSnapshot
let image = imageChildSnap.value as! String
print(" image: \(image)")
}
for planDetailChild in planDetailSnapshot.children {
let planDetailChildSnap = planDetailChild as! DataSnapshot
let planDetail = planDetailChildSnap.value as! String
print(" planDetail: \(planDetail)")
}
})
})
}
})
and the output will be:
plan: plan_0 This weekend plans
image: some image for plan_0
image: another_image for plan_0
planDetail: some plan detail for plan_0
planDetail: another plan detail for plan_0
plan: plan_1 Next weekend plans
I added some text values within my Firebase so it would be more clear what's going on in the output.
Upvotes: 1