【问题标题】:How to get sorted result start from a point in MongoDB?如何从MongoDB中的一个点开始排序结果?
【发布时间】:2015-02-28 10:18:27
【问题描述】:

例如,我在 MongoDB 中得到了一些数据

db: people

{_id:1, name:"Tom", age:26}
{_id:2, name:"Jim", age:22}
{_id:3, name:"Mac", age:22}
{_id:4, name:"Zoe", age:22}
{_id:5, name:"Ray", age:18}
....

如果我想得到按“年龄”排序的结果,这很简单,只需创建一个索引“年龄”并使用sort。然后我得到了一长串的回报。我可能会得到如下结果:

{_id:5, name:"Ray", age:18}
{_id:2, name:"Jim", age:22}
{_id:3, name:"Mac", age:22}
{_id:4, name:"Zoe", age:22}
{_id:1, name:"Tom", age:26}
...

如果我只希望这个列表也按“年龄”排序并从“Mac”开始怎么办?如下:

{_id:3, name:"Mac", age:22}
{_id:4, name:"Zoe", age:22}
{_id:1, name:"Tom", age:26}
...

我不能使用$gte,因为这可能包括“Jim”。年龄可以相同。

查询这个的正确方法是什么?谢谢。

【问题讨论】:

    标签: mongodb sorting indexing mongodb-query database


    【解决方案1】:

    我认为这更像是一个“术语”问题,因为你称之为“起点”,其他人称之为不同的东西。我在这里看到两件事,我认为这是“错误”的方法,另一件事是我认为你想做的事情的“正确”方法。不过,两者都会在这个样本上给出预期的结果。如果这也足够简单满足您的需求,当然还有“显而易见”的方法。

    对于“错误”的方法,我基本上会说在这两种情况下都使用$gte 来表示“姓名”和“年龄”。这基本上让你成为“Mac”的“起点”:

    db.collection.find(
        { "name": { "$gte": "Mac" }, "age": { "$gte": 22 } }
    ).sort({ "age": 1 })
    

    但是,如果您的“Alan”年龄为“27”,这当然行不通,因为该名称小于起始名称值。当然适用于您的样本。

    我相信你所问的“正确”的事情是你正在用一种比使用.skip() 更有效的方式来谈论“分页”数据。在这种情况下,您要做的是以类似方式“排除”结果。

    因此,这实际上意味着保留看到的文档的最后“页面”,或者可能更多取决于“范围”值的变化程度,并通过唯一的 _id 值排除。最好的证明是:

    // First iteration
    var cursor = db.collection.find({}).sort({ "age": 1 }).limit(2);
    
    cursor.forEach(function(result) {
        seenIds.push(result._id);
        lastAge = result.age;
        // do other things
    });
    
    // Next iteration
    var cursor = db.collection.find(
        { "_id": { "$nin": seenIds }, "age": { "$gte": lastAge } }
    ).sort({ "age": 1 }).limit(2);
    

    由于在第一个实例中您已经“看到”了前两个结果,因此您将 _id 值提交为 $nin 操作以排除它们,并要求任何“大于或等于”“最后一个”看过”的年龄值。

    这是一种向前“分页”数据的有效方式,并且可能确实是您所要求的,但当然它需要您知道“哪些数据”在“Mac 之前”才能正确执行操作.这样就剩下了最终的“明显”方法:

    从“Mac”开始最简单的方法是基本上查询结果并在结果达到所需值之前“丢弃”任何内容:

    var startSeen = false;
    db.collection.find(
        { "age": {"$gte": 22}}
    ).sort({ "age": 1 }).forEach(function(result) {
        if ( !startSeen )
            startSeen = ( result.name == 'Mac' );
    
        if ( startSeen ) {
            // Mac has been seen. Do something with your data
        }
    
    })
    

    归根结底,当然没有办法以任意方式“从 'Mac' 在排序列表中出现的位置开始”。你要么去:

    1. 按词法删除在
    2. 之前发生的任何其他结果
    3. 存储结果并通过它们翻页到最后看到的值的“切点”
    4. 只需迭代光标并丢弃结果,直到找到“第一个”所需匹配项。

    【讨论】:

    • 非常感谢。很高兴知道什么是可能的。如果有像您提到的“艾伦”,那么 mongoldb sort 的年龄将如何?我相信它不会在同一年龄自动按“名称”排序。那么订购的第二个字段是什么。会是“自然秩序”(磁盘秩序)吗?
    • @WiseSimpson 关于“Alan”的观点或通常任何词汇上的“小于 Mac”实际上都是关于“名称”上的 $gte 运算符,而不是与排序有关。方法会有所不同,具体取决于您真正想要做什么。顺便说一句,@ 987654333@ 顺序从技术上讲更多的是“逻辑”排序或重新排列的数据集群,而不是实际的“物理”磁盘顺序。随着新存储引擎的出现,从这些方面进行思考非常重要。
    • 你刚刚启发了我。这个db.collection.find({ $or: [{name: {$gt 'Mac'}, age: 22}, {age: {$gt: 22}}]}).sort({age:1})怎么样?
    • @WiseSimpson “绝对不是”。与"age": { "$gte": 22 } 没有什么不同。基本相同的条件,只是啰嗦。顺便说一句,您在这里提问以获得专家意见。你有一个。除了我提到的之外,没有其他方法可以“给这只猫剥皮”。如果你想“研究”任何东西,那就去研究它。或者我自己的可信度。
    【解决方案2】:

    我做了一个测试,找到了解决方案。

    db.collection.find({
      $or: [{name: {$gt 'Mac'}, age: 22}, {age: {$gt: 22}}]
    })
    .sort({age:1, name:1})
    

    真的做到了。

    【讨论】:

      猜你喜欢
      • 2022-01-12
      • 2021-11-20
      • 2014-09-01
      • 2016-03-10
      • 2018-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-26
      相关资源
      最近更新 更多