如何使用数组的最后一个元素对集合进行排序

时间:2013-08-08 12:30:37

标签: mongodb mongoengine

我的问题是,我有一个集合,_id被忽略

{ "value" : -10, "r" : [ { "v" : 1 }, { "v" : 3 } ] }
{ "value" : 2, "r" : [ { "v" : 4 }, { "v" : 1 } ] }
{ "value" : -100, "r" : [ { "v" : 4 }, { "v" : 1 }, { "v" : 10 } ] }
{ "value" : -3, "r" : [ ] }

我想用数组r的最后一个值对它进行排序,也就是说,我想在下面得到一个结果,

{ "value" : -3, "r" : [ ] } # this one should be either the first one or the last one
{ "value" : 2, "r" : [ { "v" : 4 }, { "v" : 1 } ] }
{ "value" : -10, "r" : [ { "v" : 1 }, { "v" : 3 } ] }
{ "value" : -100, "r" : [ { "v" : 4 }, { "v" : 1 }, { "v" : 10 } ] }

我知道我可以通过数组r的第一个vaue排序

db.my.find().sort({'r.0.v': 1})

但是怎么能用最后一个值来做呢?

并且,如果使用下面的mongoengine模型

class V(EmbeddedDocument):
    v = IntField()

class M(Document):
    value = IntFiled
    r = ListField(EmbeddedDocumentField(V))

我应该如何处理mongoengine? IntField可能是其他字段,例如DateTimeFieldStringField ...

由于

2 个答案:

答案 0 :(得分:1)

您可以通过以下方式使用聚合框架执行此操作:

db.so.aggregate( [
    // first we add the ``sortr`` field so that we can deal with empty arrays.
    // if we see an empty array, we create one with a large negative number
    { $project: { 
        value: 1, 
        r: 1,
        sortr: { $cond: [ { $eq : [ '$r', []  ] }, [ -100000 ], '$r' ] }
    } },

    // then we unwind on our sorting-r-variant
    { $unwind: '$sortr' },

    // so that we can group by ID and pick out the last of the $sortr values
    { $group: { 
        _id: '$_id', 
        value: { $first: '$value' }, 
        r: { $first: '$r' }, 
        sortr: { $last: '$sortr' } 
    } },

    // then we sort by the last items over all the documents
    { $sort: { sortr: 1 } },

    // and reproject so that we get rid of the ``sortr`` field
    { $project: { value: 1, r: 1 } }
] );

在输入数据上输出:

{
    "result" : [
        {
            "_id" : ObjectId("520391bd0cc1fa3c84416e9a"),
            "value" : -3,
            "r" : [ ]
        },
        {
            "_id" : ObjectId("520391a70cc1fa3c84416e98"),
            "value" : 2,
            "r" : [ { "v" : 4 }, { "v" : 1 } ]
        },
        {
            "_id" : ObjectId("520391a10cc1fa3c84416e97"),
            "value" : -10,
            "r" : [ { "v" : 1 }, { "v" : 3 } ]
        },
        {
            "_id" : ObjectId("520391ad0cc1fa3c84416e99"),
            "value" : -100,
            "r" : [ { "v" : 4 }, { "v" : 1 }, { "v" : 10 } ]
        }
    ],
    "ok" : 1
}

答案 1 :(得分:1)

如果可能的话,我建议你总是(双倍)存储你想要排序的值。将它放在数组和第二个字段中。每当您将新值推送到数组(或存储数组)时,添加一个对应于“数组中的最后一个值”的新字段,然后对其进行索引和排序。在下面的示例中,我将其称为lastR

{ "value" : -10, "r" : [ { "v" : 1 }, { "v" : 3 } ], "lastR": 3 }
{ "value" : 2, "r" : [ { "v" : 4 }, { "v" : 1 } ], "lastR": 1 }
{ "value" : -100, "r" : [ { "v" : 4 }, { "v" : 1 }, { "v" : 10 } ], "lastR": 10 }
{ "value" : -3, "r" : [ ] }

创建索引:

db.so.ensureIndex({lastR: 1})

然后使用:

> db.so.find().sort({lastR: 1})
{ "_id" : ObjectId("5203a1c83c5438af60de63a1"), "value" : -3, "r" : [ ] }
{ "_id" : ObjectId("5203a1ad3c5438af60de639f"), "value" : 2, "r" : [ { "v" : 4 }, { "v" : 1 } ], "lastR" : 1 }
{ "_id" : ObjectId("5203a1d33c5438af60de63a2"), "value" : -10, "r" : [ { "v" : 1 }, { "v" : 3 } ], "lastR" : 3 }
{ "_id" : ObjectId("5203a1b73c5438af60de63a0"), "value" : -100, "r" : [ { "v" : 4 }, { "v" : 1 }, { "v" : 10 } ], "lastR" : 10 }

与尝试使用聚合解决方案相比,它将更具通用性和可扩展性(聚合解决方案对结果集的限制为16MB,并且在需要处理投影时检索复杂文档会更加复杂)。