Home > database >  Mongodb filter by categories
Mongodb filter by categories

Time:09-19

I have Product collection. In this collection each document has same keys and different values. Several documents are shown in the example below.

[
  {
    "productCategory": "Electronics",
    "price": "20",
    "priceCondition": "Fixed",
    "adCategory": "Sale",
    "productCondition": "New",
    "addDescription": "Lorem Ipsum Dolor Sit Amet Consectetur Adipisicing Elit Maxime Ab Nesciunt Dignissimos.",
    "city": "Los Angeles",
    "rating": {
      "oneStar": 1,
      "twoStar": 32,
      "threeStar": 13,
      "fourStar": 44,
      "fiveStar": 1
    },
    "click": 12,
    "views": 3
  },
  {
    "productCategory": "Automobiles",
    "price": "1500",
    "priceCondition": "Negotiable",
    "adCategory": "Rent",
    "productCondition": "New",
    "addDescription": "Lorem Ipsum Dolor Sit Amet Consectetur Adipisicing Elit 
  Maxime Ab Nesciunt Dignissimos.",
    "city": "California",
    "rating": {
      "oneStar": 2,
      "twoStar": 13,
      "threeStar": 10,
      "fourStar": 50,
      "fiveStar": 4
    },
    "click": 22,}
  },
  {
    "productCategory": "Hospitality",
    "price": "500",
    "priceCondition": "Yearly",
    "adCategory": "Booking",
    "productCondition": "New",
    "addDescription": "Lorem Ipsum Dolor Sit Amet Consectetur Adipisicing Elit Maxime Ab Nesciunt Dignissimos.",
    "city": "Houston",
    "rating": {
      "oneStar": 16,
      "twoStar": 19,
      "threeStar": 28,
      "fourStar": 16,
      "fiveStar": 17
    },
    "click": 102,
    "views": 47
  }
]

I would like to search each document with one or more matching search queries to match the document with my search.

In the example below, I will show url query: http://localhost:8080/api/v1/filter?productCondition=New&price=100&productCategory=Hospitality

So far I have tried to solve the filtration using the $or and $and operators but here there is a problem because with the first operator $or when I do a query only the first condition is searched, the rest is omitted, with the second operator $and I have to add all the condition and the problem arises because I will not always have all querys.

I am using the find method to achieve my filtering methods.

db.collection(Index.Add)
  .find({
    $or: [
      { productCategory },
      { price },
      { adCategory },
      { priceCondition },
      { productCondition },
      { city },
    ],
  })
  .limit(pageSize)
  .skip(pageSize * parsePage)
  .toArray();

db.collection(Index.Add)
  .find({
    $and: [
      { productCategory },
      { price },
      { adCategory },
      { priceCondition },
      { productCondition },
      { city },
    ],
  })
  .limit(pageSize)
  .skip(pageSize * parsePage)
  .toArray();

CodePudding user response:

From what I understand, you're trying to build filters for your products listing API. On any listing page, if one applies two or more simultaneous filters one expects an and of all those filters. So, a query like this productCondition=New&price=100 should get all the products where productCondition is New and price is 100.

Therefore, in order to filter we should go for an $and operator in find query. As you rightly said you might not have all the query params in your path always, so a condition like this:

$and: [
  { productCategory },
  { price },
  { adCategory },
  { priceCondition },
  { productCondition },
  { city },
]

would send null or undefined to the database values for the keys that don't exist in the query param and you might get unexpected results.

To handle this, you should build the $and query based upon what is there in your query params like this (assuming you're using express.js):

let conditions = [];
for(const param in req.query) {
  if(req.query[param]) {
    conditions.push({ param: req.query[param] });
  }
}

conditions will only contain the keys whose values are there in the query params. You can then pass the conditions variable to the query like this:

db.collection(Index.Add)
  .find({
    $and: conditions
  })
  .limit(pageSize)
  .skip(pageSize * parsePage)
  .toArray();

CodePudding user response:

Thank you for bringing me to a solution. My solution is a banned query that is undefined. And just apply the valid query to find method. Example:

const {
  page,
  productCategory,
  price,
  priceCondition,
  adCategory,
  productCondition,
  city,
}: IAdds = req.query;

const check = (object: IAdds) => {
  for (let key in {
    productCategory,
    price,
    priceCondition,
    adCategory,
    productCondition,
    city,
  }) {
    if (object[key] !== undefined) return req.query;
  }
};
const resultFromCheckQuey = check(req.query);
const { page, ...res } = resultFromCheckQuey;
const filterResult = await db
    .collection(Index.Add)
    .find({
      $and: [res],
    })
    .limit(pageSize)
    .skip(pageSize * parsePage)
    .toArray();
  • Related