27

I have a collection of items that can be upvoted or downvoted.

{"_id" : 1, "name": "foo", "upvotes" : 30, "downvotes" : 10}
{"_id" : 2, "name": "bar", "upvotes" : 20, "downvotes" : 0}
{"_id" : 3, "name": "baz", "upvotes" : 0,  "downvotes" : 0}

I'd like to use aggregation to calculate the quality

db.items.aggregate([
    {"$project":
        {
            "name": "$name",
            "upvotes": "$upvotes"
            "downvotes": "$downvotes",
            "quality": {"$divide":["$upvotes", "$downvotes"]}
        }
    },
    {"$sort": {"quality":-1}}
]);

Obviously this doesn't work, because of division by zero. I need to implement appropriate conditioning:

if upvotes != 0 and downvotes == 0 then the quality = upvotes if upvotes and downvotes are both 0 then the quality is 0

I tried tweaking downvotes by 1 using ternary idiom. But to no avail.

db.items.aggregate([
    {"$project":
        {
            "name": "$name",
            "upvotes": "$upvotes",
            "downvotes": "$downvotes" ? "$downvotes": 1
        }
    },
    {"$project":
        {
            "name": "$name",
            "upvotes": "$upvotes"
            "downvotes": "$downvotes",
            "quality": {"$divide":["$upvotes", "$downvotes"]}
        }
    },
    {"$sort": {"quality":-1}}
]);

How can I integrate this kind of conditioning within mongodb aggregation framework?

gwaramadze
  • 4,523
  • 5
  • 30
  • 42

3 Answers3

55

You might want to use the $cond operator to handle this:

db.items.aggregate([
    {"$project":
        {
            "name": "$name",
            "upvotes": "$upvotes",
            "downvotes": "$downvotes",
            "quality": { $cond: [ { $eq: [ "$downvotes", 0 ] }, "N/A", {"$divide":["$upvotes", "$downvotes"]} ] }
        }
    },
    {"$sort": {"quality":-1}}
]);
Anand Jayabalan
  • 12,294
  • 5
  • 41
  • 52
8

You want the $cond operator. http://docs.mongodb.org/manual/reference/operator/aggregation/cond/

Something like:

{"$project":
  {
    "name": "$name",
    "upvotes": "$upvotes",
    "downvotes": { $cond: [ { $eq: ["$downvotes", 0] }, 1, "$downvotes"] } 
  }
},
Will Shaver
  • 12,471
  • 5
  • 49
  • 64
4

we can use $cond operator to manage the infinite value

{
      $addFields: {
        averageRating: {
          $cond: [
            { $eq: ['$totalReviews', 0] },
            0,
            { $divide: ['$totalRatings', '$totalReviews'] }
          ]
        }
      }
    }
KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133