PRODUCTS

KEYWORDS

DumboDB: Now with More Mongo!

DumboDB Logo

DumboDB is a NoSQL database which aims to be a drop-in replacement for MongoDB Community Edition, with the addition of version control features you expect from Git. This means you can branch and merge for isolated agent workflows, see the history of any document, and push and pull your data to other servers.

Being pre-1.0 software, DumboDB has some gaps where queries that would run in MongoDB get a NotImplemented error. MongoDB has existed for 17 years, so there are a lot of ways to use it, and the query language is quite robust at this point. Getting full parity is a long tail problem.

One of the nice things about matching an existing database is that you can point a coding agent at a MongoDB server, called an Oracle, and tell the agent to make our product behave exactly the same way. There isn’t necessarily a lot of mystery about how to implement trigonometric functions, for example. DumboDB just needs to behave in the same way that MongoDB does. Simple, right?

It took more than a week with multiple coding agents to significantly close the gap on query operators, stages, and pipelines. It ended up being more than 15K lines of code, and 250 commits to get this single GitHub issue resolved. To give an idea of the expansion of functionality, we went from 3,010 tests to demonstrate parity against MongoDB to 11,359 tests now. It’s honestly too much new functionality to show it all with examples. We’ll summarize, then give a few examples of the more interesting ones. Let’s go!

What’s New!?!#

In release v0.8.3, DumboDB now supports:

  • 29 additional operators:
    • Dates: $dateFromParts $dateFromString $dateToParts $dateSubtract
    • Strings and Regular Expressions: $regexFindAll $replaceOne $replaceAll
    • Trigonometry: $sin $cos $tan $asin $acos $atan $atan2 $sinh $cosh $tanh $asinh $acosh $atanh $degreesToRadians $radiansToDegrees
    • Arrays: $sortArray
    • Conversion and types: $toObjectId $toUUID $binarySize
    • Misc: $rand $meta (partial)
    • Query: $sampleRate (in $match / find filters)
  • 1 new stage: $unionWith
  • Other improvements:
    • $lookup without from, using $documents as its source
    • let variables in $lookup and $unionWith
    • $lookup, $graphLookup and $unionWith inside $facet
    • Pipeline updates in findAndModify
    • maxTimeMS in count distinct delete update findAndModify
    • Expression errors reported at parse time, as MongoDB does

Examples#

$lookup without from#

In MongoDB, if you want to perform a query which pulls data from two collections, you use $lookup. This is similar to a JOIN in SQL. Previously DumboDB required you to use the from field to name a collection, but now you can embed documents directly in the query using $documents:

shop> db.shipments.insertMany([
  {_id: 1, sku: "mug", statusCode: 2},
  {_id: 2, sku: "kettle", statusCode: 1},
  {_id: 3, sku: "tea", statusCode: 3}])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }

shop> db.shipments.aggregate([
  {$lookup: {
    as: "status",
    let: {code: "$statusCode"},
    pipeline: [
      {$documents: [                 // Implemented!
        {code: 1, label: "pending"},
        {code: 2, label: "shipped"},
        {code: 3, label: "delivered"}
      ]},
      {$match: {$expr: {$eq: ["$code", "$$code"]}}}
    ]
  }},
  {$project: {sku: 1, status: {$first: "$status.label"}}},
  {$sort: {_id: 1}}
])
[
  {
    _id: 1,
    sku: 'mug',
    status: 'shipped'
  },
  {
    _id: 2,
    sku: 'kettle',
    status: 'pending'
  },
  {
    _id: 3,
    sku: 'tea',
    status: 'delivered'
  }
]

$unionWith#

$unionWith is another variation on a multi-collection query, this time similar to UNION ALL in SQL. Here we create a union of two collections, then use that with $group and $lookup to count comments and likes on each social media post:

shop> db.posts.insertMany([
  {_id: 1, title: "My favorite pancake recipe"},
  {_id: 2, title: "How to hang a hammock"},
  {_id: 3, title: "Ladder safety tips"}
])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }

shop> db.comments.insertMany([
  {_id: 1, postId: 1, user: "sam",  text: "Great post!"},
  {_id: 2, postId: 1, user: "alex", text: "Maple or honey?"},
  {_id: 3, postId: 2, user: "jo",   text: "Which knot do you use?"}
])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }

shop> db.likes.insertMany([
  {_id: 1, postId: 1, user: "jo"},
  {_id: 2, postId: 2, user: "alex"},
  {_id: 3, postId: 2, user: "sam"},
  {_id: 4, postId: 3, user: "sam"}
])
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4 } }

shop> db.comments.aggregate([
  {$project: {postId: 1, comments: {$literal: 1}, likes: {$literal: 0}}},
  {$unionWith: {                                    // Implemented!
    coll: "likes",
    pipeline: [{$project: {postId: 1, comments: {$literal: 0}, likes: {$literal: 1}}}]
  }},
  {$group: {_id: "$postId", comments: {$sum: "$comments"}, likes: {$sum: "$likes"}}},
  {$lookup: {from: "posts", localField: "_id", foreignField: "_id", as: "post"}},
  {$project: {_id: 0, title: {$first: "$post.title"}, comments: "$comments", likes: "$likes"}},
  {$sort: {title: 1}}
])
[
  { title: 'How to hang a hammock', comments: 1, likes: 2 },
  { title: 'Ladder safety tips', comments: 0, likes: 1 },
  { title: 'My favorite pancake recipe', comments: 2, likes: 1 }
]

Date Parsing and Manipulation#

Previously, DumboDB did not support the $dateFromString operator, and now it does. MongoDB supports a wide range of time formats, and building the test harness for all of them resulted in 246 new parity tests, and 1,456 across all of the date operators!

