Запрос MongoDB для установки разности групп сбора - PullRequest
0 голосов
/ 09 января 2019

Имеется набор modified записей и original набор записей. Я хочу иметь возможность написать запрос, который, по сути, даст мне разницу между набором original "набор" и modified "набор".

Итак, с двумя наборами, original и modified, вот так:

{ "_id" : 1, "set": "original", "key" : "foo", "element" : "bar" }
{ "_id" : 2, "set": "original", "key" : "bar", "element" : "old" }
{ "_id" : 3, "set": "original", "key" : "qux", "element" : "abc" } # Deleted

{ "_id" : 4, "set": "modified", "key" : "foo", "element" : "bar" } # Unchanged
{ "_id" : 5, "set": "modified", "key" : "bar", "element" : "new" } # Changed
{ "_id" : 6, "set": "modified", "key" : "baz", "element" : "bar" } # Created

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

{ "_id" : 3, "deleted": True}
{ "_id" : 5, "changed": True}
{ "_id" : 6, "created": True}

Или менее идеальный (только потому, что он не * cursor -able):

{
    "deleted": [3],
    "changed": [5],
    "created": [6]
}

Я не ограничен форматами результатов, а просто хочу выяснить, как выполнить этот расчет, и хочу продемонстрировать, что я ищу.

Я вижу, что у Mongo есть функция $setDifference, но у меня возникают проблемы с ее применением к моей проблеме.

1 Ответ

0 голосов
/ 09 января 2019

Вы можете использовать ниже агрегации:

db.col.aggregate([
    {
        $group: {
            _id: "$key",
            docs: { $push: "$$ROOT" },
            lastId: { $last: "$_id" }
        }
    },
    {
        $project: {
            _id: 1,
            lastId: 1,
            original: { $arrayElemAt: [ { $filter: { input: "$docs", as: "d", cond: { $eq: [ "$$d.set", "original" ] } } } , 0 ] },
            modified: { $arrayElemAt: [ { $filter: { input: "$docs", as: "d", cond: { $eq: [ "$$d.set", "modified" ] } } } , 0 ] }
        }
    },
    {
        $project: {
            _id: 1,
            lastId: 1,
            state: {
                $switch: {
                    branches: [
                        { case: { $eq: [ "$original", undefined ] }, then: "created" },
                        { case: { $eq: [ "$modified", undefined ] }, then: "deleted" },
                        { case: { $ne: [ "$modified.element", "$original.element" ] }, then: "changed" }
                    ],
                    default: "notModified"
                }
            }
        }
    },
    {
        $group: {
            _id: "$state",
            ids: { $push: "$lastId" }
        }
    },
    {
        $match: {
            _id: { $ne: "notModified" }
        }
    },
    {
        $group: {
            _id: null,
            stats: { $push: { k: "$_id", v: "$ids" } }
        }
    },
    {
        $replaceRoot: {
            newRoot: {
                $arrayToObject: "$stats"
            }
        }
    }
])

Изначально вам нужно использовать $ group с $ filter , чтобы получить modified и original полей на key. Затем вы можете использовать $ switch для определения состояния на основе этих двух полей. Наконец, вы можете снова $group (этим state) и использовать $ arrayToObject с операторами $ replaceRoot для динамического получения ключей на основе обнаруженных состояний. Конечный результат:

{ "deleted" : [ 3 ], "changed" : [ 5 ], "created" : [ 6 ] }

РЕДАКТИРОВАТЬ: В качестве альтернативы вы можете получить один документ на ключ, используя агрегацию ниже:

db.col.aggregate([
    {
        $group: {
            _id: "$key",
            docs: { $push: "$$ROOT" },
            lastId: { $last: "$_id" }
        }
    },
    {
        $project: {
            _id: 1,
            lastId: 1,
            original: { $arrayElemAt: [ { $filter: { input: "$docs", as: "d", cond: { $eq: [ "$$d.set", "original" ] } } } , 0 ] },
            modified: { $arrayElemAt: [ { $filter: { input: "$docs", as: "d", cond: { $eq: [ "$$d.set", "modified" ] } } } , 0 ] }
        }
    },
    {
        $project: {
            _id: 1,
            lastId: 1,
            state: {
                $switch: {
                    branches: [
                        { case: { $eq: [ "$original", undefined ] }, then: "created" },
                        { case: { $eq: [ "$modified", undefined ] }, then: "deleted" },
                        { case: { $ne: [ "$modified.element", "$original.element" ] }, then: "changed" }
                    ],
                    default: "notModified"
                }
            }
        }
    },
    {
        $match: {
            state: { $ne: "notModified" }
        }
    },
    {
        $project: {
            _id: "$lastId",
            state: 1
        }
    }
])

выходы: * * тысяча двадцать-пять

{ "state" : "created", "_id" : 6 }
{ "state" : "changed", "_id" : 5 }
{ "state" : "deleted", "_id" : 3 }
...