Search code examples
node.jsmongodbexpressmongoosemongodb-atlas-search

MongoDB's aggregate $search is not pulling any results


So I am working on an express/mongoose/mongoDB app that requires users to query the database to find other users based upon 3 fields but for some reason, none of the documents I have made for testing purposes are being pulled as a result. Even when I am making the query parameter match identically to the fields being searched. The fields I am searching are:

  • firstName
  • lastName
  • username

I have the following model for my user (trimmed of course).

const BlockedUser = require("./blockeduser");
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
  email: {
    type: String,
    required: [true, "Please provide a valid email"],
    unique: true,
    trim: true,
    lowercase: true
  },
  password: {
    type: String,
    minlength: 10,
    trim: true,
    select: false
  },
  username: {
    type: String,
    required: [true, "Please provide a username"],
    unique: true,
    minlength: 3,
    maxlength: 255
  },
  firstName: {
    type: String,
    required: [true, "Please provide a first name"],
    minlength: 3,
    maxlength: 255
  },
  lastName: {
    type: String,
    required: [true, "Please provide a first name"],
    minlength: 3,
    maxlength: 255
  },
  blockList: {// External collection of a list of blocked users
    type: Schema.Types.ObjectId,
    ref: 'BlockedUser'
  }
});

const User = mongoose.model("User", UserSchema);

module.exports = User;

And then the following query using mongoose in the express app.

const mongoose = require("mongoose");
const User = require("../models/user");

router.get("/profiles", async (req, res, next) => {
  const users = await User.aggregate([
    {
      $search: {
        "text": {
          "query": req.query.q,
          "path": ["username", "firstName", "lastName"],
          "fuzzy": {}
        }
      }
    },
    {
      $match: {
        _id: {$ne: mongoose.Types.ObjectId(req.user.id)} // Makes sure the user is not pulling themselves as a result
      }
    },
    { // Populates blockList
      $lookup: {
        from: "blockedusers",
        let: {
          "theId": "$blockList"
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $eq: ["$_id", "$$theId"]
              }
            }
          }
        ],
        as: "blockList"
      }
    },
    {
      $match: {
        "blockList.blockedUsers": {
          $ne: mongoose.Types.ObjectId(req.user.id) // Makes sure the user is not on the queried blocked user list
        }
      }
    },
    {
      $project: {
        _id: 0,
        email: 0
      }
    }
  ]);

  res.status(200).json({
    status: "success",
    data: users
  });
});

And finally the query URL I am sending.

http://127.0.0.1:1337/profiles?q=willow

And just to be thorough, here is the document I am trying to pull up.

{
  username: "Willow",
  firstName: "Jennifer",
  lastName: "Jones",
  email: "email@example.com",
  password: "examplepass1234" // This is hashed don't worry :P
}

Did I miss something which is keeping my app from retrieving results? I also know the $text search is also available but I am currently testing and building on the free MongoDB tier and every time I try to use $text search I get the error saying that '$text is not available for this atlas tier'. Would love to get this figured out as this is one of the last steps to having a functional prototype haha!

Thank you to anyone who has some insight. It means a lot to me!


Solution

  • So an answer was posted here not too long ago and I believe the user deleted it but they mentioned something that got me thinking (I think their name was Eol so thank you Eol!!!). They mentioned setting the 'index' and then talked about using $text in a find query. Since then, I have went to my mongoose models and set the index of the 3 fields I was querying to be index: "text", created a new document by registering a new test user and BOOM it worked! I just needed to set the index on the 3 fields and that was it. To add to that, I did have to 'build' my search indexes inside of the mongoDB atlas dashboard.

    So if you are having the same issue, remember to set your index on the fields you are trying to search. In my case, I needed it to look like:

    const BlockedUser = require("./blockeduser");
    const mongoose = require("mongoose");
    const Schema = mongoose.Schema;
    const UserSchema = new Schema({
      email: {
        type: String,
        required: [true, "Please provide a valid email"],
        unique: true,
        trim: true,
        lowercase: true
      },
      password: {
        type: String,
        minlength: 10,
        trim: true,
        select: false
      },
      username: {
        type: String,
        required: [true, "Please provide a username"],
        unique: true,
        minlength: 3,
        maxlength: 255,
        index: "text" // <----------- MAKE SURE to add this to allow you to search this field!
      },
      firstName: {
        type: String,
        required: [true, "Please provide a first name"],
        minlength: 3,
        maxlength: 255,
        index: "text" // <----------- MAKE SURE to add this to allow you to search this field!
      },
      lastName: {
        type: String,
        required: [true, "Please provide a first name"],
        minlength: 3,
        maxlength: 255,
        index: "text" // <----------- MAKE SURE to add this to allow you to search this field!
      },
      blockList: {// External collection of a list of blocked users
        type: Schema.Types.ObjectId,
        ref: 'BlockedUser'
      }
    });
    

    The query was left alone and it worked perfectly.

    Seeing as I don't know too much about MongoDB (yet), my guess is you need to use the index field to, in a way, 'categorize' your fields that you will be querying with some set MongoDB categories. Again, that is the best way I can explain what little I know but hope this helps someone in the future!