{"id":21024874,"url":"https://github.com/rzubek/easy-db","last_synced_at":"2026-05-09T02:31:25.100Z","repository":{"id":68549701,"uuid":"432804040","full_name":"rzubek/easy-db","owner":"rzubek","description":"Easy, simple key-value store. Wraps standard .Net filesystem storage with an API similar to NoSQL document databases. Single C# file, no extra dependencies.","archived":false,"fork":false,"pushed_at":"2021-11-28T20:38:17.000Z","size":19,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-05T08:37:46.539Z","etag":null,"topics":["csharp","csharp-library","databases","db","dotnet","dotnet-core","dotnet-framework","keyvalue","keyvaluestore","nosql","nosql-database"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rzubek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-28T19:22:03.000Z","updated_at":"2024-12-15T11:22:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"c218271c-c4db-48b2-b72d-2f5876147a99","html_url":"https://github.com/rzubek/easy-db","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rzubek/easy-db","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzubek%2Feasy-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzubek%2Feasy-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzubek%2Feasy-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzubek%2Feasy-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rzubek","download_url":"https://codeload.github.com/rzubek/easy-db/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rzubek%2Feasy-db/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32804882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","csharp-library","databases","db","dotnet","dotnet-core","dotnet-framework","keyvalue","keyvaluestore","nosql","nosql-database"],"created_at":"2024-11-19T11:29:03.757Z","updated_at":"2026-05-09T02:31:25.062Z","avatar_url":"https://github.com/rzubek.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EasyDB\n\n**EasyDB is a tiny document store for C# applications. The API looks like a key/value database, \nwhile the implementation is a thin wrapper which saves each document as a \nseparate plain file on the local filesystem, so they can be easily\nfound, inspected, modified, backed up, etc.**\n \n\nFeatures:\n  - Basic key-value store, where each document is stored in a separate file on the filesystem\n    and retrieved by its key. Documents can be binary, or unicode text (e.g. JSON).\n  - Key structure supports key hierarchies, e.g. not just `user123` but also hierarchical keys \n    like `users/uid123` or `documents/2021/01/id123`.\n  - Supports common key-value operations: `contains`, `get`, `set`, `check and set`, `remove`.\n  - Supports key and value enumerations: `enumerate`, `enumerate documents`, starting from root\n    or specific part of the hierarchy (e.g. `documents/2021/*`).\n  - Single-file C# library, designed for embedding, requires no external libraries, \n    and does not spawn any background processes or services.\n  \nThe database is ideal for fast prototyping, tools, small websites, and other basic persistence \nthat doesn't demand a full-blown relational database.\n\n\n## Implementation\n\nEasyDB is a document-oriented storage, where each key maps to a single document (text or binary),\nwhich in turn is stored in a separate file on the filesystem. \n\nClient code can retrieve and update individual documents by key name, and\nthis is reflected directly by reading and writing to corresponding files. \n\nSome examples: \n\n```\nvar db = new EasyDB(new EasyDBSettings(\"c:\\\\storage\", \".data\"));\n\n// creates or updates a file containing the given string\n// this will be stored as \"c:\\\\storage\\\\mykey.data\"\ndb.Set(\"mykey\", \"some text value\");\n\n// creates or updates a file containing the given byte array\n// the hierarchical key means it will be stored as \"c:\\\\storage\\\\users\\\\uid001.data\"\ndb.Set(\"users/uid001\", new byte[] { ... });\n\n// reads the contents of file \"mykey.data\" as a document (can be recast to binary or text)\nvar result = db.Get(\"mykey\");\nbyte[] bytes = result.value.Bytes;\nstring text = result.value.AsString;\n\n// updates mykey.data only if it hasn't changed since last read\ndb.CheckAndSet(\"mykey\", newdocument, result.etag); \n \n// checks for existence of file \"users/uid001.data\" and deletes it (and its directory if empty)\nbool exists = db.Contains(\"users/uid001\");\nif (exists) { db.Remove(\"users/uid001\"); }\n\n// enumerates keys of all documents in \"users\" and subdirectories\nforeach (var key in db.Enumerate(\"users\", true)) { ... }\n\n// enumerates all documents in the entire database, and returns their full contents\nforeach (var result in db.EnumerateValues(\"\", true)) { ... }\n```\n\n\n### API\n\nStandard key-value operations are supported:\n  - Check if a key exists in the database\n  - Get the document for a given key\n  - Set the document for a given key (i.e. create or overwrite file if already exists)\n  - Check and set a key (i.e. update only if entry matches expected etag value)\n  - Remove entries\n  - Enumerate keys or documents at a given path (optionally including subpaths)\n  \nThis wrapper does not perform any caching, it relies on OS filesystem caching for performance.\n\n\n### Design goals\n\n  - Simplicity - Behavior needs to be understandable on first reading\n  - Inspectability - User can inspect every stored document as a separate file on the hard drive\n  - Minimal API - Simple C# API for standard key-value store commands\n  - Multi-threading - Safe to use in multi-threaded environments\n\n\n## Key space\n\nThe key/value store is keyed by strings, and stores arbitrary data blobs as values. \n\nThe key space supports key hierarchies, with slash character `/` as separator, \nso that the user can enumerate all keys at any point in the hierarchy, for example\n`users/*` or `users/europe/*` and so on.\n\nKeys can be arbitrary UTF strings, and they will be URL-encoded as needed\nto meet the requirements of the target filesystem.\n\n\n## Storage implementation details\n\nEach entry in the database corresponds to a single file on the filesystem.\n\nKeys and hierarchy segments are implemented as paths, so that users can easily find\ndata files locally. A simple ASCII key name like `foo/bar/baz` gets turned\ninto the corresponding filesystem path, and any key with special characters will be\nURL-encoded first before being used as filenames.\n\nFor atomic check-and-set operations, the database uses `etag` which is a \nunique identifier of the value being stored, which needs to match the value \npassed in from the client before an update is made. \nThis implementation uses last file update times as etag.\n\n\n### Threading model\n\nThe database is safe for multithreaded access from a single process. \n\nAll filesystem access is guarded by a reader/writer lock, so that all read operations \n(get, contains, enumerate) can occur simultaneously and without blocking on each other,\nwhile all write operations (set, check and set, remove) will wait for any read operations\nto finish, and will be mutually exclusive from each other and from any kind of a read operation. \n\n\n## Limitations\n\nBecause the database is a direct reflection of the filesystem, some limitations apply:\n  - On case-insensitive filesystems (like Windows), keys with different casing \n    will behave like multiple copies of the same key (return the same data, etc)\n    even though they're not technically equal.\n  - Having a document with the same name as a hierarchy segment is not supported,\n    since filesystems typically don't support having a file and a directory with the same name.\n    For example, when calling `Set(\"users/2021/uid123\", ...)` and then `Set(\"users/2021\", ...)` \n    (or vice versa), the latter operation will fail.\n  - The library is safe for multithreaded use from the same process, but simultaneous\n    access from multiple processes is not supported and may fail in unexpected ways.\n  - The database performs no indexing beyond key access by name; values are treated\n    as opaque binary blobs and are not indexed or otherwise processed.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzubek%2Feasy-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frzubek%2Feasy-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frzubek%2Feasy-db/lists"}