MongoDB - сумма массива c элемента массива в условиях, исключающих дубликат - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть несколько документов, которые выглядят следующим образом:

{ 
    "_id" : ObjectId("8f30b453c2ece001364dc04d"), 
    "SessionId" : "awkuTQjj53kgqAZ4J", 
    "StartDate" : ISODate("2020-02-24T11:51:36.918+0000"), 
    "EndDate" : ISODate("2020-02-24T11:51:36.918+0000"), 
    "List1" : "X", 
    "List2" : "Y", 

    "rating" : [
        {
            "ObjectId" : "5d09e98380c5d5eb89ac5069", 
            "List" : "List 2", 
            "Rate" : NumberInt(5), 
            "RatedDate" : ISODate("2020-02-24T11:55:47.774+0000")
        }, 
        {
            "ObjectId" : "5d09e98380c5d5eb89ac5069", 
            "List" : "List 2", 
            "Rate" : NumberInt(4), 
            "RatedDate" : ISODate("2020-02-24T11:55:48.408+0000")
        }, 
        {
            "ObjectId" : "5d09e98380c5d5eb89ac505b", 
            "List" : "List 2", 
            "Rate" : NumberInt(3), 
            "RatedDate" : ISODate("2020-02-24T11:55:49.520+0000")
        }, 
        {
            "ObjectId" : "5d09e98380c5d5eb89ac505c", 
            "List" : "List 2", 
            "Rate" : NumberInt(3), 
            "RatedDate" : ISODate("2020-02-24T11:55:51.787+0000")
        }, 
        {
            "ObjectId" : "5d09e98380c5d5eb89ac5057", 
            "List" : "List 1", 
            "Rate" : NumberInt(4), 
            "RatedDate" : ISODate("2020-02-24T11:55:53.865+0000")
        }, 
        {
            "ObjectId" : "5d09e98380c5d5eb89ac5058", 
            "List" : "List 1", 
            "Rate" : NumberInt(4), 
            "RatedDate" : ISODate("2020-02-24T11:55:53.865+0000")
        }, 

    ], 
    "Answers" : {
        "SelectedList" : "1", 
    }, 

}

Мне нужно подвести итоги всех рейтингов. Оценить рейтинг.List: «Список 1» и, соответственно, подвести итоги всех рейтингов. где rating.List: «Список 2», также исключать дубликаты записей (по rating.ObjectId) и считать только записи с последним рейтингом.RatedDate. Я полагаю, это групповая агрегация. Кроме того, они должны соответствовать критериям List1: 'X', Answers.selectedList: 1 То, что я написал, выглядит ниже:

[
    { 
        "$match" : { 

        "List1" : "X", 
        "Answers.SelectedList" : "1"
    }
}, 
{ 
    "$unwind" : { 
        "path" : "$rating"
    }
}, 

{ 
    "$group" : { 
        "_id" : null, 
        "sum" : { 
            "$sum" : "$Rate"
        }
    }
}
]

Можете ли вы мне помочь?

Ответы [ 2 ]

2 голосов
/ 01 апреля 2020

Я был немного озадачен в отношении List1 / List2, однако я думаю, что это даст вам большую часть пути к вашему необходимому запросу агрегации.

db.test.aggregate([
    {
        $match: {
            "List1": "X",
            "Answers.SelectedList": "1"
        }
    },
    {
        "$unwind" : "$rating"
    },
    {
        $group:{
            _id: {
                id: "$rating.ObjectId",
                list: "$rating.List"
            },
            maxRatedDate: { $max: "$rating.RatedDate" },
            ratings: { $push:  "$rating" }
        }
    },{
        $addFields: {
            ratings: {
               $filter: {
                  input: "$ratings",
                  as: "item",
                  cond: { $eq: [ "$$item.RatedDate", "$maxRatedDate" ] }
               }
            }
         }
    },
    {
        $unwind: "$ratings"
    },
    {
        $group:{
            _id: "$ratings.List",
            sum : { 
                $sum : "$ratings.Rate"
            }
        }

    }
])

Это выведет следующее

{ "_id" : "List 1", "sum" : 8 }
{ "_id" : "List 2", "sum" : 10 }

Однако давайте попробуем разбить его.

Для начала у нас есть простое совпадение, такое же, как у вас в вашем вопросе. это просто ограничивает количество документов, которые мы передаем обратно

$match: {
    "List1": "X",
    "Answers.SelectedList": "1"
}

Затем мы раскручиваем все элементы массива, чтобы получить документ для каждого рейтинга, это позволяет нам делать некоторые дополнительные запросы к данным.

{
    "$unwind" : "$rating"
}

Далее, у нас есть группа, здесь мы группа по ObjectId рейтинга, поэтому мы можем позже удалить дубликаты, мы также узнаем в группе, какой рейтинг у нас есть группа имеет самую высокую дату, поэтому мы можем принять ее позже в прогнозе. затем мы отодвигаем все оценки обратно в массиве на потом.

$group:{
    _id: {
        id: "$rating.ObjectId",
        list: "$rating.List"
    },
    maxRatedDate: { $max: "$rating.RatedDate" },
    ratings: { $push:  "$rating" }
}

Далее мы хотим спроецировать массив оценок на один элемент, в котором он содержит только самый последний рейтинг, для этого мы используем $ filter в массиве и отфильтровываем их все, которые не соответствуют нашей максимальной дате, которую мы вычислили на предыдущем шаге.

$addFields: {
    ratings: {
        $filter: {
            input: "$ratings",
            as: "item",
            cond: { $eq: [ "$$item.RatedDate", "$maxRatedDate" ] }
        }
    }
}

Следующие два шага довольно просты и просто снова разматывают массив ( у нас есть только один элемент, затем группируем их, чтобы получить общую сумму для списков.

{
    $unwind: "$ratings"
},
{
    $group:{
        _id: "$ratings.List",
        sum : { 
            $sum : "$ratings.Rate"
        }
    }
}
1 голос
/ 01 апреля 2020

На данный момент вам нужно только предоставить этапу $group поле, на котором вы фактически группируете, как поле _id и правильно ссылаться на поля, так как они все еще находятся внутри массива rating:

"$group" : { 
        "_id" : "$rating.List", 
        "sum" : { 
            "$sum" : "$rating.Rate"
        }
    } 
...