mongodb aggregate gets stringified and does nto work in nodejs Code Answer

Hello Developer, Hope you guys are doing great. Today at Tutorial Guruji Official website, we are sharing the answer of mongodb aggregate gets stringified and does nto work in nodejs without wasting too much if your time.

The question is published on by Tutorial Guruji team.

I am working with mongodb aggregate and i was able to write aggregate in mongo shell and test it and it worked fine. However when i tried to make it dynamic in Nodejs method and passed values from frontend it showed me nothing. The reason i think is with this new ObjectId(YOUR ID IN STRING TYPE). The moment i pass the aggregate to execute function it gets strigified and new ObjectId gets removed so then it does not get matched. Here is my working aggregate that i wrote in mongo shell

db.ParcelStatus.aggregate([
  {
    $match: {
      $or: [
        {
          "statusRepositoryId": new ObjectId("5dd7fa20dcfa9600152cc2d8")
        },
        {
          "statusRepositoryId": new ObjectId("5dd7fa20dcfa9600152cc2dd")
        },
        {
          "createdAt": {
            "$gte": new Date("2020-05-01T18:59:59.001Z")
          }
        },
        {
          "createdAt": {
            "$lte": new Date("2020-05-31T18:59:59.099Z")
          }
        }
      ]
    }
  },
  {
    "$lookup": {
      "from": "Parcel",
      "localField": "parcelId",
      "foreignField": "_id",
      "as": "parcel"
    }
  },
  {
    "$unwind": {
      "path": "$parcel",
      "preserveNullAndEmptyArrays": true
    }
  },
  {
    "$lookup": {
      "from": "CustomerData",
      "localField": "parcel.customerDataId",
      "foreignField": "_id",
      "as": "parcel.customerData"
    }
  },
  {
    "$unwind": "$parcel.customerData"
  },
  {
    "$lookup": {
      "from": "Customer",
      "localField": "parcel.customerData.customerId",
      "foreignField": "_id",
      "as": "parcel.customerData.customer"
    }
  },
  {
    "$unwind": "$parcel.customerData.customer"
  },
  {
    "$lookup": {
      "from": "City",
      "localField": "parcel.customerData.cityId",
      "foreignField": "_id",
      "as": "parcel.customerData.city"
    }
  },
  {
    "$unwind": "$parcel.customerData.city"
  }
])

Now in nodejs here is how i am building it

 let pipeline = [];
  const matchObj = {
    $match: { $or: [] },
  };
  filters.forEach((obj) => {
    if (obj.key === "date") {
      matchObj.$match.$or.push(
        { createdAt: { $gte: new Date(obj.values.from) } },
        { createdAt: { $lte: new Date(obj.values.to) } }
      );
    }
    if (obj.key === "status_repository") {
      if (
        report.filters.find((x) => x.key === obj.key).selectionType === "single"
      ) {
        matchObj.$match.$or.push({
          statusRepositoryId: { $toObjectId: obj.values },
        });
      } else {
        obj.values.forEach((id) => {
          matchObj.$match.$or.push({ statusRepositoryId: { $toObjectId: id } });
        });
      }
    }
  });

  pipeline.push(matchObj);

  pipeline = [
    ...pipeline,
    {
      $lookup: {
        from: "Parcel",
        localField: "parcelId",
        foreignField: "_id",
        as: "parcel",
      },
    },
    {
      $unwind: {
        path: "$parcel",
        preserveNullAndEmptyArrays: true,
      },
    },
    {
      $lookup: {
        from: "CustomerData",
        localField: "parcel.customerDataId",
        foreignField: "_id",
        as: "parcel.customerData",
      },
    },
    { $unwind: "$parcel.customerData" },
    {
      $lookup: {
        from: "Customer",
        localField: "parcel.customerData.customerId",
        foreignField: "_id",
        as: "parcel.customerData.customer",
      },
    },
    { $unwind: "$parcel.customerData.customer" },
    {
      $lookup: {
        from: "City",
        localField: "parcel.customerData.cityId",
        foreignField: "_id",
        as: "parcel.customerData.city",
      },
    },
    {
      $unwind: "$parcel.customerData.city",
    },
  ];

and in nodejs this is how it shows up in console

db.ParcelStatus.aggregate([
  {
    "$match": {
      "$or": [
        {
          "statusRepositoryId": "5dd7fa20dcfa9600152cc2d8"
        },
        {
          "statusRepositoryId":"5dd7fa20dcfa9600152cc2dd"
        },
        {
          "createdAt": {
            "$gte": "2020-05-01T18:59:59.001Z"
          }
        },
        {
          "createdAt": {
            "$lte": "2020-05-31T18:59:59.099Z"
          }
        }
      ]
    }
  },
  {
    "$lookup": {
      "from": "Parcel",
      "localField": "parcelId",
      "foreignField": "_id",
      "as": "parcel"
    }
  },
  {
    "$unwind": {
      "path": "$parcel",
      "preserveNullAndEmptyArrays": true
    }
  },
  {
    "$lookup": {
      "from": "CustomerData",
      "localField": "parcel.customerDataId",
      "foreignField": "_id",
      "as": "parcel.customerData"
    }
  },
  {
    "$unwind": "$parcel.customerData"
  },
  {
    "$lookup": {
      "from": "Customer",
      "localField": "parcel.customerData.customerId",
      "foreignField": "_id",
      "as": "parcel.customerData.customer"
    }
  },
  {
    "$unwind": "$parcel.customerData.customer"
  },
  {
    "$lookup": {
      "from": "City",
      "localField": "parcel.customerData.cityId",
      "foreignField": "_id",
      "as": "parcel.customerData.city"
    }
  },
  {
    "$unwind": "$parcel.customerData.city"
  }
])

Notice the difference in nodejs result in $match,new Date(DATE) and in new ObjectId(ID). i would very much appreciate if you can tell me how can i fix this.

Answer

From the $match docs:

The $match query syntax is identical to the read operation query syntax; i.e. $match does not accept raw aggregation expressions. To include aggregation expression in $match, use a $expr query expression:

What are raw aggregation expressions?

Expressions can include field paths, literals, system variables, expression objects, and expression operators. Expressions can be nested.

And in our context $toObjectId is an aggregation expression operators which means we cannot use it in $match without using $expr, like so:

db.collection.aggregate([
  {
    $match: {
      $expr: {
        $eq: [
          "$statusRepositoryId",
          {
            $toObjectId: "5dd7fa20dcfa9600152cc2d8"
          }
        ]
      }
    }
  }
])

Mongo Playground

Meaning you’ll have to re-structure your query which could be quite annoying. But we do have a better solution, just import ObjectId from Mongo and cast the string to that while constructing the query:

 if (obj.key === "status_repository") {
      if (
        report.filters.find((x) => x.key === obj.key).selectionType === "single"
      ) {
        matchObj.$match.$or.push({
          statusRepositoryId: new ObjectId(obj.values),
        });
      } else {
        obj.values.forEach((id) => {
          matchObj.$match.$or.push({ statusRepositoryId:  new ObjectId(id) });
        });
      }
    }
We are here to answer your question about mongodb aggregate gets stringified and does nto work in nodejs - If you find the proper solution, please don't forgot to share this with your team members.

Related Posts

Tutorial Guruji