Pinky Promise
Pinky Promise

Reputation: 223

MongoDB query filter

I have a collection Restaurant, Products, and Reviews

restaurants: [
    {
      _id: 1,
      name: "Burger King",
      location: {
        type: "Point",
        coordinates: [
          11.111,
          11.111
        ]
      },
      isOpen: true
    },
    {
      _id: 2,
      name: "McDonald's",
      location: {
        type: "Point",
        coordinates: [
          22.222,
          22.222
        ]
      },
      isOpen: true
    },
    {
      _id: 3,
      name: "Chick-fil-A",
      location: {
        type: "Point",
        coordinates: [
          33.333,
          33.333
        ]
      },
      isOpen: true
    }
  ],
products: [
    {
      _id: 1,
      name: "Breakfast Whopper Jr.",
      price: "$1.29",
      isAvailable: true,
      isApproved: true,
      quantitySold: 50,
      restaurant: ObjectId("1")
      
    },
    {
      _id: 2,
      name: "Big Mac",
      price: "$4.35",
      isAvailable: true,
      isApproved: true,
      quantitySold: 59,
      restaurant: ObjectId("2")
    },
    {
      _id: 3,
      name: "Spicy Chicken Sandwich",
      price: "$3.29",
      isAvailable: true,
      isApproved: true,
      quantitySold: 60,
      restaurant: ObjectId("3")
    },
    {
      _id: 4,
      name: "Chicken Sandwich",
      price: "$2.29",
      isAvailable: true,
      isApproved: true,
      quantitySold: 58,
      restaurant: ObjectId("3")
    }
  ],
reviews: [
    {
      _id: 1,
      message: "Big burger even if it's junior size.",
      restaurant: ObjectId("1"),
      product: ObjectId("1")
    },
    {
      _id: 2,
      message: "Big Mac is really the best burger in town.",
      restaurant: ObjectId("2"),
      product: ObjectId("2")
      
    },
    {
      _id: 3,
      message: "Spicy Chicken Sandwich rocks!",
      restaurant: ObjectId("3"),
      product: ObjectId("3")
    },
    {
      _id: 4,
      message: "Chicken Sandwich is the best sandwich of Chick-fil-A!",
      restaurant: ObjectId("3"),
      product: ObjectId("4")
    },
    {
      _id: 5,
      message: "Chicken Sandwich is the best!",
      restaurant: ObjectId("3"),
      product: ObjectId("4")
    }
  ]

My implementation

db.products.aggregate([
    { 
        $lookup: { 
            "from": "restaurant", 
            "localField": "restaurant", 
            "foreignField": "_id", 
            "as": "restaurant" 
        } 
    },
    {
        $match: {
            "restaurant.isOpen": true,
            "isApproved": true,
            "isAvailable": true
        }
    },
    {
        $project: {
            "restaurant.isOpen": 1,
            "isApproved": 1,
            "isAvailable": 1,
            "restaurant.location": 1,
            "quantitySold": 1
        }
    },
    {
        $match: {
            "restaurant.location": {
                $geoWithin: {
                    $centerSphere: [[222.22, 222.33], 10/6378.1] // sample coordinates
                }
            }
        }
    },
    {
        $sort: {
            // best seller
            "quantitySold": -1
        }
    }

In my implementation. I'm currently getting the 10km of restaurant that isOpen: true and its products that isAvailable: true and isApproved: true. I also sorted the best selling by the quantitySold field.

Now, I want to query also the most reviewed products and I want to show only 1 product per restaurant in my app based on best seller and most reviewed. Then, after that the remaining products will randomize below the sorted best seller and most reviewed.

Example. I'm in 10km of Burger King, MCDO, Chick-fil-A and I will see their products based on best seller and most reviewed (1 product). Then after, I will see all of their products randomize below.

enter image description here

Upvotes: 1

Views: 289

Answers (1)

Avik
Avik

Reputation: 817

Currently you have OPEN (restaurant), APPROVED and AVAILABLE list of items. (You have to adjust the existing query's $project section to get few more fields accordingly).

To get the reviews you can use $lookup, where

{
     $lookup:
       {
         from: "reviews",
         localField: "_id",
         foreignField: "product",
         as: "product_reviews"
       }
  }

you will get product_reviews array, for each product. You can then perform $groupby and $count to get the total count per product in the next pipeline stage.

After getting the above list in the aggregation pipeline use GROUP BY as follows

{ $group : { _id : "$restaurant_id", items: { $push : "$$ROOT" } }}

(Get restaurant id and name for products in $project stage in existing query). Now you have a list with restaurants id/names and their product arrays. You can refer to below links to know more about $$ROOT

Mongo group and push: pushing all fields

As your list is already sorted you will have the best seller item at top.

Regarding the other food items do you really need to randomize every time? you can then try to work with $sample (refer :https://docs.mongodb.com/manual/reference/operator/aggregation/sample/) or follow this mongo db aggregate randomize ( shuffle ) results as per your need.

Upvotes: 2

Related Questions