问题
My problem is that, i have a collection below, _id is ignored
{ "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" : [ ] }
And i what to sort it by the last value of array r, that is to say, i want to have a result below,
{ "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 } ] }
I know that i can sort by the first vaue of the array r by
db.my.find().sort({'r.0.v': 1})
But how can do it by the last value?
And, if with its mongoengine model below
class V(EmbeddedDocument):
v = IntField()
class M(Document):
value = IntFiled
r = ListField(EmbeddedDocumentField(V))
How should i do with mongoengine? IntField
maybe other fields such as DateTimeField
, StringField
...
Thanks
回答1:
If possible, I'd suggest you just always (double) store the value you want to sort on. Put it in the array and in a second field. Whenever you push a new value to the array (or store the array), add a new field corresponding to the "last value in array" and then index and sort on that. In the example below, I called it 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" : [ ] }
Create an index:
db.so.ensureIndex({lastR: 1})
And then use:
> 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 }
It will be far more versatile and expandable than trying to use an aggregation solution (which will have a 16MB limit to the result set and makes it far more complex to retrieve complex documents when needing to deal with a projection).
回答2:
You can do this with the aggregation framework in the following way:
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 } }
] );
On your input data, this outputs:
{
"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
}
来源:https://stackoverflow.com/questions/18126094/how-to-sort-a-collection-using-the-last-element-of-an-array