diegoaguilar
diegoaguilar

Reputation: 8376

How to use multiple operators to a MongoDB aggregation pipeline?

I have a collection with documents in this schema (yep, I know this is schemaless world though):

{
  name: "Real Colicos",
  fee: 123,
  creator: {},
  participants: [{},{}]
}

So, I need a query where I can get first groups sorted by participants count. Of course I could have a participantsCount attribute and increment it at update by using $inc but I feel it's a naive approach. Is this aggregation or map reduce realm?

Edit. Solution is by using aggregate:

From docs I could take this:

  db.groups.aggregate(
    [
      {
        $project: {
          name: 1,
          participantsCount: {$size: "$participants"}
        }
      }
    ])

This works. Now I wonder, how can I paginate the search, order by participantsCount and include only some document properties?

I've tried:

  db.groups.aggregate(
    [
      {

        $project: {
          name: 1,
          participantsCount: {$size: "$participants"}
        },
        $skip: 10,
        $limit: 5,
        $sort: {participantsCount: -1},
        $match: {isPrivate: false}

      }
    ],
    function (err, results) {
      console.log(results);
    }
  );

But throws undefined as result.

Also:

  db.groups.aggregate(
    [
      {

        $project: {
          name: 1,
          participantsCount: {$size: "$participants"}
        },
        {$skip: 10},
        {$limit: 5},
        {$sort: {participantsCount: -1}},
        {$match: {isPrivate: false}}

      }
    ],
    function (err, results) {
      console.log(results);
    }
  );

Which is throwing an empty array.

And even:

db.groups.aggregate(
    [
      {$project: {name: 1, isPrivate: 1, participantsCount: {$size: "$participants"}}},
      {$match: {isPrivate: false}},
      {$skip: 10},
      {$limit: 5},
      {$sort: {participantsCount: -1}}
    ],
    function (err, results) {
      console.log(results);
    }
  );

Which also throws an empty array.

Upvotes: 0

Views: 2595

Answers (2)

diegoaguilar
diegoaguilar

Reputation: 8376

Answer: Aggregation can be a pipeline.

From docs:

The aggregation pipeline is a framework for data aggregation modeled on the concept of data processing pipelines. Documents enter a multi-stage pipeline that transforms the documents into an aggregated results.

If your aggregation operation requires only a subset of the data in a collection, use the $match, $limit, and $skip stages to restrict the documents that enter at the beginning of the pipeline.

So this means a pipeline is processed to get the wanted results. And as second paragraph suggests, order in pipeline processing matters.

This is how it must look like:

collection.aggregate(
    [
      {$match: {isPrivate: false}},
      {$skip: 0},
      {$limit: 2},
      {$project: {name: 1, participantsCount: {$size: "$participants"}}},
      {$sort: {participantsCount: -1}}
    ]
  );

Upvotes: 1

Andrew Ryder
Andrew Ryder

Reputation: 41

I can see two immediate problems:

  1. The stages are not properly pipelined. They are all part of one object at the moment but should appear as separate documents in the array:
    db.groups.aggregate(
        [
          {$project: {name: 1, participantsCount: {$size: "$participants"}}},
          {$skip: 10},
          {$limit: 5},
          {$sort: {participantsCount: -1}},
          {$match: {isPrivate: false}}
        ],
        function (err, results) {
          console.log(results);
        }
      );
  1. Nothing will pass the last $match because isPrivate was stripped at the first $project. You probably need to include that field in the $project.

Upvotes: 2

Related Questions