Search code examples
mongodbmongoose

MongoDB/mongoose: query array field elements to match a part of a string


GIVEN

a searchString "*** test text before string *** john doe | test string after string"

AND

a MongoDB document:

{
  "name": "John Doe",
  "matchingWords": [
    "john doe", "another string"
  ]
}

I WANT TO query for any documents that have at least 1 element in the matchingWords field (string[]) that is found in the searchString. !! The elements in matchingWords might contains spaces.

Expected result: the mongoDB document above is found because element "john doe" is found in the searchString.

Problem

The code below is only working when matchingWords elements do not contain spaces... And I cannot find a solution to fulfill my requirement. So please help me :-)

private async findByMatchingWords(searchString: string): Promise<boolean> {
if (!searchString) return false;

const lowerCaseSearchStrings: string[] = searchString.toLowerCase().split(' ');
const results = await paymentPartyModel
  .find<IPaymentParty>({
    matchingWords: {
      $exists: true,
      $not: { $size: 0 }, // Check if array exists and is not empty
      $in: lowerCaseSearchStrings,
    },
  })
  .lean();

if (!results || results.length === 0) {
  return false;
} else if (results.length === 1) {
  this.info = results[0];
  return true;
} else {
  // 2 or more matches found...
  Todo('What to do when more than one payment party matches the search?');
  return false;
}

}


Solution

  • Use $indexOfCP to check for the existence of the matchingWords within the substring. If the matchingWord is not inside, it would return -1. $map this result to an array of boolean from matchingWords array. Use $anyElementTrue to check for any words that are within the search string.

    db.collection.find({
      $expr: {
        $anyElementTrue: {
          "$let": {
            "vars": {
              "searchString": "*** test text before string *** john doe | test string after string"
            },
            "in": {
              "$map": {
                "input": "$matchingWords",
                "as": "mw",
                "in": {
                  "$ne": [
                    -1,
                    {
                      "$indexOfCP": [
                        "$$searchString",
                        "$$mw"
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    })
    

    Mongo Playground