《开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的RxDB(Reactive Database的缩写)是一个NoSQL数据库,用于 JavaScript 应用程序,如网站、混合应用程序、电子应用程序、渐进式 Web 应用程序和 Node.js。
反应式意味着您不仅可以查询当前状态,还可以订阅所有状态更改,例如查询结果甚至文档的单个字段。这对于基于 UI 的实时应用程序来说非常有用,因为它易于开发并且具有巨大的性能优势。 为了在客户端和服务器之间复制数据,RxDB 提供了用于实时复制的模块,可以使用任何符合CouchDB的端点以及自定义GraphQL端点。
RxDB特征- 对浏览器、nodejs、electron、cordova、react-native 和所有其他 javascript-runtime 的多平台支持
- 基于RxJS的响应式数据处理
- 离线优先让您的应用在用户没有互联网时仍然可以运行
- 客户端和服务器数据之间的复制
- 基于模式的json-schema易于学习的标准
- Mango-Query与您从 mongoDB 和 mongoose 中了解的完全一样
- 加密单个数据字段以保护您的用户数据
- 数据库状态 (json) 的导入/导出,非常适合使用TDD进行编码
- 多窗口在不同的浏览器窗口或 nodejs 进程之间同步数据
- ORM 功能可轻松处理数据代码关系并自定义文档和集合的功能
- 完整的TypeScript支持以实现快速安全的编码(需要 Typescript v3.8 或更高版本)
安装 RxDB 库和 RxJS(如果之前没有安装)
npm install rxdb rxjs --save
使用 PouchDB RxStorage创建一个数据库(您也可以使用其他基于LokiJS或Dexie.js的存储)。
import { createRxDatabase } from 'rxdb';
import { getRxStoragePouch, addPouchPlugin } from 'rxdb/plugins/pouchdb';
addPouchPlugin(require('pouchdb-adapter-idb'));
const myDatabase = await createRxDatabase({
name: 'heroesdb',
storage: getRxStoragePouch('idb')
});
为集合创建架构
const mySchema = {
title: 'human schema',
version: 0,
primaryKey: 'passportId',
type: 'object',
properties: {
passportId: {
type: 'string',
maxLength: 100 // <- the primary key must have set maxLength
},
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
age: {
description: 'age in years',
type: 'integer',
// number fields that are used in an index, must have set minium, maximum and multipleOf
minimum: 0,
maximum: 150,
multipleOf: 1
}
},
required: ['firstName', 'lastName', 'passportId'],
indexes: ['age']
}
将集合添加到数据库
const myCollections = await myDatabase.addCollections({
humans: {
schema: mySchema
},
});
插入文档
const myDocument = await myDatabase.humans.insert({
passportId: 'foobar',
firstName: 'Alice',
lastName: 'Bobby',
age: 42
});
订阅文档值
myDocument.lastName$.subscribe(lastName => {
console.log('lastName is now ' lastName);
});
查询一些文档
const foundDocuments = await myDatabase.humans.find({
selector: {
age: {
$gt: 21
}
}
}).exec();
订阅查询结果
myDatabase.humans.find({
selector: {
age: {
$gt: 21
}
}
}).$.subscribe(documents => {
console.log('query has found ' documents.length ' documents');
});
更新文档
// either via atomicUpdate()
await myDocument.atomicUpdate(data => {
data.lastName = 'Carol';
return data;
});
// or via atomicPatch()
await myDocument.atomicPatch({
lastName: 'Carol'
});
删除文档
await myDocument.remove();
开发模式
dev-mode 插件为 RxDB 添加了许多检查和验证。这可确保您正确使用 RxDB API,因此在开发模式下使用 RxDB 时应始终使用开发模式插件。
- 为模式、查询、ORM 方法和文档字段添加验证检查。
- 添加可读的错误消息。
- 确保readonlyJavaScript 对象不会意外变异。
重要: : dev-mode 插件会增加你的构建大小并降低性能。它必须始终在开发中使用。你永远不应该在生产中使用它。
import { addRxPlugin } from 'rxdb';
import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode';
addRxPlugin(RxDBDevModePlugin);
与 Node.js 一起使用
async function createDb() {
if (process.env.NODE_ENV !== "production") {
await import('rxdb/plugins/dev-mode').then(
module => addRxPlugin(module as any)
);
}
const db = createRxDatabase( /* ... */ );
}
与 Angular 一起使用
import { isDevMode } from '@angular/core';
async function createDb() {
if (isDevMode()){
await import('rxdb/plugins/dev-mode').then(
module => addRxPlugin(module as any)
);
}
const db = createRxDatabase( /* ... */ );
// ...
}
模式验证
RxDB 有多个插件可用于确保您的文档数据始终与提供的 JSON 模式匹配。
注意:模式验证可能会占用 CPU 资源并增加构建大小。您应该始终在开发模式下使用 scehma 验证插件。对于大多数用例,您不应在生产中使用验证插件。
当您插入或更新 aRxDocument或使用复制插件复制文档数据时,验证模块会执行模式验证。当不使用验证插件时,可以保护任何文档数据,但在保存不符合架构的数据时可能会出现未定义的行为。
证实
lidate插件使用is-my-json-valid进行模式验证。
import { addRxPlugin } from 'rxdb';
import { RxDBValidatePlugin } from 'rxdb/plugins/validate';
addRxPlugin(RxDBValidatePlugin);
ajv-验证
另一个执行模式验证的验证模块。这个使用ajv作为验证器,速度更快。更好地符合 jsonschema-standard,但也有更大的构建大小。
import { addRxPlugin } from 'rxdb';
import { RxDBAjvValidatePlugin } from 'rxdb/plugins/ajv-validate';
addRxPlugin(RxDBAjvValidatePlugin);
验证-z-模式
者is-my-json-valid和ajv-validate用于执行内容安全策略中不允许eval()时可能不需要的验证。'unsafe-eval'这个使用z-schema作为验证器,不使用eval.
import { addRxPlugin } from 'rxdb';
import { RxDBValidateZSchemaPlugin } from 'rxdb/plugins/validate-z-schema';
addRxPlugin(RxDBValidateZSchemaPlugin);
数据迁移
使用 RxDB,您可以为您的集合提供迁移策略,自动(或随叫随到)将现有数据从旧模式转换为新模式。这可确保客户的数据始终与您的最新代码版本相匹配。
添加迁移插件
要启用数据迁移,您必须添加migration插件。
import { addRxPlugin } from 'rxdb';
import { RxDBMigrationPlugin } from 'rxdb/plugins/migration';
addRxPlugin(RxDBMigrationPlugin);
提供策略
创建集合后,您必须在架构的版本号大于 时提供 migrationStrategies 0。为此,您必须将一个对象添加到migrationStrategies为每个模式版本分配一个函数的属性中。migrationStrategy 是一个函数,它获取旧文档数据作为参数并返回新的、转换后的文档数据。如果策略返回null,文档将被删除而不是迁移。
myDatabase.addCollections({
messages: {
schema: messageSchemaV1,
migrationStrategies: {
// 1 means, this transforms data from version 0 to version 1
1: function(oldDoc){
oldDoc.time = new Date(oldDoc.time).getTime(); // string to unix
return oldDoc;
}
}
}
});
也可以使用异步策略:
myDatabase.addCollections({
messages: {
schema: messageSchemaV1,
migrationStrategies: {
1: function(oldDoc){
oldDoc.time = new Date(oldDoc.time).getTime(); // string to unix
return oldDoc;
},
/**
* 2 means, this transforms data from version 1 to version 2
* this returns a promise which resolves with the new document-data
*/
2: function(oldDoc){
// in the new schema (version: 2) we defined 'senderCountry' as required field (string)
// so we must get the country of the message-sender from the server
const coordinates = oldDoc.coordinates;
return fetch('http://myserver.com/api/countryByCoordinates/' coordinates '/')
.then(response => {
const response = response.json();
oldDoc.senderCountry=response;
return oldDoc;
});
}
}
}
});
您还可以筛选应迁移的文档:
myDatabase.addCollections({
messages: {
schema: messageSchemaV1,
migrationStrategies: {
// 1 means, this transforms data from version 0 to version 1
1: function(oldDoc){
oldDoc.time = new Date(oldDoc.time).getTime(); // string to unix
return oldDoc;
},
/**
* this removes all documents older then 2017-02-12
* they will not appear in the new collection
*/
2: function(oldDoc){
if(oldDoc.time < 1486940585) return null;
else return oldDoc;
}
}
}
});
—END—
开源协议:Apache-2.0 license
开源地址:https://github.com/pubkey/rxdb