【问题标题】:Firebase database query from multiple childs来自多个孩子的 Firebase 数据库查询
【发布时间】:2022-03-16 21:03:51
【问题描述】:

我对 Firebase 数据库结构有一些疑问。我不熟悉它,因为我前几天才开始学习它。基本上1个账户可以有很多个收据,1个收据可以有很多不同类型的物品,1个商店的1个收据和1个货币的1个收据。

我想出了如下数据库设计:

receipts {
    accountID1 : {
        receiptID1 : {
            date:
            store: {
                storeName: store1
                storeAddr: addr1
            }
            currency: {
                currencyName: currency1
                currentcySymbol: $
            }
            totalAmount: 50.00
        }
        receiptID2 : { ... }
    }
    accountID2 : { ... }
},
itemlists {
    receiptID1: {
        items: {
            itemID1 : {
                type: food
                name: snack
                price: 10.00
                quantity: 2
            }
            itemID2 : { ... } 
        }
    }
    receiptID2: { ... }
},
receiptIDsByStore {
    storeID1: {
        receiptID1: true
        receiptID2: true
    }
},
receiptIDsByCurrency {
    currencyID1: {
        receiptID1: true
        receiptID2: true
    }
},
stores {
    storeID1: {
        storeName: store1
        storeAddress: addr1
    }
},
currencies {
    currencyID1: {
        currencyName: currency1
        currencySymbol: $
    }
}   
itemIDsByType {
food: {
    itemID1: true,
    itemID2: true,
}
entertainment: {
    itemID3: true,
    itemID4: true,
}
}

所以我的问题是:

  • 我对上面的设计有什么冗余错误吗?

  • 我可以从收据中获得每个用户的总消费金额,对吗?我可以像receipts/accountID1 一样查询以获取所有收据然后总结总金额。

  • 我如何才能实际总结每个用户在每种类型的项目上的总支出?例如,我想找食物。所以我查询itemIDsByType/food,然后获取itemIDs的列表,然后从那里查询itemlists并检查receiptID是否属于该特定帐户,然后得到单价?

编辑

receipts {
    accountID1 : {
        receiptID1 : {
            date : "07/07/2017"
            store : {
                storeName : "store1"
                storeAddr : "addr1"
            }
            currency : {
                currencyName : "currency1"
                currentcySymbol : "$"
            }
            totalAmount : "50.00"
            items : {
                itemID1 : true,
                itemID2 : true,
            }
        }
        receiptID2 : {
            date : "08/07/2017"
                store : {
                    storeName : "store1"
                    storeAddr : "addr1"
                }
                currency : {
                    currencyName : "currency1"
                    currentcySymbol : "$"
                }
                totalAmount : "20.00"
                items : {
                    itemID3 : true,
                    itemID4 : true,
                }
        }
    }
    
    accountID2 : { 
        receiptID3 : {
                date : "08/07/2017"
                    store : {
                        storeName : "store2"
                        storeAddr : "addr2"
                    }
                    currency : {
                        currencyName : "currency1"
                        currentcySymbol : "$"
                    }
                    totalAmount : "100.00"
                    items : {
                        itemID5 : true,
                        itemID6 : true,
                    }
            }
    }
},
items {
        itemID1 : {
            type : "food"
            name : "snack"
            unitprice : "10.00"
            quantity : "2"
        }
        itemID2 : { 
            type : "entertainment"
            name : "gaming equipment"
            unitprice : "150.00"
            quantity : "1"
        }
        itemID3 : { 
            type : "food"
            name : "fruit juice"
            unitprice : "4.00"
            quantity : "1"
        } 
        itemID4 : {
            type : "entertainment"
            name : "new year clothes"
            unitprice : "100.00"
            quantity : "1"
        }
        itemID5 : {
            type : "healthcare"
            name : "fever meds"
            unitprice : "100.00"
            quantity : "1"
        }
        itemID6 : {
            type : "healthcare"
            name : "flu meds"
            unitprice : "100.00"
            quantity : "1"
        }
},
receiptIDsByStore {
    storeID1 : {
        receiptID1 : true,
        receiptID2 : true,
    }
    storeID2 : {
        receiptID3 : true,
    }
},
receiptIDsByCurrency {
    currencyID1 : {
        receiptID1 : true,
        receiptID2 : true,
        receiptID3 : true,
    }
},
stores {
    storeID1 : {
        storeName : "store1"
        storeAddress : "addr1"
    }
    storeID2 : {
        storeName : "store2"
        storeAddress : "addr2"
    }
},
currencies {
    currencyID1 : {
        currencyName : "currency1"
        currencySymbol : "$"
    }
},
itemIDsByType {
    food : {
        itemID1 : true,
        itemID3 : true,
    }
    entertainment: {
        itemID2 : true,
        itemID4 : true,
    }
    healthcare : {
        itemID5 : true,
        itemID6 : true,
    }
}

