到目前为止,我有一个查询如下:
db.variants.aggregate({'$unwind':'$samples'},{$project:{"_id":0,"samples":1,"chr":1,"pos":1,"ref":1,"alt":1}});
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "0|0", "GQ" : 48, "DP" : 1, "HQ" : [ 34, 1 ], "GTC" : 0, "sample_id" : "559de1b2aa43f47656b2a3fa"}
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "1|0", "GQ" : 15, "DP" : 8, "HQ" : [ 5, 51 ], "GTC" : 1, "sample_id" : "559de1b2aa43f47656b2a3f9"}
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "1/1", "GQ" : 43, "DP" : 5, "HQ" : [ 0, 2 ], "GTC" : 2, "sample_id" : "559de1b2aa43f"}
{ "chr" : "20", "pos" : 14371, "ref" : "A", "alt" : "G", "samples" : { "GT" : "0|0", "GQ" : 48, "DP" : 1, "HQ" : [ 51, 51 ], "GTC" : 0, "sample_id" : "559de1b2aa43f47656b2a3fa"}
{ "chr" : "20", "pos" : 14371, "ref" : "A", "alt" : "G", "samples" : { "GT" : "1|0", "GQ" : 48, "DP" : 8, "HQ" : [ 51, 51 ], "GTC" : 1, "sample_id" : "559de1b2aa43f47656b2a3f9"}
但在查询之外,我定义了这样的样本组:
SID1=['559de1b2aa43f47656b2a3fa','559de1b2aa43f47656b2a3f9']
SID2=['559de1b2aa43f']
但当谈到如何进行实际分组时,我陷入了困境,因为我的sample_id数组实际上不在文档中,所以普通的$group运算符不起作用。如果samples.study_id在SID1数组中(SID2也是如此(,我想创建名为SID1和SID2的组,将"samples.GTC"相加。我的预期输出是:
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", SID1:1, SID2:2}
{ "chr" : "22", "pos" : 14371, "ref" : "A", "alt" : "G", SID1:1, SID2:0}
我猜它应该有点接近这个,但显然不完全:
db.variants.aggregate(
{'$unwind':'$samples'},
{$project:
{"_id":0,"chr":1,"pos":1,"ref":1,"alt":1,"samples":1}
},
{ "$group":{
"_id":{"chr":"$chr","pos":"$pos","ref":"$ref","alt":"$alt"},
"SID1" : {$cond:{if:{"$sample_id":{$in:SID1}},then:{$sum:"$GTC"},else:0}},
"SID2" : {$cond:{if:{"$sample_id":{$in:SID2}},then:{$sum:"$GTC"},else:0}},
}
});
您在查找$cond
时是对的,但语法有点错误,这里还需要一些其他帮助:
var SID1 = ['559de1b2aa43f47656b2a3fa','559de1b2aa43f47656b2a3f9'],
SID2 = ['559de1b2aa43f'];
db.variants.aggregate([
{ "$unwind": "$samples" },
{ "$group": {
"_id": {
"chr": "$chr",
"pos": "$pos",
"ref": "$ref",
"alt": "$alt"
},
"SID1": {
"$sum": {
"$cond": [
{ "$setIsSubset": [
{ "$map": {
"input": { "$literal": ["A"] },
"as": "el",
"in": "$samples.sample_id"
}},
SID1
]},
"$samples.GTC",
0
]
}
},
"SID2": {
"$sum": {
"$cond": [
{ "$setIsSubset": [
{ "$map": {
"input": { "$literal": ["A"] },
"as": "el",
"in": "$samples.sample_id"
}},
SID2
]},
"$samples.GTC",
0
]
}
}
}}
])
结果是:
{
"_id" : {
"chr" : "20",
"pos" : 14371,
"ref" : "A",
"alt" : "G"
},
"SID1" : 1,
"SID2" : 0
}
{
"_id" : {
"chr" : "22",
"pos" : 14373,
"ref" : "C",
"alt" : "T"
},
"SID1" : 1,
"SID2" : 2
}
因此,$cond
进入$sum
的"内部",因为这是一个"累加器",因此您在$group
下的结构也是如此。
在定义管道时直接使用变量名没有错,因为该值只会"插值"并被视为文字。当然,因为这些都是"数组",所以需要对它们进行比较。更重要的是,它们实际上是"集合"。
$setIsSubset
运算符可以"逻辑"比较两个"集",以查看其中一个是否包含另一个的元素。这为$cond
提供了一个逻辑true/false
。
但是,"samples.sample_id"字段不是数组。但是,我们可以简单地通过使用$map
运算符将声明为单个元素的$literal
数组提供给它并转置该值来"使其成为一个"。
在许多编程语言中,$map
运算符的作用与同名函数相同,它将数组作为"输入"。它通过处理"in"中的函数表达式,将每个数组元素作为"as"中的声明变量进行处理。它返回一个与输入长度相同的数组,但其结果由函数表达式应用。另一个例子:
{ "$map": {
"input": { "$literal": [1,2,3,4] }, // input array
"as": "el", // variable represents element
"in": {
"$multiply": [ "$$el", "$$el" ] // square of element
}
}
退货:
[1,4,9,16] // All array elements "squared"
$literal
操作符实际上是在MongoDB 2.2引入聚合框架后才出现的,但它是未记录的操作符$const
。虽然前面已经提到,如图所示,将外部变量"注入"到聚合管道中没有错,但您不能做的一件事是将该值作为文档的属性"返回"。作为一个表达式参数,在大多数情况下这是可以的,但例如,您不能这样做:
{ "$project": {
"myfield": ["bill","ted","fred"]
}}
这会导致错误,所以你会这样做:
{ "$project": {
"myfield": { "$literal": ["bill","ted","fred"] }
}}
这允许将字段设置为您希望的值数组。
所以,和清单中的$map
结合使用,它只是表示管道中不存在的单个元素的数组的一种方式,以便用当前字段"转换"它的值。
它变成了:
"559de1b2aa43f47656b2a3fa"
通过代码:
{ "$map": {
"input": { "$literal": ["A"] },
"as": "el",
"in": "$samples.sample_id" // into this ["559de1b2aa43f47656b2a3fa"]
}}
这使得$setIsSubset
操作在内部看起来是这样的:
{ "$setIsSubset": [
["559de1b2aa43f47656b2a3fa"],
["559de1b2aa43f47656b2a3fa","559de1b2aa43f47656b2a3f9"]
}} // true
最终的结果是,对每个变量进行比较,以查看所包含的值是否与其中一个元素匹配,并将适当的"字段值"发送到$sum
进行累加。
此外,丢弃$project
,因为这通常由$group
阶段处理,并且将其留在那里会导致处理开销,因为需要首先循环处理管道中的每个文档。所以它并没有真正优化任何东西,而是让你付出代价。
顺便说一句。到目前为止,管道输出中的示例数据缺少一个右大括号。我在下面使用了这个数据(当然没有$unroll(
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "0|0", "GQ" : 48, "DP" : 1, "HQ" : [ 34, 1 ], "GTC" : 0, "sample_id" : "559de1b2aa43f47656b2a3fa"} },
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "1|0", "GQ" : 15, "DP" : 8, "HQ" : [ 5, 51 ], "GTC" : 1, "sample_id" : "559de1b2aa43f47656b2a3f9"}},
{ "chr" : "22", "pos" : 14373, "ref" : "C", "alt" : "T", "samples" : { "GT" : "1/1", "GQ" : 43, "DP" : 5, "HQ" : [ 0, 2 ], "GTC" : 2, "sample_id" : "559de1b2aa43f"}},
{ "chr" : "20", "pos" : 14371, "ref" : "A", "alt" : "G", "samples" : { "GT" : "0|0", "GQ" : 48, "DP" : 1, "HQ" : [ 51, 51 ], "GTC" : 0, "sample_id" : "559de1b2aa43f47656b2a3fa"}},
{ "chr" : "20", "pos" : 14371, "ref" : "A", "alt" : "G", "samples" : { "GT" : "1|0", "GQ" : 48, "DP" : 8, "HQ" : [ 51, 51 ], "GTC" : 1, "sample_id" : "559de1b2aa43f47656b2a3f9"}}