解析:关注者的复合查询超时
Parse: Compound Query on Followers times out
我有一个 activity table 表示哪些用户关注谁。 (fromUser
和 toUser
)
我正在构建一个排行榜,以查看关注者中谁发布的评分最高。
所以我创建了这个查询:
ParseQuery<ParseObject> queryActivityFollowing = new ParseQuery<>("Activity");
queryActivityFollowing.whereEqualTo("type", "follow");
queryActivityFollowing.whereEqualTo("fromUser", ParseUser.getCurrentUser());
queryActivityFollowing.setLimit(500);
// innerQuery, only get Users posted by the users I follow
ParseQuery<ParseUser> queryUserFollowing = ParseUser.getQuery();
queryUserFollowing.whereMatchesQuery("toUser", queryActivityFollowing);
// querySelf
ParseQuery<ParseUser> querySelf = ParseUser.getQuery();
querySelf.whereEqualTo("objectId", ParseUser.getCurrentUser().getObjectId());
List<ParseQuery<ParseUser>> queries = new ArrayList<>();
queries.add(queryUserFollowing);
queries.add(querySelf);
query = ParseQuery.or(queries);
query.orderByDescending("rating_count");
query.setLimit(20);
但不知何故,它超时并且永远不会显示结果。
我的查询是否效率低下?
谢谢!
编辑:
数据说明:
Activity
是具有 3 列的 class,fromUser
、toUser
、type
。 fromUser
和toUser
是指向_User
的指针class,type
是一个字符串
在 _User
中,我有 classic 属性和一个名为 rating_count
的整数,它是 orderBy 标准(上面更新的代码)。
实际上,我认为查询没有超时,只是 returns 0 个结果。我关注了我的一些用户,所以这绝对不是预期的输出。
这很难,因为 parse 的查询只支持最低限度的这类事情。我能提供的最好的想法是这个:
- 对 Activity table
whereEqualTo("type", "follow")
和 whereEqualTo("fromUser", ParseUser.getCurrentUser())
的一个查询
- 没有queryUserFollowing,没有querySelf。这些都是不必要的。这也将您从
Parse.Query.or()
中解放出来。
setLimit(1000)
下面会解释原因
include("toUser")
- 完成后,遍历结果,为
result.get("toUser").getInt("rating_count")
最大化,因为结果将是 Activity 的实例,您将急切地获取它们的相关 toUsers。
此方案比您编写的方案更简单,并且可以完成工作。然而,一个主要问题可能是它会丢失拥有超过 1000 个关注者的用户的数据。让我知道这是否是一个问题,我可以建议一个更复杂的答案。一个小缺点是您将被迫自己在内存中进行搜索(也许是排序)以找到最大值 rating_count.
EDIT - 对于 > 1k 的关注者,您不得不多次调用查询,将 skip
设置为上一个查询中收到的记录数, 将结果收集到一个大数组中。
你关于传输这么多数据的观点很好,你可以通过将所有这些放入云函数中来最小化网络使用,在云中进行内存工作并且只返回客户端需要的记录。 (这种方法的额外好处是用 java 脚本编码,我说的比 java 更流利,所以我可以对代码更具规范性)。
编辑 2 - 在云代码中执行此操作的好处是可以减少那些具有最高评级的用户(比如 20 个)的网络流量。它没有解决我之前指出的其他问题。以下是我在云中的操作方式...
var _ = require('underscore');
Parse.Cloud.define("topFollowers", function(request, response) {
var user = new Parse.User({id:request.params.userId});
topFollowers(user, 20).then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
// return the top n users who are the top-rated followers of the passed user
function topFollowers(user, n) {
var query = new Parse.Query("Activity");
query.equalTo("type", "follow");
query.equalTo("fromUser", user);
query.include("toUser");
return runQuery(query).then(function(results) {
var allFollowers = _.map(results, function(result) { return result.get("toUser"); });
var sortedFollowers = _.sortBy(allFollowers, function(user) { return user.get("rating_count"); });
return _.first(sortedFollowers, n);
});
}
// run and rerun a query using skip until all results are gathered in results array
function runQuery(query, results) {
results = results || [];
query.skip(results.length);
return query.find().then(function(nextResults) {
results = results.concat(nextResults);
return (nextResults.length)? runQuery(query, results) : results;
});
}
注意 - 我没有对此进行测试,但在生产中有类似的东西。
如果您打算更改数据模型,那么有一个解决方案可以满足您的需求,而且还有一些附带好处。考虑一个系统,其中用户 class 仅与应用程序和真人之间的关系有关。用户的 public 面孔由一个新的 class(称之为 PublicUser 或 Persona)呈现。
在这个 PublicUser
class 中,你有一个指向拥有它的 user
的 指针 ,以及一个 指向其他 PublicUser
的指针数组,这个 following
是谁。此 class 还包含 rating
属性。现在 OP 中的查询很简单:
- 查询 PublicUser whereKey "following" 等于 currentUser
- 按等级排序,限制为 20 或您希望限制的任何数量
就是这样。该方案的另一个好处是访问控制。系统理解 PublicUser 中的任何内容都可以被其他 PublicUser 读取,并且关于 __User 的所有内容都保存在该个人和应用程序之间。
我有一个 activity table 表示哪些用户关注谁。 (fromUser
和 toUser
)
我正在构建一个排行榜,以查看关注者中谁发布的评分最高。
所以我创建了这个查询:
ParseQuery<ParseObject> queryActivityFollowing = new ParseQuery<>("Activity");
queryActivityFollowing.whereEqualTo("type", "follow");
queryActivityFollowing.whereEqualTo("fromUser", ParseUser.getCurrentUser());
queryActivityFollowing.setLimit(500);
// innerQuery, only get Users posted by the users I follow
ParseQuery<ParseUser> queryUserFollowing = ParseUser.getQuery();
queryUserFollowing.whereMatchesQuery("toUser", queryActivityFollowing);
// querySelf
ParseQuery<ParseUser> querySelf = ParseUser.getQuery();
querySelf.whereEqualTo("objectId", ParseUser.getCurrentUser().getObjectId());
List<ParseQuery<ParseUser>> queries = new ArrayList<>();
queries.add(queryUserFollowing);
queries.add(querySelf);
query = ParseQuery.or(queries);
query.orderByDescending("rating_count");
query.setLimit(20);
但不知何故,它超时并且永远不会显示结果。 我的查询是否效率低下?
谢谢!
编辑:
数据说明:
Activity
是具有 3 列的 class,fromUser
、toUser
、type
。 fromUser
和toUser
是指向_User
的指针class,type
是一个字符串
在 _User
中,我有 classic 属性和一个名为 rating_count
的整数,它是 orderBy 标准(上面更新的代码)。
实际上,我认为查询没有超时,只是 returns 0 个结果。我关注了我的一些用户,所以这绝对不是预期的输出。
这很难,因为 parse 的查询只支持最低限度的这类事情。我能提供的最好的想法是这个:
- 对 Activity table
whereEqualTo("type", "follow")
和whereEqualTo("fromUser", ParseUser.getCurrentUser())
的一个查询
- 没有queryUserFollowing,没有querySelf。这些都是不必要的。这也将您从
Parse.Query.or()
中解放出来。 setLimit(1000)
下面会解释原因include("toUser")
- 完成后,遍历结果,为
result.get("toUser").getInt("rating_count")
最大化,因为结果将是 Activity 的实例,您将急切地获取它们的相关 toUsers。
此方案比您编写的方案更简单,并且可以完成工作。然而,一个主要问题可能是它会丢失拥有超过 1000 个关注者的用户的数据。让我知道这是否是一个问题,我可以建议一个更复杂的答案。一个小缺点是您将被迫自己在内存中进行搜索(也许是排序)以找到最大值 rating_count.
EDIT - 对于 > 1k 的关注者,您不得不多次调用查询,将 skip
设置为上一个查询中收到的记录数, 将结果收集到一个大数组中。
你关于传输这么多数据的观点很好,你可以通过将所有这些放入云函数中来最小化网络使用,在云中进行内存工作并且只返回客户端需要的记录。 (这种方法的额外好处是用 java 脚本编码,我说的比 java 更流利,所以我可以对代码更具规范性)。
编辑 2 - 在云代码中执行此操作的好处是可以减少那些具有最高评级的用户(比如 20 个)的网络流量。它没有解决我之前指出的其他问题。以下是我在云中的操作方式...
var _ = require('underscore');
Parse.Cloud.define("topFollowers", function(request, response) {
var user = new Parse.User({id:request.params.userId});
topFollowers(user, 20).then(function(result) {
response.success(result);
}, function(error) {
response.error(error);
});
});
// return the top n users who are the top-rated followers of the passed user
function topFollowers(user, n) {
var query = new Parse.Query("Activity");
query.equalTo("type", "follow");
query.equalTo("fromUser", user);
query.include("toUser");
return runQuery(query).then(function(results) {
var allFollowers = _.map(results, function(result) { return result.get("toUser"); });
var sortedFollowers = _.sortBy(allFollowers, function(user) { return user.get("rating_count"); });
return _.first(sortedFollowers, n);
});
}
// run and rerun a query using skip until all results are gathered in results array
function runQuery(query, results) {
results = results || [];
query.skip(results.length);
return query.find().then(function(nextResults) {
results = results.concat(nextResults);
return (nextResults.length)? runQuery(query, results) : results;
});
}
注意 - 我没有对此进行测试,但在生产中有类似的东西。
如果您打算更改数据模型,那么有一个解决方案可以满足您的需求,而且还有一些附带好处。考虑一个系统,其中用户 class 仅与应用程序和真人之间的关系有关。用户的 public 面孔由一个新的 class(称之为 PublicUser 或 Persona)呈现。
在这个 PublicUser
class 中,你有一个指向拥有它的 user
的 指针 ,以及一个 指向其他 PublicUser
的指针数组,这个 following
是谁。此 class 还包含 rating
属性。现在 OP 中的查询很简单:
- 查询 PublicUser whereKey "following" 等于 currentUser
- 按等级排序,限制为 20 或您希望限制的任何数量
就是这样。该方案的另一个好处是访问控制。系统理解 PublicUser 中的任何内容都可以被其他 PublicUser 读取,并且关于 __User 的所有内容都保存在该个人和应用程序之间。