skip to Main Content

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));
});

2

Answers


  1. Few things from the documentation

    $where docs

    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

    Login or Signup to reply.
  2. 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…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search