如何从MongoDB中的一个点开始排序结果?

How to get sorted result start from a point in MongoDB?

比如我在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}
....

如果我想获得按 "age" 排序的结果,这很简单,只需创建一个索引 "age" 并使用 sort。然后得到一长串return。我可能会得到如下结果:

{_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}
...

如果我只希望此列表也按 "age" 排序并从 "Mac" 开始怎么办?如下所示:

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

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

正确的查询方式是什么?谢谢。

我认为这更像是一个 "terminology" 问题,因为你所说的 "start point" 其他人称之为不同的东西。我在这里看到两件事,一件事我认为是 "wrong" 方法,一件事我认为是 "right" 方法来实现您想要做的事情。不过,两者都会在此样本上给出所需的结果。当然还有 "obvious" 方法,如果它也足以满足您的需求。

对于 "wrong" 方法,我基本上会说在两种情况下都使用 $gte,对于 "name" 和 "age"。这基本上给你 "starting point" at "Mac":

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

当然,如果您 "Alan" 的年龄为“27”,这当然不会起作用,因为名字小于起始名字值。当然适用于您的样本。

我相信 "right" 你所问的是你在谈论 "paging" 数据时使用比使用 .skip() 更有效的方式。在这种情况下,您想要做的是 "exclude" 以类似的方式产生结果。

因此,这意味着基本上保留最后 "page" 看到的文档,或者可能更多,具体取决于 "range" 值的变化程度,并排除唯一的 _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);

由于在第一个实例中您已经 "seen" 前两个结果,您将 _id 值作为 $nin 操作提交以排除它们并要求任何东西 "greater than or equal to" "last seen" 年龄值。

这是向前 "page" 数据的有效方式,可能确实是你要问的,但当然它需要你知道 "which data" 来 "before Mac"为了把事情做好。所以剩下最后的 "obvious" 方法:

从 "Mac" 开始的最简单方法是基本上查询结果,然后 "discard" 在结果达到所需值之前查询任何内容:

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
    }

})

归根结底,当然无法以任何任意方式 "start from where 'Mac' appears in a a sorted list"。你要么去:

  1. 从词法上删除
  2. 之前出现的任何其他结果
  3. 存储结果并将它们分页到 "cut points" 以获取最后看到的值
  4. 只需迭代游标并丢弃结果,直到找到 "first" 所需的匹配项。

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

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

真的很神奇。