{"id":29189478,"url":"https://github.com/teambition/reactivedb","last_synced_at":"2025-07-01T23:08:24.250Z","repository":{"id":10873355,"uuid":"67320761","full_name":"teambition/ReactiveDB","owner":"teambition","description":"Reactive ORM for Lovefield","archived":false,"fork":false,"pushed_at":"2022-12-07T01:01:15.000Z","size":1545,"stargazers_count":105,"open_issues_count":48,"forks_count":6,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-06-27T00:52:16.213Z","etag":null,"topics":["lovefield","reactivedb","rxjs","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/teambition.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}},"created_at":"2016-09-04T02:04:37.000Z","updated_at":"2024-05-23T07:54:02.000Z","dependencies_parsed_at":"2023-01-11T20:15:13.041Z","dependency_job_id":null,"html_url":"https://github.com/teambition/ReactiveDB","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/teambition/ReactiveDB","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teambition%2FReactiveDB","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teambition%2FReactiveDB/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teambition%2FReactiveDB/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teambition%2FReactiveDB/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teambition","download_url":"https://codeload.github.com/teambition/ReactiveDB/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teambition%2FReactiveDB/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263047676,"owners_count":23405280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["lovefield","reactivedb","rxjs","typescript"],"created_at":"2025-07-01T23:08:23.531Z","updated_at":"2025-07-01T23:08:24.204Z","avatar_url":"https://github.com/teambition.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![CircleCI](https://circleci.com/gh/teambition/ReactiveDB.svg?style=svg)](https://circleci.com/gh/teambition/ReactiveDB)\n[![Coverage Status](https://coveralls.io/repos/github/teambition/ReactiveDB/badge.svg?branch=master)](https://coveralls.io/github/teambition/ReactiveDB?branch=master)\n[![Dependency Status](https://david-dm.org/teambition/ReactiveDB.svg)](https://david-dm.org/teambition/ReactiveDB)\n[![devDependencies Status](https://david-dm.org/teambition/ReactiveDB/dev-status.svg)](https://david-dm.org/teambition/ReactiveDB?type=dev)\n[![Greenkeeper badge](https://badges.greenkeeper.io/teambition/ReactiveDB.svg)](https://greenkeeper.io/)\n# ReactiveDB\n\n一个 Reactive 风格的前端 ORM。基于 [Lovefield](https://github.com/google/lovefield) 与 [RxJS](https://github.com/ReactiveX/rxjs)。\n\n## Features\n- 响应式查询\n\n  支持以 Observable 的形式返回响应式数据\n- 数据一致性\n\n  所有的执行过程都是`事务性`的，在遇到环境异常时(indexDB 异常，浏览器限制，隐私模式导致的功能性缺失等) 也不会产生脏数据。\n\n- 数据持久化\n\n  大量的数据场景下，极端如单页应用不间断运行几个月的情况下，不会造成内存占用量过多。所有的数据都可以持久化在本地存储而非内存中，~~并支持丰富的数据换页配置~~[WIP]。\n\n- debug tools\n\n  [Lovefield debug tool for Chrome](https://chrome.google.com/webstore/detail/lovefield-db-inspector/pcolnppcajocbhmgmljobphopnchkcig)\n\n\n## Documents\n- [Design Document](./docs/Design-Document)\n- [API Description](./docs/API-description)\n\n\n## Scenarios\n\u003e 在单页实时性应用的场景下，抽象出在前端维护数据以及其关联的数据的变更的逻辑\n\n考虑下面的场景，在一个单页前端应用中，需要展示 A，B, C, D 四个列表:\n\n其中列表 A 展示所有 ownerId 为 `user1` 的 Item :\n\n```json\n[\n  {\n    \"_id\": 1,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user1\",\n    \"creatorId\": \"user2\",\n    \"created\": \"2016-01-31T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user1\",\n      \"name\": \"user1 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user2\",\n      \"name\": \"user2 name\"\n    }\n  },\n  {\n    \"_id\": 3,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user1\",\n    \"creatorId\": \"user3\",\n    \"created\": \"2016-05-03T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user1\",\n      \"name\": \"user1 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user3\",\n      \"name\": \"user3 name\"\n    }\n  }\n  ...\n]\n```\n\n列表 B 展示所有 creatorId 为 `user2` 的 Item:\n\n```json\n[\n  {\n    \"_id\": 1,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user1\",\n    \"creatorId\": \"user2\",\n    \"created\": \"2016-01-31T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user1\",\n      \"name\": \"user1 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user2\",\n      \"name\": \"user2 name\"\n    }\n  },\n  {\n    \"_id\": 2,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user2\",\n    \"creatorId\": \"user3\",\n    \"created\": \"2016-04-20T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user2\",\n      \"name\": \"user2 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user3\",\n      \"name\": \"user3 name\"\n    }\n  }\n  ...\n]\n```\n\n列表 C 展示所有 `created` 时间为 `2016年3月1日` 以后的 Item:\n\n```json\n[\n  {\n    \"_id\": 2,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user2\",\n    \"creatorId\": \"user3\",\n    \"created\": \"2016-04-20T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user2\",\n      \"name\": \"user2 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user3\",\n      \"name\": \"user3 name\"\n    }\n  },\n  {\n    \"_id\": 3,\n    \"name\": \"item 1\",\n    \"ownerId\": \"user1\",\n    \"creatorId\": \"user3\",\n    \"created\": \"2016-05-03T16:00:00.000Z\",\n    \"owner\": {\n      \"_id\": \"user1\",\n      \"name\": \"user1 name\"\n    },\n    \"creator\": {\n      \"_id\": \"user3\",\n      \"name\": \"user3 name\"\n    }\n  }\n]\n```\n\n列表 D 展示所有的用户信息:\n```json\n[\n  {\n    \"_id\": \"user1\",\n    \"name\": \"user1 name\",\n    \"avatarUrl\": \"user1 avatarUrl\",\n    \"birthday\": \"user1 birthday\"\n  },\n  {\n    \"_id\": \"user2\",\n    \"name\": \"user2 name\",\n    \"avatarUrl\": \"user2 avatarUrl\",\n    \"birthday\": \"user2 birthday\"\n  },\n  {\n    \"_id\": \"user3\",\n    \"name\": \"user3 name\",\n    \"avatarUrl\": \"user3 avatarUrl\",\n    \"birthday\": \"user3 birthday\"\n  }\n]\n```\n\n\n这四个列表的数据分别从四个 API 获取。在大多数单页应用的架构中，数据层会缓存这几个接口的数据，避免重复请求。而在实时性的单页应用中，这些数据的更新通常需要通过 `WebSocket` 等手段进行更新。根据缓存策略的不同（单例存储/同一 ID 存储多份数据），则有不同的更新方式。但这个过程一般是 *业务化且难以抽象* 的。\n\n\n比如单一引用存储数据时, 上面场景中列举到的数据只会存储为:\n\n```\n{\n  item1, item2, item3,\n  user1, user2, user3\n}\n```\n\n在这种缓存策略下，一个数据变更后，将变更后的结果通知到所属的集合是一件非常麻烦的事情。\n假设现在我们的应用收到一条 socket 消息:\n\n```json\n{\n  \"change:item1\": {\n    \"ownerId\": \"user3\"\n  }\n}\n```\n\n按照业务需求我们应该将 `item1` 从 `ListA` 中移除。在这种缓存策略中，如果使用的 `pub/sub` 的模型进行通知(Backbone 之类)，则会导致数据层外的代码不得不进行大量的计算，不停的 `filter` 一个变更是否满足某个列表的需求。这种重复的过程是非常难以维护，业务化，且难以抽象的。\n而按照 `ReactiveDB` 的设计理念，所有的数据都有可选的响应模式，即任何与之相关的变动都会让数据`自行`更新为最新的值:\n\n伪代码如下:\n\n```ts\n/**\n * @param tableName\n * @param queryOptions\n * @return QueryToken\u003cT\u003e\n **/\ndatabase.get\u003cItemSchema\u003e('Item', {\n  where: {\n    ownerId: 'user1'\n  },\n  fields: [\n    '_id', 'name', 'ownerId',\n    {\n      owner: ['_id', 'name']\n    }\n  ]\n})\n  .changes()\n  .subscribe(items =\u003e {\n    console.log(items)\n  })\n```\n\n使用 ReactiveDB 的情况下，无论是 `Item` 本身的变更还是与之关联的 `User` 变更，都会产生新的 items 值。\n更复杂的比如 `ListC`:\n\n```ts\n/**\n * @param tableName\n * @param queryOptions\n * @return QueryToken\u003cT\u003e\n **/\ndatabase.get\u003cItemSchema\u003e('Item', {\n  where: {\n    created: {\n      // $gte means great than and equal\n      // 更多操作符参见详细的使用文档\n      '$gte': new Date(2016, 3, 1).valueOf()\n    }\n  },\n  fields: [\n    '_id', 'name', 'ownerId',\n    {\n      owner: ['_id', 'name']\n    }\n  ]\n})\n  .changes()\n  .subscribe(items =\u003e {\n    console.log(items)\n  })\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteambition%2Freactivedb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteambition%2Freactivedb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteambition%2Freactivedb/lists"}