使用多个聚合运算符聚合子字段中的多个列表

时间:2020-09-09 15:20:17

标签: python-3.x mongodb

我有一个具有以下结构的文档集合:

{'_id': 'xxx',
 'name': 'some_name',
 'data': {
     'first_thing': [1, 2, 3, 4],
     'second_thing': [5, 3, 2, 4],
     }
}

我的目标是计算“数据”下每个子字段的值的平均值和标准差。我尝试以下查询单个子字段:

cursor = db.testcoll.aggregate([
    {
        '$project': {
            '_id': '$name',
            'data.first_thing.mean': {'$avg': '$data.first_thing'},
            'data.first_thing.std': {'$stdDevPop': '$data.first_thing'}
        }
    }
])

结果是,字段“数据”是一个列表,其中对原始列表“ first_thing”中的每个元素重复进行聚合$avg$stdDevPop

{'data': {
    'first_thing': [
       {'mean': 2.5, 'std': 1.118033988749895},
       {'mean': 2.5, 'std': 1.118033988749895},
       {'mean': 2.5, 'std': 1.118033988749895},
       {'mean': 2.5, 'std': 1.118033988749895}]},
 '_id': 'some_name'
}

很明显,我只需要一次聚合,即:

{'data': {
    'first_thing': {
        'mean': 2.5,
        'std': 1.118033988749895
     },
 '_id': 'some_name'
}

我观察到,当我将结果字段的名称更改为除原始字段名称之外的其他名称时,聚合仅执行一次。因此,查询:

cursor = db.testcoll.aggregate([
    {
        '$project': {
            '_id': '$name',
            'data.some_thing.mean': {'$avg': '$data.first_thing'},
            'data.some_thing.std': {'$stdDevPop': '$data.first_thing'}
        }
    }
])

给我我想要的。但是,在生成的文档中,“ first_thing”的集合位于称为“ some_thing”的子字段中。

如何更改查询以使结果为:

{'data': {
    'first_thing': {
        'mean': 2.5, 
        'std': 1.118033988749895},
 '_id': 'some_name'
}

我进一步观察到删除了“数据”。就像在结果字段的命名中一样

cursor = db.testcoll.aggregate([
    {
       ...
       'first_thing.mean': {'$avg': '$data.first_thing'},
       ...
        }}])

结果符合预期;唯一的缺点是所有“事物”都位于结果文档的顶层。但是,我需要它们位于子字段中。

2 个答案:

答案 0 :(得分:0)

data.first_thing是一个数组,因此您需要为此数组中的每个项目设置值。

尝试一下:

db.collection.aggregate([
  {
    "$set": {
      "data.mean": { "$avg": "$data.first_thing" },
      "data.std": { "$stdDevPop": "$data.first_thing" }
    }
  },
  { "$set": { "data.first_thing": null } },
  {
    "$project": {
      "data.first_thing.mean": "$data.mean",
      "data.first_thing.std": "$data.std",
      "_id": "$name",
      
    }
  }
])

或这个:

db.collection.aggregate([
  {
    "$set": {
      "data.first_thing.mean": { "$avg": "$data.first_thing" },
      "data.first_thing.std": { "$stdDevPop": "$data.first_thing" }
    }
  },
  {
    "$project": {
      "data.first_thing": { $setUnion: "$data.first_thing" },
      "_id": "$name"
    }
  }
])

在我看来,之后再使用$unwind$group似乎是过大的选择。

答案 1 :(得分:0)

之所以发生,是因为first_thing是一个数组。您可以通过多种方式实现目标。

  1. 一种通过unwind展平数组并将其分组的方法。

代码在下面给出

[
  {
    "$project": {
      "_id": "$name",
      "data.first_thing.mean": {
        "$avg": "$data.first_thing"
      },
      "data.first_thing.std": {
        "$stdDevPop": "$data.first_thing"
      }
    }
  },
  {
    "$unwind": "$data.first_thing"
  },
  {
    $group: {
      _id: "$_id",
      data: {
        $addToSet: {
          first_thing: [
            "$data.first_thing"
          ]
        }
      }
    }
  }
]

工作Mongo playground

  1. 您可以将mean和std放在不同的变量中,并将其放在数组中

代码在下面给出

 [
      {
        "$project": {
          "_id": "$name",
          "mean": {
            "$avg": "$data.first_thing"
          },
          "std": {
            "$stdDevPop": "$data.first_thing"
          }
        }
      },
      {
        $addFields: {
          "data.first_thing": [
            {
              mean: "$mean",
              std: "$std"
            }
          ]
        }
      }
    ]

工作Mongo playground