$lookup returns empty arrays

Hello, I’m trying to fetch reviews from a review collection. The review collection has a field “tour” which has the id for the tour. I have done an aggregation as attached here :point_down:t4:. The problem is that the results do not have the reviews for the specific tour but an empty array!. Please help

the code👇🏽

the tour schema👇🏽

the review schema :point_down:t4:

the results :point_down:t4:( why the empty arrays for the reviews)

The localField:_id is an ObjectId. The foreignField:tour is a string. I do not think they could match.

Hello Steeve, have tried to wrap with ObjectId() but it returns :


First, localField:_id is already an ObjectId() so you do not want to do

localField: ObjectId( "_id" )

It is foreignField:tour that needs to become an ObjectId(). But doing ObjectId( “tour” ) will not work because the string tour is not the string representation of an ObjectId(). I am not sure how to do it but you might take a look at alternative forms of $lookup where you can specify a matching expression.

The best would be to have all you reference (tour and user) be ObjectId() directly in your collection so that you do not do type conversions.

Rather than screenshots of documents and code snippets it would be best that you cut-n-paste them as properly formatted text. This way we can copy them in our env. to experiment.

1 Like

Thank you the feedbak is helpful and i have tried to think about it and implemented this code "to make " the referenced id ObjectIds, however I still get empty arrays when i run the above aggregation!
This is the code👇🏽i did to ‘make the ids’ ObjectId()

exports.updateIds = async function (req, res) {

try {

const { ObjectId } = Types;

const reviews = await Review.find();

const updatedReviews = reviews.map((rev) => {

  const userId = rev.user;

  const tourId = rev.user;

  return {

    rating: rev.rating,

    review: rev.review,

    tour: ObjectId(tourId),

    user: ObjectId(userId),

    date: Date.now(),



await Review.deleteMany();

const newReviews = await Review.insertMany(updatedReviews);


  status: 'updated',

  data: newReviews,


} catch (err) {


  status: 'failed to update',

  message: err.message,




Share some documents that have been created with this new code.

As I wrote the above, I found:

I think that the second one should be rev.tour or something like that.

If you do :slight_smile:

const user = ObjectId( rev,user ) ;
const tour = ObjectId( rev.tour ) ;

you will be able to do:

return  { ... , tour , user , ... }

Yes thank you, i noted the above variable error and corrected ealier🙂. I did run the update and this is how the updated doc looks like:


            "review": "Convallis turpis porttitor sapien ad urna efficitur dui vivamus in praesent nulla hac non potenti!",

            "rating": 5,

            "tour": "5c88fa8cf4afda39709c295d",

            "user": "5c8a1dfa2f8fb814b56fa181",

            "date": "2021-09-15T22:17:46.950Z",

            "_id": "6142710bf9a3fdb5d8506f7a",

            "__v": 0


However, I ran the aggregation with $match, $lookup and $project (with the updated reviews collection) but the result is an empty array:

Here is the route handler:

exports.getTourStats = async function (req, res) {

  try {

    const { id } = req.params;

    const { ObjectId } = Types;

    const match = { $match: { _id: ObjectId(id) } };

    const lookup = {

      $lookup: {

        from: 'reviews',

        localField: '_id',

        foreignField: 'tour',

        as: 'reviews',



    const project = { $project: { reviews: 1, name: 1, _id: 0 } };

    const stages = [match, lookup, project];

    const tour = await Tour.aggregate(stages);


      status: `success`,

      _id: id,

      data: tour,


  } catch (err) {


      status: 'fail',

      message: err.message,




This is what I get as response from postman:

the $match and $project seem to work, but the &lookup stage is not and i dont understand why

Both are still strings.

Thank you so much. It has worked. I realised the change was to be made on the review schema and not the doc data! I updated the schema to accept ObjectId as shown here and the results are as expected!:grinning_face_with_smiling_eyes:

const schema = new Schema({

  review: {

    type: String,

    required: [true, 'No empty input'],


  rating: {

    type: Number,

    min: 1.0,

    max: 5.0,

    default: 5.0,


  tour: {

    type: ObjectId,

    required: [true, 'story must be from a tour'],


  user: {

    type: ObjectId,

    required: [true, 'story must be from a tour'],


  date: {

    type: Date,

    default: new Date(),



exports.Review = model('Review', schema);


This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.