shalvi muqta
shalvi muqta

Reputation: 193

Mongoose count doucments greater and less than specific value

I have a model of the following schema:

const mongoose = require('mongoose');

const logSchema = new mongoose.Schema({
    probability: {
        type: Number,
        required: false,
    },
    timestamp: {
        type: Date,
        required: true,
    }
}, {
    timestamps: true
});

const Log = mongoose.model('Log', logSchema);

module.exports = Log;

I want to split all documents to 2 groups: those whose probability value is less than 0.001 and those whose value is greater than 0.001. Also, in each group, I want to count for each probabilty value - how many documents has the same value.

So basically if I had the following probabilities data: [0.00001, 0.000003, 0.000025, 0.000003, 0.9, 0.6, 0.6], I'd like to get as a result: { less: { 0.00001: 1, 0.000003: 2, 0.000025:1 }, greater: { 0.9: 1, 0.6: 2 }.

This is my current aggregate method:

const livenessProbilitiesData = await Log.aggregate([
    {
        $match: {
            timestamp: {
                $gte: moment(new Date(startDate)).tz('Asia/Jerusalem').startOf('day').toDate(),
                $lte: moment(new Date(endDate)).tz('Asia/Jerusalem').endOf('day').toDate(),
            }
        }
    },
    {
        $group: {
        }
    }
]);

Note that I use undeclared variables startDate, endDate. These are input I get to filter out irrelevant documents (by timestamp).

Upvotes: 0

Views: 60

Answers (2)

shalvi muqta
shalvi muqta

Reputation: 193

Solved it by myself:

const livenessProbilitiesData = await LivenessLog.aggregate([
    {
        $match: {
            eventName: 'probability',
            timestamp: {
                $gte: moment(new Date(startDate)).tz('Asia/Jerusalem').startOf('day').toDate(),
                $lte: moment(new Date(endDate)).tz('Asia/Jerusalem').endOf('day').toDate(),
            }
        },
    },
    {
        $group: {
            _id: { $trunc: [ '$probability', 4 ] },
            count: { $sum: 1 },
        },
    },
    {
        $sort: { _id: 1 },
    },
    {
        $project: {
            _id: 0,
            less: { $lt: ['$_id', 0.001] },
            x: '$_id',
            y: '$count',
        },
    },
    {
        $group: {
            _id: '$less',
            probabilities: { $push: { x: '$x', y: '$y' } },
        },
    },
    {
        $project: {
            _id: 0,
            probabilities: 1,
        },
    }
]);

if (livenessProbilitiesData.length === 0) {
    return {
        less: [],
        greater: [],
    }
}

return {
    less: livenessProbilitiesData[0].probabilities,
    greater: livenessProbilitiesData[1].probabilities,
};

Upvotes: 1

J.F.
J.F.

Reputation: 15207

This is what I've tried:

The aggregate has 4 stages:

  1. match: To filter by date (same what you have).
  2. group: To agroup every probability and it count. That is how many times appears every probability'. Here, the result is something like: [ {prob: 0.6, count:2}, {prob:0.9, count:1}, ... ].
  3. group to insert in an array 'less' or 'greater'. Here I've used $cond. So the result here is: {greater:[{prob:0.9, count:1}, ...], less:[{prob:0.00001, count:1}]}
  4. $project to remove _id field from result.
db.collection.aggregate([
  {
    "$match": {
      "timestamp": {
        "$gte": ISODate("2012-10-15T21:26:17Z"),
        "$lte": ISODate("2012-10-15T21:26:17Z")
      }
    }
  },
  {
    "$group": {
      "_id": "$probability",
      "count": {
        "$sum": 1
      }
    }
  },
  {
    "$group": {
      "_id": null,
      "less": {
        "$push": {
          "$cond": {
            "if": {
              "$lte": [
                "$_id",
                0.001
              ]
            },
            "then": {
              "probability": "$_id",
              "count": "$count"
            },
            "else": "$$REMOVE"
          }
        }
      },
      "greater": {
        "$push": {
          "$cond": {
            "if": {
              "$gte": [
                "$_id",
                0.001
              ]
            },
            "then": {
              "probability": "$_id",
              "count": "$count"
            },
            "else": "$$REMOVE"
          }
        }
      }
    }
  },
  {
    "$project": {
      "_id": 0
    }
  }
])

It has not the output exactly you want. Instead of

{
  less: {
    probability: count
  }
  ...
}

Is like:

{
  less:{
    probability: X.XXX,
    count: X
  }
}

This is a mongo playground example

Upvotes: 0

Related Questions