Long story short, I have in my MongoDB database a collection of posts and with node.js
, express
and mongoose
, I'm trying to find all the documents using the $where method.
So I try this and it works perfectly, all the results, if their name includes Jo
get returned.
app.get("/posts", (req, res) => {
const nameSearch = "Jo";
Post.find({
$where: function () {
return this.name.includes("Jo");
},
}).then((data) => res.send(data));
});
But if I do something like this, it throws me an error
app.get("/posts", (req, res) => {
const nameSearch = "Jo";
Post.find({
$where: function () {
return this.name.includes(nameSearch);
},
}).then((data) => res.send(data));
});
Also, if I do this, it says that $where
requires a string or a function.
function runThis(post) {
return post.name.includes("Jo");
}
app.get("/posts", (req, res) => {
Post.find({
$where: runThis(this),
}).then((data) => res.send(data));
});
Even weirder, I think, is that if I change the function inside the $where
to an arrow function
, no matter what I put in the function, it will return all the results
app.get("/posts", (req, res) => {
const nameSearch = "Jo";
Post.find({
$where: () => {
return this.name.includes("Jo");
},
}).then((data) => res.send(data));
});
CodePudding user response:
Caveat: I don't use Mongoose or MongoDb.
But the documentation says the function you pass for $where
isn't executed locally, it's executed on the Mongoose server. So it doesn't have access to the variable.
But a search (1, 2) suggests that the usual way to find a substring within a field is to use $regex
. If so, and you have a user-entered string (which might contain characters that have special meaning to $regex
), you'll want to escape them. So for instance:
app.get("/posts", (req, res) => {
const nameSearch = "Jo";
Post.find({
name: {
$regex: new RegExp(escapeRegex(nameSearch)),
}),
}).then((data) => res.send(data));
});
...where escapeRegex
is from that linked question's answers. (Apparently JavaScript regular expression objects are supported, as well as strings using PCRE syntax instead.)
If for some reason you can't do that, you can also pass a string for $where
, so you could create the string dynamically:
app.get("/posts", (req, res) => {
const nameSearch = "Jo";
Post.find({
$where: `this.name.includes(${JSON.stringify(nameSearch)})`,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}).then((data) => res.send(data));
});
Normally, writing JavaScript code in strings is a bad idea (even though Mongoose has to convert whatever function you give it to a string to send it to the server anyway), but if you need to do this kind of substitution...
CodePudding user response:
Few things from the documentation
About scope
Starting in MongoDB 4.4, $where no longer supports the deprecated BSON type JavaScript code with scope (BSON type 15)
You should avoid using $where
$where evaluates JavaScript and cannot take advantage of indexes. Therefore, query performance improves when you express your query using the standard MongoDB operators (e.g., $gt , $in ).
In general, you should use $where only when you cannot express your query using another operator. If you must use $where , try to include at least one other standard query operator to filter the result set. Using $where alone requires a collection scan.
About variables
I have no idea, but you can try using $let