skip to Main Content

Introduction

I am building a search feature using mongodb with mongoose ODM. The codebase is already having an search feature using javascript. The code

  terms.every((term) => {
    const cleanedTerm = term.trim();

    return (
      usernameLower.includes(cleanedTerm) ||
      nameLower.includes(cleanedTerm) ||
      userTagsString.includes(cleanedTerm) ||
      userLocationString.includes(cleanedTerm)
    );
  });

First all the users are fetched and then getting filtered. The documents which consists of every elements of the array in any of its fields are getting returned.

My Approach

async function searchUsers(termsArray) {
  const query = { $in: termsArray }
  return await Profile.find({
    $or: [
      { username: query },
      { name: query },
      { "location.name": query },
      { tags: { $all: termsArray } },
    ],
  });
}

Problem and Example
Basically this query is returning the documents which is matching any of the elements of the terms or tags.
Example – Suppose a profile is having tags [JS,TS,OSS] and search terms are [JS,TS,OSS,JAVA] then also the profile is getting returned which is not expected as JAVA is not present.

2

Answers


  1. first thing you have use $or so any of condition satisfied it will return records,
    any username or name matches in record and that record is having this [JS,TS,OSS,JAVA] it is valid,
    you have to make condition using $AND or otherwise

    you can try $ size along with you query

    async function searchUsers(termsArray) {
    const query = { $in: termsArray }
    return await Profile.find({
     $or: [
      { username: query },
      { name: query },
      { "location.name": query },
      { tags: { $size: termsArray.length, $all: termsArray } },
     ],
    });
    }
    
    Login or Signup to reply.
  2. So if you only want profiles to be returned if ALL tags listed in the termsArray are present in the profile, and if ANY of the termsArray terms are present in either the username or name or location.name of the same profile then you need to combine $and with $or. Refactor your aggregation like so:

    async function searchUsers(termsArray) {
       const query = { $in: termsArray };
       return await Profile.find({
           $and: [
               {
                   $or: [
                       { username: query },
                       { name: query },
                       { "location.name": query }
                   ]
               },
               { tags: { $all: termsArray } }
           ]
       });
    }
    

    The $and states that both conditions need to be true. One of those conditions is an $or so any of the 3 conditions in the $or can be true but the { tags: { $all: termsArray } } must also be true.

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