Search code examples
node.jsmongodbmongooseaggregation-framework

How to perform where in clause with Mongoose aggregate function


I have these two mongoose documents

 [
        {
            "_id": "63a7b67b333394043fdca934",
            "name": "Macbook M2 PRO",
            "category": [
                "62bacb137e6e0b6ac850b9ce",
                "63a7b5bb333394043fdca923"
            ],
            "avgRating": 5,
            "price": 5000.95,
            "slug": "macbook-pro-m2",
            "images": [
                {
                    "_id": "63a7b67b333394043fdca935",
                    "public_id": "ecommerce/1671935608704",
                    "url": "xxx"
                },
               ..
            ],
            "color": [
                "White",
                "Silver"
            ],
            "description": "The 13-inch MacBook Pro is more capable than ever. Supercharged by the next-generation M2 chip, it’s Apple’s most portable pro laptop, with up to 20 hours of battery life.",
            "ratings": [
                {
                    "_id": "63fc7c670a3b7a0824c4b4ba",
                    "star": 5,
                    "postedBy": "6241b32d8b01924f5c75cd85"
                }
            ],
            "sold": 0
        },
        {
            "_id": "63a7b280333394043fdca8fb",
            "name": "Ipad Pro",
            "category": [
                "63a7b1c0333394043fdca8ea",
                "62bacb137e6e0b6ac850b9ce"
            ],
            "avgRating": 4,
            "price": 3000.54,
            "slug": "ipad-pro",
            "images": [
                {
                    "_id": "63a7b280333394043fdca8fc",
                    "public_id": "ecommerce/1671934588743",
                    "url": "xxx"
                },
               ...
            ],
            "color": [
                "White",
                "Silver"
            ],
            "description": "Just another Ipad",
            "ratings": [
                {
                    "_id": "63fc05ee981f4c0b61dc8c89",
                    "star": 5,
                    "postedBy": "6241b25f8b01924f5c75cd82"
                },
                ...
            ],
            "sold": 0
        }
    ]

I would like to filter out the document with the category in "62bacb137e6e0b6ac850b9ce", "63a7b5bb333394043fdca923" . Basically a where in clause, document where the category ids matches both the category ids above.

So this is what I did but does not work

const product = await Product.aggregate([
    {
      $match: {
        $text: { $search: `"${searchText}"` },
      },
    },
    {
      $addFields: {
        root: "$$ROOT",
      },
    },
    {
      $project: {
        name: "$root.name",
        // category: "$root.category", //this will give a list of categories
        category: {
          $filter: {
            input: "$root.category",
            as: "category",
            cond: { $in: ["62bacb137e6e0b6ac850b9ce" , "63a7b5bb333394043fdca923"] },
          },
        },
        avgRating: {
          $floor: { $avg: "$ratings.star" },
        },
        price: "$root.price",
        slug: "$root.slug",
        images: "$root.images",
        color: "$root.color",
        description: "$root.description",
        ratings: "$root.ratings",
        sold: "$root.sold",
      },
    },
  ]);

my product Model

const product = {
  name: {
    type: String,
    required: true,
    trim: true,
    minlength: [2, "Too short"],
    maxlength: [32, "Too long"],
    unique: true,
  },
  slug: {
    type: String,
    unique: true,
    lowercase: true,
    index: true,
  },
  category: [
    {
      type: ObjectId,
      ref: "Category",
    },
  ],
  description: {
    type: String,
    maxlength: 200,
  },
  price: {
    type: Number,
    required: true,
    trim: true,
    maxlength: 32,
  },
  shipping: {
    type: String,
    enum: ["Yes", "No"],
  },
  color: [
    {
      type: String,
      enum: ["Black", "Brown", "Silver", "White", "Blue"],
    },
  ],
  sold: {
    type: Number,
    default: 0,
  },
  quantity: {
    type: Number,
  },
  images: [
    {
      public_id: String,
      url: String,
    },
  ],
  ratings: [
    {
      star: Number,
      postedBy: {
        type: ObjectId,
        ref: "User",
      },
    },
  ],
};
const productSchema = new Schema(product, { timestamps: true });
productSchema.index({ name: 1, category: 1 });

Solution

  • So I just found the answer from this post , Can I use $in within mongodb's $match aggregate function

    Basically normal string does not work. We have to convert it to Mongodb object string. So this is what I did

        const ObjectId = require("mongodb").ObjectID;
        const product = await Product.aggregate([
           {
             $match: {
               $text: { $search: `"${searchText}"` },
               price: 3000.54,
               category: {
                $in: [ObjectId("62bacb137e6e0b6ac850b9ce") , ObjectId
    ('63a7b5bb333394043fdca923')],
               },
             },
           },
          {
            $addFields: {
              root: "$$ROOT",
            },
          },
          {
            $project: {
              name: "$root.name",
              category: "$root.category", //this will give a list of categories
              avgRating: {
                $floor: { $avg: "$ratings.star" },
              },
              price: "$root.price",
              slug: "$root.slug",
              images: "$root.images",
              color: "$root.color",
              description: "$root.description",
              ratings: "$root.ratings",
              sold: "$root.sold",
            },
          }    
        ]);
    

    Thank you