user1872384
user1872384

Reputation: 7147

Mongo DB Design for user favourites (Pros and cons)

I need to have 2 API to

1) Retrieve a list of clothes (while checking which items are the user's favourites, and mark it with a heart shape)

2) Retrieve a list of user's favourite clothes

enter image description here

How should I store user favourites?

What I've figured out so far:

Embed all user's ID in each clothing item in "Clothes" document. And keep an array of user's favourite in "User" document. To find out which clothes is a user's favourite we will do a match between "Clothes" and "User" document by leveraging the ID field.

Clothes collection:

{
    "id" : "clothesID01",
    "images": [
        {"imgUrl": "https://s3-ap-1.amazonaws.com/rental/img1.png"},
        {"imgUrl": "https://s3-ap-1.amazonaws.com/rental/img2.png"},
        {"imgUrl": "https://s3-ap-1.amazonaws.com/rental/img3.png"}
    ],
    "brand" : "Halo",
    "title" : "Cheryl Dress",
    "retailPrice" : {
        "currency": "USD",
        "amount": 447 
    },
    "description":"Halo pieces are xxxx",
    "favorites" : [
        {"userId" : "user01"},
        {"userId" : "user02"}
    ],
    "isPremium" : true,
    "publishedDate" : "2019-04-04T06:12:46.839+00:00"

    ...
}

User collection:

{
    "id": "user01",
    "phoneNumber": "+123456789",
    "favourites":[
        {"clothesId" : "clothesID01"},
        {"clothesId" : "clothesID04"}
    ]

    ...
}

Base on the Rules of thumb for mongoDB Design , in rule 3,

if there are more than a few thousand documents on the “many” side, don’t use an array of ObjectID references

The "clothes" document favourite might consist of about 100k users (fingers crossed) which might not be suitable to use an array of ObjectID references.

What's the remedy for this?

Upvotes: 7

Views: 2422

Answers (1)

XaxD
XaxD

Reputation: 1538

First of all, you have no need to store the favorite data in the clothes collection at all. In addition to the issues you identified, you will have a race condition whenever two users update favorite the same clothing item simultaneously.

Instead, only store the user's favorites in the User collection, then highlight the hearts during the rendering phase if the ClothesID matches the User's list of favorites.

Second, use a dictionary hashmap for more performative lookups, rather than a list which will require searching every item.

 {
    "id": "user01",
    "phoneNumber": "+123456789",
    "favourites": {
       "clothesID01": true, 
       "clothesID04": true
    }
} 

When you want to know if a clothesID is a favorited, you can check if

if (user.favourites[clothesID] === true)

This does not require iterating over every item in an Array, instead you are checking just one specific location in memory.

So the direct queries you would use:

1) Retrieve a list of clothes (while checking which items are the user's favourites, and mark it with a heart shape)

const user = await db.User.findOne(ObjectId(userId));
const clothes = await db.Clothes.find(query).toArray();
for(let article of clothes) {
  if(user.favourites[article.clotheId]) {
    // display full heart
  } 
}

2) Retrieve a list of user's favourite clothes

const user = await db.User.findOne(ObjectId(userId), {favourites:true});
const favouriteIds = Object.keys(user.favourites);
const favourites = await db.Collection.find({_id: {$in: favouriteIds}}).toArray();

Upvotes: 12

Related Questions