Compare the size of the document table with another document field

advertisements

The document might look like:

{
  _id: 'abc',
  programId: 'xyz',
  enrollment: 'open',
  people: ['a', 'b', 'c'],
  maxPeople: 5
}

I need to return all documents where enrollment is open and the length of people is less than maxPeople

I got this to work with $where:

const
  exists = ['enrollment', 'maxPeople', 'people'],
  query = _.reduce(exists, (existsQuery, field) => {
    existsQuery[field] = {'$exists': true}; return existsQuery;
  }, {});

query['$and'] = [{enrollment: 'open'}];
query['$where'] = 'this.people.length<this.maxPeople';
return db.coll.find(query, {fields: {programId: 1, maxPeople: 1, people: 1}});

But could I do this with aggregation, and why would it be better?

Also, if aggregation is better/faster, I don't understand how I could convert the above query to use aggregation. I'm stuck at:

db.coll.aggregate([
  {$project: {ab: {$cmp: ['$maxPeople','$someHowComputePeopleLength']}}},
  {$match: {ab:{$gt:0}}}
]);

UPDATE:

Based on @chridam answer, I was able to implement a solution like so, note the $and in the $match, for those of you that need a similar query:

  return Coll.aggregate([
    {
      $match: {
        $and: [
          {"enrollment": "open"},
          {"times.start.dateTime": {$gte: new Date()}}
        ]
      }
    },
    {
      "$redact": {
        "$cond": [
          {"$lt": [{"$size": "$students" }, "$maxStudents" ] },
          "$$KEEP",
          "$$PRUNE"
        ]
      }
    }
  ]);


The $redact pipeline operator in the aggregation framework should work for you in this case. This will recursively descend through the document structure and do some actions based on an evaluation of specified conditions at each level. The concept can be a bit tricky to grasp but basically the operator allows you to proccess the logical condition with the $cond operator and uses the special operations $$KEEP to "keep" the document where the logical condition is true or $$PRUNE to "remove" the document where the condition was false.

This operation is similar to having a $project pipeline that selects the fields in the collection and creates a new field that holds the result from the logical condition query and then a subsequent $match, except that $redact uses a single pipeline stage which restricts contents of the result set based on the access required to view the data and is more efficient.


To run a query on all documents where enrollment is open and the length of people is less than maxPeople, include a $redact stage as in the following::

db.coll.aggregate([
    { "$match": { "enrollment": "open" } },
    {
        "$redact": {
            "$cond": [
                { "$lt": [ { "$size": "$people" }, "$maxPeople" ] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])