Arrays 将数据作为数组发送到Firebase会给我带来bug

Arrays 将数据作为数组发送到Firebase会给我带来bug,arrays,swift,firebase,firebase-realtime-database,Arrays,Swift,Firebase,Firebase Realtime Database,到目前为止,我的数据库的结构是这样的——这对我来说很有用。但这是我遇到的不幸——这两个由自动ID分隔的节点实际上是同一个帖子 如果您看到前面的was my DB是结构化的(使用数组),您将看到每个帖子都可以有多个图像、日期和计划。这很有效,但当我试图访问索引之类的东西时,我会做噩梦。所以我选择了这个更明智的方法。但现在我在将帖子分组时遇到了问题。例如,下面这两个应该是完全相同的帖子 当我从数据库中读取数据时,我如何确保将每个自动ID节点与其相应的帖子组合在一起?即:这两个节点实际上是同一篇帖子,

到目前为止,我的数据库的结构是这样的——这对我来说很有用。但这是我遇到的不幸——这两个由自动ID分隔的节点实际上是同一个帖子

如果您看到前面的was my DB是结构化的(使用数组),您将看到每个帖子都可以有多个图像、日期和计划。这很有效,但当我试图访问索引之类的东西时,我会做噩梦。所以我选择了这个更明智的方法。但现在我在将帖子分组时遇到了问题。例如,下面这两个应该是完全相同的帖子

当我从数据库中读取数据时,我如何确保将每个自动ID节点与其相应的帖子组合在一起?即:这两个节点实际上是同一篇帖子,你可以从它们各自具有相同的标题看出

   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?""
我的数据库以前是以这种方式构建的——这种方式在功能级别上工作得很好,但Firebase编程负责人认为这是错误的。我只是把它放进去,这样你就可以知道我实际需要的结构,这些帖子需要以某种方式分组,这样当我从FB中读取并将它们加载到表视图中时,适当的数据存储在一起-例如,一篇帖子可以有两张图片、两个计划和两个日期,但只有一个标题(帖子)

下面是我用来将数据推送到Firebase的函数——我循环了一个数组,这样每次迭代都会将图像、日期、计划、标题作为一个“束”推送到Firebase

这是我在数据服务类中的实际实现

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)
}

我想我理解这个问题,有几种方法可以解决

1)岗位内的团队

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
image_0、image_0、plan_0、plan_1等的键将使用childByAutoId创建

这样,您就可以加载一次post,并获得所需的所有子数据

请注意,虽然此结构显示为“deep”(在Firebase NoSQL中通常不鼓励使用),但您不会对deep数据执行任何查询,并且该数据是特定于post的,因此在本例中没有问题

2)使结构非规范化/扁平化

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"
第二个选项需要初始加载post节点以获取其密钥,然后调用all_images节点以加载图像,并调用all_plans以获取其计划。请注意,我正在使用posts键来引用all_images和all_plans子节点

这里的想法是,我们使用PlanIt子节点的键作为对all_images和all_plans节点中该节点的引用。image_0、image_1、plan_0、plan_1是使用.childByAutoId生成的键

第二个选项有点酷,因为例如,假设您有一个只包含标题和日期列表的tableview,然后当用户点击它时,您可以将图像或计划加载到另一个视图中。。。这个选项将一次加载更少的数据,这使得它在内存方面更易于管理

编辑

为了澄清这个过程,下面是要在平面图、图像和平面图细节中读取的代码,并根据上面的结构将它们打印到控制台

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)")
             }
         })
     })
  }
})
输出将是:

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

我在Firebase中添加了一些文本值,这样可以更清楚地了解输出中的情况。

很高兴您改变了方向,因为数组不太适合NoSQL数据库。这里有一些问题和答案的链接,可以给你一些指导。而且很难直接回答关于上传阵列的最佳替代方案是什么的问题。对我来说,很早的时候,我就意识到Firebase(JSON)其实只是一本大字典。一切都是一个关键:价值对。虽然键是单个元素,但值可以是字符串、数字,最有趣的是,还有键的另一个字典:值对。所以,有两件事;1) 用您希望从Firebase获得的信息更新您的问题,例如,您需要什么样的查询?2) 请不要在问题中包含屏幕截图;将代码和结构包含为文本片段。@Jay抱歉,我正在工作-刚下班。但是谢谢你的建议,在阅读了你的回复之后,你给了我很多关于这个话题的见解和清晰。那么,我是否可以将数组存储为:值对对不起,我以为截图会更清晰,但我想不会。明白。通常不要使用数组。它们的使用非常因地制宜,而且(通常)有更好的选项来存储数据。阅读我先前评论中包含的链接。同样,以特定格式推送数据的机制取决于您希望从firebase中获得什么。i、 我想查询昨天的帖子。或者,我想我想展示前5名喜欢的帖子。等等。定义它,更新你的问题,然后创建一个适当的结构-实现这一点的代码就会到位。@Jay嘿,我刚刚看到你关于更新问题以获得适当结构的回答,你能看看这个问题吗。谢谢你的回复!是的,你完全理解这个问题!但有一个问题——通过实施解决方案1或2,我最终不会遇到同样的“阵列”问题吗?似乎它们仍然被存储为一个数组,不是吗?@CodingwhileLoading No在Firebase意义上的数组是一个整数的有序数组,1,2,3,4等用作键。无法修改数组或使用单个元素-要更改数组,必须重新写入它。在我的解决方案中(如上所述),plan_0、plan_1等是childByAutoId生成的任何内容的占位符。我只是不想一遍又一遍地输入-Yj9j99kaj9oir94n9g9。啊!现在很有道理了!我会继续尝试,然后回来检查你的答案作为解决方案,因为我相信它是。哈哈——这不是我们开始发展的原因吗——不打字还是不打字
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)")
             }
         })
     })
  }
})
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