Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform full text search in mongoose aggregrate

Given a mongoose object. How can I do a full text search in document object

 {
    "product" :[
        {
            "_id": "633d32c4ea9b06177e183388",
            "document": {
                "_id": "633d32c4ea9b06177e183388",
                "category": [
                    "62bacb137e6e0b6ac850b9ce",
                    "633d19260b72ba127d928e74"
                ],
                "name": "Iphone 13",
                "slug": "iphone-13",
                "__v": 0,
                "price": 2000.5,
                "images": [
                    {
                        "_id": "63cca1a271c3635246eee937",
                        "url": "https://res.cloudinary.com/..."
                    }
                ],
                "sold": 15,
                "ratings": []
            },
            "avgRating": null
        },
    ...

]


}

This is what I have tried but it doesn't work

const product = await Product.aggregate([
  {
    $project: {
      document: "$$ROOT",
      avgRating: {
        $floor: { $avg: "$ratings.star" },
      },
    },
  },

  {
    $match: {
      document: { $text: { $search: `"${searchText}"` } },
    },
  },
]);

This is 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",
      },
    },
  ],
};

The goal is to aggregate product ratings by getting the average of product ratings star then chain multiple where clauses , one of the where clause is to perform a full text search in the document object.

Kindly advise.

Thank you.

like image 508
desh Avatar asked Feb 01 '26 06:02

desh


2 Answers

$text requires a text index on the field, whose contents are to be searched. In your case, you are using $text on a document, rather than a text field, so most likely it will give unexpected results. One way to solve this is to add a field in your schema let's say textContent, which is a stringified version of your document, and then create a text index on this field. So your pipeline would become something like this:

const product = await Product.aggregate([
  {
    $match: {
      textContent: { $text: { $search: `"${searchText}"` } },
    },
  },
  {
    $project: {
      avgRating: {
        $floor: { $avg: "$ratings.star" },
      },
    },
  },
]);

Note: I have moved $match above because it's expected to be the first stage of the pipeline. Refer to $text documentation here.

like image 189
Charchit Kapoor Avatar answered Feb 03 '26 23:02

Charchit Kapoor


From the docs:

The $match stage that includes a $text must be the first stage in the pipeline.

So it should work if you flip your pipeline order:

const product = await Product.aggregate([
  {
    $match: {
      document: { $text: { $search: `"${searchText}"` } }
    }
  },
  {
    $project: {
      document: "$$ROOT",
      avgRating: {
        $floor: { $avg: "$ratings.star" }
      }
    }
  }
]);

This would be faster as you are running your project stage only on documents which match the search, rather than the entire collection.

like image 34
caffeinated.tech Avatar answered Feb 03 '26 23:02

caffeinated.tech



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!