shop> db.aggregate([
  {$documents: [
    {ts: "31/10/2026 21:30", tz: "America/Los_Angeles"},
    {ts: "01/11/2026 09:00", tz: "Asia/Kolkata"}
  ]},
  {$project: {_id: 0, ts: 1, tz: 1,
    when: {$dateFromString:            // Implemented!
      {dateString: "$ts", format: "%d/%m/%Y %H:%M", timezone: "$tz" }   
  }}}
])
[
  {
    ts: '31/10/2026 21:30',
    tz: 'America/Los_Angeles',
    when: ISODate('2026-11-01T04:30:00.000Z')
  },
  {
    ts: '01/11/2026 09:00',
    tz: 'Asia/Kolkata',
    when: ISODate('2026-11-01T03:30:00.000Z')
  }
]

$dateSubtract and $dateAdd allow you to take a timestamp and calculate offsets to another timestamp. These functions have many edge cases because calendars are hard. For example, with leap years, one month before March 31 clamps to the end of February, which may be the 28th or 29th, depending on the year:

shop> db.aggregate([
  {$documents: [
    {d: ISODate("2026-03-31")}, 
    {d: ISODate("2024-03-31")}
  ]},
  {$project: {_id: 0, d: 1, 
    monthBefore: 
    {$dateSubtract:          // Implemented!
      {startDate: "$d", unit: "month", amount: 1}
  }}}
])
[
  {
    d: ISODate('2026-03-31T00:00:00.000Z'),
    monthBefore: ISODate('2026-02-28T00:00:00.000Z')
  },
  {
    d: ISODate('2024-03-31T00:00:00.000Z'),
    monthBefore: ISODate('2024-02-29T00:00:00.000Z')
  }
]

Another example, given that some of us just dropped out of daylight saving time this past weekend. Adding 24 hours is different than adding one day:

shop> db.aggregate([
  {$documents: [{d: ISODate("2026-11-01T06:30:00Z")}]},
  {$project: {_id: 0,
    plus24h:  {$dateAdd:          // Implemented Correctly!
      {startDate: "$d", unit: "hour", amount: 24, timezone: "America/Los_Angeles"}},
    plus1day: {$dateAdd: 
      {startDate: "$d", unit: "day", amount: 1, timezone: "America/Los_Angeles"}}
  }}
])
[
  {
    plus24h: ISODate('2026-11-02T06:30:00.000Z'),
    plus1day: ISODate('2026-11-02T07:30:00.000Z')
  }
]

$dateToParts and $dateFromParts are supported as well!

Regular Expressions#

Regular expressions can be used to search strings in DumboDB now. The $regexFindAll operator will find all matches of a pattern in a string.

shop> db.aggregate([
  {$documents: [{post: "Pancakes for breakfast #weekend #brunch #maplesyrup"}]},
  {$project: {_id: 0, tags: {
    $regexFindAll:                // Implemented!
    {input: "$post", regex: /#(\w+)/}}}}
])
[
  {
    tags: [
      {
        match: '#weekend',
        idx: 23,
        captures: [
          'weekend'
        ]
      },
      {
        match: '#brunch',
        idx: 32,
        captures: [
          'brunch'
        ]
      },
      {
        match: '#maplesyrup',
        idx: 40,
        captures: [
          'maplesyrup'
        ]
      }
    ]
  }
]

$replaceOne and $replaceAll are supported as well, for plain-string find and replace.

Trigonometric Functions#

Doing basic math is a requirement of many applications. DumboDB already supported basics like $sqrt, $pow, and $round. The trigonometric and hyperbolic operators were absent though. Here is a trigonometric example, using a ladder leaning against a wall:

shop> db.aggregate([
  {$documents: [{angle: 45}, {angle: 60}, {angle: 75}]},      // a 5m ladder leaning against a wall
  {$set: 
    {rad: {$degreesToRadians: "$angle"}} // Implemented!
  },
  {$project: {_id: 0, angle: 1,
    radians: {$round: ["$rad", 4]},
    reachesUpWall: {$round: [{$multiply: [5, {$sin: "$rad"}]}, 2]}, // Implemented!
    baseFromWall:  {$round: [{$multiply: [5, {$cos: "$rad"}]}, 2]}, // Implemented!
    steepness:     {$round: [{$tan: "$rad"}, 2]}                    // Implemented!
  }}
])
[
  {
    angle: 45,
    radians: 0.7854,
    reachesUpWall: 3.54,
    baseFromWall: 3.54,
    steepness: 1
  },
  {
    angle: 60,
    radians: 1.0472,
    reachesUpWall: 4.33,
    baseFromWall: 2.5,
    steepness: 1.73
  },
  {
    angle: 75,
    radians: 1.309,
    reachesUpWall: 4.83,
    baseFromWall: 1.29,
    steepness: 3.73
  }
]

What’s Still Missing#

We are still working on the long tail of query features in DumboDB to reach full parity with MongoDB.

$function allows you to embed JavaScript code into your query. This is a very powerful feature, but MongoDB’s own docs recommend using it only when nothing else will work, and it can be turned off entirely with --noscripting. The number of security concerns here is pretty high, so we are going to hold off on this until someone asks for it (#187).

{$meta: "textScore"} is a way to rank text matches with a relevance score. This allows for basic search with stemming and per-field weights. MongoDB Atlas can also use a Lucene-based Atlas Search index for fuzzy matching and richer search, and DumboDB doesn’t support that either. Text scoring is a pretty large feature, and it may take some time to get it to the top of the priority list (#185).

$group accumulators such as $topN and $percentile are currently unimplemented. We’ll fill this gap before too long now (#188).

A handful of aggregation stages, such as $densify and $fill, are also still missing (#186).

Join us on our Discord and tell us what features are most important for you!