我有以下查询。其中field_a
为String
属性,field_b
为Number
类型的数组。我想要一个具有唯一组合的属性field_a
和field_b
的数组。这里field_a
包含数值但是以字符串格式。所以我想在聚合管道中应用自然排序。 $natural
只能用于此类查询db.collection.find().sort( { $natural: 1 } )
那么如何在MongoDB中使用自然排序或者我需要依赖JS函数或lodash / underscore.js?
db.collection.aggregate([
{"$group": { "_id": { field_a: "$field_a", field_b: "$field_b" } } },
{ $project: { a: "$_id" } },
{"$group": {"_id": 'a', "res": {"$addToSet": "$_id" }}},
{"$unwind": "$res"},
{"$sort": { "res": 1}},
{"$group": { "_id": null, "res": {"$push": "$res" }}}
])
答案 0 :(得分:0)
我说,这就是你想要做的事情:
db.collection.aggregate([
{ "$group": {
"_id": {
"field_a": {
"$concat": [
{ "$substrCP": [
"0000000000",
0,
{ "$subtract": [ 10, { "$strLenCP": "$field_a" } ] }
]},
"$field_a"
]
},
"field_b": "$field_b"
}
}},
{ "$sort": { "_id": 1 } }
])
作为一个基本概念,您遇到的问题是“字符串”的排序方式不能转化为数字排序的工作方式。
作为一个简短的例子,这些文档使用字符串值:
{ "_id" : ObjectId("5928276f84c3559bc2fd458b"), "a" : "5" }
{ "_id" : ObjectId("5928277484c3559bc2fd458c"), "a" : "50" }
{ "_id" : ObjectId("5928277e84c3559bc2fd458d"), "a" : "60" }
{ "_id" : ObjectId("5928278284c3559bc2fd458e"), "a" : "6" }
如果您尝试对这些进行排序,则适用词法顺序:
> db.list.find().sort({ "a": 1 })
{ "_id" : ObjectId("5928276f84c3559bc2fd458b"), "a" : "5" }
{ "_id" : ObjectId("5928277484c3559bc2fd458c"), "a" : "50" }
{ "_id" : ObjectId("5928278284c3559bc2fd458e"), "a" : "6" }
{ "_id" : ObjectId("5928277e84c3559bc2fd458d"), "a" : "60" }
作为字符串,这是有道理的。由于"50"
以"5"
开头,因此小于"6"
。
由于聚合框架不能将这些“转换”为数值,因此您唯一的选择是以一种词汇方式呈现“字符串”,其方式与数值相同。
简而言之,我们将它们“零填充”它们,这实际上是使值固定长度字符串,这些字符串带有前缀或“填充”0
,这使得“字符串”看起来像它们在数字上一样有序:
db.list.aggregate([
{ "$project": {
"a": {
"$concat": [
{ "$substrCP": [
"0000000000",
0,
{ "$subtract": [ 10, { "$strLenCP": "$a" } ] }
]},
"$a"
]
}
}},
{ "$sort": { "a": 1 } }
])
这将产生一个列表,如:
{ "_id" : ObjectId("5928276f84c3559bc2fd458b"), "a" : "0000000005" }
{ "_id" : ObjectId("5928278284c3559bc2fd458e"), "a" : "0000000006" }
{ "_id" : ObjectId("5928277484c3559bc2fd458c"), "a" : "0000000050" }
{ "_id" : ObjectId("5928277e84c3559bc2fd458d"), "a" : "0000000060" }
这里的基本前提是你接受一个“模板”字符串,在这个例子中是一个0
字符串,长度为10个字符。然后,我们使用$strLenCP
和$subtract
来查看要转换的字段数据的长度,该长度来自10
,这是此处使用的模板字符串的长度。
长度差异作为从模板中获取的字符数馈送到$substrCP
运算符。然后将此输出提供给$concat
,以便创建一个类似于模板的“字符串”长度为10个字符,但以零开头并以初始数字字符串结尾。
在您的实际使用中,这将最终作为复合键的一部分。然而,由于转换后的密钥首先在密钥顺序中,因此简单地按_id
排序会考虑这一点并主要按第一个键中的值排序,然后按第二个键排序。