Добавление нового поля в N самых сбрасываемых документов по дням в MongoDB 4.2 - PullRequest
1 голос
/ 24 января 2020

У меня есть коллекция документов со схемой:

{ _id: ObjectId, userId: ObjectId, marker: string, datetime: Date, etc... }

Это коллекция маркеров (marker), привязанная к пользователю (userId). Дата привязки сохраняется в поле datetime.

Каждый день пользователь может получить произвольное количество маркеров.

Когда я выбираю данные из этой коллекции, мне нужно добавить дополнительное поле с именем allowed типа boolean, и это поле должно быть true, только если эта запись находится в большинстве N повторно отправлять записи за календарный день для пользователя.

Например, если начальная коллекция выглядит следующим образом и N == 2 :

{_id: ..., userId: "a", marker: "m1", datetime: "2020-01-01.10:00"}
{_id: ..., userId: "a", marker: "m2", datetime: "2020-01-02.10:00"}
{_id: ..., userId: "a", marker: "m3", datetime: "2020-01-02.11:00"}
{_id: ..., userId: "a", marker: "m4", datetime: "2020-01-02.12:00"}
{_id: ..., userId: "a", marker: "m5", datetime: "2020-01-02.13:00"}
{_id: ..., userId: "b", marker: "m1", datetime: "2020-01-01.10:00"}
{_id: ..., userId: "b", marker: "m2", datetime: "2020-01-01.11:00"}
{_id: ..., userId: "b", marker: "m3", datetime: "2020-01-01.13:00"}
{_id: ..., userId: "b", marker: "m4", datetime: "2020-01-02.11:00"}
{_id: ..., userId: "b", marker: "m5", datetime: "2020-01-02.12:00"}
{_id: ..., userId: "b", marker: "m6", datetime: "2020-01-03.10:00"}

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

{_id: ..., userId: "a", marker: "m1", datetime: "2020-01-01.10:00", allowed: true}
{_id: ..., userId: "a", marker: "m2", datetime: "2020-01-02.10:00", allowed: true}
{_id: ..., userId: "a", marker: "m3", datetime: "2020-01-02.11:00", allowed: true}
{_id: ..., userId: "a", marker: "m4", datetime: "2020-01-02.12:00", allowed: false}
{_id: ..., userId: "a", marker: "m5", datetime: "2020-01-02.13:00", allowed: false}
{_id: ..., userId: "b", marker: "m1", datetime: "2020-01-01.10:00", allowed: true}
{_id: ..., userId: "b", marker: "m2", datetime: "2020-01-01.11:00", allowed: true}
{_id: ..., userId: "b", marker: "m3", datetime: "2020-01-01.13:00", allowed: false}
{_id: ..., userId: "b", marker: "m4", datetime: "2020-01-02.11:00", allowed: true}
{_id: ..., userId: "b", marker: "m5", datetime: "2020-01-02.12:00", allowed: true}
{_id: ..., userId: "b", marker: "m6", datetime: "2020-01-03.10:00", allowed: true}

Я использую MongoDB 4.2.

1 Ответ

1 голос
/ 24 января 2020

Пожалуйста, попробуйте следующие запросы:

Запрос 1:

 db.markers.aggregate([
    /** group docs based on userId & date(2020-01-01), push all matched docs to data */
    { $group: { _id: { userId: '$userId', datetime: { $arrayElemAt: [{ $split: ["$datetime", "."] }, 0] } }, data: { $push: '$$ROOT' } } },
    /** Re-forming data field with added new field allowed for only docs where criteria is met */
    {
        $addFields: {
            data: {
                $map:
                {
                    input: "$data",
                    as: "each",
                    /** conditional check to add new field on only docs which are 0 & 1 position of array */
                    in: { $cond: [{ $lte: [{ $indexOfArray: ["$data", '$$each'] }, 1] }, { $mergeObjects: ['$$each', { allowed: true }] }, { $mergeObjects: ['$$each', { allowed: false }] }] }
                }
            }
        }
    },
    /** unwind data */
    { $unwind: '$data' },
    /** making data object as root level doc */
    { $replaceRoot: { newRoot: "$data" } }])

Запрос 2:

db.markers.aggregate([
    { $group: { _id: { userId: '$userId', datetime: { $arrayElemAt: [{ $split: ["$datetime", "."] }, 0] } }, data: { $push: '$$ROOT' } } }, {
        $addFields: {
            data: {
                $map:
                {
                    input: "$data",
                    as: "each",
                    in: {
                        $cond: [{
                            $or: [{ $eq: [{ $arrayElemAt: ["$data", -1] }, '$$each'] }, { $eq: [{ $arrayElemAt: ["$data", -2] }, '$$each'] }]
                        },
                        { $mergeObjects: ['$$each', { allowed: true }] },
                        { $mergeObjects: ['$$each', { allowed: false }] }]
                    }
                }
            }
        }
    }, { $unwind: '$data' }, { $replaceRoot: { newRoot: "$data" } }])

Query1 будет работать и получать вам результаты, но при условии, что данные, представленные в вопросе, являются выборочными данными и в режиме реального времени, когда вы смотрите на коллекцию, userId: "a", marker: "m5" будет первым документом, как если бы эта коллекция имела непрерывную запись данных, затем последний документ будет иметь последний data time, поэтому индекс 0 или 1 Query1 не будет работать, но здесь Query2 будет работать. Вы можете использовать Query1 , если коллекция маркеров имеет точно такие же упорядоченные данные, как указано в вопросе.

Примечание: В Query2 - Мы можем использовать те же logi c из Query1 (который должен проверять индексы (0,1)) вместо сравнения объектов, но это может быть применимо, только если в качестве первого этапа у нас есть поле $sort из dateTime, И я не пошел по этому пути, потому что сортировка данных всей коллекции на поле не будет эффективной, чем эта.

...