George Kosmidis

Microsoft MVP | Speaks of Azure, AI & .NET | Founder of Munich .NET
Building tomorrow @
slalom
slalom

MongoDB Shell or Compass query with a GUID

by George Kosmidis / Published 3 years and 1 month ago, modified 3 years ago
MongoDB Shell or Compass query with a GUID

Just a small one for today. Have you ever tried to query MongoDB with a GUID? Well I did and it can be confusing!

Problem

Some time ago I created a project for storing product prices but for multiple markets around the world. Markets would be a dynamic thing, meaning different ones being added every once and a while, and old ones potentially removed. Since the data to be stored were fairly simple, I was lead to a design featuring one documentDB per market. The document-oriented database selected was MongoDB, but I always had in mind a future move to Azure Cosmos DB, so avoiding vendor lock was a requirement. That thought alone and the fact that a unique ID through out all databases for each price would be a nice to have, made me decide for a GUID instead of MongoBD’s ObjectId().

ObjectIds are small, likely unique, fast to generate, and ordered. ObjectId values are 12 bytes in length, consisting of:

  • 4-byte timestamp value, representing the ObjectId’s creation, measured in seconds since the Unix epoch
  • 5-byte random value generated once per process. This random value is unique to the machine and process.
  • 3-byte incrementing counter, initialized to a random value

The schema of the document is not very important other than the fact that the ID should be of type GUID:

    public class PriceSet
    {
        [BsonId]
        public Guid Id { get; init set; }
        public decimal Price { get; init set; }
        //...
    }

Nevertheless, the schema above gave room for some beautiful data like the following image, with some databases and collections ending up having millions of data located in an availability zone probably near you!

MongoDB Compass GUID
MongoDB Compass GUID

The capture above is showing a part of MongoDB compass connected to a beta database with partially real but old data, located locally. Far from the previous useless sentence though, mind the blue square because this is how MongoDB stores a GUID. The value you see is actually a base64 encoded string of the GUID value.

And everything was cool for years, up until I had to manually debug a situation where I had to connect directly to MongoDB to search for a document based on a GUID I knew. No matter how many times I attempted to convert the GUID using various online services to its base64 equivalent, I always failed because these services were converting the GUID to base64 as string!

And there’s your problem! Encoding a GUID’s bytes is different than encoding GUID’s representative string value.

Worth mentioning here that we were working on this problem with Dean Herringer and he was the first to notice the problem.

Solution

Follows a C# solution which is simple enough, but of course requires to at least copy-paste the code to a project and run it:

var base64Encoded = Convert.ToBase64String("YOUR_GUID_HERE");

And here is a way to decode a base64 string that contains GUID bytes:

var bytes = new Span<byte>(new byte[256]); //arbitrary number

if (Convert.TryFromBase64String("YOUR_BASE64_STRING_HERE", bytes, out int bytesWritten))
{
  var guidDecoded = new Guid(bytes.Slice(0, bytesWritten).ToArray()).ToString();
}

Finally, if you just need it now without writing code use a Blazor WebAssembly app I made just for this: https://base64guid.azurewebsites.net/. It’s open source and the code is available in Github.

And if it is a bit slow on startup, be patient! Dev/Test Service Plan doesn’t allow Always On!

This page is open source. Noticed a typo? Or something unclear?
Edit Page Create Issue Discuss
Microsoft MVP - George Kosmidis
Azure Architecture Icons - SVGs, PNGs and draw.io libraries