使用猫鼬的Mongo自定义排序策略
Mongo Custom Sort Strategy using mongoose
首先:我使用的是Mongo 2.6和Mongoose 3.8.8
我有以下模式:
var Link = new Schema({
title: { type: String, trim: true },
owner: { id: { type: Schema.ObjectId }, name: { type: String } },
url: { type: String, default: '', trim: true},
stars: { users: [ { name: { type: String }, _id: {type: Schema.ObjectId} }] },
createdAt: { type: Date, default: Date.now }
});
我的收藏已经有50万份文件了。
我需要的是使用自定义策略对文档进行排序。我最初的解决方案是使用聚合框架。
var today = new Date();
//fx = (TodayDay * TodayYear) - ( DocumentCreatedDay * DocumentCreatedYear)
var relevance = { $subtract: [
{ $multiply: [ { $dayOfYear: today }, { $year: today } ] },
{ $multiply: [ { $dayOfYear: '$createdAt' }, { $year: '$createdAt' } ] }
]}
var projection = {
_id: 1,
url: 1,
title: 1,
createdAt: 1,
thumbnail: 1,
stars: { $size: '$stars.users'}
ranking: { $multiply: [ relevance, { $size: '$stars.users' } ] }
}
var sort = {
$sort: { ranking: 1, stars: 1 }
}
var page = 1;
var limit = { $limit: 40 }
var skip = { $skip: ( 40 * (page - 1) ) }
var project = { $project: projection }
Link.aggregate([project, sort, limit, skip]).exec(resultCallback);
它在100k之前都能很好地工作,之后查询变得越来越慢。我怎么能做到这一点
重新设计
投影的错误使用我在做什么?
谢谢你抽出时间!
您可以在更新时完成所有这些操作,然后您可以对排名进行索引,并使用范围查询来实现分页。比使用$skip
和$limit
要好得多,后者在任何形式下对大数据来说都是坏消息。您应该能够找到许多来源来确认跳过和限制是分页的一种糟糕做法。
这里唯一的问题是,由于不能使用.update()
类型的语句来实际引用另一个字段的现有值,因此必须小心更新时的并发问题。这需要"引入"一些自定义锁处理,您可以使用.findOneAndUpdate()
方法:
Link.findOneAndUpdate(
{ "_id": docId, "locked": false },
{ "locked": true },
function(err,doc) {
if ( doc.locked.true ) {
// then update your document
// I would just use the epoch date difference per day
var relevance = (
( Date.now.valueOf() - ( Date.now().valueOf() % 1000 * 60 * 60 * 24) )
- ( doc.createdAt.valueOf() - ( doc.createdAt.valueOf() % 1000 * 60 * 60 * 24 ))
);
var update = { "$set": { "locked": false } };
if ( actionAdd ) {
update["$push"] = { "stars.users": star };
update["$set"]["score"] = relevance * ( doc.stars.users.length +1 );
} else {
update["$pull"] = { "stars.users": star };
update["$set"]["score"] = relevance * ( doc.stars.users.length -1 );
}
// Then update
Link.findOneAndUpdate(
{ "_id": doc._id, "locked": update,function(err,newDoc) {
// possibly check that new "locked" is false, but really
// that should be okay
});
} else {
// some mechanism to retry "n" times at interval
// or report that you cannot update
}
}
)
其想法是,您只能获取"锁定"状态等于false
的文档才能进行实际更新,而第一个"更新"操作只将该值设置为true
,这样在完成之前,其他操作都无法更新文档。
根据代码注释,您可能希望尝试几次,而不仅仅是更新失败,因为可能会有另一个从数组中添加或减去的操作。
然后,根据当前更新的"模式",如果您要添加到数组中或从中删除某个项目,只需更改要发布的更新语句即可执行任一操作,并在文档中设置适当的"分数"值。
当然,更新会将"锁定"状态设置为false
,检查当前状态是否为true
是有意义的,尽管此时确实应该可以。但这给了你一些提出例外情况的空间。
这管理了一般的更新情况,但在这里整理你的"排名"顺序仍然有问题,因为跳过和限制仍然不是你想要的性能。这可能最好通过定期更新另一个字段来处理,您可以使用该字段进行最终的"范围"查询,但您可能只想关注页面范围中最"相关"的分数范围,而不是更新整个集合。
更新需要定期进行,因为如果您试图在单个更新中更改多个文档的"排名"顺序,则会出现并发问题。因此,您需要确保此过程不会与另一个此类更新重叠。
最后,考虑一下你的"分数"计算,因为你真正想要的是最新的、"最受欢迎"的内容。目前的计算有一些缺陷,比如在同一天和0颗"星星",但我会让你来解决。
这基本上就是您需要为解决方案做的事情。尝试使用聚合框架在大型集合上动态执行此操作不会为您的应用程序体验带来良好的性能。因此,这里有一些建议可以帮助你更有效地维护结果的顺序。
- Dojo不解析自定义小部件的模板html中的小部件声明性
- 在自定义mean.io包中使用angular-chart.js作为依赖项
- jQuery自定义验证比较多个输入的序列
- 从HTTPGET返回一个自定义对象列表,以便在Angular 2应用程序中使用
- 在wordpress一定时间后更改自定义字段
- 表达式引擎扩展开发-向呈现的条目添加自定义javascript
- 自定义函数中的光标位置
- 用于检查数组中是否存在元素的javascript自定义方法
- 绑定时将Parsley minlength消息作为选项传递时,未对其进行自定义
- Meteor-添加用户自定义字段的方法不起作用
- 自定义表单验证和提交
- jQuery工具验证器自定义效果-添加&消除影响
- 可以't计算自定义谷歌地图的js
- 谷歌水印未显示在自定义搜索框中
- vaadin:使用自定义布局集成angular js
- DataTables-创建自定义分页样式(加载更多样式)
- 实现一个建立在google.com之上的自定义搜索引擎
- 添加自定义标记以自动完成
- 使用猫鼬的Mongo自定义排序策略
- 设置mongo'具有自定义UID的s_id