如何从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"。你要么去:
- 从词法上删除
之前出现的任何其他结果
- 存储结果并将它们分页到 "cut points" 以获取最后看到的值
- 只需迭代游标并丢弃结果,直到找到 "first" 所需的匹配项。
我做了测试,找到了解决方案。
db.collection.find({
$or: [{name: {$gt 'Mac'}, age: 22}, {age: {$gt: 22}}]
})
.sort({age:1, name:1})
真的很神奇。
比如我在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"。你要么去:
- 从词法上删除 之前出现的任何其他结果
- 存储结果并将它们分页到 "cut points" 以获取最后看到的值
- 只需迭代游标并丢弃结果,直到找到 "first" 所需的匹配项。
我做了测试,找到了解决方案。
db.collection.find({
$or: [{name: {$gt 'Mac'}, age: 22}, {age: {$gt: 22}}]
})
.sort({age:1, name:1})
真的很神奇。