今天给大家介绍一款开源文档数据库:FerretDB。它使用 PostgreSQL 作为数据库存储,兼容 MongoDB 6.0+ 协议,可以作为 MongoDB 一个开箱即用的替代产品。

⚠️由于 MongoDB 修改了它的开源协议(SSPL),导致很多开源软件和商用软件无法使用 MongoDB。

从原理上来讲,FerretDB 实现了一个无状态的代理,将 MongoDB 协议的查询转换为 SQL 语句,从而兼容 MongoDB 驱动。FerretDB 使用 GO 语言实现,遵循 Apache-2.0 开源协议,2023 年 4 月 11 日发布了第一个正式版本

在这里插入图片描述

作为文档数据库,FerretDB 使用 BSON(二进制 JSON)存储数据,可以支持的数据类型比常规 JSON 更多,不过目前还不支持 128 位浮点数。

文档数据库中的集合(Collection)对应 PostgreSQL 中的表(Table),文档(Document)则对应行(Row)。对于文档数据库而言,插入数据之前不需要定义模式。因此很适合用于需要灵活模式的应用程序,例如博客、聊天应用、视频游戏等。

下载安装

FerretDB 安装文件可以通过 Github 链接进行下载,目前只提供了 Docker、DEB 以及 RPM 安装包。

最简单的使用方式就是利用 Docker 镜像,例如:

docker run -d --rm --name ferretdb -p 27017:27017 ghcr.io/ferretdb/all-in-one

注意,默认的”all-in-one“镜像不适合生产环境,其它镜像类型包括 development 和 production,具体可以参考官方文档

以上命令可以启动一个包括 FerretDB、PostgreSQL 以及 MongoDB Shell 的容器,可以用于测试和试验。

启动容器之后,我们可以:

  • 使用 MongoDB 客户端工具连接到 FerretDB,URI 如下:mongodb://127.0.0.1:27017/
  • 运行命令mongosh连接到 FerretDB,MongoDB Shell 可以使用以下命令进行安装:docker exec -it ferretdb mongosh
  • 执行以下命令连接到容器内部的 PostgreSQL:docker exec -it ferretdb psql -U username ferretdb。FerretDB 使用 PostgreSQL 模式作为 MongoDB 数据库。因此,如果我们使用 MongoDB 客户端在 test 数据库中创建了一些集合,就可以运行 PostgreSQL 命令SET search_path = 'test';切换到该模式,并且利用 psql 命令\d查看集合对应的数据表。

停止容器的命令如下:

docker stop ferretdb

关于二进制包的安装,可以参考官方文档

基本操作

接下来我们介绍一些使用 FerretDB 时的基本 CURD 操作,它们和 MongoDB 中的文档操作相同。

创建文档

以下方法可以用于创建新的文档。

db.collection.insertOne({field1: value1, field2: value2,.... fieldN: valueN})
db.collection_name.insertMany([{document1}, {document2},... {documentN}])

它们分别用于创建单个文档和多个文档。如果集合不存在,它们会先创建集合。

例如,以下示例为集合 scientists 创建了一个新的文档:

db.scientists.insertOne({
    name: {
        firstname: "Thomas",
        lastname: "Edison"
    },
    born: 1847,
    invention: "lightbulb"
})

{
  acknowledged: true,
  insertedId: ObjectId("6346fcafd7a4a1b0b38eb2db")
}

操作成功之后,返回 acknowledged 以及自动生成的文档 ID。

以下是一个创建多个文档的示例:

db.scientists.insertMany([
    {
        name: {
        firstname: "Alan",
        lastname: "Turing"
        },
        born: 1912,
        invention: "Turing Machine"
    },
    {
        name: {
        firstname: "Graham",
        lastname: "Bell"
        },
        born: 1847,
        invention: "telephone"
    },
    {
        name: {
        firstname: "Ada",
        lastname: "Lovelace"
        },
        born: 1815,
        invention: "computer programming"
    }
])

查看文档

以下方法可以用于查询集合中的文档:

db.collection.find()
db.collection.findOne()

以下示例查询 scientists 集合中满足条件的第一个问题:

db.scientists.findOne({invention: "Turing Machine"})

find() 方法可以返回集合中的全部文档,例如:

 db.scientists.find()

查询操作可以基于指定条件搜索文档,查询条件可以包含各种操作符($gt、$lt、$in 等),也可以查询数组或者嵌套文档。

更新文档

以下方法可以用于更新文档:

db.collection.updateOne()
db.collection.updateMany()
db.collection.replaceOne()

例如,以下示例用于更新 firstname 字段等于“Graham”的文档,将其更新为“Alexander Graham”。updateOne() 方法只会更新满足条件的第一个文档。

db.scientists.updateOne({
   firstname: "Graham"
},
{
   $set:{
      firstname: "Alexander Graham"
   }
})

下面的示例使用 replaceOne() 方法替换整个文档:

db.scientists.replaceOne({
    lastname: "Bell",
},
{
    lastname: "Einstein",
    firstname: "Albert",
    born: 1879,
    invention: "Photoelectric effect",
    nobel: true
})

删除文档

以下方法可以删除满足条件的文档:

db.collection.deleteOne()
db.collection.deleteMany()

例如,以下示例用于删除 nobel 字段为 false 的所有文档:

db.scientists.deleteMany({nobel:false})

已知差异

FerretDB 目前存在一些和 MongoDB 不同的实现:

  1. FerretDB 使用和 MongoDB 相同的协议错误名和代码,但是某些情况下的具体错误消息可能不同;
  2. FerretDB 不支持字符串中的 NUL(\0)字符;
  3. FerretDB 不支持嵌套数组;
  4. FerretDB 会将负零(-0)转换为正零(0);
  5. 文档限制:key 不能包含点号(.),key 不能以美元符号($)开始,double 类型的字段不能包含 Infinity、-Infinity 或者 NaN;
  6. 插入的文档不能包含重复的 key;
  7. 更新文档的操作不能产生 Infinity、-Infinity 或者 NaN;
  8. 数据库和集合名不能以保留字 _ferretdb_ 开头,数据库名不能包含非拉丁字母,集合名必须是有效的 UTF-8 字符,数据库名不能以数字开头,也不能包含大写字母。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