
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/findfilters)
- Dates:
- 1 new stage:
$unionWith - Other improvements:
$lookupwithoutfrom, using$documentsas its sourceletvariables in$lookupand$unionWith$lookup,$graphLookupand$unionWithinside$facet- Pipeline updates in
findAndModify maxTimeMSincountdistinctdeleteupdatefindAndModify- 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!