使用聚合将字段与外部数组值匹配



到目前为止,我有一个查询如下:

 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"}}

相关内容

  • 没有找到相关文章

最新更新