克服 google 数据存储的 30 个子查询限制
Overcoming 30 sub-query limit for google datastore
Google datastore 开始时看起来很好,现在变得非常令人沮丧,但也许这只是我习惯了关系数据库。一般来说,我对数据存储和 nosql 还很陌生,并且进行了大量研究,但似乎无法找到解决此问题的方法。
假设我有一个用户 class 看起来像这样
class User{
@Id
Long id;
String firstName, lastName;
List<Key<User>> friends;
}
我还有另一个 class 可以模拟用户已经完成的事件
class Event{
Key<User> user;
Date eventTime;
List<Key<User>> receivers;
}
现在我要做的是查询我的朋友们完成的事件。
在通常的关系方式中,我会说:
select * from Event where user in (select friends from User where id = ?)
以此为起点我尝试了
// Key<User> userKey = ...
User user = ofy.load.type(User.class).key(userKey).first.now;
List<Key<User>> friends = user.getFriends();
ofy.load.type(Event.class).filter("user in", friends).order("-eventTime")list();
但我听说这个 30 个子查询的限制使它变得不可持续,因为我假设最终某人会有超过 30 个朋友,更不用说使用 'in' 子句将保证你无法将游标定位到继续加载事件。我做了很多研究并尝试了很多选择,但还没有找到解决这个问题的好方法,除了说 "why Google, why."
我考虑过的事情:
- 在事件中添加一个额外的字段,该字段是用户好友列表的副本,并在 MVP 上使用单个 equals 来查找事件(非常浪费,因为可能有很多事件。
- 将事件查询一次分成 30 个朋友的批次,并以某种方式确定一种方法来确保根据时间从合成游标中继续检索,然后合并它们(问题是边缘情况太多,使得读取事件非常难。)
我真的很感激你能提供的任何意见,因为我 100% 没有想法
TL;DR ~ GAE 对 in-clause 可以处理的项目数量有限制,fml。
您来自关系数据库背景,所以非规范化的概念可能有点痛苦 - 我知道它适合我。
现在您有一个 table,其中包含来自所有用户的所有事件。这种方法在关系数据库中运行良好,但由于您提到的原因,在数据存储中是一场噩梦。
因此,要解决这个具体问题,您可以按如下方式重组数据:
- 所有用户都有两条时间线。一份来自他们自己的帖子,一份来自朋友的帖子。 (public 内容可能有第三条时间线。)
- 发布新事件时,它会写入创建它的用户的时间线,以及所有接收用户的时间线。 (您可能希望在用户的时间线中添加第三方时间线的引用,以便在用户决定删除事件时知道删除什么)
现在每个用户都可以访问完整的时间线,his/her自己的时间线和由第三方事件创建的时间线。这些时间线很容易查询,您根本不需要子选择。
这种方法有缺点:
- 写作成本较高。你必须写出比之前更多的时间线。您可能必须将其放入任务队列中,以便有足够的时间写入所有这些时间线。
- 您使用了更多的存储空间,但存储空间真的很便宜,我猜存储空间会比 运行 长期 运行 中昂贵的查询便宜。
尽管如此,您在 return 中得到的是通过这种反规范化对简单查询的快速响应。剩下的就是在 UI 中合并来自不同时间线的响应(您可以在服务器端进行,但我会在 UI 中进行)
Google datastore 开始时看起来很好,现在变得非常令人沮丧,但也许这只是我习惯了关系数据库。一般来说,我对数据存储和 nosql 还很陌生,并且进行了大量研究,但似乎无法找到解决此问题的方法。
假设我有一个用户 class 看起来像这样
class User{
@Id
Long id;
String firstName, lastName;
List<Key<User>> friends;
}
我还有另一个 class 可以模拟用户已经完成的事件
class Event{
Key<User> user;
Date eventTime;
List<Key<User>> receivers;
}
现在我要做的是查询我的朋友们完成的事件。 在通常的关系方式中,我会说:
select * from Event where user in (select friends from User where id = ?)
以此为起点我尝试了
// Key<User> userKey = ...
User user = ofy.load.type(User.class).key(userKey).first.now;
List<Key<User>> friends = user.getFriends();
ofy.load.type(Event.class).filter("user in", friends).order("-eventTime")list();
但我听说这个 30 个子查询的限制使它变得不可持续,因为我假设最终某人会有超过 30 个朋友,更不用说使用 'in' 子句将保证你无法将游标定位到继续加载事件。我做了很多研究并尝试了很多选择,但还没有找到解决这个问题的好方法,除了说 "why Google, why."
我考虑过的事情:
- 在事件中添加一个额外的字段,该字段是用户好友列表的副本,并在 MVP 上使用单个 equals 来查找事件(非常浪费,因为可能有很多事件。
- 将事件查询一次分成 30 个朋友的批次,并以某种方式确定一种方法来确保根据时间从合成游标中继续检索,然后合并它们(问题是边缘情况太多,使得读取事件非常难。)
我真的很感激你能提供的任何意见,因为我 100% 没有想法
TL;DR ~ GAE 对 in-clause 可以处理的项目数量有限制,fml。
您来自关系数据库背景,所以非规范化的概念可能有点痛苦 - 我知道它适合我。
现在您有一个 table,其中包含来自所有用户的所有事件。这种方法在关系数据库中运行良好,但由于您提到的原因,在数据存储中是一场噩梦。
因此,要解决这个具体问题,您可以按如下方式重组数据:
- 所有用户都有两条时间线。一份来自他们自己的帖子,一份来自朋友的帖子。 (public 内容可能有第三条时间线。)
- 发布新事件时,它会写入创建它的用户的时间线,以及所有接收用户的时间线。 (您可能希望在用户的时间线中添加第三方时间线的引用,以便在用户决定删除事件时知道删除什么)
现在每个用户都可以访问完整的时间线,his/her自己的时间线和由第三方事件创建的时间线。这些时间线很容易查询,您根本不需要子选择。
这种方法有缺点:
- 写作成本较高。你必须写出比之前更多的时间线。您可能必须将其放入任务队列中,以便有足够的时间写入所有这些时间线。
- 您使用了更多的存储空间,但存储空间真的很便宜,我猜存储空间会比 运行 长期 运行 中昂贵的查询便宜。
尽管如此,您在 return 中得到的是通过这种反规范化对简单查询的快速响应。剩下的就是在 UI 中合并来自不同时间线的响应(您可以在服务器端进行,但我会在 UI 中进行)