Как восстановить максимальное вхождение значения в массив mongoDB - PullRequest
0 голосов
/ 30 марта 2019

У меня есть массив в mongodb: я хочу, чтобы максимально полученное значение devDependenciesList из заданного массива

[{
    "_id" : 0,
    "repoId" : 460078,
    "devDependenciesList" : [ 
        "value1", 
        "value2", 
        "value3", 
        "value4"
    ]
},{
    "_id" : 1,
    "repoId" : 1232,
    "devDependenciesList" : [ 
        "value1", 
        "value4", 
        "value7", 
        "value93"
    ]
},{
    "_id" : 2,
    "repoId" : 5423,
    "devDependenciesList" : [ 
        "value1", 
        "value23", 
        "value3", 
        "value4"
    ]
}]

Выходные данные должны быть:

[value1: 3, value4: 3, value3: 2]

Ответы [ 2 ]

3 голосов
/ 30 марта 2019

В основном вам нужно $unwind содержимое массива и затем $group для каждого значения в качестве ключа группировки с $sum для подсчета :

db.collection.aggregate([
  { "$unwind": "$devDependenciesList" },
  { "$group": { 
    "_id": "$devDependenciesList",
    "count": { "$sum": 1 }
  }}
])

Что вернет:

{ "_id" : "value23", "count" : 1 }
{ "_id" : "value93", "count" : 1 }
{ "_id" : "value7", "count" : 1 }
{ "_id" : "value2", "count" : 1 }
{ "_id" : "value3", "count" : 2 }
{ "_id" : "value1", "count" : 3 }
{ "_id" : "value4", "count" : 3 }

Это базовые данные, но если вам действительно нужна форма «ключ / счет», вы можете сделать:

db.collection.aggregate([
  { "$unwind": "$devDependenciesList" },
  { "$group": { 
    "_id": "$devDependenciesList",
    "count": { "$sum": 1 }
  }},
  { "$sort": { "count": -1 } },
  { "$group": {
    "_id": null,
    "items": { "$push": { "k": "$_id", "v": "$count" } }
  }},
  { "$replaceRoot": {
    "newRoot": { "$arrayToObject": "$items" }
  }}
])

Что вернет:

{
        "value1" : 3,
        "value4" : 3,
        "value3" : 2,
        "value23" : 1,
        "value93" : 1,
        "value7" : 1,
        "value2" : 1
}

Дополнительные $group и $push предназначены для сбора всех результатов в один документ с массивом с именами "k" и "v". Вы хотите эту форму для оператора $arrayToObject, который используется на следующем этапе $replaceRoot, возвращающем окончательный результат.

Вам нужна версия MongoDB, которая поддерживает эти последние операторы, но на самом деле это не так. На самом деле это наиболее эффективно выполняется в клиентском коде. Например с JavaScript в оболочке:

db.collection.aggregate([
  { "$unwind": "$devDependenciesList" },
  { "$group": { 
    "_id": "$devDependenciesList",
    "count": { "$sum": 1 }
  }},
  { "$sort": { "count": -1 } }    
]).toArray().reduce((o,e) => Object.assign(o, { [e._id]: e.count }),{})

И это дает те же результаты, что и выше.

И, конечно, если вы хотите исключить все результаты в единственном числе или что-то подобное, просто добавьте $match после $group:

db.collection.aggregate([
  { "$unwind": "$devDependenciesList" },
  { "$group": { 
    "_id": "$devDependenciesList",
    "count": { "$sum": 1 }
  }},
  { "$match": { "count": { "$gt": 1 } } },
  { "$sort": { "count": -1 } }    
]).toArray().reduce((o,e) => Object.assign(o, { [e._id]: e.count }),{})

Или используя собственный драйвер узла, который будет выглядеть примерно так:

