# FerretDB

> MongoDB 线协议兼容的 PostgreSQL 方案
---

[FerretDB](https://github.com/FerretDB/documentdb) 是开源的 MongoDB 线协议兼容中间件，可将 PostgreSQL 作为 MongoDB 的直接替代后端。依赖 MongoDB 线协议的应用可以通过它无缝使用 PostgreSQL，在两个数据库生态之间建立桥梁。

启用 FerretDB 需要使用 FerretDB 修订的 [`documentdb`](https://pgext.cloud/e/documentdb) 扩展；Pigsty 软件仓库也提供该扩展。此冻结版本中的最新组合为 FerretDB 2.7 与 DocumentDB 0.107.0。

------

## 快速上手

按照 Pigsty 的[标准安装流程](/docs/install/start/)，使用 [`mongo`](https://github.com/pgsty/pigsty/blob/v3.7.0/conf/mongo.yml) 配置模板：

```bash
./configure -c mongo    # 使用 FerretDB / DocumentDB 配置模板
./install.yml           # 安装；生产部署前请先修改 pigsty.yml 中的密码
```

生产部署时，务必在运行安装剧本前修改 `pigsty.yml` 中的密码参数。

------

## 配置

```yaml
pg-meta:
  hosts:
    10.10.10.10: { pg_seq: 1, pg_role: primary }
  vars:
    pg_cluster: pg-meta
    pg_users:
      - { name: mongod      ,password: DBUser.Mongo  ,pgbouncer: true ,roles: [dbrole_admin   ] ,comment: ferretdb super user ,superuser: true }
      - { name: dbuser_meta ,password: DBUser.Meta   ,pgbouncer: true ,roles: [dbrole_admin   ] ,comment: pigsty admin user }
      - { name: dbuser_view ,password: DBUser.Viewer ,pgbouncer: true ,roles: [dbrole_readonly] ,comment: read-only viewer  }
    pg_databases:
      - {name: meta, owner: mongod ,baseline: cmdb.sql ,comment: pigsty meta database ,schemas: [pigsty] ,extensions: [ documentdb, postgis, vector, pg_cron, rum ]}
    pg_hba_rules:
      - { user: dbuser_view , db: all ,addr: infra ,auth: pwd ,title: 'allow grafana dashboard access cmdb from infra nodes' }
      - { user: mongod      , db: all ,addr: world ,auth: pwd ,title: 'mongodb password access from everywhere' }
    node_crontab: [ '00 01 * * * postgres /pg/bin/pg-backup full' ]

    # DocumentDB 设置
    pg_extensions: [ documentdb, citus, postgis, pgvector, pg_cron, rum ]
    pg_libs: 'pg_documentdb, pg_documentdb_core, pg_cron, pg_stat_statements, auto_explain'
    pg_parameters: { cron.database_name: meta }
```

------

## 使用

完整说明请参阅 [FERRET 模块](/docs/ferret/)文档。

### 安装客户端工具

可以使用 MongoDB 命令行工具 [MongoSH](https://www.mongodb.com/docs/mongodb-shell/) 访问 FerretDB。

先用 `pig` 添加 MongoDB 软件仓库，再通过 `yum` 或 `apt` 安装 `mongosh`：

```bash
pig repo add mongo -u
yum install mongodb-mongosh
apt install mongodb-mongosh
```

### 连接 FerretDB

任何语言的 MongoDB 驱动都可以使用 MongoDB 连接串访问 FerretDB。以下为 `mongosh` 示例：

```bash
$ mongosh
Current Mongosh Log ID: 67ba8c1fe551f042bf51e943
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.4.0
Using MongoDB:          7.0.77
Using Mongosh:          2.4.0

For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/

test>
```

### 认证

可以使用不同用户登录，细节参阅 [FerretDB：认证](https://docs.ferretdb.io/security/authentication/)：

```bash
mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017/meta'      # 业务管理员
mongosh 'mongodb://dbuser_view:DBUser.Viewer@10.10.10.10:27017/meta'    # 只读用户
```

### 使用示例

连接 FerretDB 后，可以像使用 MongoDB 集群一样执行命令：

```bash
$ mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017/meta'
```

MongoDB 命令会转换为 `SQL`，并在底层 PostgreSQL 中执行：

```javascript
use test                            // CREATE SCHEMA test;
db.dropDatabase();                  // DROP SCHEMA test;
db.createCollection('posts');       // CREATE TABLE posts(_data JSONB,...)
db.posts.insertOne({                // INSERT INTO posts VALUES(...);
    title: 'Post One',body: 'Body of post one',category: 'News',tags: ['news', 'events'],
    user: {name: 'John Doe',status: 'author'},date: Date()}
);
db.posts.find().limit(2).pretty();  // SELECT * FROM posts LIMIT 2;
db.posts.createIndex({ title: 1 })  // CREATE INDEX ON posts(_data->>'title');
```

如果不熟悉 MongoDB，可以参考同样适用于 FerretDB 的 [MongoDB Shell CRUD 教程](https://www.mongodb.com/docs/ferretdb-shell/crud/)。

下面的 `mongosh` 脚本可用于生成简单的示例负载：

```bash
cat > benchmark.js <<'EOF'
const coll = "testColl";
const numDocs = 1000;

for (let i = 0; i < numDocs; i++) {
  db.getCollection(coll).insertOne({ num: i, name: "MongoDB Benchmark Test" });
}
for (let i = 0; i < numDocs; i++) {
  db.getCollection(coll).find({ num: i });
}
for (let i = 0; i < numDocs; i++) {
  db.getCollection(coll).updateOne({ num: i }, { $set: { name: "Updated" } });
}
for (let i = 0; i < numDocs; i++) {
  db.getCollection(coll).deleteOne({ num: i });
}
EOF

mongosh 'mongodb://dbuser_meta:DBUser.Meta@10.10.10.10:27017' benchmark.js
```

FerretDB 的[支持命令列表](https://docs.ferretdb.io/reference/supported-commands/)和[已知差异](https://docs.ferretdb.io/diff/)说明了兼容边界；对基本使用场景而言，这些差异通常影响不大。
