Монго агрегация: разбиение значений на группы - PullRequest
1 голос
/ 12 апреля 2019

Это, вероятно, длинный снимок, но:

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

Конечно, я могу сделать это легко после получения документов; в этом примере исходные документы получают новое поле номера раздела:

// assuming sorted docs
var partition = 0;
var partitioned = docs.map((e,i) => {
        if(i > 0)
            if(e.date - docs[i-1].date > minInterval) partition++;

        return {
            date: e.date,
            partition: partition
        }
    });

Но мне не нужны сами документы, мне просто нужны первая и последняя даты и количество документов для каждого раздела. Просто непонятно, как бы я сделал функцию разбиения.

Возможно ли это с помощью агрегации? Я вижу , возможно, соответствующий билет Mondo , который открыт, так что я думаю, нет.

1 Ответ

1 голос
/ 13 апреля 2019

Да, это возможно.Для сравнения нескольких документов вам нужно поместить их в один массив, используя $ group и передав null как _id.Затем, чтобы начать сравнение значений, вам нужен индекс, как в цикле for, чтобы вы могли сгенерировать его с помощью оператора $ range .

Для определения разделов вам понадобится двойной $ map .Сначала будет возвращен массив значений 0 и 1, где 1 означает, что эта дата начинает новый раздел.

Второй $map - объединение дат с индексами разделов.Чтобы получить индекс раздела, вы можете $ sum подмассив ( $ slice ) из нулей и единиц.

Например:

db.col.save({ date: ISODate("2019-04-12T21:00:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T21:15:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T21:45:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T23:00:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T20:00:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T18:30:00.000Z") })
db.col.save({ date: ISODate("2019-04-12T20:10:00.000Z") })

Для интервала 20 минут вы можете запустить ниже агрегации:

db.col.aggregate([
    { $sort: { date: 1 } },
    { $group: { _id: null, dates: { $push: "$date" } } },
    {
        $addFields: {
            partitions: {
                $map: {
                    input: { $range: [ 0, { $size: "$dates" } ] },
                    as: "index",
                    in: {
                        $let: {
                            vars: {
                                current: { $arrayElemAt: [ "$dates", "$$index" ] },
                                prev: { $arrayElemAt: [ "$dates", { $add: [ "$$index", -1 ] } ] }
                            },
                            in: {
                                $cond: [
                                    { $or: [ { $eq: [ "$$index", 0 ] }, { $lt: [ { $subtract: [ "$$current", "$$prev" ] }, 1200000 ] } ] },
                                    0,
                                    1
                                ]
                            }
                        }
                    }
                }
            }
        }
    },
    {
        $project: {
            datesWithPartitions: {
                $map: {
                    input: { $range: [ 0, { $size: "$dates" } ] },
                    as: "index",
                    in: {
                        date: { $arrayElemAt: [ "$dates", "$$index" ] },
                        partition: { $sum: { $slice: [ "$partitions", { $add: [ "$$index", 1 ] } ] } }
                    }
                }
            }
        }
    }
])

, который будет печатать:

{
    "_id" : null,
    "datesWithPartitions" : [
        {
            "date" : ISODate("2019-04-12T18:30:00Z"),
            "partition" : 0
        },
        {
            "date" : ISODate("2019-04-12T20:00:00Z"),
            "partition" : 1
        },
        {
            "date" : ISODate("2019-04-12T20:10:00Z"),
            "partition" : 1
        },
        {
            "date" : ISODate("2019-04-12T21:00:00Z"),
            "partition" : 2
        },
        {
            "date" : ISODate("2019-04-12T21:15:00Z"),
            "partition" : 2
        },
        {
            "date" : ISODate("2019-04-12T21:45:00Z"),
            "partition" : 3
        },
        {
            "date" : ISODate("2019-04-12T23:00:00Z"),
            "partition" : 4
        }
    ]
}

MongoDB детская площадка

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