Cornwell
Cornwell

Reputation: 3410

Compare Size of Arrays Inside an Array of Objects

I want to find all documents where sCompetitions.length is greater than competitions.length.

Here's some sample documents document:

{
    "_id" : ObjectId("59b28f432b4353d3f311dd1b"),
    "name" : "Ford Focus RS 2008",
    "requirements" : [ 
        {
            "rankType" : "D1",
            "competitions" : [ 
                ObjectId("59b151fd2b4353d3f3116827"), 
                ObjectId("59b151fd2b4353d3f3116829")
            ],
            "sCompetitions" : [ 
                "Rallye Monte-Carlo", 
                "Rally Sweden"
            ]
        }, 
        {
            "rankType" : "A3",
            "competitions" : [
                ObjectId("59b151fd2b4353d3f3116f6b")
            ],
            "sCompetitions" : [ 
                "Rally Italia Sardegna", 
                "Neste Rally Finland"
            ]
        }
    ]
},
{
    "_id" : ObjectId("0000b28f432b4353f311dd1b"),
    "name" : "Ford Focus RS 2012",
    "requirements" : [ 
        {
            "rankType" : "D1",
            "competitions" : [ 
                ObjectId("59b151fd2b4353d3f3116827"), 
                ObjectId("59b151fd2b4353d3f3116829")
            ],
            "sCompetitions" : [ 
                "Rallye Monte-Carlo", 
                "Rally Sweden"
            ]
        }, 
        {
            "rankType" : "A3",
            "competitions" : [
                ObjectId("59b151fd2b4353d3f3116f6b"),
                ObjectId("59b151fd2b4353d3f3116f6b")
            ],
            "sCompetitions" : [ 
                "Rally Italia Sardegna", 
                "Neste Rally Finland"
            ]
        }
    ]
}

So looking at the samples it would only return ObjectId("59b28f432b4353d3f311dd1b")

My problem is that requirements is an array by itself, so I would need to somehow iterate it

Upvotes: 0

Views: 117

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151180

No need to "iterate". All you really need is an $anyElementTrue check after returning results from $map. And you can do this all inside a $redact action:

Model.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$anyElementTrue": {
          "$map": {
            "input": "$requirements",
            "as": "r",
            "in": { 
              "$gt": [
                { "$size": "$$r.sCompetitions" },
                { "$size": "$$r.competitions" }
              ]
            }
          }
        }
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

So it's a simple comparison by $size for each array element, and then if "any" of those elements is true, the document is "kept" or otherwise "pruned" from the results.

Upvotes: 1

Related Questions