LiteDB - 一个文件式.NET NoSQL 文档数据库






4.88/5 (118投票s)
简单、快速、免费的嵌入式.NET NoSQL 单文件文档数据库
引言
本文档是对我的数据库项目 LiteDB
的概述——一个小型、快速、免费的嵌入式 .NET NoSQL 单文件文档数据库——现在是新版本 **4.0**。
- 更多示例和在线 Shell 请访问 LiteDB.org
- 源代码托管在 GitHub 或 下载二进制文件
- 完整文档请参见 GitHub Wiki 页面
特点
- 无服务器 NoSQL 文档数据库
- API 极其简洁,非常类似于 **MongoDB 官方驱动**
- 100% C# 代码,支持 .NET 3.5/4.0,单个 DLL(小于 300KB)- 无外部依赖
- 支持跨平台:**完整 .NET 和 NET Standard 1.3+2.0(适用于 UWP 10、Xamarin iOS 和 Android)**
- 线程安全 / 多进程
- 写入失败时可恢复(日志模式)
- 支持 POCO 类或
BsonDocument
- 使用 DES 加密数据文件
FileStorage
用于存储文件和流数据(类似于 MongoDB 中的GridFS
)- 单文件存储(类似于 SQLite)
- 为文档字段建立索引以实现快速搜索(每个集合最多 16 个索引)
- 支持 LINQ 查询,带有
Include
支持 - 命令行 Shell(尝试在线版本)
- 开源免费,适用于所有人,包括商业用途
- 通过 NuGet 安装:
Install-Package LiteDB
有什么新内容
- 新增 JSON Path 支持
- 索引定义支持表达式。您可以索引文档中的任何字段
- 使用
Attributes
或Fluent
API 映射实体类 LiteRepository
类,提供简单易用的访问方式DbRef
用于跨文档集合引用
LiteDB
的主要灵感来自于 MongoDB。我试图创建一个功能类似于 MongoDB 的数据库,但规模非常小,只包含小型应用程序最重要的一些功能。如果您熟悉 MongoDB,那么您就已经了解 LiteDB 了。
如何使用
存储和搜索文档的快速示例
// Open data file (or create if not exits)
using(var db = new LiteDatabase(@"C:\Temp\MyData.db"))
{
// Get customer collection
var col = db.GetCollection<Customer>("customers");
var customer = new Customer { Id = 1, Name = "John Doe" };
// Insert new customer document
col.Insert(customer);
// Update a document inside a collection
customer.Name = "Joana Doe";
col.Update(customer);
// Index document using a document property
col.EnsureIndex(x => x.Name);
// Simple Linq support
var result = col.Find(x => x.Name.StartsWith("Jo"));
}
适用场景?
- 小型 Web 应用程序、网站博客或论坛
- 每个**账户/用户**使用一个
datafile
进行数据存储 - 桌面/本地小型应用程序
- 应用程序文件格式
- 少量并发写入用户操作
快速入门
让我们来看一些关于 LiteDB 数据库和数据结构的示例。如果您需要更多文档,可以阅读 Github Wiki 文档。
Documents
LiteDB 通过文档来存储和检索数据文件中的数据。您的文档定义可以是 POCO 类或 BsonDocument
类。无论哪种情况,LiteDB 都会将您的文档转换为 BSON 格式存储在磁盘上。
BSON 是 Binary
JSON,一种将数据对象序列化为二进制数组的格式。BSON 拥有比 JSON 更多的数据类型,例如 DateTime
、Guid
和 ObjectId
。
使用 POCO 类存储文档
POCO 类是简单的 C# 类,只包含 get
/set
属性。这是创建强类型文档的最佳方式。您的类必须有一个标识符属性。您可以使用名为 Id
的属性、<ClassName>Id
或使用 [BsonId]
属性装饰任何属性。
// A poco entity, must have Id
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Phone> Phones { get; set; }
}
// It's not a entity, don't need Id
public class Phone
{
public int Code { get; set; }
public string Number { get; set; }
public PhoneType Type { get; set; }
}
public enum PhoneType { Mobile, Landline }
- 在内部,文档 ID 表示为
_id
属性 - 不要使用复杂的数据类型(例如
DataSet
、DataTable
) - 不要使用可处置对象(例如
Stream
、Graphics
) Enum
在序列化时将被转换为string
- 您的
Id
值必须是唯一的且不能为null
- 如果您将
Id
留空,LiteDB 将在insert
时自动生成。
映射约定
BsonMapper.ToDocument()
会自动将每个类属性转换为文档字段,遵循以下约定:
- 类必须是
public
,并有一个public
的无参构造函数 - 属性必须是
public
- 属性可以是只读或读写的
- 类必须有一个
Id
属性、<ClassName>Id
属性或任何带有[BsonId]
属性的属性来存储Id
信息 - 属性可以用
[BsonIgnore]
装饰,以使其不被映射到文档 - 属性可以用
[BsonField]
装饰,以获得自定义字段名称 - 不允许循环引用
- 最多允许 20 层嵌套类
- 类的字段不会被转换为文档
BsonMapper
使用一个全局实例来缓存映射信息,以提高性能。该实例也可以通过LiteDatabase.Mapper
属性访问。
用于文档映射的 Fluent API
如果您想更改默认的约定映射,可以使用 EntityBuilder
将您的实体类映射到 BsonDocument
。
var mapper = BsonMapper.Global;
mapper.Entity<Customer>()
.Id(x => x.CustomerKey)
.Field(x => x.Name, "customer_name")
.Ignore(x => x.Age)
.Index(x => x.Name, unique);
DbRef 用于跨文档集合引用
LiteDB 可以使用跨文档引用,仅通过 _id
值映射内部实体(而不是嵌入整个子文档)。
// Your entity order class
public class Order
{
public int OrderId { get; set; }
public Customer Customer { get; set; }
}
// Map Customer property to "customers" collections
BsonMapper.Global.Entity<Order>()
.DbRef(x => x.Customer, "customers");
// When insert an order, only customer _id will be included as reference
// Get orders collection
var orders = db.GetCollection<Order>("orders");
// Find an order including Customer reference
var order = orders
.Include(x => x.Customer)
.FindById(123);
LiteDB
将 Order
文档序列化为 `{ _id: 123, Customer: { $id:1, $ref: "customers" } }
`。当您需要查询 Order
并包含 Customer
实例时,只需在运行 Find
方法之前添加即可。
使用 BsonDocument 存储文档
BsonDocument
是一个特殊的类,它使用内部的 Dictionary<string, object>
来映射任何文档。这对于读取未知文档类型或将其用作通用文档非常有用。
// Create a BsonDocument for Customer with phones
var doc = new BsonDocument();
doc["_id"] = ObjectId.NewObjectId();
doc["Name"] = "John Doe";
doc["Phones"] = new BsonArray();
doc["Phones"].Add(new BsonObject());
doc["Phones"][0]["Code"] = 55;
doc["Phones"][0]["Number"] = "(51) 8000-1234";
doc["Phones"][0]["Type"] = "Mobile";
使用 BsonDocument
,您可以创建任何复杂的文档架构。
集合(Collections)- 存储
LiteDB 将文档组织在存储中(在 LiteDB
中称为集合)。每个集合都有一个唯一的名称,并包含具有相同架构/类型的文档。您可以通过 LiteDatabase
实例的 GetCollection
方法获取强类型集合或通用的 BsonDocument
集合。
var db = new LiteDatabase(stringConnection);
// Get a strong typed collection
var customers = db.GetCollection<Customer>("Customers");
// Get a BsonDocument collection
var customers = db.GetCollection("Customers");
Collection
包含所有用于操作文档的方法
Insert
- 插入新文档FindById
、FindOne
或Find
- 使用 Query 对象或 LINQ 表达式查找文档。此时,只支持简单的 LINQ - 左侧是属性,右侧是值。Update
- 更新文档Delete
- 删除指定 ID 的文档或使用查询删除文档Include
- 使用 include 填充基于其他集合的属性EnsureIndex
- 如果索引不存在则创建。所有查询都必须有索引。
查询
在 LiteDB
中,查询使用索引来查找文档。您可以使用 Query 辅助器或 Linq 表达式。
var customers = db.GetCollection<Customer>("customers");
// Create a new index (if not exists)
customers.EnsureIndex("Name");
// Query documents using 'Name' index
var results = customers.Find(Query.StartsWith("Name", "John"));
// Or using Linq
var results = customers.Find(x => x.Name.StartsWith("John"));
// Return document by ID (PK index)
var customer = customers.FindById(1);
// Count documents using Query helper
var count = customers.Count(Query.GTE("Age", 22));
// All query results returns an IEnumerable<T>, so you can use Linq in results
var results = customers
.Find(x => x.Name.StartsWith("John") && x.Salary > 500) // two indexed queries
.Where(x => x.LastName.Length > 5) // in memory query
.Select(x => new { x.Name, x.Salary })
.OrderBy(x => x.Name);
Query 类支持 **All**、**Equals**、**Not**、**GreaterThan**、**LessThan**、**Between**、**In**、**StartsWith**、**Contains**、**AND** 和 **OR**。
故障容错 - 日志记录/恢复
LiteDB
使用磁盘上的日志文件来保证写操作的持久性。这意味着在执行写操作之前,所有脏页都会先写入同一个文件,然后再写入主数据文件(使用文件末尾来存储干净的页面)。
此功能可以在连接字符串中禁用 - 运行速度更快,但如果在写操作时发生崩溃,您的数据文件可能会不一致。
处理文件 - FileStorage
同时,我们也需要在数据库中存储文件。为此,LiteDB
有一个特殊的 FileStorage
集合来存储文件,没有文档大小限制(单个文件最大限制为 2GB)。它类似于 MongoDB 的 GridFS
。
// Storing a file stream inside database
db.FileStorage.Upload("my_key.png", stream);
// Get file reference using file id
var file = db.FileStorage.FindById("my_key.png");
// Find all files using StartsWith
var files = db.Files.Find("my_");
// Get file stream
var stream = file.OpenRead();
// Write file stream in a external stream
db.FileStorage.Download("my_key.png", stream);
LiteDB
创建两个集合来处理文件:_files
和 _chunks
。_files
集合包含文件信息(文件 ID、文件名、上传日期和元数据)。文件数据内容被分割存储在 _chunks
集合中。
LiteDB.Shell
LiteDB
包含一个 Shell 命令行应用程序。该 Shell 可用于在数据文件中运行命令,而无需其他应用程序。以下是一些最有用的命令。要查看所有命令,请使用 "help
" 命令。
Shell commands
==============
> open <filename|connectionString> : Open a new database
> pretty on|off : Turns on/off pretty json format
> timer : Show timer before prompt
> dump > <dumpfile.dmp> : Export database as insert commands
> dump < <dumpfile.dmp> : Import database
Collections commands
====================
> db.<collection>.insert <jsonDoc> : Insert a new document into collection
> db.<collection>.update <jsonDoc> : Update a document inside collection
> db.<collection>.delete <filter> : Delete documents using a filter clausule (see find)
> db.<collection>.find [top N] <filter> : Show filtered documents based on index search
> db.<collection>.count <filter> : Show count rows according query filter
> db.<collection>.ensureIndex <field> [unique] : Create a new index document field
> db.<collection>.drop : Drop collection and destroy all documents inside
<filter> = <field> [=|>|>=|<|<=|!=|like|contains|between] <jsonValue> :
Filter query syntax
<filter> = (<filter> [and|or] <filter> [and|or] ...) : Multi queries syntax
<jsonDoc> = {_id: ... , key: value, key1: value1 } :
Represent an extended json for a BsonDocument.
File storage commands
=====================
> fs.find : List all files on datafile
> fs.find <fileId> : List file info from a key. Supports * for starts with key
> fs.upload <fileId> <filename> : Insert a new file inside database
> fs.download <fileId> <filename> : Save a file to disk passing a file key and filename
> fs.update <fileId> {key:value} : Update metadata file
> fs.delete <fileId> : Remove a file inside database
历史
- 更新 v4.0.0 - 2017 年 10 月 18 日
- 仅支持 .NET4
- 新增表达式支持
- 修复了 v3 中的并发问题
- 通过 NETStandard 1.3 + 2.0 支持跨平台
- 更新 v2.0.0 - 2016 年 8 月 1 日
- 支持跨平台 - UWP、Xamarin - iOS 和 Android
- .NET 3.5
- 新的磁盘访问方式,避免使用锁定函数:仅使用读/写独占模式
- 恢复了一些 v1 的功能:用户事务控制、注册
autoId
和全局映射器实例
- 更新 v.2.0.0-rc - 2015 年 12 月 24 日
- 加密数据文件
- 数据文件从 v0.9.0 / v1.0.x 迁移
- 将数据文件转储为 insert 语句
- 改进了缓存/并发支持
- 圣诞快乐 :)
- 更新 v.2.0.0-beta - 2015 年 11 月 27 日
- 抽象持久化层
- Fluent 映射 API
- 新的
DbRef
- 虚拟索引字段
- 新的缓存清理系统
- 支持初始数据库大小和最大数据库大小
- 延迟加载
ThreadSafe
和ProcessSafe
- 收缩数据文件
- 数据库日志信息
- 更新 v1.0.2 - 2015 年 5 月 17 日
- 改进了
BsonMapper
对接口和基类的序列化/反序列化,通过文档中的_type
字段(类似于 MongoDB 中的用法) BsonMapper
支持NameValueCollection
- 添加了对布尔 Linq 操作的支持,例如
x => x.IsActive
- 修复了
string
索引update
/delete
操作中的 bug - 移除了全文档扫描查找操作 - 现在仅通过索引查找
- 自动创建不存在的索引
- 改进了
- 更新 v1.0 - 2015 年 3 月 28 日
- 新的
BsonSerializer
,移除了fastBinaryJson
并实现了真正的 BSON 规范 - 新的
BsonMapper
,用于更灵活地配置 POCO 与BsonDocument
之间的转换 - 新的
JsonReader
实现:未使用正则表达式,速度提升 4 倍 - 新的
ObjectId
,用于Id
文档 - 索引创建选项 - 去除空格、去除重音符、忽略大小写
[BsonIndex]
属性,用于标记实体属性,以便在查询时自动创建索引- 为实体 Id 属性自动生成 Id
Find()
可以在没有索引的情况下执行(将执行全文档扫描)FindAll()
支持升序/降序结果- 新的
Query.Contains
- 获取索引的
Min()
/Max()
值 DbRef<>
- 一个简单的引用文档的类- 改进了 Drop collection 和 drop index
- 移除了
_master
集合 - 避免 1 页读取
- 新的
- 更新 v0.9 - 2015 年 2 月 7 日
- 添加 MongoDB 性能测试
- 添加 Shell 快速上手指南
- 初始版本 - 2015 年 1 月 25 日