【问题标题】:Mongo: Order by item presence in array field, then by timestamp fieldMongo:按数组字段中的项目存在排序,然后按时间戳字段排序
【发布时间】:2014-04-02 07:54:00
【问题描述】:

让我先明确一点,如果之前已提出此问题的答案,我将很乐意接受此问题的答案。我不知道如何简洁地表达它以便在搜索引擎中寻找它,对不起。

我有一个名为 articles 的博客文章集合。 这些文章的相关标签存储在一个数组字段(称为 tags)中。它们还有一个帖子时间戳字段(称为 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.
]

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

[
    {
        _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
    },
    {
        _id: '526dd103f00c470200000001',
        title: 'Lorem ipsum 1.',
        body: 'Dolor sit amet 1.',
        tags: ['lorem', 'ipsum'],
        on: 1000
    }
    // Etc.
]

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

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

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

谢谢。

【问题讨论】:

  • 您能否使用一两个文档样本来编辑您的答案。然后我们就有了工作要做。
  • 刚刚添加,@NeilLunn。
  • 那里也应该有一个日期字段吗?你说你要排序。请问可以添加吗?
  • on 字段应该是时间戳字段。我知道有一种专门的日期数据类型,但我的架构目前只使用时间戳。
  • 看起来这回答了我的问题:stackoverflow.com/a/22108015/180581

标签: node.js mongodb


【解决方案1】:

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

使用 Mongo 的aggregation pipeline 实现此目的的一种方法是首先通过标签unwind 所有文章,然后project 他们在所有具有标签“固定”的展开文档中创建is_pinned: true 字段,然后@987654325通过 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_pinnedon sort 他们:

    {
        $sort:
        {
            is_pinned: -1,
            on: -1
        }
    }
])

显然,map-reduce 也可以实现类似的功能,但感觉有点矫枉过正,而且我希望聚合管道在未来会得到高度优化,以一种手动 map-reduce 的方式也觉得……不对。

如果您遇到聚合管道/分组限制,可能需要使用 Map-reduce。在给出这个答案时(MongoDB 2.4.9)aggregation pipeline documentation states

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

另外,$group documentation states:

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

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

因为这些对我来说不是问题,所以这里提供的解决方案似乎已经足够好了。

【讨论】:

  • 刚刚意识到展开没有标签的帖子会导致文档从管道中删除。我还不确定如何解决这个问题。当我发现时会更新。
  • 使用 MongoDB 3.2,您可以保留空数组或空数组:{ "$unwind": { "path": "$tags", "preserveNullAndEmptyArrays": true } }
【解决方案2】:

这应该可以完成这项工作,使用聚合:

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 为您想要在排序顶部的项目分配权重,然后进行适当的排序。

【讨论】:

  • $group 不是太重了吗?我认为 _id 应该只是 '$_id',否则当 _id 应该足够时,$group 也会在分组时比较标题、正文和字段。如果我错了,请纠正我。
  • @n2liquid-GuilhermeVieira 如果您想取回原始文件,请不要
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-27
相关资源
最近更新 更多