Mongo:按数组字段中的项目存在排序,然后按时间戳字段排序

时间:2014-03-01 04:08:17

标签: node.js mongodb

让我清楚地表明,如果以前提出这个问题,我很乐意接受这个问题答案的链接。我不确定如何在搜索引擎中寻找它,对不起。

我有一个名为文章的博客文章集。这些文章的关联标签存储在数组字段中(称为tags。)它们还有一个帖子时间戳字段(称为{{1 }}。)

所以这个集合看起来像这样:

on

我希望文章按日期排序(降序),但我也希望包含标签“固定”的文章始终显示在顶部。所以结果集应如下所示:

[
    {
        _id: '526dd103f00c470200000001',
        title: 'Lorem ipsum 1.',
        body: 'Dolor sit amet 1.',
        tags: ['lorem', 'ipsum'],
        on: 1000
    },
    {
        _id: '526fda069909000200000002',
        title: 'Lorem ipsum 2.',
        body: 'Dolor sit amet 2.',
        tags: ['lorem', 'ipsum', 'pinned'],
        on: 2000
    },
    {
        _id: '527366a11f58a90200000001',
        title: 'Lorem ipsum 3.',
        body: 'Dolor sit amet 3.',
        tags: ['lorem', 'ipsum'],
        on: 3000
    },
    // Etc.
]

我想要做的是使用Mongo聚合,map-reduce或类似的东西来对数据库本身内的结果集进行排序。

我只能进行2次查询(其中一次仅包括固定帖子,另一次仅包括未固定的帖子,按日期排序)并使用JavaScript加入它们(服务器是nodejs),但想知道更好的方法。

P.S。:我正在使用node-mongodb-native驱动程序。

感谢。

2 个答案:

答案 0 :(得分:0)

这应该使用aggregate:

完成这项工作
db.ipsum.aggregate([

    {$unwind: "$tags" },

    {$project: { 
        _id: "$_id",
        title: "$title",
        body: "$body",
        on: "$on",
       "tags": "$tags",
       weight: {$cond: [{$eq: ["$tags", "fixed"]}, 1, 0]} 
    }},

    {$group: {
        _id: { 
            _id: "$_id",
            title: "$title",
            body: "$body",
            on: "$on"
        },
        tags: {$push: "$tags"},
        weight: {$sum: "$weight"}
    }},

   {$sort: { weight: -1, "_id.on": -1 }},

   {$project: { 
       _id: 0,
       _id: "$_id._id",
       title: "$_id.title",
       body: "$_id.body",
       on: "$_id.on",
       tags: "$tags" 
    }}

])

所以主要部分是使用$ cond为你想要的排序顶部的项目分配一个权重,然后进行适当的排序。

答案 1 :(得分:0)

借用另一个答案(https://stackoverflow.com/a/22108015/180581):

使用Mongo的aggregation pipeline实现此目的的一种方法是按标记首先unwind所有文章,然后project在所有已展开的文档中创建is_pinned: true字段标记为“固定”,然后将group标记为ID,如下所示:

db.articles.aggregate([
    {
        $unwind: '$tags'
    },
    {
        $project:
        {
            title: 1,
            body: 1,
            tags: 1,
            on: 1,
            is_pinned: { $eq: ['$tags', 'pinned'] }
        }
    },

为了对相同的文档进行分组,我们可以在每个字段上使用$first,在展开的字段上使用$push来重新创建原始数组,$max$sum汇总is_pinned($ max的工作正常,正如预期的那样,与布尔值相同,所以我更喜欢它):

    {
        $group:
        {
            _id: '$_id',
            title: { $first: '$title' },
            body: { $first: '$body' },
            tags: { $push: '$tags' },
            on: { $first: '$on' },
            is_pinned: { $max: '$is_pinned' }
        }
    },

然后,最后,我们可以is_pinned使用on { $sort: { is_pinned: -1, on: -1 } } ]) sort

{{1}}

使用map-reduce显然也可以实现类似的功能,但感觉有点过分,我希望汇聚管道在未来能够以一种手动映射的方式进行大量优化也觉得......错了。

如果您达到聚合管道/分组限制,则可能需要Map-reduce。在回答这个问题时(MongoDB 2.4.9,)aggregation pipeline documentation states

  

重要说明:聚合管道的结果是一个文档,受BSON文档大小限制的约束,目前为16兆字节。

此外,$group documentation states

  

警告:聚合系统当前将$ group操作存储在内存中,这可能会在处理大量组时出现问题。

我不确定这是如何应用于map-reduce的,但目前map-reduce可能是解决这些潜在问题的一种方法。

由于这些对我来说不是问题,因此这里提出的解决方案似乎已经足够了。