2
votes

I am working on a e-commerce project and want to build faceted search in it. I have been able to create different kinds of filters using the $facet operator. One of the tags is colors. Say I search for dress and I get a list of dresses with their possible colors. .ie. colors ["RED","BLUE","ORANGE"]. Now when I select "RED" all the other colors go away coz mongo filters for "RED" "DRESS".

But I want to show "BLUE" and "ORANGE" as options so that they can be selected as well. How to do this?

[
  {
    "$lookup": {
      "from": "inventories",
      "localField": "colors.sizes.sizeId",
      "foreignField": "sku",
      "as": "inventory"
    }
  },
  {
    "$project": {
      "productId": 1,
      "name": 1,
      "description": 1,
      "brand": 1,
      "mainImage": 1,
      "colors": 1,
      "currency": "HK$",
      "availableOnline": 1,
      "sellingPrice": 1,
      "created": 1,
      "status": 1,
      "tags": 1,
      "available": {
        "$sum": "$inventory.available"
      }
    }
  },
  {
    "$match": {
      "status": "active",
      "productId": {
        "$in": [
          "1604",
          "2062",
          "3706",
          "233",
          "3043",
          "1130",
          "2087",
          "2096",
          "2896",
          "364",
          "2532",
          "2774",
          "5397",
          "451",
          "926",
          "1568",
          "6272",
          "84",
          "655",
          "1714",
          "3187",
          "3489",
          "766",
          "604",
          "1096",
          "2108",
          "3608",
          "1542",
          "626",
          "729",
          "1017",
          "2583",
          "1980",
          "3586",
          "1921",
          "3650",
          "636",
          "2448",
          "2044",
          "3553",
          "5883",
          "213",
          "438",
          "2110",
          "2706",
          "4223",
          "338",
          "578",
          "1738",
          "748",
          "1069",
          "1785",
          "1889",
          "2169",
          "150",
          "256",
          "4715",
          "5116",
          "360",
          "4514",
          "5052",
          "870",
          "5626",
          "131",
          "248",
          "1139",
          "2155",
          "4158",
          "5322",
          "3635",
          "4689",
          "4870",
          "6124",
          "4018"
        ]
      }
    }
  },
  {
    "$addFields": {
      "__order": {
        "$indexOfArray": [
          [
            "1604",
            "2062",
            "3706",
            "233",
            "3043",
            "1130",
            "2087",
            "2096",
            "2896",
            "364",
            "2532",
            "2774",
            "5397",
            "451",
            "926",
            "1568",
            "6272",
            "84",
            "655",
            "1714",
            "3187",
            "3489",
            "766",
            "604",
            "1096",
            "2108",
            "3608",
            "1542",
            "626",
            "729",
            "1017",
            "2583",
            "1980",
            "3586",
            "1921",
            "3650",
            "636",
            "2448",
            "2044",
            "3553",
            "5883",
            "213",
            "438",
            "2110",
            "2706",
            "4223",
            "338",
            "578",
            "1738",
            "748",
            "1069",
            "1785",
            "1889",
            "2169",
            "150",
            "256",
            "4715",
            "5116",
            "360",
            "4514",
            "5052",
            "870",
            "5626",
            "131",
            "248",
            "1139",
            "2155",
            "4158",
            "5322",
            "3635",
            "4689",
            "4870",
            "6124",
            "4018"
          ],
          "$productId"
        ]
      }
    }
  },
  {
    "$match": {
      "tags.colors": {
        "$in": [
          "RED"
        ]
      }
    }
  },
  {
    "$match": {
      "sellingPrice": {
        "$gte": 96,
        "$lte": 47000
      }
    }
  },
  {
    "$facet": {
      "category": [
        {
          "$unwind": "$tags.category"
        },
        {
          "$sortByCount": "$tags.category"
        }
      ],
      "color": [
        {
          "$unwind": "$tags.colors"
        },
        {
          "$sortByCount": "$tags.colors"
        }
      ],
      "size": [
        {
          "$unwind": "$tags.size"
        },
        {
          "$sortByCount": "$tags.size"
        }
      ],
      "season": [
        {
          "$unwind": "$tags.season"
        },
        {
          "$sortByCount": "$tags.season"
        }
      ],
      "brand": [
        {
          "$sortByCount": "$brand"
        }
      ],
      "price": [
        {
          "$unwind": "$tags.price"
        },
        {
          "$group": {
            "_id": null,
            "max": {
              "$max": {
                "$toDouble": "$tags.price"
              }
            },
            "min": {
              "$min": {
                "$toDouble": "$tags.price"
              }
            }
          }
        }
      ],
      "location": [
        {
          "$lookup": {
            "from": "inventories",
            "localField": "colors.sizes.sizeId",
            "foreignField": "sku",
            "as": "inventory"
          }
        },
        {
          "$lookup": {
            "from": "locations",
            "localField": "storeId",
            "foreignField": "storeId",
            "as": "shops"
          }
        },
        {
          "$unwind": "$shops"
        },
        {
          "$unwind": "$shops.stores"
        },
        {
          "$sortByCount": "$shops.stores.name"
        }
      ],
      "data": [
        {
          "$project": {
            "productId": 1,
            "name": 1,
            "description": 1,
            "brand": 1,
            "mainImage": 1,
            "colors": 1,
            "currency": "HK$",
            "availableOnline": 1,
            "sellingPrice": 1,
            "created": 1,
            "__order": 1
          }
        },
        {
          "$sort": {
            "__order": 1
          }
        }
      ]
    }
  }
]

