Агрегация в MongoDB с массивом объектов - PullRequest
1 голос
/ 21 марта 2020

У меня есть следующий документ mongoDB -

{
    "_id" : ObjectId("5e71a1f3081c4b70cdbc438f"),
    "DataSetID" : ObjectId("5e71a1f3081c4b70cdbc438e"),
    "row" : [ 
        {
            "key" : "Region",
            "prev" : "root",
            "value" : "Australia and Oceania",
            "typeOfValue" : "string",
            "currentDepth" : 1
        }, 
        {
            "key" : "Country",
            "prev" : "root",
            "value" : "Tuvalu",
            "typeOfValue" : "string",
            "currentDepth" : 1
        }, 
        {
            "key" : "Item Type",
            "prev" : "root",
            "value" : "Baby Food",
            "typeOfValue" : "string",
            "currentDepth" : 1
        }, 
        {
            "key" : "Sales Channel",
            "prev" : "root",
            "value" : "Offline",
            "typeOfValue" : "string",
            "currentDepth" : 1
        }, 
        {
            "key" : "Order Priority",
            "prev" : "root",
            "value" : "H",
            "typeOfValue" : "string",
            "currentDepth" : 1
        }, 
        {
            "key" : "Order Date",
            "prev" : "root",
            "value" : ISODate("2010-05-27T18:30:00.000Z"),
            "typeOfValue" : "date",
            "currentDepth" : 1
        }, 
        {
            "key" : "Order ID",
            "prev" : "root",
            "value" : 669165933,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Ship Date",
            "prev" : "root",
            "value" : ISODate("2010-06-26T18:30:00.000Z"),
            "typeOfValue" : "date",
            "currentDepth" : 1
        }, 
        {
            "key" : "Units Sold",
            "prev" : "root",
            "value" : 9925,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Unit Price",
            "prev" : "root",
            "value" : 255.28,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Unit Cost",
            "prev" : "root",
            "value" : 159.42,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Total Revenue",
            "prev" : "root",
            "value" : 2533654,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Total Cost",
            "prev" : "root",
            "value" : 1582243.5,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }, 
        {
            "key" : "Total Profit",
            "prev" : "root",
            "value" : 951410.5,
            "typeOfValue" : "number",
            "currentDepth" : 1
        }
    ]
}

Допустим, у нас есть сотни документов, подобных этим. Я хочу сделать запрос агрегации, который группирует, скажем, по значениям ключа == 'Страна', т. Е. Тувалу, Индия и т. Д. c, и дает мне сумму значений ключа == 'Общая прибыль' для каждой страны.

Другими словами, дайте мне сумму значений , где ключ == 'Общая прибыль' при группировании по значениям из ключ == 'Страна '.

Структура данных может быть изменена, учитывая, что введенные данные являются неструктурированными JSON данными, и я заранее не знаю ключей, поэтому я выбрал json массивы.

В конце Результат я хочу что-то вроде этого:

[
{ 
_id : 'Tuvalu',
value : 100
},
{
_id : 'India',
value : 160
}
]

Как мы можем достичь этого?

1 Ответ

1 голос
/ 21 марта 2020

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

db.collection.aggregate([
  /** Optional match stage but can reduce data set size for further stages
   * (Get docs where rows array has an object with a key field & value 'Country') */
  { $match: { "row.key": "Country" } },
  /** Using project to retain only needed fields which reduce size of doc,
   * Convert row array into row object {country : ..., totalProfit : ... } */
  {
    $project: {
      _id: 0,
      row: {
        /** Iterate on row's, So '$$this' is each object & '$$value' is values in initialValue */
        $reduce: {
          input: "$row",
          initialValue: {
            country: "",
            totalProfit: 0
          },
          in: {
            country: {
             /** If current object key is Country then push value from current object to 'country' in initialValue
              * otherwise return existing 'country' value to 'country' every time */
              $cond: [
                { $eq: ["$$this.key", "Country"] }, 
                "$$this.value",
                "$$value.country"
              ]
            },
            totalProfit: {
              $cond: [
                { $eq: ["$$this.key", "Total Profit"] },
                "$$this.value",
                "$$value.totalProfit"
              ]
            }
          }
        }
      }
    }
  },
  /** group on country field & sumup values of totalProfit */
  {
    $group: { _id: "$row.country", value: { $sum: "$row.totalProfit" } }
  }
]);

Тест: MongoDB-Playground

...