Javascript 在一个查询中为每个属性分组不同的值和计数
我有个人资料收集中的数据Javascript 在一个查询中为每个属性分组不同的值和计数,javascript,mongodb,mongodb-query,aggregation-framework,Javascript,Mongodb,Mongodb Query,Aggregation Framework,我有个人资料收集中的数据 [ { name: "Harish", gender: "Male", caste: "Vokkaliga", education: "B.E" }, { name: "Reshma", gender: "Female", caste: "Vokkaliga", education: "B.E" }, {
[
{
name: "Harish",
gender: "Male",
caste: "Vokkaliga",
education: "B.E"
},
{
name: "Reshma",
gender: "Female",
caste: "Vokkaliga",
education: "B.E"
},
{
name: "Rangnath",
gender: "Male",
caste: "Lingayath",
education: "M.C.A"
},
{
name: "Lakshman",
gender: "Male",
caste: "Lingayath",
education: "B.Com"
},
{
name: "Reshma",
gender: "Female",
caste: "Lingayath",
education: "B.E"
}
]
这里我需要计算不同性别的总人数,不同种姓的总人数和不同教育的总人数。
预期o/p
{
gender: [{
name: "Male",
total: "3"
},
{
name: "Female",
total: "2"
}],
caste: [{
name: "Vokkaliga",
total: "2"
},
{
name: "Lingayath",
total: "3"
}],
education: [{
name: "B.E",
total: "3"
},
{
name: "M.C.A",
total: "1"
},
{
name: "B.Com",
total: "1"
}]
}
使用mongodb聚合如何才能获得预期结果。根据可用版本的不同,有不同的方法,但它们基本上都会分解为将文档字段转换为“数组”中的单独文档,然后“展开”该数组包含并执行连续阶段,以累积输出总计和数组 MongoDB 3.4.4及以上版本 最新版本具有特殊的运算符,如and,它可以使从源文档到初始“数组”的传输比早期版本更具动态性:
db.profile.aggregate([
{ "$project": {
"_id": 0,
"data": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
}
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
])
因此,使用将初始文档制作成一个由其键和值组成的数组,作为结果对象数组中的“k”
和“v”
键。我们在这里申请是为了按“键”进行选择。这里使用的是我们想要的键列表,但这可以更动态地用作键列表,以便在更短的地方“排除”。它只是使用逻辑运算符来评估条件
这里的结束阶段使用,并且由于中间的所有操作和“分组”仍然保持“k”
和“v”
形式,因此我们在这里使用将结果中的“对象数组”提升为输出中顶层文档的“键”
MongoDB 3.6$mergeObjects
在这里,MongoDB 3.6包含了一个额外的折痕,它也可以用作管道阶段中的,因此替换了,并使最终的只需将“data”
键改为返回文档的“root”:
db.profile.aggregate([
{ "$project": {
"_id": 0,
"data": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
}
}
}},
{ "$unwind": "$data" },
{ "$group": { "_id": "$data", "total": { "$sum": 1 } }},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": {
"$mergeObjects": {
"$arrayToObject": [
[{ "k": "$_id", "v": "$v" }]
]
}
}
}},
{ "$replaceRoot": { "newRoot": "$data" } }
])
这与整体演示的内容并没有太大区别,只是演示了如何以这种方式使用,并且在分组键不同的情况下可能很有用,我们不希望最终“合并”到对象的根空间
请注意,仍然需要将“值”转换回“键”的名称,但我们只是在累积期间而不是在分组之后进行,因为新的累积允许键的“合并”
MongoDB 3.2
将其恢复为一个版本,或者即使您的MongoDB 3.4.x版本低于3.4.4版本,我们仍然可以使用其中的大部分内容,但我们以更静态的方式处理阵列的创建,以及以不同的方式处理输出的最终“转换”,因为我们没有聚合运算符:
db.profile.aggregate([
{ "$project": {
"data": [
{ "k": "gender", "v": "$gender" },
{ "k": "caste", "v": "$caste" },
{ "k": "education", "v": "$education" }
]
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
/*
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
*/
]).map( d =>
d.data.map( e => ({ [e.k]: e.v }) )
.reduce((acc,curr) => Object.assign(acc,curr),{})
)
这是完全相同的事情,除了将文档动态转换为数组之外,我们实际上“显式”地使用相同的“k”
和“v”
符号指定每个数组成员。实际上,在这一点上只保留约定的键名,因为这里的聚合运算符都不依赖于此
此外,我们没有使用,只是做了与前面的管道阶段实现完全相同的事情,而是使用客户机代码。所有MongoDB驱动程序都有一些实现来启用“游标转换”。在这个shell中,我们使用和的基本JavaScript函数获取输出,并再次将数组内容提升为返回的顶级文档的键
MongoDB 2.6
回到MongoDB 2.6来介绍两者之间的版本,这里唯一改变的是使用和a for input以及数组声明:
db.profile.aggregate([
{ "$project": {
"data": {
"$map": {
"input": { "$literal": ["gender","caste", "education"] },
"as": "k",
"in": {
"k": "$$k",
"v": {
"$cond": {
"if": { "$eq": [ "$$k", "gender" ] },
"then": "$gender",
"else": {
"$cond": {
"if": { "$eq": [ "$$k", "caste" ] },
"then": "$caste",
"else": "$education"
}
}
}
}
}
}
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
/*
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
*/
])
.map( d =>
d.data.map( e => ({ [e.k]: e.v }) )
.reduce((acc,curr) => Object.assign(acc,curr),{})
)
因为这里的基本思想是“迭代”提供的字段名数组,所以实际的赋值是通过“嵌套”语句来实现的。对于三种可能的结果,这意味着只有一个嵌套,以便为每个结果“分支”
3.4版本的现代MongoDB使这种分支变得更简单,但这证明了逻辑始终是可能的,并且自从MongoDB 2.2引入聚合框架以来,操作符一直存在
同样,游标结果上的相同转换也适用,因为这里没有什么新的东西,大多数编程语言都有能力这么做多年,如果不是从一开始
当然,基本过程甚至可以追溯到MongoDB 2.2,但只是以不同的方式应用数组创建。但目前还不应该有人在2.8下运行MongoDB,即使是3.0的官方支持也很快就要用完了
输出 为了可视化,在最后一次“转换”完成之前,此处演示的所有管道的输出具有以下形式: 然后通过或光标变换(如图所示),结果变为:
/* 1 */
{
"gender" : [
{
"name" : "Male",
"total" : 3.0
},
{
"name" : "Female",
"total" : 2.0
}
],
"education" : [
{
"name" : "M.C.A",
"total" : 1.0
},
{
"name" : "B.E",
"total" : 3.0
},
{
"name" : "B.Com",
"total" : 1.0
}
],
"caste" : [
{
"name" : "Lingayath",
"total" : 3.0
},
{
"name" : "Vokkaliga",
"total" : 2.0
}
]
}
因此,虽然我们可以将一些新的、奇特的运算符放入聚合管道中,但最常见的用例是这些“管道末端转换”在这种情况下,我们不妨对返回的游标结果中的每个文档执行相同的转换。提供的答案中是否有您认为无法解决您的问题的内容?如果是,请对答案进行评论,以澄清哪些问题需要解决,哪些问题尚未解决。如果它确实回答了您提出的问题,那么请注意您提出的问题。抱歉,这是我一直在寻找的问题。谢谢您的帮助Hi@neil我需要您的帮助,我有更多的要求为上述结果应用过滤器
/* 1 */
{
"gender" : [
{
"name" : "Male",
"total" : 3.0
},
{
"name" : "Female",
"total" : 2.0
}
],
"education" : [
{
"name" : "M.C.A",
"total" : 1.0
},
{
"name" : "B.E",
"total" : 3.0
},
{
"name" : "B.Com",
"total" : 1.0
}
],
"caste" : [
{
"name" : "Lingayath",
"total" : 3.0
},
{
"name" : "Vokkaliga",
"total" : 2.0
}
]
}