1

In my app I have a schema for comments:

var CommentsSchema = new Schema({
    user_id: {type: String, required: true, ref: 'users'},
    text_content: {type: String},
    is_anonymous: {type: Boolean, default: false}
});

and now I'm constructing a mongoose query to download all comments and display it to the users.

Since I don't want to download posts from users that are blocked by the end user, I introduced a possibility of excluding posts from blocked authors:

var blockedUsers = req.body.blockedUsers;

function withBlockedUsers(query, blockedUsers) {
    if(blockedUsers != undefined){
        query.$and.push({ 'user_id' : { $nin: blockedUsers } });
    }

    return query;
}

var query = {};
query.$and = [];

query = withBlockedUsers(query, blockedUsers)
...
query = Comment.find(query);

query.exec(function(err, comments){
        if(err) {
            callback(err);
            return;
        }
    return callback(null, comments);
    });

That code works, when I call my endpoint I need to send there a string array of blocked user ids and their posts will be excluded.

Now I'm changing my functionality and instead of passing string array of blocked users I'm passing array of objects:

{ 
 user_id: '586af425378c19fc044aa85f'
 is_anonymous: '0' 
},

I don't want to download posts from those users when those two conditions are met.

So for example when I have two posts in my app:

{
    user_id: '586af425378c19fc044aa85f',
    text_content: 'text1',
    is_anonymous: true
},
{
    user_id: '586af425378c19fc044aa85f', //same as above
    text_content: 'text2',
    is_anonymous: false
}

and I pass blockedUsers object:

{ 
 user_id: '586af425378c19fc044aa85f'
 is_anonymous: '0' 
},

as a return I need to display only:

{
    user_id: '586af425378c19fc044aa85f',
    text_content: 'text1',
    is_anonymous: true
},

The other post should be blocked because user_id is recognized and is_anonymous is false.

With my current code I'm getting error:

message: 'Cast to string failed for value "[object Object]" at path "user_id"', name: 'CastError', kind: 'string', value: { user_id: '586af425378c19fc044aa85f', is_anonymous: '0' }, path: 'user_id', reason: undefined } Cast to string failed for value "[object Object]" at path "user_id"

1 Answer 1

1

You would need a way to construct a query that uses the $nor operator which performs a logical NOR operation on an array of one or more query expression and the documents that fail all the query expressions in the array are returned.

For example, when you pass an array of objects like

var blockedUsers = [
    {
        user_id: '586af425378c19fc044aa85f'
        is_anonymous: '0' 
    },
    {
        user_id: '585808969e39db5196444c06'
        is_anonymous: '0' 
    },
    {
        user_id: '585808969e39db5196444c07'
        is_anonymous: '0' 
    }
]

your query should end up fundamentally as

Comment.find({
    "$nor": [
        {
            user_id: '586af425378c19fc044aa85f'
            is_anonymous: false 
        },
        {
            user_id: '585808969e39db5196444c06'
            is_anonymous: false
        },
        {
            user_id: '585808969e39db5196444c07'
            is_anonymous: false 
        }
    ]
}).exec(function(err, comments){
    if(err) {
        callback(err);
        return;
    }
    return callback(null, comments);
});

So the task is to convert something like

var blockedUsers = [
    {
        user_id: '586af425378c19fc044aa85f'
        is_anonymous: '0' 
    },
    {
        user_id: '585808969e39db5196444c06'
        is_anonymous: '0' 
    },
    {
        user_id: '585808969e39db5196444c07'
        is_anonymous: '0' 
    }
]

to

var query = {
    "$nor": [
        {
            user_id: '586af425378c19fc044aa85f'
            is_anonymous: false 
        },
        {
            user_id: '585808969e39db5196444c06'
            is_anonymous: false
        },
        {
            user_id: '585808969e39db5196444c07'
            is_anonymous: false 
        }
    ]
}

Since you are passing in an array of objects, you can use the map() method to cast is_anonymous values to Boolean so your refactored function should look like:

function withBlockedUsers(query, blockedUsers) {
    var norOperator = [];
    if(blockedUsers != undefined){
        norOperator = blockedUsers.map(function(user){
            return {
                "user_id": user.user_id,
                "is_anonymous": (user.is_anonymous === '1')
            }
        });
        query["$nor"] = norOperator;
    }
    return query;
}
Sign up to request clarification or add additional context in comments.

3 Comments

@chrisdam thank you for very good explanation! I just have one more question here - I see that you are comparing user.is_anonymous === '1', but in my case I can send the flag with either true or false. By that I mean that user { "user: '1234', "is_anonymous": true} should be treated as a different user than { "user: '1234', "is_anonymous": false}. I think your code currently only checks whether the flag is equals to true, am I right?
No, you said in your question Now I'm changing my functionality and instead of passing string array of blocked users I'm passing array of objects: { user_id: '586af425378c19fc044aa85f' is_anonymous: '0' }, The is_anonymous flag is a string and the part user.is_anonymous === '1' converts that to Boolean. Now you are saying you CAN send the flag with either true or false (not a string), can you please update your question accordingly (rather confusing)? Also, if you are sending the data as Boolean then you can change the part "is_anonymous": (user.is_anonymous === '1')
to just "is_anonymous": user.is_anonymous) and the query should work. The code doesn't check for the is_anonymous field only but for BOTH the user_id AND is_anonymous fields because the AND logic is implicitly declared when you specify comma separated values in an expression, no need to include the $and operator.

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.