1

I need to find elements in a schema and after count the total messages sent by them. I have tryed this code above but does not work for me.

User.find({type: 'psycologist'}).exec(function (err, users) {
    if(err){
        callback(new Error("Something went wrong"), null);
    }else{

        Message.count({userFrom: { "$in" : users} }).exec(function (err, counter) {
            if(err){
                callback(new Error("Something went wrong."), null);
            }else{
                console.log(counter); //displays 0
                callback(null, counter);
            }
        });
    }
});

Message Schema

var schema = new Schema({
    content: {type: String, required: true},
    type: {type: String, required: true, default: 'text'},
    status: {type: String, default: 'not_read'},
    created_at: {type: Date, default: Date.now},
    userFrom: {type: Schema.Types.ObjectId, ref: 'User', required: true},
    userTo: {type: Schema.Types.ObjectId, ref: 'User', required: true}
});

User Schema

var schema = new Schema({
    name: {type: String, required: true},
    email: {type: String, required: true, unique: true},
    phone: {type: String, required: true},
    password: {type: String, required: true, select: false},
    type: {type: String, required: true},
    created_at: {type: Date, required: true, default: Date.now}
});

Someone know how to fix it or a better way to do? Thanks in advance.

If there is any expert mongo here can tell me in more details about the performance of this query for large volumes of data.

2
  • Can you add the schemas and what is the error so we can better see the real issue? Commented Oct 16, 2016 at 21:15
  • @andresk sure. I updated the description Commented Oct 16, 2016 at 21:36

3 Answers 3

1

The problem is that the result of your first find is a array of users, it are the complete documents. you can only use an array of values for the $in find operator.

change it to:

User.find({type: 'psycologist'}).exec(function (err, users) {
    if(err){
        callback(new Error("Something went wrong"), null);
    }else{
        var MyUserIdArray = users.map(function(x) { return x._id} );
        Message.count({userFrom: { "$in" : MyUserIdArray} }).exec(function (err, counter) {
            if(err){
                callback(new Error("Something went wrong."), null);
            }else{
                console.log(counter); //displays 0
                callback(null, counter);
            }
        });
    }
});
Sign up to request clarification or add additional context in comments.

Comments

1

A much better way would involve using the aggregation framework to run a pipeline that uses the $lookup operator to do a "left join" on the messages collection from the users collection.

Consider running the following aggregation pipeline:

Users.aggregate([
    { "$match": { "type": "psycologist"} },
    {
        "$lookup": {
            "from": "messages",
            "localField": "_id",
            "foreignField": "userFrom",
            "as": "messagesFrom"
        }
    },  
    { 
        "$project": {
            "messages": { "$size": "$messagesFrom" }            
        }
    },
    {
        "$group": {
            "_id": null,
            "counter": { "$sum": "$messages" }
        }
    }
]).exec(function(err, result) {
    if(err){
        callback(new Error("Something went wrong."), null);
    }else{
        console.log(JSON.stringify(result, null, 4)); 
        callback(null, result[0].counter);
    }
});

In the above aggregation operation, the first step is the $match operator which filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage and can take as its parameters MongoDB query syntax. This is similar to MongoDB Collection's find() method and SQL's WHERE clause and the filter { "$match": { "type": "psycologist"} } is just the same as

User.find({type: 'psycologist'})

In the next pipeline, the filtered documents are then passed to the $lookup operator which performs a left outer join to the messages collection in the same database to filter in documents from the "joined" collection for processing. The "as" option is the name of the new array field to add to the input documents. The new array field contains the matching documents from the "from" collection.

You would then need to get the length of this array as it indicates the number of messages per user. This is made possible in the next pipeline using the $project operator which reshapes each document in the stream, include, exclude or rename fields, inject computed fields, create sub-document fields, using mathematical expressions, dates, strings and/or logical (comparison, boolean, control) expressions.

In this case you use the $size operator which returns the number of elements in the array thus you get the number of messages per user.

The final pipeline step is the $group which summarize all the documents. This groups input documents by a specified identifier expression and applies the accumulator expression(s). But in this instance, you specify an _id value of null to calculate accumulated values for all the input documents as a whole. You aggregate all the messages count using the $sum accumulator operator applied on the messages field (computed from the previous pipeline) thus you get the total number of messages from for a particular user type.

Comments

0

Try

User.find({type: 'psycologist'}, {_id: 1}).exec(function (err, users) {
    if(err){
        callback(new Error("Something went wrong"), null);
    }else{
        var ids = users.map(function(user){
           return user._id;
        })
        Message.count({userFrom: { "$in" : ids } }).exec(function (err, counter) {
            if(err){
                callback(new Error("Something went wrong."), null);
            }else{
                console.log(counter); //displays 0
                callback(null, counter);
            }
        });
    }
});

Comments

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.