简体   繁体   中英

mongodb aggregate gets stringified and does nto work in nodejs

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.

From the $match docs:

The $match query syntax is identical to the read operation query syntax; ie $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) });
        });
      }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM