mongodbaggregation-framework

mongodb aggregation, is it possible to add entries to an array during $project


I have documents of the form:

{
    _id : ObjectId(.....),
    prop1 : "foo",
    links : [ 1, 2, 3, 4 ]
}

{
    _id : ObjectId(.....),
    prop1 : "bar",
    links : [ 5, 6, 7, 8 ]
}

I am using the aggregation framework to process these documents, I use $unwind to generate a document for each value in the links array.

But I have three cases where I need to update the documents before calling $unwind, I have been looking at the $project operation, but I can find no information about how to create or update arrays for the following cases.

1) The links property is missing

{
    _id : ObjectId(.....),
    prop1 : "far"
}

I need to insert the links array

2) The links array property is an empty array

{
    _id : ObjectId(.....),
    prop1 : "far",
    links : []
}

I need to insert a value into the array

3) The links array has too few values

{
    _id : ObjectId(.....),
    prop1 : "far",
    links : [ 9, 10 ]
}

I need to insert additional values into the array


Solution

  • You should be able to use $ifNull (reference):

    db.test.aggregate({ $project : { the_links: { $ifNull : ["$links" , [5,6]]}} })
    

    It's simple logic that if the referenced field ($links) is null, the replacement value (in this case [5, 6]) is used. I renamed the field to the_links in the example.

    Assuming the links field is null (and not an empty array). Given data like:

    { "_id" : ObjectId(...), "prop1" : "foo", "links" : [  1,  2,  3,  4 ] }
    { "_id" : ObjectId(...), "prop1" : "bar" }
    

    The aggregation above produces:

    {
        "result" : [
                {
                        "_id" : ObjectId("52a869d51d02442354276cff"),
                        "the_links" : [
                                1,
                                2,
                                3,
                                4
                        ]
                },
                {
                        "_id" : ObjectId("52a869e31d02442354276d00"),
                        "the_links" : [
                                5,
                                6
                        ]
                }
        ],
        "ok" : 1
    }
    

    If links were an empty array [] rather than null, you could do something like:

    db.test.aggregate({ $project : 
         { the_links: { $cond : [ { $eq : ["$links", []]}, '$links', [5,6]]}} })
    

    But, if it's either null or [], then you'd need to add an additional check for that condition as an $or within the $cond operator.

    If the list has values, and you want to add more values conditionally, the current (2.4.x) production build of MongoDB does not have an effective solution. The development branch has an operator called $size which will return the length of an array (jira). You could then conditionally add them using yet another development feature called $setUnion:

    $setUnion Takes any number of arrays and returns an array that containing the elements that appear in any input array.