Mongodb find() only include non-empty arrays

10,477

The general case here to check for a non-empty array is to check to see if the "first" element actually exists. For single matches you can project with the positional $ operator:

Vist.find(
    { "someArray.innerArray.0": { "$exists": true } },
    { "document": 1,"someArray.$": 1},
    function(err,data) {

    }
);

If you need more than a single match or have arrays nested more deeply than this, then the aggregation framework is what you need to handle the harder projection and/or "filter" the array results for more than one match:

Visit.aggregate(
    [
        // Match documents that "contain" the match
        { "$match": {
            "someArray.innerArray.0": { "$exists": true }
        }},

        // Unwind the array documents
        { "$unwind": "$someArray" },

        // Match the array documents
        { "$match": {
            "someArray.innerArray.0": { "$exists": true }
        }},

        // Group back to form
        { "$group": {
            "_id": "$_id",
            "document": { "$first": "$document" },
            "someArray": { "$push": "$someArray" }
        }}

    ],function(err,data) {


    }
)

Worth noting here that you are calling this "empty" but in fact is is not, as it actually contains another empty array. You probably don't want to do that with real data, but if you have then you would need to filter like this:

Visit.aggregate(
    [
        { "$match": {
            "someArray": { "$elemMatch": { "innerArray.0": { "$ne": [] } } }
        }},
        { "$unwind": "$someArray" },
        { "$match": {
            "someArray.innerArray.0": { "$ne": [] }
        }},
        { "$group": {
            "_id": "$_id",
            "document": { "$first": "$document" },
            "someArray": { "$push": "$someArray" }
        }}
     ],function(err,data) {


     }
);
Share:
10,477
AshClarke
Author by

AshClarke

Currently studying computer science at QUT, Brisbane. I'm available for work, depending on the task. I get excited by small amounts of code that save a lot of time.

Updated on June 04, 2022

Comments

  • AshClarke
    AshClarke almost 2 years

    What I'm trying to achieve with a find query is to only include "someArray"s if it's inner array is not empty. For example the JSON below:

    {
      "document": "some document",
      "someArray": [
        {
          "innerArray": [
            "not empty"
          ]
        },
        {
          "innerArray": [
            [] //empty
          ]
        }
      ]
    }
    

    Would return this:

    {
      "document": "some document",
      "someArray": [
        {
          "innerArray": [
            "not empty"
          ]
        }
      ]
    }
    

    I'm using the following find:

    Visit.find({'someArray.innerArray.0': {$exists: true}}, function(err, data){});
    

    However, this returns all data.

    Have also tried:

    Visit.find({}, {'someArray.innerArray': {$gt: 0}}, function(err, data) {});
    

    But this returns nothing

    Any ideas on how to approach this?

    Cheers