Jerren Saunders
Jerren Saunders

Reputation: 1435

How do you convert an array of ObjectIds into an array of embedded documents with a field containing the original array element value

I have a collection of documents where one of the fields is currently an array of ObjectId items.

{
    _id: ObjectId(...),
    user: "jdoe",
    docs: [
        ObjectId(1),
        ObjectId(2),
        ...
    ]
}
{
    _id: ObjectId(...),
    user: "jsmith",
    docs: [
        ObjectId(3),
        ObjectId(4),
        ...
    ]
}

How can I update all of the documents in my collection to convert the docs field into an array of objects that contain a "docID" field equal to the original element value?

For example, I'd want my documents to end up looking like:

{
    _id: ObjectId(...),
    user: "jdoe",
    docs: [
        { docID: ObjectId(1) },
        { docID: ObjectId(2) },
        ...
    ]
}
{
    _id: ObjectId(...),
    user: "jsmith",
    docs: [
        { docID: ObjectId(3)},
        { docID: ObjectId(4)},
        ...
    ]
}

I'm hoping there is a command that I can run from the shell such as:

db.getCollection('myCollection').update(
    {}, 
    { 
        $set: { 
            'docs.$[]: { docID: '$$VALUE'}
        }
    }, 
    {multi: true }
);

But I can't figure out how to reference the original value of the element.


Update:

I'm marking @mickl with the correct answer since it got me on the correct track. Below is the final aggregate that I ended up with which only changes the docs field if it is an array of object IDs, otherwise the existing value is left as-is, including documents that don't have a docs field.

db.getCollection('myCollection').aggregate([
    { $addFields: {
        'docs': { $cond: {
            if : { $eq: [{ $type: { $arrayElemAt: [ '$docs', 0]} }, "objectId"]},
            then: { $map: {
                input: '$docs',
                in: { tocID: '$$this'}
            }},
            else : '$docs'
        }}
    }},
    { $out: "myCollection" }
])

Upvotes: 2

Views: 676

Answers (1)

mickl
mickl

Reputation: 49985

You can use $map to reshape your data and $out to replace existing collection with aggregation result:

db.col.aggregate([
    {
        $addFields: {
            docs: {
                $map: {
                    input: "$docs",
                    in: { docID: "$$this" }
                }
            }
        }
    },
    { $out: "col" }
])

Upvotes: 1

Related Questions