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

时间:2015-01-02 00:43:10

标签: mongodb sorting indexing mongodb-query database

例如,我在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因为这可能包括“吉姆”。年龄可以相同。

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

2 个答案:

答案 0 :(得分:3)

我认为这更像是一个“术语”问题,因为你所谓的“起点”,其他人称之为不同的东西。我在这里看到的两件事是我认为是“错误”的方法,而我认为是你想要做的“正确”的方法。两者都会在这个样本上给出所需的结果。如果这对你的需求来说足够简单,那么当然有“明显的”方法。

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

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

但是,如果你的年龄为“27”的“Alan”,因为名称小于起始名称值,这当然不会起作用。当然可以使用你的样本。

我相信你所问的“正确”的事情是你正在谈论使用.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. 只需迭代光标并丢弃结果,直到找到“第一个”所需匹配项。

答案 1 :(得分:0)

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

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

真的做了神奇的事。