Mongoose/MongoDB:$in 和 .sort()

Posted

技术标签:

【中文标题】Mongoose/MongoDB:$in 和 .sort()【英文标题】:Mongoose/MongoDB: $in and .sort() 【发布时间】:2017-05-18 22:00:18 【问题描述】:

我使用了一个 API,它每天跟踪 50 个成员在游戏中的数据,并使用 mongoose 将 JSON 转换为集合中的单个文档。几天之间有一致的数据,例如每个成员的标签(游戏中成员的 id),但有不同的数据(不同的分数等)。每个文档都有一个createdAt 属性。

我想找到每个成员的最新文档,因此有一个包含每个成员标签的数组。

我目前使用以下查询来查找标签匹配的所有文档,但是它们返回的是 所有 文档,而不仅仅是一个。如何将文档排序/限制为最近的文档,同时将其保留为一个查询(或者是否有更多“mongodb 方式”)?

memberTags = [1,2,3,4,5];
ClanMember.find(
    'tag': 
        $in: memberTags
    
).lean().exec(function(err, members) 
    res.json(members);
);

谢谢

【问题讨论】:

可以通过聚合来完成。 【参考方案1】:

好的,首先,让我们使用findOne(),这样您就只能从请求中获取一个文档 然后按照最新的文档排序,你可以使用.sort(elementYouWantToSort: -1)(-1表示你想从最新到最旧排序,1从最旧到最新排序) 我建议在 _id 上使用这个函数,它已经包含了文档的创建日期 这给了我们以下要求:

ClanMember.findOne(
    'tag': 
        $in: memberTags
    
).sort(_id: -1).lean().exec(function(err, members) 
    res.json(members);
);

【讨论】:

不会只返回 1 个文档,而不是每个标签的最新文档吗?【参考方案2】:

您可以通过聚合框架进行查询。您的查询将涉及pipeline,它具有处理输入文档的阶段,以便为您提供所需的结果。在您的情况下,管道将有一个 $match 阶段,它充当初始过滤器的查询。 $match 使用标准 MongoDB 查询,因此您仍然可以使用 $in 进行查询。

下一步是按createdAt 字段对过滤后的文档进行排序。这是使用 $sort 运算符完成的。

前面的管道阶段涉及聚合有序文档以返回每个组的顶部文档。 $group 运算符和 $first 累加器是使这成为可能的运算符。

综上所述,您可以运行以下聚合操作来获得所需的结果:

memberTags = [1,2,3,4,5];
ClanMember.aggregate([
     "$match":  "tag":  "$in": memberTags   ,
     "$sort":  "tag": 1, "createdAt: -1 "  ,
    
        "$group": 
            "_id": "$tag",
            "createdAt":  "$first": "$createdAt"  /*,
            include other necessary fields as appropriate
            using the $first operator e.g.
            "otherField1":  "$first": "$otherField1" ,
            "otherField2":  "$first": "$otherField2" ,
            ...

            */
        
    
]).exec(function(err, members) 
    res.json(members);
);

或者使用find() 调整您当前的查询,以便您可以对两个字段进行排序,即tag(升序)和createdAt(降序)属性。然后,您可以使用 limit 选择前 5 个文档,如下所示:

memberTags = [1,2,3,4,5];
ClanMember.find(
     'tag':  $in: memberTags  , // query
    , // projection
     // options
        sort:  'createdAt': -1, 'tag': 1 ,
        limit: memberTags.length, 
        skip: 0
    
).lean().exec(function(err, members) 
    res.json(members);
);

memberTags = [1,2,3,4,5];
ClanMember.find(
    'tag': 
        $in: memberTags
    
).sort('-createdAt tag')
  .limit(memberTags.length)
  .lean()
  .exec(function(err, members) 
    res.json(members);
);

【讨论】:

太棒了,虽然aggregate 查询有效,但我不想重复查询返回所需的字段,但是您对find() 查询的调整完美无缺。谢谢! 不用担心,很高兴这有效。最初,我只能想到一个aggregate 的方法,只是为了发现一些事情不需要考虑太多:)

以上是关于Mongoose/MongoDB:$in 和 .sort()的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose (mongodb) 批量插入、删除、更新和无操作

从两个数据库集合(Mongoose/MongoDB)中查询和匹配

详解node+mongoose+mongoDb(mongoDb安装运行,在node中链接)

Mongoose/Mongodb Aggregate - 对多个字段进行分组和平均

$gte 和 $lte 在 Mongoose/MongoDB 中没有按预期工作

如何对 mongoose/mongodb 和 nodejs 中的对象数组进行排序?