skip to Main Content

Main task: apply tags filters for products (could be one or multiple).

Need to return filtered products list with specified dietary tags in query.
Product tags field is a ref to tags collection.

$lookup and related stages:

{
  $lookup: {
    from: 'tags',
    localField: 'tags',
    foreignField: '_id',
    as: 'tags',
    pipeline: [
      {
        $project: {
          tags: '$tags',
          _id: 0,
        },
      },
    ],
  },
},
{ $unwind: { path: '$tags', preserveNullAndEmptyArrays: true } },
{
  $addFields: {
    tags: '$tags.tags',
  },
},

Output product if tags collection has ref tags id. Else tags field doesn’t exist.

{
  "_id": "65543472bde41c7561151fd3",
  "slug": "green-olives",
  "name": "Green Olives",
  "description": "Some description",
  "category": "food-cupboard",
  "subCategory": "canned-vegetables"
  "popularity": 76,
  "views": 327,
  "rating": 5,
  "tags": {
    "dietaries": [
      "HALAL"
    ],
    "promotions": [
      "PWP"
    ]
  },
}

Expected (conjectural) aggregation $match stage:

{
  $match: {
    // if query "tags" exist => filter products which have (tags.dietaries) with query values
    // if query "tags" doesn't exist => all product with tags field and without it
  }
}

Tried match ‘tags.dietaries’ array with regex if dietary (query) exists, else return all. The problem is products returns but only with existing tags field.

{
  $match: {
    'tags.dietaries': dietary
      ? {
          $regex: dietary.split('+').join('|'),
        }
      : /.*/,
  },
},

Another idea is to use $cond operator but couldn’t apply it properly.

Also I thought keeping tags field null by default that way making possible/improving matching tags.
Is it good idea?

I would be grateful for help.

2

Answers


  1. Chosen as BEST ANSWER

    Seems like crutchy but works fine.

    {
     $match: {
      'tags.dietaries': {
        $in: dietary ? dietary.split('+') : [null, /.*/],
      },
     },
    },
    

    Description:

    1. If query exists ('HALAL+GLUTEN_FREE') return documents which 'tags.dietaries' tags array contains one of query tags array: $in: dietary ? dietary.split('+')
    2. Else return documents where 'tags.dietaries' field exists and doesn't: : [null, /.*/]

    Thanks Abdellatif Sraiti for help. He turned me in the right direction.
    Hope helps someone.

    P.S. I would be grateful if someone could share a better solution


  2. From what i understood i made this aggregation,

    I took the liberty to remove the unwind stage because i didn’t see a use for it,and i noticed that you have a one to one relationship so i just took the first item .

    and if i understood you filtering criteria correctly you want to filter by the dietaries if the tag is available if not to just return everything ,

    I hope my understanding is on point if not leave me some comment so we can tweak the aggregation .

    [{
        "$lookup": {
          "from": "tags",
          "localField": "tags",
          "foreignField": "_id",
          "as": "tags"
        }
      },
      {
        "$addFields": {
          "tags": {
            "$arrayElemAt": [
              "$tags.tags",
              0
            ]
          }
        }
      },
      {
        "$addFields": {
          "dietariesIntersectionSize": {
            "$size": {
              "$ifNull": [{
                  "$setIntersection": [
                    /// the array of the query
                    [
                      "HALAL",
                      "GLUTEN_FREE"
                    ],
                    "$tags.dietaries"
                  ]
                },
                []
              ]
            }
          }
        }
      },
      {
        "$match": {
          "$or": [{
              "tags": {
                "$eq": null
              }
            },
            {
              "dietariesIntersectionSize": {
                "$gt": 0
              }
            }
          ]
        }
      }
    
    ]
    

    I hope that was helpful

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