let result = (await db.collection('collection').aggregate([
  { "$unwind": "$devDependenciesList" },
  { "$group": { 
    "_id": "$devDependenciesList",
    "count": { "$sum": 1 }
  }},
  { "$match": { "count": { "$gt": 1 } } },
  { "$sort": { "count": -1 } }    
]).toArray()).reduce((o,{ _id, count }) => ({ ...o,  [_id]: count }),{})

Учитывая использование async/await при возврате фактического массива и использование функций ES6, таких как распространение объектов и деструктуризация.

Что, конечно, просто:

{ "value1" : 3, "value4" : 3, "value3" : 2 }

Только для справки, вот полностью воспроизводимый список:

const { MongoClient } = require('mongodb');

const uri = 'mongodb://localhost:27017';
const opts = { useNewUrlParser: true };

const data = [
  {
    "_id" : 0,
    "repoId" : 460078,
    "devDependenciesList" : [
      "value1",
      "value2",
      "value3",
      "value4"
    ]
  },{
    "_id" : 1,
    "repoId" : 1232,
    "devDependenciesList" : [
      "value1",
      "value4",
      "value7",
      "value93"
    ]
  },{
    "_id" : 2,
    "repoId" : 5423,
    "devDependenciesList" : [
      "value1",
      "value23",
      "value3",
      "value4"
    ]
  }
];

const log = data => console.log(JSON.stringify(data, undefined, 2));

(async function() {

  let client;

  try {
    client = await MongoClient.connect(uri, opts);

    const db = client.db('test');

    // Clean data
    await db.collection('collection').deleteMany();

    // Insert data
    await db.collection('collection').insertMany(data);

    let result = (await db.collection('collection').aggregate([
      { "$unwind": "$devDependenciesList" },
      { "$group": {
        "_id": "$devDependenciesList",
        "count": { "$sum": 1 }
      }},
      { "$match": { "count": { "$gt": 1 } } },
      { "$sort": { "count": -1 } }
    ]).toArray()).reduce((o, { _id, count }) => ({ ...o, [_id]: count }),{});

    log(result);

    let sample = await db.collection('collection').aggregate([
      { "$unwind": "$devDependenciesList" },
      { "$sortByCount": "$devDependenciesList" },
    ],{ "explain": true }).toArray();

    log(sample);

  } catch(e) {
    console.error(e);
  } finally {
    if (client)
      client.close();
  }

})()

И вывод, показывающий ожидаемый результат, и вывод "объяснения", показывающий, что $sortByCount не является "реальной" стадией агрегации и является просто более коротким способом ввода вещей, существовавших еще с MongoDB 2.2:

{
  "value1": 3,
  "value4": 3,
  "value3": 2
}
[
  {
    "stages": [
      {
        "$cursor": {
          "query": {},
          "fields": {
            "devDependenciesList": 1,
            "_id": 0
          },
          "queryPlanner": {
            "plannerVersion": 1,
            "namespace": "test.collection",
            "indexFilterSet": false,
            "parsedQuery": {},
            "winningPlan": {
              "stage": "COLLSCAN",
              "direction": "forward"
            },
            "rejectedPlans": []
          }
        }
      },
      {
        "$unwind": {
          "path": "$devDependenciesList"
        }
      },
      {
        "$group": {
          "_id": "$devDependenciesList",
          "count": {
            "$sum": {
              "$const": 1
            }
          }
        }
      },
      {
        "$sort": {
          "sortKey": {
            "count": -1
          }
        }
      }
    ],
    "ok": 1,
    "operationTime": "6674186995377373190",
    "$clusterTime": {
      "clusterTime": "6674186995377373190",
      "signature": {
        "hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=",
        "keyId": 0
      }
    }
  }
]
1 голос
/ 30 марта 2019

Пожалуйста, попробуйте использовать $sortByCount и $unwind, как показано ниже:

db.getCollection("test").aggregate([
    {
        $unwind: "$devDependenciesList"
    },
    {
        $sortByCount: "$devDependenciesList"
    }
]).map((obj)=>{return {[obj._id]:obj.count}})

Это простое и краткое решение, о котором я мог подумать.

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