草庐IT

mongodb - 对集合进行排序和分页

coder 2023-10-27 原文

如何对按非唯一字段排序的查询进行分页?例如,集合中的文档可能是(按 s:1 排序,然后是 _id:-1):

{_id: 19, s: 3},
{_id: 17, s: 3},
{_id: 58, s: 4},
// etc...

有一个简单的限制/跳过方法可以工作......很慢。

是否可以使用类似的东西:

db.collection.find()
  .sort({s:1, _id:-1})
  .min({s:3, _id:17})    // this does not work as wanted!
  .limit(2);

找回

{_id: 17, s: 3},
{_id: 58, s: 4}

?

最佳答案

如果您想按“页码”分页,那么您几乎只能使用 .limit().skip()对键的结果进行排序后应用的方法。您可能已经阅读了一些内容,发现它“效率不高”,主要是因为“跳过”“n”个结果以到达特定页面的成本。

但原则在您需要的地方是合理的:

db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>)

如果您只需要在分页中“向前”移动,则可以使用更快的替代方法,也可以用于“排序”结果。

关键是保持对“s”的“最后一次看到”值的引用,然后通常是一个 _id 值列表,直到“s”的值发生变化。因此,使用更多文档进行演示,已经为演示目的进行了排序:

{ "_id": 1, "s": 3 },
{ "_id": 2, "s": 3 },
{ "_id": 3, "s": 3 },
{ "_id": 4, "s": 2 },
{ "_id": 5, "s": 1 },
{ "_id": 6, "s": 1 },

为了获得“两个”结果的“第一页”,您的第一个查询很简单:

db.collection.find().sort({ "s": -1, "_id": 1}).limit(2)

但是在处理文档时要遵循这一点:

var lastVal = null,
    lastSeen = [];

db.collection.find().sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
    if ( doc.s != lastVal ) {    // Change when different
        lastVal = doc.s;
        lastSeen = [];
    }
    lastSeen.push(doc._id);      // Push _id onto array
    // do other things like output
})

因此在第一次迭代中,lastVal 值将为 3 并且 lastSeen 将同时包含文档 _id数组 [1,2] 中的值。 您可以将这些内容存储在诸如等待下一个页面请求的用户 session 数据之类的内容中。

根据您对下一页集的请求,您可以发出以下命令:

var lastVal = 3,
    lastSeen = [1,2];

db.collection.find({ 
    "_id": { "$nin": lastSeen }, 
    "s": { "$lte": lastVal }
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
    if ( doc.s != lastVal ) {    // Change when different
        lastVal = doc.s;
        lastSeen = [];
    }
    lastSeen.push(doc._id);      // Push _id onto array
    // do other things like output
})

这要求“s”的选择需要从 lastVal 记录的“小于或等于”(因为排序方向)的值开始,并且“_id"字段不能包含记录在 lastSeen 中的值。

生成的下一页是:

{ "_id": 3, "s": 3 },
{ "_id": 4, "s": 2 },

但是现在,如果您遵循逻辑,lastVal 当然是 2lastSeen 现在只有单个数组元素 [4]。由于下一个查询只需要从 2 开始作为小于或等于的值,因此无需保留其他先前看到的“_id”值,因为它们不在该选择范围内。

然后流程继续:

var lastVal = 2,
    lastSeen = [2];

db.collection.find({ 
    "_id": { "$nin": lastSeen }, 
    "s": { "$lte": lastVal }
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) {
    if ( doc.s != lastVal ) {    // Change when different
        lastVal = doc.s;
        lastSeen = [];
    }
    lastSeen.push(doc._id);      // Push _id onto array
    // do other things like output
})

因此,通过遵循该逻辑模式,您可以“存储”从结果的“前一页”中找到的信息,并非常有效地“向前”移动结果。

但是,如果您需要跳转到“第 20 页”或类似类型的操作,那么您将无法使用 .limit().skip()。那样比较慢,但这取决于您能忍受什么。

关于mongodb - 对集合进行排序和分页,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31163090/

有关mongodb - 对集合进行排序和分页的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby - 如何进行排列以有效地定制输出 - 2

    这是一道面试题,我没有答对,但还是很好奇怎么解。你有N个人的大家庭,分别是1,2,3,...,N岁。你想给你的大家庭拍张照片。所有的家庭成员都排成一排。“我是家里的friend,建议家庭成员安排如下:”1岁的家庭成员坐在这一排的最左边。每两个坐在一起的家庭成员的年龄相差不得超过2岁。输入:整数N,1≤N≤55。输出:摄影师可以拍摄的照片数量。示例->输入:4,输出:4符合条件的数组:[1,2,3,4][1,2,4,3][1,3,2,4][1,3,4,2]另一个例子:输入:5输出:6符合条件的数组:[1,2,3,4,5][1,2,3,5,4][1,2,4,3,5][1,2,4,5,3][

  5. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  6. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  7. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  8. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  9. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  10. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

随机推荐