14

I have this Mongoose schema:

var postSchema = mongoose.Schema({

    postId: {
        type: Number,
        unique: true
    },

    upvotes: [
        {
            type: Number,
            unique: true
        }
    ]

});

what the best query to use to get the length of the upvotes array? I don't believe I need to use aggregation because I only want to query for one model, just need the length of the upvotes array for a given model.

Really struggling to find this info online - everything I search for mentions the aggregation methodology which I don't believe I need.

Also, as a side note, the unique schema property of the upvotes array doesn't work, perhaps I am doing that wrong.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • Why don't you want to use aggregation? – michelem Oct 31 '15 at 07:34
  • 2
    [Why do you think that you don't need aggregation?](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – styvane Oct 31 '15 at 11:12
  • For the unique index part of your question, see http://stackoverflow.com/questions/15921700/mongoose-unique-values-in-nested-array-of-objects – JohnnyHK Oct 31 '15 at 14:32
  • thanks @johnnyHK, $addToSet is working for me - the only problem I have with it is that it doesnt throw an error when you try to add a duplicate value, it merely returns "not modified" - seems more consistent to return an error but idk – Alexander Mills Oct 31 '15 at 17:08

4 Answers4

20

find results can only include content from the docs themselves1, while aggregate can project new values that are derived from the doc's content (like an array's length). That's why you need to use aggregate for this, even though you're getting just a single doc.

Post.aggregate([{$match: {postId: 5}}, {$project: {upvotes: {$size: '$upvotes'}}}])

1Single exception is the $meta projection operator to project a $text query result's score.

JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
8

I'm not normally a fan of caching values, but it might be an option (and after finding this stackoverflow answer is what I'm going to do for my use case) to calculate the length of the field when the record is updated in the pre('validate') hook. For example:

var schema = new mongoose.Schema({
    name: String,
    upvoteCount: Number,
    upvotes: [{}]
});

schema.pre('validate', function (next) {
  this.upvoteCount = this.upvotes.length
  next();
});

Just note that you need to do your updates the mongoose way by loading the object using find and then saving changes using object.save() - don't use findOneAndUpdate

Ryan Knell
  • 6,204
  • 2
  • 40
  • 32
3
postSchema.virtual('upvoteCount').get(function () {
    return this.upvotes.length
});

let doc = await Post.findById('foobar123')

doc.upvoteCount // length of upvotes

ram
  • 680
  • 5
  • 15
2

My suggestion would be to pull the entire upvotes fields data and use .length property of returned array in node.js code

//logic only, not a functional code
post.find( filterexpression, {upvote: 1}, function(err, res){
  console.log(res.upvotes.length); 
});

EDIT:

Other way of doing would be stored Javascript. You can query the upvote and count the same in mongodb side stored Javascript using .length

prasun
  • 7,073
  • 9
  • 41
  • 59