3

I had an aggregate call that was working when revisions was an Object (with the keys being the entryId).

await Project.aggregate([
  { $limit: 1 },
  { $match: { _id: ObjectId(projectId) } },
  {
    $project: {
      limits: 1,
      revisions: { $slice: [`$revisions.${entryId}.${languageId}`, startIndex, PageSize] },
      totalRevisions: {
        $size: { $ifNull: [`$revisions.${entryId}.${languageId}`, []] },
      },
    },
  },
]);

Now that I converted revisions to an array with entryId inside it, I'm not sure how to get the same result. I tried:

await Project.aggregate([
  { $limit: 1 },
  {
    $match: {
      _id: ObjectId(projectId),
      'revisions.entryId': ObjectId(entryId),
    },
  },
  {
    $project: {
      limits: 1,
      revisions: { $slice: [`$revisions.$.${languageId}`, startIndex, PageSize] },
      totalRevisions: {
        $size: { $ifNull: [`$revisions.$.${languageId}`, []] },
      },
    },
  },
]);

But I get the error:

MongoError: FieldPath field names may not start with '$'.

How can I use $slice and $ifNull with an item in an array? Thanks

Sample doc:

{
  "revisions" : [ 
    {
      "entryId" : ObjectId("5bbf8813c272e05171463bc4"),
      "5bbe76c6d3fb1a4f143f8304" : [ 
        {
          "authorId" : ObjectId("5b1c5384d75d9f3b0eb65c2a"),
          "revisionId" : ObjectId("5bbf8813c272e05171463bc7"),
          "updated" : "2018-10-11T17:27:47.842Z",
          "value" : "County"
        }
      ]
    }
  ]
}
2
  • First could not able to get why $match has been used if applied $limit first!!! And please post the sample document and output as well Commented Oct 14, 2018 at 18:11
  • @AnthonyWinzlet I was imagining that match would continue searching all documents even after the first match without it? Thanks, sample added Commented Oct 14, 2018 at 18:17

1 Answer 1

1

Order of the pipeline stages really matters here. When you use $limit before the $match then it filters the data from the single document found in the $limit stage.

And if you will use $match before the $limit then it will filter the documents from your all the collection inside the database and will throw single document in the $limit stage.

After that You can try below aggregation

db.collection.aggregate([
  { "$match": {
    "_id": ObjectId(projectId),
    "revisions.entryId": ObjectId(entryId)
  }},
  {
    "$project": {
      "revisions": {
        "$map": {
          "input": "$revisions",
          "in": {
            "$arrayToObject": {
              "$map": {
                "input": {
                  "$filter": {
                    "input": { "$objectToArray": "$$this" },
                    "as": "ee",
                    "cond": { "$eq": ["$$ee.k", "5bbe76c6d3fb1a4f143f8304"] }
                  }
                },
                "as": "dd",
                "in": {
                  "k": "$$dd.k",
                  "v": { "$slice": [startIndex, 1 ] }
                }
              }
            }
          }
        }
      },
      "totalRevisions": {
        "$arrayElemAt": [
          {
            "$map": {
              "input": "$revisions",
              "in": {
                "$size": {
                  "$map": {
                    "input": {
                      "$filter": {
                        "input": { "$objectToArray": "$$this" },
                        "as": "ee",
                        "cond": { "$eq": ["$$ee.k", "5bbe76c6d3fb1a4f143f8304"] }
                      }
                    },
                    "as": "dd",
                    "in": {
                      "k": "$$dd.k",
                      "v": { "$slice": [startIndex, 1] }
                    }
                  }
                }
              }
            }
          },
          0
        ]
      }
    }
  }
])

But If you are just started with your project then I will highly recommend you to don't go with this structure because your nested array keys are dynamic and playing with the dynamic keys are always like playing with naked electric wire.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for this, wow that's a complex looking aggregation! It is a new project so would you advise having languages in an array instead? Would rather get rid of the complexity as much as poss. In my head I think object lookups are computationally faster but it seems mongo is more comfortable with arrays (though in this case it seems not!).
Mongo is comfortable with the arrays but it is hard to apply operations on arrays and nested arrays and especially when the keys of an array are not static. So in my opinion you should make another collection for entryId and then can go for it. Refer $lookup aggregation before start with your project.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.