An embedded document database for .NET 10, built from scratch with zero runtime dependencies.
Naming note: this project reuses the name LiteDB, which clashes with a well-known open-source database of the same name. It is unrelated and not published to NuGet under the
LiteDBid (the package id is a placeholder). Do not publish it asLiteDBwithout renaming.
- SQLite-like deployment: a class library over a single data file (plus a
-walsibling), or a pure in-memory database. Never a server or a separate process. - MongoDB-like data model: schemaless BSON documents in named collections, queried with Mongo-style JSON filter strings.
- ACID storage: a page-based single file with a write-ahead log (WAL), crash recovery, and data that can exceed RAM. The in-memory database runs the identical WAL/commit/checkpoint code path.
- Secondary indexes: B+tree indexes used automatically by a small query planner.
using LiteDB;
using LiteDB.Bson;
using var db = new LiteDatabase(@"C:\data\app.db"); // or LiteDatabase.CreateInMemory()
LiteCollection users = db.GetCollection("users"); // created lazily on first write
BsonValue anaId = users.InsertOne("{ Name: 'Ana', Age: 34, Address: { City: 'Porto' }, Tags: ['admin','dev'] }");
users.InsertMany(new[] { "{ Name: 'Bruno', Age: 28 }", "{ Name: 'Carla', Age: 41 }" }); // atomic batch
users.EnsureIndex("Age");
users.EnsureIndex("Email", unique: true);
IReadOnlyList<BsonDocument> adults =
users.Find("{ Age: { $gt: 30 } }", new FindOptions { Sort = "{ Age: -1 }", Skip = 0, Limit = 10 });
BsonDocument? ana = users.FindOne("{ 'Address.City': 'Porto', Tags: 'admin' }");
BsonDocument? doc = users.FindById(anaId);
long juniors = users.Count("{ Age: { $lt: 30 } }");
string plan = users.Explain("{ Age: { $gt: 30 } }"); // "IXSCAN(Age asc, (30, +inf)"
users.ReplaceById(anaId, new BsonDocument { ["Name"] = "Ana", ["Age"] = 35 });
users.UpdateMany("{ Age: { $lt: 30 } }", "{ $set: { Level: 'junior' }, $inc: { Rev: 1 } }");
users.UpdateMany("{ Email: 'x@y.z' }", "{ $set: { Seen: true } }", new UpdateOptions { Upsert = true });
users.DeleteOne("{ Name: 'Bruno' }");
long removed = users.DeleteMany("{ Level: 'junior' }");
users.DropIndex("Age");
db.DropCollection("audit");
IReadOnlyList<string> names = db.ListCollections();
db.Checkpoint(); // manual WAL checkpoint (also on Dispose)Filters and updates are relaxed JSON strings (unquoted keys with dots, single quotes, trailing
commas) with MongoDB extended-JSON literals ({"$oid":…}, {"$date":…}, {"$numberDecimal":…}, …).
- Operators: implicit/explicit
$eq,$ne,$gt/$gte/$lt/$lte,$in/$nin,$exists, and$and/$or/$not/$nor. - Paths: dotted paths, numeric array indexing, and MongoDB array any-element matching
(
{ Tags: 'admin' }matches an element;{ Tags: ['admin','dev'] }matches the whole array). - Updates:
$set(creates intermediate documents on dotted paths),$inc(numeric type promotion),$unset.
Commits append full-page images to the WAL and fsync (with DurabilityMode.Full, the default);
DurabilityMode.Lazy skips fsync for tests and bulk load. A checkpoint folds the WAL into the main
file and is idempotent, so a crash mid-checkpoint replays cleanly on the next open. The WAL is
salt-seeded and CRC-checked, so torn or stale frames are never replayed.
- Document ≤ 16 MiB; index key ≤ 512 B (throws); path depth ≤ 32; page size 8192 (fixed).
DateTimehas millisecond precision; string comparison is ordinal only.- A unique index is non-sparse: documents missing the field all index as
null, so at most one may omit it (matching a non-sparse MongoDB unique index).
POCO mapping (use BsonDocument + JSON strings), LINQ, async, encryption, multi-process sharing
(FileShare.None, fail fast), aggregation pipeline, $regex/$elemMatch/$size/$type, compound
and multi-field-sort indexes, sparse indexes, a public transaction API, and vacuum/Rebuild().
See PLAN.md for the full scope.
dotnet build -c Release
dotnet test -c Release
dotnet run --project samples/LiteDB.Sample
Requires the .NET 10 SDK (pinned in global.json).