This is how the product schema looks like

{"_id":ObjectId("5f1bd013a945ba002914cb8f"),"created":ISODate("2017-03-22T00:00:00.000Z"),"updated":ISODate("2020-07-25T06:22:41.000Z"),"isPhotoUploadable":true,"status":"active","productId":"4","tags":{"colors":["BLACK","DARK RED","RED"],"size":["42.5","43"],"category":["SHOES","SLIP-ONS","MENS SHOES & ACCESSORIES"],"price":["2650.00","1120.00"],"season":["AW(2017)","SS(2017)"],"hash":["CASUAL","CHINOS","COLOUR","DESIGNER","HORSE","LEATHER","RICH","ROUND","ROUND TOE","SMOOTH","STAPLE","TAILORED","CRAFTED"]},"name":"PENNY LOAFER MOCCASS","description":"A classic, staple style, these penny loafers from Antonio Maurizi are crafted in Italy from smooth leather. Saturated in rich dark brown, this pair can be styled with a pair of casual chinos or tailored pants.","brand":"ANTONIO MAURIZI","mainImage":"https://abc.jpg","styleCode":"111","availableOnline":false,"sellingPrice":2650}
1
Show us your doc structure and if any query triedGibbs
@Gibbs added the query which I am using aboveVisakh Vijayan

1 Answers

0
votes

Well, I did end up developing the exact same faceted navigation using the $facet in mongodb.

There are two basic requirements to build such a faceted navigation

  1. It should keep track of all the selected filters and never lose the selected attributes i.e., it should always display the selected attributes.
  2. The filter applied on an attribute effects other attributes in the faceted naviagtion

Let's say you have multiple attributes like color, brands, size, etc. At any given point of the navigation. Any attribute selected by the user should not disappear and the other attributes should be filtered based on the selection of this attribute.

For example let's say the user selected Red color. Ideally, in this case, other attributes will be filtered based on this selection but the state of the color attribute will remain the same. So what you may want to do is have for a given attribute a facet field should be dedicated to bringing the selected faceted(here RED color) and there will be a different facet stage (you can even handle it in the same facet stage maybe using the $or aggs) which will filter your color attribute based on other filters applied. Let's say a size filter was applied. So you will have a query that gets red color and a query that gets color which has an attribute of the specified size available in them.