例如,我在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
因为这可能包括“吉姆”。年龄可以相同。
查询这个的正确方法是什么?感谢。
答案 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 :(得分:0)
我做了一个测试并找到了解决方案。
db.collection.find({
$or: [{name: {$gt 'Mac'}, age: 22}, {age: {$gt: 22}}]
})
.sort({age:1, name:1})
真的做了神奇的事。