28-Dec-2020 | Like this? Dislike this? Let me know |
A collection (pun!) of snippets that I find useful. Some of these are "easier" to deal with if you simply $unwind the arrays but unwinding big arrays in big objects can become very expensive. In general, be kind: don't unwind. Use $map,$reduce, and $filter whenever you can!
{"items": [ [0, 1, 2], [0], [2,5] ]}
db.foo.aggregate([ // This is unfortunately not as straightforward as simply doing // {newList: {$concatArrays: "$items"}} // because $items is a single array of arrays, NOT a vararg list of // arrays that is expected in $concatArrays. In other words, // {$concatArrays: "$items"}} // is not the same thing as // {$concatArrays: [ arr1, arr2, arr3 ]} // from a call signature perspective. // // Solution: We will "walk" $items with $reduce and call $concatArrays over and // over to build the list. We use $items as the input and overwrite it for output. // This is a clever use of $reduce by treating an array as the output value instead of a simple scalar: {$project: {items: {$reduce: { input: "$items", initialValue:[], in:{$concatArrays: [ "$$value", "$$this"]} }} }} ]); { "_id" : 0, "items" : [ 0, 1, 2, 0, 2, 5 ] }
db.foo.aggregate([ {$project: { items: {"$setUnion": [ {$reduce: { input: "$items", initialValue:[], in:{$concatArrays: [ "$$value", "$$this"]} }} ] } }} ]); { "_id" : 0, "items" : [ 0, 1, 2, 5 ] }
{_id:0, items: [ {a:0}, {a:0}, {b:7}, {a:1},{b:0},{b:7} ] }
matches = []; for(i = 0; i < items.size(); i++) { if(items[i]['b'] == 7) { matches.push(i); } }
db.foo.aggregate([ {$project: {XX: {$reduce: { input: {$range:[0,{$size:"$items"}]}, initialValue: [], // Clever use of array as value. // Good use of $let here to extract the object from the // $items array at offset $$this. It would be nice if MongoDB could let us do // {$arrayElemAt:["$items","$$this"]}.b // or some such but you cannot so we have to get to our target field b in TWO steps. // Almost looks like perl! :-) If $ee.b does not exist, $eq will eval it // to null, which is fine. If you need more control, use the $ifNull function // to set a default value, e.g. {$ifNull:["$$ee.b",-1]} // If the condition is true, append the index to the growing // result array. Note we must wrap the integer index with [] to make it an // array of size 1 because $concatArrays need an array as input, not int. If // the condition is not true, append a blank list [] (clever!) in: { $let: { vars: { ee: {$arrayElemAt:["$items","$$this"] } }, in: {$concatArrays: [ "$$value", {$cond: [ {$eq:[7,"$$ee.b"]} , ["$$this"] , [] ] } ]} }} }} }} ]); { XX: [ 2, 5 ] }
Like this? Dislike this? Let me know