Relational Storage
Authentication
功能概述
为 key, value 型数据存储提供关联的能力,提供方便使用的 API;适合以本地数据为准又有跨设备同步数据的需要的应用。
- 支持一对多关系。
- 支持一级数据关联查询。
配置
relational_storage.json
json
{
"models": [
{
"name": "habit_types",
"attributes": [
{
"name": "name",
"type": "string",
"max_length": 2000
}
]
},
{
"name": "habits",
"attributes": [
{"name": "title", "type": "string", "max_length": 2000},
{"name": "type", "type": "string", "max_length": 1000},
{"name": "status", "type": "string", "max_length": 1000},
{"name": "timestamp", "type": "integer", "max_length": 1000},
{"name": "enabled", "type": "boolean", "max_length": 1000},
{"name": "value", "type": "float", "max_length": 1000}
]
},
{
"name": "checklist",
"attributes": [
{
"name": "content",
"type": "string",
"max_length": 2000
}
]
},
{
"name": "habit_records",
"attributes": [
{
"name": "date",
"type": "string",
"max_length": 2000
}
]
}
],
"relationships": [
{
"name": "",
"type": "1:N",
"primary_model": "habit_types",
"related_model": "habits",
"cascade": ["delete"] // 配置 "delelte" 后删除 model 时会同步删除 related model, 但是不会删除 related model 的 related model
},
{
"type": "1:N",
"primary_model": "habits",
"related_model": "checklist"
},
{
"type": "1:N",
"primary_model": "habits",
"related_model": "habit_records"
}
]
}
- model.name 不可重复
- model.type 枚举值: string, integer, float, boolean;当选择 string 时增加 max_length 字段。
- relationship.type 枚举值: 1:N
API
创建数据
支持关联 model
Method & Path
-
POST {domain}/bp/relational_storage/{model_name}
-
POST {domain}/bp/server/user/{user_id}/relational_storage/{model_name}
json
{
"key": "",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight": 1.1,
"related_keys": "{\"example_model\": [\"key1\", \"key2\"]}",
// optional, 支持关联创建多个数据
"related_models": {
"example_model": [
{
"key": "key3",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight", 0.001,
"related_keys": "{}"
}
]
}
}
Error
- 400 duplicate_key
- 404 model_not_found
- 400 data_invalid
示例
sql
insert into related_storage (type, key, data) values ('habits', '', {});
insert into related_storage (type, key, data) values ('checklist', '', {});
更新数据
支持关联 model
Method & Path
-
PUT {domain}/bp/relational_storage/{model_name}
-
PUT {domain}/bp/server/user/{user_id}/relational_storage/{model_name}
json
{
"key": "k1",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight": 1.1,
"related_keys": "{\"example_model\": [\"key1\", \"key2\"]}",
"related_models": {
"example_model": [
{
"key": "k1",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight": 1.1,
"related_keys": "{\"example_model\": [\"key1\", \"key2\"]}"
}
]
}
}
Error
- 404 key_not_found
删除数据
Method & Path
-
DELETE {domain}/bp/relational_storage/{model_name}?key={key}
-
DELETE {domain}/bp/server/user/{user_id}/relational_storage/{model_name}?key={key}
Error
- 404 key_not_found
查询数据
支持关联 model; 支持分页。支持排序。
Method & Path
-
GET {domain}/bp/relational_storage/{model_name}?key={}&related_model=a,b,c&page_size={}&page={}
-
GET {domain}/bp/server/user/{user_id}/relational_storage/{model_name}?key={}&related_model=a,b,c&page_size={}&page={}
Response
批量分页查询
示例:GET /bp/relational_storage/habits?page_size=10&page=1
json
{
"total_count": 100,
"models": [
{
"data": "{\"key\": \"value\"}", // 序列化的数据
"key": "h1",
"weight": 0.3,
"related_keys": ""
},
{
"data": "{\"key\": \"value\"}", // 序列化的数据
"key": "h2",
"weight": 0.4,
"related_keys": ""
}
]
}
查询指定 key, 可返回关联 Model
示例:GET /bp/relational_storage/habits?key=h2&related_models=a,b,c
json
{
"model": {
"data": "{\"key\": \"value\"}", // 序列化的数据
"key": "h2",
"weight": 1.2,
"related_models": {
"a": [
{
"key": "a2",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight", 0.001,
"related_keys": ""
}
]
}
}
}
示例
同步数据
通过数据同步接口支持可离线使用的 APP; token 为最新的数据的更新时间 !建议先下载再上传,分两次进行数据同步。
Method & Path
-
DELETE {domain}/bp/relational_storage
-
DELETE {domain}/bp/server/user/{user_id}/relational_storage
Request
json
// Optional
{
"token": "1683534940098",
"model_names": ["habits", ""], // 过滤指定 model
"batch_size": 10,
"operations": [
// 非必需,用于将本地数据修改同步到服务器。
{
"id": 1,
"type": "create|update|delete", // delete 时 model 中只需要传 model_name 和 key
"model": {
"model_name": "example_model",
"key": "",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight": 1.1,
"related_keys": "{\"example_model\":[\"rk1\"]}"
}
}
]
}
Response
json
{
"token": "1683534940798",
"operation_result": {
"succeed_ids": [1, 2],
"failed_ids": [11, 22]
},
"unsynchronized_count": 200,
"models": [ // 从服务器同步到本地的数据
{
"model_name": "habits",
"key": "",
"data": "{\"key\": \"value\"}", // 序列化的数据
"weight", 0.001
},
{
"model_name": "checklist",
"key": "checklist_123",
"is_deleted": true,
}
]
}
参考
PostgREST
更接近 SQL 的使用
- GET /directors?select=last_name,films(title) HTTP/1.1
json
[
{
"last_name": "Lumière",
"films": [{"title": "Workers Leaving The Lumière Factory In Lyon"}]
},
{
"last_name": "Dickson",
"films": [{"title": "The Dickson Experimental Sound Film"}]
},
{"last_name": "Méliès", "films": [{"title": "The Haunted Castle"}]}
]
GET /films?select=,roles()&roles.character=in.(Chico,Harpo,Groucho) HTTP/1.1
GET /films?select=*,90_comps:competitions(name),91_comps:competitions(name)&90_comps.year=eq.1990&91_comps.year=eq.1991 HTTP/1.1
JSON:API
json
{
"links": {
"self": "http://example.com/articles",
"next": "http://example.com/articles?page[offset]=2",
"last": "http://example.com/articles?page[offset]=10"
},
"data": [
{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": {"type": "people", "id": "9"}
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{"type": "comments", "id": "5"},
{"type": "comments", "id": "12"}
]
}
},
"links": {
"self": "http://example.com/articles/1"
}
}
],
"included": [
{
"type": "people",
"id": "9",
"attributes": {
"firstName": "Dan",
"lastName": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
},
{
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": {"type": "people", "id": "2"}
}
},
"links": {
"self": "http://example.com/comments/5"
}
},
{
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": {"type": "people", "id": "9"}
}
},
"links": {
"self": "http://example.com/comments/12"
}
}
]
}