【问题讨论】:

  • 您为一家公司使用不同的商店还是完全不同的公司?此外,您希望汇总每个用户的总支出,但发布的结构中没有对用户的引用。
  • @Jay 是的,这些商店来自不同的商店。至于总支出,可以直接查询receipts/accountID1,然后得到里面每个receiptID的totalAmt,对吗?
  • @Jay 关于第三个要点,我查询 itemIDsByType/food,然后获取项目列表,然后查询收据/帐户 ID,然后比较 itemID。如果匹配,我会查询 items/itemID 以获取项目详细信息,然后将结果存储到另一个单独的数组中。我怎样才能真正按月“分组”特定用户的每个类别的支出金额?
  • 总和;使用当前结构,很容易迭代receipt/accountID1的所有子节点-如果它是一个大数据集,则使用.childAdded;如果它很小,则使用.value,并从每个节点检索totalAmt并将它们相加,所以是的,你是对的。对于您的第二条评论,正如我在第一条评论中提到的那样,发布的结构中没有对用户的引用,并且不清楚 每个类别 的含义。我认为这可能是一个单独的问题,应该包括您的用户结构、类别是什么以及它们与其余数据的关系。
  • @Jay 我明白了。非常感谢!

标签: firebase firebase-realtime-database


【解决方案1】:

您的数据库结构非常好。在构建 Firebase 数据库时,您需要记住的一件事是让数据尽可能扁平化并为视图制作所有内容。如果您想了解有关 Firebase 数据库结构的更多信息,我建议您阅读以下帖子:NOSQL DATA MODELING TECHNIQUESStructuring your Firebase Data correctly for a Complex App

关于您的问题,您可能会在这些帖子中看到,在不同位置拥有相同数据并不是错误,实际上恰恰相反,这是 Firebase 中的常见做法。是的,您可以像receipts/accountID1 一样查询并获取所有收据。如果您需要计数,您可以直接在DataSnapshot 上使用getChildrenCount() 方法。 Firebase 不禁止嵌套查询。您可以查询一次,获取这些 id,第二次根据这些 id 获取所需的数据。

至少,如果您有 SQL 背景,我建议您观看此 youtube 教程系列,The Firebase Database For SQL Developers

希望对你有帮助。

【讨论】:

  • 非常感谢您的回复!但只是一些问题,上面的设计有没有冗余?因为我在考虑是否可以消除这些receiptIDsByStore 和receiptIDsByCurrency,因为它们已经嵌套在收据中。你介意帮我看看另一个设计吗?因为在等待回复时,我提出了另一种设计,但我不确定哪个更好。另外,对于问题中的第三个要点,这是正确的方法吗?
  • 我已经更新了这个问题。在设计二中,我对每张收据中的项目进行了嵌套查找。为了查询上面的第三个要点,我查询 itemIDsByType/food,然后获取项目列表,然后查询收据/帐户 ID,然后比较 itemID。如果匹配,我查询 items/itemID 然后将结果存储到另一个单独的数组中。在性能和设计理论方面,哪个设计更好?
  • 看到你的数据库的第二个版本,我可以说它更好。为什么?因为它比拳头版本更平。将这些项目作为单独的类别非常好,因为如果要显示所有项目,则无需下载整个 itemlists 节点。我再说一遍,您需要根据自己的需要构建数据库。关于 2 个不同地方的数据,我坚持观看有关 Denormalization 的视频。
  • 非常感谢!但是有什么方法可以进一步扁平化编辑部分的设计吗?另外,这是否意味着我可以放置尽可能多的查找表以便于检索?这是否违反了设计规则?抱歉,我几天前刚学过,现在还在练习。
  • @DeniseTan 这是一个很好的答案!我想补充一点,虽然“扁平化”或“非规范化”您的数据是一种常见的良好做法,但不要仅仅为了这样做而这样做。就像复制数据一样——只有在需要时才这样做——有时将数据绑定到包含它的父节点更有意义。这是情境性的,Firebase 结构没有“规则”。
猜你喜欢
  • 2019-03-23
  • 1970-01-01
  • 2021-08-15
  • 2017-10-13
  • 2018-01-25
  • 2018-10-23
  • 2019-08-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多