【问题标题】:Ordering and filtering denormalized data with Firebase使用 Firebase 对非规范化数据进行排序和过滤
【发布时间】:2016-09-24 03:50:18
【问题描述】:

我正在使用 Firebase 构建一个具有两个模型的应用程序,AB

  • AB 之间存在一对多的关系(A 有很多 B)。
  • AB 需要放在易于查询的地方,因此我需要一种方法来获取所有具有满足条件的属性的 A 模型。与B 相同。
  • B 有一个属性 createdAt (永远不会改变)和一个属性 status (不时改变),我想列出由特定 A 拥有的所有 Bs 以及 @987654338 @ 和/或 createdAt 属性满足某些条件。
  • Bs 是私有的,只能由经过身份验证的用户读取,并且可以读取它们所属的A(如果可以读取a1a1 属于b1,则用户可以读取b1) .

Firebase guidelines 之后,我的第一次尝试是做这样的事情:

"As": {
  "a1": {
    ...
    Bs: {
      "b1": true,
      ...
    },
  },
  ...
},
"Bs": {
  "b1": {
    ...,
    "status": "OPEN",
    "createdAt": 1464249410579,
  },
  ...
},

此解决方案的问题是,要访问属于a1Bs,其状态为OPEN 并且不超过一个月,我必须访问a1.Bs,获取所有id 然后访问Bs 一个一个(这很好,根据Firebase guidelines)然后过滤它们以找出它们是否满足我的条件,我发现这非常低效。

关于如何做到这一点的任何建议?

【问题讨论】:

  • 这取决于您希望如何访问数据,但将其全部放在As 下将解决您描述的问题
  • 当你说A 有很多Bb 可以链接到许多a 吗?我的意思是a1a2 都可以有b1
  • @eikooc 这样做的问题是它会使B 难以搜索。
  • @Crema no.这是一对多的关系,而不是多对多的关系。
  • 复制数据即可。这就是人们通常使用 NoSQL 数据库所做的事情

标签: firebase firebase-realtime-database


【解决方案1】:

经过一番思考,我们想出了这个解决方案:

"As": {
  "a1": {
    ...
    Bs: {
      "b1": { "status": "OPEN", "createdAt": 1464249410579 },
      ...
    },
  },
  ...
},
"Bs": {
  "b1": {
    ...,
    "status": "OPEN",
    "createdAt": 1464249410579,
  },
  ...
},

此解决方案允许我们保持数据库的结构平坦,以便数据AsBs 可供管理员用户搜索和列出。它还尝试最大程度地减少数据重复,并且它适用于我们希望在查询中包含的任意数量的属性。

获取属于ABs

ref.child('as/:aId/bs').once('value')
  .then(snapshot => snapshot.forEach(...) // Access /bs/:bId where :bId = snapshotItem.key()

要获取属于AstatusBsOPEN

ref.child('as/:aId/bs')
  .orderByChild('status')
  .equalTo('OPEN')  
  .once('value')
  .then(snapshot => snapshot.forEach(...) // Access /bs/:bId where :bId = snapshotItem.key()

要获取属于AstatusBsOPENcreatedAt 满足条件:

ref.child('as/:aId/bs')
  .orderByChild('status')
  .equalTo('OPEN')  
  .once('value')
  .then(filterBsThatDontFulFillCreatedAtCondition)
  .then(snapshot => snapshot.forEach(...) // Access /bs/:bId where :bId = snapshotItem.key() 

在最后一种情况下,我们必须在客户端进行一些过滤,但这是由于 Firebase 的限制,我认为我们无法避免。无论如何,这是在一个 id 数组上完成的,而不是在模型本身上完成的,模型本身可能有 20 或 30 个属性,因此我们避免带来我们不需要的完整模型。

【讨论】:

  • 好吧,如果在客户端过滤没问题,那么整个事情就超级简单了。如果您的客户端是 iOS,请查询属于 A 的 B 并将它们填充到一个数组中。然后您可以过滤该数组,其中 open = true 和 createdAt = 12345。大约 10 行代码。我相信其他平台上也有过滤方法。
  • 哦,为了确定哪个用户可以读取节点,定义用户的规则必须经过身份验证,并且他们的 user_id 存在于他们试图读取的 A 节点和 B 节点中;只需添加 /user/uid_0:true, uid_1: true 等。
  • @Jay,您的第一条评论:是的,这正是我在回答中提出的解决方案。 “棘手”的部分是 statuscreatedAt 必须在 a1.Bs 中,以便您可以在将带有完整对象的整个数组带到客户端之前过滤数组,这会很昂贵。
  • @Jay,您的第二条评论:非常感谢,这正是我们目前正在做的事情。我在我的问题中添加了该约束,因为否则就不需要嵌套,您可以使用某些参数查询/offers,仅此而已。出于安全原因,普通用户无法访问/offers(管理员可能可读)。
【解决方案2】:

我会好好研究一下。

As
  a1
    Bs
      b1: true
      b2: true
  a2
    Bs
      b2: true
Bs
  b1:
    "open_a1": 1464249410579
  b2:
    "open_a1": 1464249410590
    "open_a2": 1464249410595

这个结构允许你的代码从 As 节点知道所有链接到 a1 的 B

你可以查询所有open_a1

ref.queryByChild("open_a1").queryStaringAtValue(today)
                           .queryEndingAtValue(one month from today)

这完全未经测试,所以我可能需要稍微修改一下解决方案。

【讨论】:

  • 我认为将状态用作 id 很麻烦,因为它会时不时地更改并且您必须对其进行更新。
【解决方案3】:

我会做这样的事情:

 "As": {
  "a1": {
    ...
  },
  ...
},
"Bs": {
  "a1" : {
      "b1": {
        "status": "OPEN",
        "createdAt": 1464249410579,
      },
  },
},

a1 是你的钥匙A

这样您就可以轻松查询属于a的所有bs

这是我建议here

【讨论】:

  • 当您在a1.Bs 中建立关系时,您实际上是打算使用{ "status": "OPEN", "createdAt": 1464249410579 } 而不是true,对吧?我认为这个例子令人困惑。
  • 不,我的意思是从您的模型A 中删除所有bs。在您的模型B 中,将所有bs 存储在它们所属的a 下。所以不要在firebaseUrl/Bs/上查询firebaseurl/Bs/${aKey}/上的查询
  • 这个解决方案的问题是B很难搜索(获取所有B's,状态为OPEN
猜你喜欢
  • 2013-06-30
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2016-06-02
  • 1970-01-01
相关资源
最近更新 更多