BlogNode.js

How to compare MongoDB ObjectId() equality in Node.js

Written by Codemzy on July 21st, 2023

When you compare `ObjectId()`'s with the strict equality operator `===`, you might be surprised to get false, even when they match. It's caught me out a few times, so let's see how we can compare the MongoDB `ObjectId()`.

If you use MongoDB to handle your database requirements, you need to get to know the ObjectId(). Every document in your database has one!

And sometimes, you might want to reference the ObjectId() in another document.

For example, say you have a posts collection, with all your blog posts in it. And then you have a comments collection, where each comment will reference which blog post it belongs to.

Or you have a products collection, and an orders collection, where each order will reference which products are being purchased.

I can think of a million other examples where you will reference the ObjectId() of other documents. If you are using MongoDB, I'm sure you have examples in your own database.

So from time to time in your code, you might want to check if the ObjectId() referenced in one document, matches the ObjectId() in another.

Maybe you will use the strict equality operator ===?

But that's where you can run into problems. Because ObjectId("507c7f79bcf86cd7994f6c0e") === ObjectId("507c7f79bcf86cd7994f6c0e") will always be false. Even if the ObjectId() values are the same.

And that's caught me out more times than I care to admit!

Imagine you have a bunch of users in your database:

const { ObjectId } = require('mongodb');

const user1 = {
  _id: new ObjectId(),
  name: "codemzy",
}

And a team:

const team = {
  manager: new ObjectId(user1._id)
}

Each team has one team manager. And you want to check if the user is the team manager, so you write some code like:

isTeamManager = user._id === team.manager;

But even if the user is the team manager, we get... false. Huh?

console.log(user1._id); // new ObjectId("64b9523b35795e90949872a1")
console.log(team.manager); // new ObjectId("64b9523b35795e90949872a1")
console.log(user1._id === team.manager); // false

It looks right, but we are forgetting a golden rule of JavaScript. Equality doesn't work with objects. Well, it does, but not in the same way as strings or numbers. And our ObjectId() - that's an object!

console.log(typeof user1._id); // object
console.log(typeof team.manager); // object

And even if when objects theoretically match, they are not equal.

console.log({} === {});
// false

So unless our team.manager and user_id reference the exact same ObjectId() (not just the value, the object), they won't match. And since the ObjectId() is stored in different documents in a database, goes to and from the client, and moves around our code - that's unlikely. The value might match - but the object does not.

Ok, that’s why equality checks don’t work on ObjectId(). So what can we use instead?

ObjectId.equals()

My favourite way of comparing ObjectId()'s is by using the .equals() method.

console.log(team.manager.equals(user1._id)); // true

Yep, if those ObjectId() values match, .equals gives us our answer. And .equals is an inbuilt method on our ObjectId() objects - so we can use it on any ObjectId()!

I tend to use this method the most.

ObjectId.toString()

We can’t use equality checks on objects, but we can use equality checks on strings. And you know what else we can do with our ObjectId()'s? Turn them into strings, using the .toString() method.

So let’s turn our ObjectId()'s into strings, and then compare them to see if they match!

console.log(team.manager.toString() === user1._id.toString()); // true

This works because we are just comparing the value as a string, instead of the object.

What I like about this method is it works even if we have stored one (or both) of the ObjectId()'s as a string.

For example, let's say the user id is sent back from the browser, it's not an ObjectId() anymore, it's a string. If we try to compare that using the .equals() method, it's not going to work, because strings don't have an .equals() method. You will get an error.

.equals is not a function

But running .toString() on a string works, so if you are unsure if one of the variables might be a string version of the ObjectId(), then use the toString() method or convert them both to ObjectId()'s before using .equals()`.

let managerId = new ObjectId(team.manager);
let userId = new ObjectId(user1._id);

console.log(managerId.equals(userId)); // true

So there we have it. When === doesn't work, we have two new ways to compare equality in our ObjectId()'s.

console.log(team.manager === user1._id); // false 😬
console.log(team.manager.equals(user1._id)); // true 🥳
console.log(team.manager.toString() === user1._id.toString()); // true 🥳