MongoDB: Как я могу проецировать все типы полей для одного документа? - PullRequest
3 голосов
/ 27 января 2020

Мне нужно проверить каждый тип поля в каждом документе данной коллекции.

Хотя я могу написать отдельные $type команды для каждого поля и сопоставить их для каждого документа в отдельности, мне интересно, есть ли это более элегантный и эффективный способ проецирования всех типов полей.

Ответы [ 3 ]

2 голосов
/ 27 января 2020

Попробуйте:

db.collection.aggregate([{
    $addFields: {
        types: {
            $arrayToObject: {
            $map:
            {
                input: { $objectToArray: "$$ROOT" },
                as: "each",
                in: { k: '$$each.k', v: { $type: '$$each.v' } }
            }
        }}
    }
}])

Сбор данных:

/* 1 */
{
    "_id" : ObjectId("5e065992400289966eefb9a8"),
    "username" : "myName",
    "blog" : "myBlog",
    "details" : "myBlogDetails",
    "Object" : {
        "inOneObject" : true
    }
}

/* 2 */
{
    "_id" : ObjectId("5e0659ae400289966eefbc3a"),
    "username" : "othersName",
    "blog" : "not a blog",
    "details" : "useless"
}

Результат:

/* 1 */
{
    "_id" : ObjectId("5e065992400289966eefb9a8"),
    "username" : "myName",
    "blog" : "myBlog",
    "details" : "myBlogDetails",
    "Object" : {
        "inObject" : true
    },
    "types" : {
        "_id" : "objectId",
        "username" : "string",
        "blog" : "string",
        "details" : "string",
        "Object" : "object"
    }
}

/* 2 */
{
    "_id" : ObjectId("5e0659ae400289966eefbc3a"),
    "username" : "othersName",
    "blog" : "not a blog",
    "details" : "useless",
    "types" : {
        "_id" : "objectId",
        "username" : "string",
        "blog" : "string",
        "details" : "string"
    }
}

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

"Object" : {
            "inOneObject" : true
        }
1 голос
/ 27 января 2020

Вам нужно будет перечислить все поля и обработать каждое из них в отдельности. Вы можете сделать это с помощью комбинации $objectToArray и $unwind для целей перечисления, а затем $push и $arrayToObject для группировки всего вместе:

db.collection.aggregate([
    // Convert the root document into an array.
    {$project: {
        fields: {
            $objectToArray: "$$ROOT"
        }
    }},

    // Turn each array element into a separate document representing a field-value pair of the root document.
    {$unwind: "$fields"},

    // Apply the $type projection to the value portion of the field-value pair.
    {$project: {
        fields: {
            k: "$fields.k",
            v: {$type: "$fields.v"}
        }
    }},

    // Grab the first instance of each unique field name.
    {$group: {
        _id: "$fields.k",
        fields: {$first: "$fields"}
    }},

    // Take the unique field instances and recollect them all into an array.
    {$group: {
        _id: null,
        fields: {$push: "$fields"}
    }},

    // Convert our array back into an object.
    {$project: {
        fields: {$arrayToObject: "$fields"}
    }},

    // Replace the root document with our nested "fields" sub-document.
    {$replaceRoot: {
        newRoot: "$fields"
    }}
])

Небольшое предупреждение: если поле может содержать несколько типов, например, «строка» и «ноль», тогда это решение не будет учитывать этот случай. Чтобы решить эту проблему, вам нужно изменить этапы $group, чтобы использовать оператор $addToSet для сбора уникальных экземпляров $type для каждого ключа, прежде чем $push вставить их в массив fields.

0 голосов
/ 28 января 2020

Вы пробовали использовать продукт Mon go Compass?

это было бы легко, я считаю, для этого требования .....

...