Невозможно уменьшить глубоко вложенный массив в MongoDB - PullRequest
0 голосов
/ 07 апреля 2020

У меня есть база данных Mon go с документами, подобными этим, в коллекции:

{
   date:"2019-06-12T00:09:03.000Z",
   actions:{
      actionDate:"2019-06-12T00:15:25.000Z",
      data:{
         users:[
            [{gender:"Male",age:24},
             {gender:"Female",age:25}
            ],
            [{gender:"Male",age:34},
             {gender:"Male",age:26}
            ],
            [{gender:"Female",age:19},
             {gender:"Male",age:21}
            ]
         ]
      }
   }
}

Я хотел бы суммировать пользователей, появляющихся внутри массива users в одном документе, например

{
   "date":"2019-06-12T00:09:03.000Z",
   "actionDate":"2019-06-12T00:15:25.000Z",
   "summary":{
      "countFemale":2,
      "meanFemaleAge":22,
      "countMale":4,
      "meanMaleAge":26.25
   }
}

Некоторые соображения, которые следует учитывать: не может быть случаев для одного пола, а также, массив users может быть ограничен одним или двумя массивами внутри него. Я пытался решить эту проблему, используя мое, теперь я знаю, ограниченное знание языка запросов Mon go, но мне оно кажется неразрешимым. Хотя это может быть полезно, проверяя MongoDB: Сократите массив объектов до единого объекта, вычислив среднее значение для каждого поля , но не можете догнать идею.

Есть идеи, пожалуйста?

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

Попробуйте запрос ниже:

db.collection.aggregate([
    /** Merge all arrays inside 'users' & push to 'summary' field */
  {
    $project: {
      date: 1,
      actionDate: "$actions.actionDate",
      summary: {
        $reduce: {
          input: "$actions.data.users",
          initialValue: [],
          in: { $concatArrays: ["$$value", "$$this"] },
        },
      },
    },
  },
  {
    $unwind: "$summary",
  },
  /** Group on 'date' to push data related to same date */
  {
    $group: {
      _id: "$date",
      actionDate: {$first: "$actionDate",},
      countFemale: {$sum: {$cond: [{$eq: ["$summary.gender", "Female"]},1,0]}},
      countMale: {$sum: {$cond: [{$eq: ["$summary.gender", "Male"]},1,0]}},
      meanFemaleAge: {$sum: {$cond: [{$eq: ["$summary.gender", "Female"]},"$summary.age",0]}},
      meanMaleAge: {$sum: {$cond: [{$eq: ["$summary.gender", "Male"]},"$summary.age",0]}}
    }
  },
  /** Re-create 'meanFemaleAge' & 'meanMaleAge' fields to add mean */
  {
    $addFields: {
      meanFemaleAge: {$cond: [{$ne: ["$meanFemaleAge", 0]},{$divide: ["$meanFemaleAge","$countFemale"]},0]},
      meanMaleAge: {$cond: [{$ne: ["$meanMaleAge", 0]},{$divide: ["$meanMaleAge","$countMale"]},0]},      
    }
  }
]);

Тест: MongoDB-Playground

Примечание: Неважно, что как вы это сделаете, я бы посоветовал вам не реализовывать такого рода операции для всей коллекции с огромными наборами данных.

1 голос
/ 07 апреля 2020

Нам нужно выполнить $reduce оператор.

На первом этапе мы создаем отдельные массивы (Male | Female) и пользователей pu sh в соответствии с их полом.

На втором этапе мы преобразуем / вычисляем результат.

Попробуйте это:

db.collection.aggregate([
  {
    $addFields: {
      "users": {
        $reduce: {
          input: "$actions.data.users",
          initialValue: {
            "Male": [],
            "Female": []
          },
          in: {
            Male: {
              $concatArrays: [
                "$$value.Male",
                {
                  $filter: {
                    input: "$$this",
                    cond: {
                      $eq: [
                        "$$this.gender",
                        "Male"
                      ]
                    }
                  }
                }
              ]
            },
            Female: {
              $concatArrays: [
                "$$value.Female",
                {
                  $filter: {
                    input: "$$this",
                    cond: {
                      $eq: [
                        "$$this.gender",
                        "Female"
                      ]
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  {
    $project: {
      _id: 0,
      date: 1,
      actionDate: "$actions.actionDate",
      summary: {
        "countFemale": {
          $size: "$users.Female"
        },
        "meanFemaleAge": {
          $avg: "$users.Female.age"
        },
        "countMale": {
          $size: "$users.Male"
        },
        "meanMaleAge": {
          $avg: "$users.Male.age"
        }
      }
    }
  }
])

MongoPlayground

...