15

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.

4
  • Why don't you want to use aggregation? Commented Oct 31, 2015 at 7:34
  • 2
    Why do you think that you don't need aggregation? Commented Oct 31, 2015 at 11:12
  • For the unique index part of your question, see stackoverflow.com/questions/15921700/… Commented Oct 31, 2015 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 Commented Oct 31, 2015 at 17:08

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I will try this but I swear I tried this last night and it didnt work
But Aggregation return JSON objects instead of Model objects. So we can't do any operation in returned documents. Any other ways ?
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

Comments

3
postSchema.virtual('upvoteCount').get(function () {
    return this.upvotes.length
});

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

doc.upvoteCount // length of upvotes

Comments

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

2 Comments

I think ideally we would want to make mongo do the work and leave as much code out of Node.js as possible, but yes this might work
thanks - JohnnyHKs solution is by far the most superior one

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.