计算对象属性在 mongo 对象数组中存在的次数
Count the number of times object an property exists in mongo array of objects
我有这样的文档结构
[{
name: "Something",
codes: [
{type: 11},
{type: 11},
{type: 15}
]
},
{
name: "Another",
codes: [
{type: 11},
{type: 12},
{type: 15},
{type: 11}
]
}]]
我需要计算集合中每个条目 type = 11
出现的次数。
我很难过。
虽然可以应用$match
只过滤掉包含特定类型代码的文档,但它不应该应用于这个特定的问题陈述。因为它会从输出中过滤掉没有特定类型代码的文档。
您需要:
Unwind
每个文档基于代码字段。
- 如果代码字段是必需的类型,
project
字段 wantedType
的值为 1
或者使用值 0
.
Group
由_id
字段得到wantedType
字段的总和,即
为您提供特定文档中所需类型代码的数量。
- 因此,即使文档没有所需的类型代码,它也会在输出中显示为
0
作为计数。
代码:
var typeCountToCalculate = 11;
db.collection.aggregate([
{$unwind:"$codes"},
{$project:{"name":1,
"wantedType":{$cond:[{$eq:["$codes.type",typeCountToCalculate ]},1,0]}}},
{$group:{"_id":"$_id",
"name":{$first:"$name"},"count":{$sum:"$wantedType"}}}
])
o/p:
{
"_id" : ObjectId("54ad79dae024832588b287f4"),
"name" : "Another",
"count" : 2
}
{
"_id" : ObjectId("54ad79dae024832588b287f3"),
"name" : "Something",
"count" : 2
}
试试这个:
db.test.aggregate([
{$unwind : "$codes"},
{$match : { 'codes.type' : 11}} ,
{$group : {
_id : { 'name': '$name', 'type' : '$codes.type'}
,count: { $sum: 1 }
}},
]).result
输出将是:
{
"0" : {
"_id" : {
"name" : "Another",
"type" : 11
},
"count" : 2
},
"1" : {
"_id" : {
"name" : "Something",
"type" : 11
},
"count" : 2
}
}
MongoDB的聚合框架就是这里的答案。关键操作是 $unwind
for processing the array contents into "normalized" documents and the $group
用于获取计数的流水线阶段。
$match
pipeline stages. Both at the beginning of the query in order to filter out documents that cannot possibly match and after the $unwind
阶段也进行了优化,以删除那些肯定不符合条件的元素(现在是文档):
db.collection.aggregate([
// Match to filter documents
{ "$match": { "codes.type": 11 }},
// Unwind to 'de-normalize'
{ "$unwind": "$codes" },
// Match to filter again, but remove the array elements
{ "$match": { "codes.type": 11 }},
// Count the occurrences of the the matches
{ "$group": {
"_id": "$codes.type",
"count": { "$sum": 1 }
}}
])
当然,如果您取出所有 "matching",那么您会在整个集合中得到每个 "type" 的 "count"。
在现代版本中,从 MongoDB 2.6 及更高版本开始,您可以使用 $redact
运算符稍微改变一下。如果由于此管道阶段的递归性质而有些做作:
db.collection.aggregate([
// Match to filter documents
{ "$match": { "codes.type": 11 }},
// Filter out non matches
{ "$redact": {
"$cond": {
"if": { "$eq": [
{ "$ifNull": [ "$type", 11 ] },
11
]},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
// Unwind to 'de-normalize'
{ "$unwind": "$codes" },
// Count the occurrences of the the matches
{ "$group": {
"_id": "$codes.type",
"count": { "$sum": 1 }
}}
)
这是一种不同的过滤方式,如果您的服务器支持,则完全有效。如果在其他具有嵌套级别的示例中使用,请小心。
Always 在执行任何其他操作之前过滤您要在 "first" 上处理的匹配值。这消除了在聚合管道中处理的不必要的工作。如果只有 10,000 个可能匹配并且这些文档中只有 2,000 个元素也匹配,则处理 100,000 个文档没有用。
我有这样的文档结构
[{
name: "Something",
codes: [
{type: 11},
{type: 11},
{type: 15}
]
},
{
name: "Another",
codes: [
{type: 11},
{type: 12},
{type: 15},
{type: 11}
]
}]]
我需要计算集合中每个条目 type = 11
出现的次数。
我很难过。
虽然可以应用$match
只过滤掉包含特定类型代码的文档,但它不应该应用于这个特定的问题陈述。因为它会从输出中过滤掉没有特定类型代码的文档。
您需要:
Unwind
每个文档基于代码字段。- 如果代码字段是必需的类型,
project
字段wantedType
的值为1
或者使用值0
. Group
由_id
字段得到wantedType
字段的总和,即 为您提供特定文档中所需类型代码的数量。- 因此,即使文档没有所需的类型代码,它也会在输出中显示为
0
作为计数。
代码:
var typeCountToCalculate = 11;
db.collection.aggregate([
{$unwind:"$codes"},
{$project:{"name":1,
"wantedType":{$cond:[{$eq:["$codes.type",typeCountToCalculate ]},1,0]}}},
{$group:{"_id":"$_id",
"name":{$first:"$name"},"count":{$sum:"$wantedType"}}}
])
o/p:
{
"_id" : ObjectId("54ad79dae024832588b287f4"),
"name" : "Another",
"count" : 2
}
{
"_id" : ObjectId("54ad79dae024832588b287f3"),
"name" : "Something",
"count" : 2
}
试试这个:
db.test.aggregate([
{$unwind : "$codes"},
{$match : { 'codes.type' : 11}} ,
{$group : {
_id : { 'name': '$name', 'type' : '$codes.type'}
,count: { $sum: 1 }
}},
]).result
输出将是:
{
"0" : {
"_id" : {
"name" : "Another",
"type" : 11
},
"count" : 2
},
"1" : {
"_id" : {
"name" : "Something",
"type" : 11
},
"count" : 2
}
}
MongoDB的聚合框架就是这里的答案。关键操作是 $unwind
for processing the array contents into "normalized" documents and the $group
用于获取计数的流水线阶段。
$match
pipeline stages. Both at the beginning of the query in order to filter out documents that cannot possibly match and after the $unwind
阶段也进行了优化,以删除那些肯定不符合条件的元素(现在是文档):
db.collection.aggregate([
// Match to filter documents
{ "$match": { "codes.type": 11 }},
// Unwind to 'de-normalize'
{ "$unwind": "$codes" },
// Match to filter again, but remove the array elements
{ "$match": { "codes.type": 11 }},
// Count the occurrences of the the matches
{ "$group": {
"_id": "$codes.type",
"count": { "$sum": 1 }
}}
])
当然,如果您取出所有 "matching",那么您会在整个集合中得到每个 "type" 的 "count"。
在现代版本中,从 MongoDB 2.6 及更高版本开始,您可以使用 $redact
运算符稍微改变一下。如果由于此管道阶段的递归性质而有些做作:
db.collection.aggregate([
// Match to filter documents
{ "$match": { "codes.type": 11 }},
// Filter out non matches
{ "$redact": {
"$cond": {
"if": { "$eq": [
{ "$ifNull": [ "$type", 11 ] },
11
]},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
// Unwind to 'de-normalize'
{ "$unwind": "$codes" },
// Count the occurrences of the the matches
{ "$group": {
"_id": "$codes.type",
"count": { "$sum": 1 }
}}
)
这是一种不同的过滤方式,如果您的服务器支持,则完全有效。如果在其他具有嵌套级别的示例中使用,请小心。
Always 在执行任何其他操作之前过滤您要在 "first" 上处理的匹配值。这消除了在聚合管道中处理的不必要的工作。如果只有 10,000 个可能匹配并且这些文档中只有 2,000 个元素也匹配,则处理 100,000 个文档没有用。