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.
$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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With