Разделение данных с MongoDB - PullRequest
0 голосов
/ 13 мая 2018

У меня есть следующая коллекция

[
  {
    "setting": "Volume",
    "_id": ObjectId("5a934e000102030405000000"),
    "counting": 1
  },
  {
    "setting": "Brightness",
    "_id": ObjectId("5a934e000102030405000001"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000002"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000003"),
    "counting": 1
  },
  {
    "setting": "Contrast",
    "_id": ObjectId("5a934e000102030405000004"),
    "counting": 0
  },
  {
    "setting": "Sharpness",
    "_id": ObjectId("5a934e000102030405000005"),
    "counting": 1
  },
  {
    "setting": "Sharpness",
    "_id": ObjectId("5a934e000102030405000006"),
    "counting": 1
  },
  {
    "setting": "Language",
    "_id": ObjectId("5a934e000102030405000007"),
    "counting": 1
  },
  {
    "setting": "Language",
    "_id": ObjectId("5a934e000102030405000008"),
    "counting": 0
  }
]

Теперь я хочу group на setting и хочу, чтобы только два самых верхних данных в результате оставались в useless

Таким образом, мой вывод должен быть после sort путем подсчета

[
  {
    "setting": "Contrast",
    "counting": 2
  },
  {
    "setting": "Sharpness",
    "counting": 2
  },
  {
    "setting": "Useless",
    "counting": 3
  }
]

1 Ответ

0 голосов
/ 13 мая 2018

Если вам это сойдет с рук, то, вероятно, лучше «сложить» уменьшенные результаты в один документ, а затем $slice два верхних и $sumостальное:

Model.aggregate([
  { "$group": {
    "_id": "$setting",
    "counting": { "$sum": "$counting" }
  }},
  { "$sort": { "counting": -1 } },
  { "$group": {
    "_id": null,
    "data": { "$push": "$$ROOT" }
  }},
  { "$addFields": {
     "data": {
       "$let": {
         "vars": { "top": { "$slice": ["$data", 0, 2 ] } },
         "in": {
           "$concatArrays": [
             "$$top",
             { "$cond": {
               "if": { "$gt": [{ "$size": "$data" }, 2] },
               "then": 
                 [{ 
                   "_id": "Useless",
                   "counting": {
                     "$sum": {
                       "$map": {
                         "input": {
                           "$filter": {
                             "input": "$data",
                             "cond": { "$not": { "$in": [ "$$this._id", "$$top._id" ] } }
                           }
                         },
                         "in": "$$this.counting"
                       }
                     }
                   }
                 }],
               "else": []
             }}
           ]
         }
       }
     }
  }},
  { "$unwind": "$data" },
  { "$replaceRoot": { "newRoot": "$data" } }
])

Если это потенциально очень «большой» результат, даже уменьшенный, то $limit используйте $facet для «отдыха»:

Model.aggregate([
  { "$facet": {
    "top": [
      { "$group": {
        "_id": "$setting",
        "counting": { "$sum": "$counting" }
      }},
      { "$sort": { "counting": -1 } },
      { "$limit": 2 }
    ],
    "rest": [
      { "$group": {
        "_id": "$setting",
        "counting": { "$sum": "$counting" }
      }},
      { "$sort": { "counting": -1 } },
      { "$skip": 2 },
      { "$group": {
        "_id": "Useless",
        "counting": { "$sum": "$counting" }
      }}
    ]
  }},
  { "$project": {
    "data": {
      "$concatArrays": [
        "$top","$rest"
      ]
    }
  }},
  { "$unwind": "$data" },
  { "$replaceRoot": { "newRoot": "$data" } }
])

Или даже $lookup с MongoDB 3.6:

Model.aggregate([
  { "$group": {
    "_id": "$setting",
    "counting": { "$sum": "$counting" }
  }},
  { "$sort": { "counting": -1 } },
  { "$limit": 2 },
  { "$group": {
    "_id": null,
    "top": { "$push": "$$ROOT" }   
  }},
  { "$lookup": {
    "from": "colllection",
    "let": { "settings": "$top._id" },
    "pipeline": [
      { "$match": {
        "$expr": {
          "$not": { "$in": [ "$setting", "$$settings" ] }
        }
      }},
      { "$group": {
        "_id": "Useless",
        "counting": { "$sum": "$counting" }
      }}
    ],
    "as": "rest"
  }},
  { "$project": {
    "data": {
      "$concatArrays": [ "$top", "$rest" ]
    }
  }},
  { "$unwind": "$data" },
  { "$replaceRoot": { "newRoot": "$data" } }
])

Все в действительности одинаково и все возвращают один и тот же результат:

{ "_id" : "Contrast", "counting" : 2 }
{ "_id" : "Sharpness", "counting" : 2 }
{ "_id" : "Useless", "counting" : 3 }

Опционально $project прямо в конце каждого вместо $replaceRoot, если для вас действительно важен контроль над именами полей.Обычно я просто придерживаюсь $group значений по умолчанию


В случае, если ваш MongoDB предшествует 3.4, а полученный остаток "Useless" на самом деле слишком велик, чтобы использовать какой-либо вариантпервый подход, затем простое Promise разрешение - это, в основном, ответ, один для aggregate, а другой для базового счета и просто выполнить математику:

let [docs, count] = await Promise.all([
  Model.aggregate([
    { "$group": {
      "_id": "$setting",
      "counting": { "$sum": "$counting" }
    }},
    { "$sort": { "counting": -1 } },
    { "$limit": 2 },
  ]),
  Model.count().exec()
]);

docs = [ 
  ...docs,
  { 
    "_id": "Useless",
    "counting": count - docs.reduce((o,e) => o + e.counting, 0)
  }
];

или без async/await:

Promise.all([
  Model.aggregate([
    { "$group": {
      "_id": "$setting",
      "counting": { "$sum": "$counting" }
    }},
    { "$sort": { "counting": -1 } },
    { "$limit": 2 },
  ]),
  Model.count().exec()
]).then(([docs, count]) => ([ 
  ...docs,
  { 
    "_id": "Useless",
    "counting": count - docs.reduce((o,e) => o + e.counting, 0)
  }
]).then( result => /* do something */ )

Это, в основном, разновидность устаревшего подхода "общее количество страниц" путем простого запуска отдельного запроса для подсчета элементов коллекции.

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

Один вариант помещает все результаты водин документ (где это возможно, из-за ограничения BSON, конечно) и другие, в основном, отличаются от «старого» подхода, снова выполняя запрос в другой форме.$facet параллельно и $lookup последовательно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...