How can I make sure that I have roles column with client?

I was able to get the User document with this code

Users.findOne(userId)

also Users looks like this.

{
  "_id" : "123sdasdasd",
  "createdAt" : ISODate("2022-06-15T20:36:01.549+0000"),
  "archived" : false,
  "roles" : {
      "internal" : [
          "admin",
          "maneger"
      ],
      "client" : [
          "admin",
          "maneger",
          "normal-user",
      ],
      "other" : [
          "admin",
          "maneger",
          "normal-user",
          "manage"
      ],
  },
  "username" : "sample@gmail.com"
}

My goal here is to make sure if this user has a roles column with client.

I believe this is on the server side but part of my code looks like this.

Roles.removeUsers = (userId, groupName) => {

  console.log(Users.findOne(userId)); // got document successfully 
  console.log(Users.findOne(userId).roles[groupName]); // got undefined

  if (Users.findOne(userId).roles[groupName].length < 1) {
    sampledataUpdate.$pull.userIds = userId;
  }
};

The groupName could be internal, client, other or other strings.

Currently, I’m getting groupName as client.

How can I make sure if this user has client in roles column?

also another little question is if you look at the data structure above,

can I say roles as column?

_id, createdAt, archived are column?

and inside of column(roles for this case), what is the right word to describe internal, client or other? Would that be key?

Thank you!

In general you validate conditions with Mongo by using .find() instead of .findOne() because .findOne() also retrieves the data, which you don’t need.
.find() gives you a reference to data (a cursor) and the result of a .count() can be indeed 0 (false), or 1 or many (true).

// just for example, $exists tells you if a FIELD exists in a DOCUMENT (KEY in a JSON/OBJECT)

if (Users.find({ _id: userId, 'roles.client': { $exists: true }}).count()) {
  // .......
}
// However, you want more, you want to know if a role (does not) exists as a STRING within the "client" ARRAY.

// You can query for : do I have a user with this userId and role client is empty.

if (Users.find({ _id: userId, 'roles.client': [] }).count()) {
  // .......
}
But then, in fact, you might need a combination of the two conditions, no client role or the client role is empty.

if (Users.find({ _id: userId, $or: [{'roles.client': { $exists: false }}, { $and: [{ 'roles.client': { $exists: true }}, {'roles.client': [] }]  }]}).count()) {
  // .......
}
// In your case however, you could also use $size.

// Because you already query by the userId which is indexed by default in the Users collection, you can disregard the fact that the $size operator doesn't exploit indexes. So you come down to:

if (Users.find({ _id: userId, 'roles.client': { $size: 0 } }).count()) { // you might want to add from above and make sure 'roles.client' actually exists. If it doesn't exists,  'roles.client': { $size: 0 } is going to be false indeed although it doesn't have a size 0 of the array ... because it doesn't exist.
  // .......
}

I am not sure what this part is about: ‘sampledataUpdate.$pull.userIds = userId;’ If this is supposed to update something in Mongo, I think you have to write it such as

Collection.update({your_query}, {
     $pull: { userIds: userId }
})

1 Like

@paulishca

I was stuck for this for a whole and posted this on a couple of communities but had not got any answers yet and so I really appreciate your help!

I think for my case this might work perfectly

if (Users.find({ _id: userId, 'roles.client': { $exists: true }}).count()) {
  // .......
}

but I also need to make sure that if the roles has other name of the arraies like internal, other or something else.

Can I do something like this?

if (Users.find({ _id: userId, 'roles.[groupName]': { $exists: true }}).count()) {
  // .......
}

'roles.[groupName]'
groupName is passed as an argument.

It seems like I’m getting a right return but actually is this a right way to do it?

With Mongo you need to make sure you query correctly because otherwise you get wrong results which may at time look right.

if (Users.find({ _id: userId, [`roles.${groupName}`]: { $exists: true }}).count()) {
  // .......
}

Note that your query for { $exists: true } only tells you if the field exists or not, but if it exists, it doesn’t tell you if it is an empty array.

1 Like

Great point!
I have to check if I’m trying to see if client field on role needs to exist but it’s ok to be empty or it has to have at least some kind of values.

Thank you a lot @paulishca !

Just a quick note that find() and count() is the same as executing find() with fetch() and then counting the results.

So technically, you are just doing findOne() if you do find() and count() with a single document.

In the example above the point is to only determine if a document matching the query exists, the count is 1 (or 0).

As per the Mongo documentation about count():
“Returns the count of documents that would match a find() query for the collection or view.”

My understanding is that it returns a count and not the documents. So, there is no fetch, no transfer of the document/s from MongoDB to server.

Does Meteor behave any different?

Yes, you are correct.

What I was thinking is that the performance of count() is similar to the performance of find() and fetch(). I am no longer using count() because of this and moved to using countDocuments()

The query includes the _id which is unique and this makes the find().count() top efficient.