按最大数组字段排序,升序或降序

时间:2016-02-13 00:18:21

标签: mongodb meteor mongodb-query aggregation-framework

在我的Meteor应用程序中,我有一组文档,其中包含一系列子文档,如下所示:

/* 1 */
{
    "_id" : "5xF9iDTj3reLDKNHh",
    "name" : "Lorem ipsum",
    "revisions" : [ 
        {
            "number" : 0,
            "comment" : "Dolor sit amet",
            "created" : ISODate("2016-02-11T01:22:45.588Z")
        }
    ],
    "number" : 1
}

/* 2 */
{
    "_id" : "qTF8kEphNoB3eTNRA",
    "name" : "Consecitur quinam",
    "revisions" : [ 
        {
             "comment" : "Hoste ad poderiquem",
             "number" : 1,
             "created" : ISODate("2016-02-11T23:25:46.033Z")
        }, 
        {
            "number" : 0,
            "comment" : "Fagor questibilus",
            "created" : ISODate("2016-02-11T01:22:45.588Z")
        }
    ],
    "number" : 2
}

我想要做的是查询此集合,并按created数组的revisions字段中的最大日期对结果集进行排序。我还没有能够完成的事情。我有一些限制:

  • 仅按revisions.created排序不会削减它,因为从集合中使用的日期取决于排序方向。无论排序顺序如何,我都必须在集合中使用最大日期
  • 我不能依赖对未排序结果集的查询后操作,因此,这必须通过数据库的正确查询或聚合来完成。
  • 无法保证revisions数组将被预先排序。
  • 某些文档中可能还有其他字段,但必须谨慎使用$project
  • Meteor仍在使用MongoDB 2.6,较新的API功能并不好:(

1 个答案:

答案 0 :(得分:3)

你在这里要问的基本问题归结为这样一个事实,即所讨论的数据在一个"数组内,因此MongoDB对如何处理这些数据做了一些基本的假设

如果您按照"降序排序"进行排序,那么MongoDB将完全按照您的要求进行排序,并按照"最大的"数组中指定字段的值:

.sort({ "revisions.created": -1 ))

但是,如果你改为排序"升序"当然,顺序是正确的,最小的"价值被考虑。

.sort({ "revisions.created": 1 })

所以这样做的唯一方法就是计算出数组中数据的最大日期,然后对结果进行排序。这基本上意味着应用.aggregate(),这对于meteor来说是一个服务器端操作,不幸的是这样:

Collection.aggregate([
    { "$unwind": "$revisions" },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "revisions": { "$push": "$revisions" },
        "number": { "$first": "$number" }
        "maxDate": { "$max": "$revisions.created" }
    }},
    { "$sort": { "maxDate": 1 }
])

或者至多使用MongoDB 3.2,其中$max可以直接应用于数组表达式:

Collection.aggregate([
    { "$project": {
        "name": 1,
        "revisions": 1,
        "number": 1,
        "maxDate": {
            "$max": {
                "$map": {
                    "input": "$revisions",
                    "as": "el",
                    "in": "$$el.created"
                }
            }
        }
    }},
    { "$sort": { "maxDate": 1 } }
])

但实际上两者都不是那么好,即使MongoDB 3.2方法的开销低于以前版本的开销,但由于需要,它仍然没有达到性能方面的好传递数据并计算出值以进行排序。

因此,对于最佳表现,"始终"保留您需要的数据"外部"数组。为此,有$max "update"运算符,它只会替换文档中的值"如果"提供的值大于"大于"已有的现有价值。即:

Collection.update(
    { "_id": "qTF8kEphNoB3eTNRA" },
    { 
        "$push": {
            "revisions": { "created": new Date("2016-02-01") }            
        },
        "$max": { "maxDate": new Date("2016-02-01") }
    }
)

这意味着您想要的价值将始终是"已存在于具有预期值的文档中,因此现在只需对该字段进行排序即可:

.sort({ "maxDate": 1 })

因此,对于我的钱,我会使用任何.aggregate()语句中的任何一个来查看现有数据,并使用这些结果更新每个文档以包含" maxDate"领域。然后更改阵列数据的所有添加和修订的编码,以便在每次更改时应用$max "update"

如果你经常使用它,那么拥有一个坚实的领域而不是计算总是更有意义。维护很简单。

在任何情况下,考虑上面应用的例子日期,即"小于"其他最大日期将以各种形式返回给我:

{
        "_id" : "5xF9iDTj3reLDKNHh",
        "name" : "Lorem ipsum",
        "revisions" : [
                {
                        "number" : 0,
                        "comment" : "Dolor sit amet",
                        "created" : ISODate("2016-02-11T01:22:45.588Z")
                }
        ],
        "number" : 1,
        "maxDate" : ISODate("2016-02-11T01:22:45.588Z")
}
{
        "_id" : "qTF8kEphNoB3eTNRA",
        "name" : "Consecitur quinam",
        "revisions" : [
                {
                        "comment" : "Hoste ad poderiquem",
                        "number" : 1,
                        "created" : ISODate("2016-02-11T23:25:46.033Z")
                },
                {
                        "number" : 0,
                        "comment" : "Fagor questibilus",
                        "created" : ISODate("2016-02-11T01:22:45.588Z")
                },
                {
                        "created" : ISODate("2016-02-01T00:00:00Z")
                }
        ],
        "number" : 2,
        "maxDate" : ISODate("2016-02-11T23:25:46.033Z")
}

正确地将第一个文档放在排序顺序的顶部并考虑到" maxDate"。