Schema for User Ratings - Key/Value DB

前端 未结 4 972
暖寄归人
暖寄归人 2021-02-02 18:15

We\'re using MongoDB and I\'m figuring out a schema for storing Ratings.

  • Ratings will have values of 1-5.
  • I want to store other values such as from
4条回答
  •  一向
    一向 (楼主)
    2021-02-02 18:30

    First of all 'Dictionary in User Class' is not a good idea. why? Adding extra rate object requires pushing a new item to the array, which implies the old item will be removed, and this insertion is so called "moving a document". Moving documents is slow and MongoDB is not so great at reusing empty space, so moving documents around a lot can result in large swaths of empty data file (some text in 'MongoDB The Definitive Guide' book).

    Then what is the correct solution: assume you have a collection named Blogs, and want to implement a rating solution for your blog posts, and additionally keep track of every user-based rate operation.

    The schema for a blog document would be like:

    {
       _id : ....,
       title: ....,
       ....
       rateCount : 0,
       rateValue : 0,
       rateAverage: 0
    }
    

    You need another collection (Rates) with this document schema:

    {
        _id: ....,
        userId: ....,
        postId:....,
        value: ..., //1 to 5
        date:....   
    }
    

    And you need to define a proper index for it:

    db.Rates.ensureIndex({userId : 1, postId : 1})// very useful. it will result in a much faster search operation in case you want to check if a user has rated the post previously

    When a user wants to rate, firstly you need to check whether the user has rated the post or not. assume the user is 'user1', the query then would be

    var ratedBefore = db.Rates.find({userId : 'user1', postId : 'post1'}).count()
    

    And based on ratedBefore, if !ratedBefore then insert new rate-document to Rates collection and update blog status, otherwise, user is not allowed to rate

    if(!ratedBefore)
    {
        var postId = 'post1'; // this id sould be passed before by client driver
        var userId = 'user1'; // this id sould be passed before by client driver
        var rateValue = 1; // to 5
        var rate = 
        {       
           userId: userId,
           postId: postId,
           value: rateValue,
           date:new Date()  
        };
    
        db.Rates.insert(rate);
        db.Blog.update({"_id" : postId}, {$inc : {'rateCount' : 1, 'rateValue' : rateValue}});
    }
    

    Then what is gonna happen to rateAverage? I strongly recommend to calculate it based on rateCount and rateValue on client side, it is easy to update rateAverage with mongoquery, but you shouldn't do it. why? The simple answer is: this is a very easy job for client to handle these kind of works and putting average on every blog document needs an unnecessary update operation.

    the average query would be calculated as:

    var blog = db.Blog.findOne({"_id" : "post1"});
    var avg = blog.rateValue / blog.rateCount;
    print(avg);
    

    With this approach you will get maximum performance with mongodb an you have track of every rate based by user, post and date.

提交回复
热议问题