How can I combine the results of 3 queries in MongoDB?

125 views Asked by At

I made the following filter in hopes that I would be combining the results from all 3 $and arrays but it is only matching one of those blocks.

How can I combine the results of what would be returned from each $and array if conditions are met. Hopefully that's clear. I don't know what to call the $and array.

const filter = {
      $or: [
        {
          $and: [
            { category: req.query.category },
            { tags: req.query.subCategory },
            {contentType: req.query.contentType},
            req.query.searchTerm !== ""
              ? {
                  name: {
                    $regex: "(?i)" + req.query.searchTerm + "(?-i)",
                    $options: "i",
                  },
                }
              : {},
          ],
          $and: [
             { category: req.query.category },
            { tags: req.query.subCategory },
            {contentType: req.query.contentType},
            req.query.searchTerm !== ""
              ? {
                  description: {
                    $regex: "(?i)" + req.query.searchTerm + "(?-i)",
                    $options: "i",
                  },
                }
              : {},
          ],
          $and: [
             { category: req.query.category },
            { tags: req.query.subCategory },
            {contentType: req.query.contentType},
            req.query.searchTerm !== ""
              ? {
                  tags: {
                    $regex: "(?i)" + req.query.searchTerm + "(?-i)",
                    $options: "i",
                  },
                }
              : {},
          ],
        },
      ],
    };
 await Content.paginate(filter, options, (err, result) => {
      if (err) {
        res.status(500).send(err);
      } else {
        res.json(result);
      }
    });

EDIT: Below is an example of two entries that would be found in the database. The way it should work is it should use category, subCategory, and contentType to filter out the entries in the database so that what I have now are only the entries which have the same category, subCategory, and contentType as specified in req.query, I'll call this the firstFilterResult. From there, I am trying to search within firstFilterResult to see if I have entries that have name, tag, or description matches. So basically catgeory, subCategory and contentType are just used to narrow down the results so that I can find matches for name, tag, and description. My code above doesn't do exactly this but this is the idea behind it and I thought that what I have would do similar, but I guess I'm wrong.

contents: [
      {
        tags: [
          'food',
          'drinks',
          'card',
          'account'
        ],
        _id: '1d13ff7m6db4d5417cd608f4',
        name: 'THE NAME FOR THIS PIECE OF CONTENT',
        description: 'In here I will begin to talk about...',
        content_id: '5dbcb998ad4144390c244093',
        contentType: 'quiz',
        date: '2019-06-03T04:00:00.000Z',
        category: 'food',
        image: 'https://IMAGE.PNG',
        __v: 0
      },
      {
        tags: [
          'computer',
          'laptop'
        ],
        _id: '7d1b940b1c9d44000025db8c',
        name: 'THE NAME FOR THIS PIECE OF CONTENT',
        description: 'This is another description',
        content_id: '5f1b963d1c9d44000055db8d',
        contentType: 'tool',
        date: '2019-06-03T04:00:00.000Z',
        category: 'money',
        image: 'https://IMAGE.PNG',
        __v: 0
      }
 ]
2

There are 2 answers

0
mastercool On BEST ANSWER

I finally got it to work with this

 const catFilter =
      req.query.category !== "" ? { category: req.query.category } : {};
    const subCatFilter =
      req.query.subCategory !== "" ? { tags: req.query.subCategory } : {};
    const typeFilter =
      req.query.contentType !== ""
        ? { contentType: req.query.contentType }
        : {};
    const filter = {
      $and: [
        {
          $or: [
            {
              name: {
                $regex: req.query.searchTerm,
                $options: "i",
              },
            },
            {
              description: {
                $regex: req.query.searchTerm,
                $options: "i",
              },
            },
            {
              tags: {
                $regex: req.query.searchTerm,
                $options: "i",
              },
            },
          ],
        },
        catFilter,
        subCatFilter,
        typeFilter,
      ],
    };
1
Joe On

Since each element of the $or contains the same 3 checks with a single one that varies, these can be separated out, and the $or is then only needed if a search term is specified.

Passing options:"i" makes the entire regex match case insensitive, so it is not necessary to surround the search string with (?i) and (?-i)

The following should build the filter that you are attempting, without using empty objects:

// base query that checks the common fields
var filter = {
               category: req.query.category,
               tags: req.query.subCategory,
               contentType: req.query.contentType
};

// if a search term is provided, add in the additional critera
if (req.query.searchTerm !== "") {
  var regex = {
               $regex: req.query.searchTerm, 
               options:"i"
  };
  filter['$or'] = [
       { name: regex },
       { description: regex },
       { tags: regex }
  ]
}

If this doesn't obtain the results you're after, please edit the question and add in some sample documents so we can see the problem.