Skip to content

Relational Storage

Authentication

参考接入 BytePower

功能概述

为 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"
      }
    }
  ]
}

京ICP备19011570号-2