域表示法#

域表示法将“如何表示数据”与“如何存储数据”分开。它的工作原理是创建 domainscasts ,后者作用于前者,以不同的格式呈现和接收数据。

自定义域#

假设您想要使用 uuid 键入主键,并希望将其缩写显示给Web用户。

为此,让我们基于以下内容创建域 uuid

create domain app_uuid as uuid;

-- and use it as our table PK.
create table profiles(
  id   app_uuid
, name text
);

-- some data for the example
insert into profiles values ('846c4ffd-92ce-4de7-8d11-8e29929f4ec4', 'John Doe');

域响应格式#

我们可以缩短 uuid 使用 base64 编码。让我们使用JSON作为本例的响应格式。

要更改JSON的域格式,请创建一个函数来转换 app_uuidjson

-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION json(app_uuid) RETURNS json AS $$
  select to_json(encode(uuid_send($1),'base64'));
$$ LANGUAGE SQL IMMUTABLE;

-- check it works
select json('846c4ffd-92ce-4de7-8d11-8e29929f4ec4'::app_uuid);
            json
----------------------------
 "hGxP/ZLOTeeNEY4pkp9OxA=="

然后创建一个CAST,告诉PostgREST在请求JSON响应时自动转换它。

CREATE CAST (app_uuid AS json) WITH FUNCTION json(app_uuid) AS IMPLICIT;

这样,您就可以获得缩写格式的数据。

curl "http://localhost:3000/profiles" \
  -H "Accept: application/json"
[{"id":"hGxP/ZLOTeeNEY4pkp9OxA==","name":"John Doe"}]

备注

  • 域上的强制转换被PostgreSQL忽略,它们的解释留给应用程序。我们正在讨论将域表示行为包括在 pgsql-hackers

  • 使用它会更有意义 base58 编码因为它是URL友好的,但为了简单起见,我们使用 base64 (在PostgreSQL中本机支持)。

重要

在域上创建CAST后,您必须刷新PostgREST架构缓存。看见 架构缓存重新加载

域筛选格式#

水平滤波 要使用缩短格式,您需要不同的转换。

PostgREST认为URL查询字符串在最一般的意义上是, text 。因此,让我们创建一个函数来将 textapp_uuid

-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION app_uuid(text) RETURNS app_uuid AS $$
  select substring(decode($1,'base64')::text from 3)::uuid;
$$ LANGUAGE SQL IMMUTABLE;

-- plus a CAST to tell PostgREST to use this function
CREATE CAST (text AS app_uuid) WITH FUNCTION app_uuid(text) AS IMPLICIT;

现在,您可以像往常一样进行筛选。

curl "http://localhost:3000/profiles?id=eq.ZLOTeeNEY4pkp9OxA==" \
  -H "Accept: application/json"
[{"id":"hGxP/ZLOTeeNEY4pkp9OxA==","name":"John Doe"}]

备注

如果没有演员阵容 textapp_uuid 定义后,过滤器仍将使用本机UUID格式 (846c4ffd-92ce-4de7-8d11-8e29929f4ec4 )。

域请求正文格式#

要在JSON请求正文中接受缩短格式,例如在创建新记录时,请定义 jsonapp_uuid 转换。

-- the name of the function is arbitrary
CREATE OR REPLACE FUNCTION app_uuid(json) RETURNS public.app_uuid AS $$
  -- here we reuse the previous app_uuid(text) function
  select app_uuid($1 #>> '{}');
$$ LANGUAGE SQL IMMUTABLE;

CREATE CAST (json AS public.app_uuid) WITH FUNCTION app_uuid(json) AS IMPLICIT;

现在我们可以 插入 (或 更新 )像往常一样。

curl "http://localhost:3000/profiles" \
  -H "Prefer: return=representation" \
  -H "Content-Type: application/json" \
  -d @- <<JSON

{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}

JSON

他们的回应是:

[{"id":"zH7HbFJUTfy/GZpwuirpuQ==","name":"Jane Doe"}]

请注意,在数据库端,我们有我们的常规 uuid 格式化。

select * from profiles;

                  id                  |   name
--------------------------------------+----------
 846c4ffd-92ce-4de7-8d11-8e29929f4ec4 | John Doe
 cc7ec76c-5254-4dfc-bf19-9a70ba2ae9b9 | Jane Doe
(2 rows)

备注

如果没有演员阵容 jsonapp_uuid 定义后,请求正文仍将使用本机UUID格式 (cc7ec76c-5254-4dfc-bf19-9a70ba2ae9b9 )。

相对于视图的优势#

Views 还允许我们更改基础类型的格式。然而,它们也伴随着增加复杂性的缺点。

  1. 在视图中设置列的格式使其成为 non-updatable 因为波斯格雷斯不知道如何逆转这种转变。这可以使用INSTEAD OF触发器来解决。

  2. 当按此列进行过滤时,出于相同的原因,我们会获得完整的表扫描(也适用于 计算字段 )。这里的性能损失可以通过计算索引或使用物化生成的列来避免。

  3. 如果格式化的列用作外键,则PostgREST无法再检测到该关系,并且 资源嵌入 休息一下。这可以通过以下方式解决 计算出的关系

域表示避免了所有上述缺点。它们唯一的缺点是对于现有的表,您必须更改列类型。但这应该是一个快速的操作,因为域对其底层类型是二进制强制的。将不需要重写表。

备注

为什么不创建一个 base type 取而代之的是? CREATE TYPE app_uuid (INTERNALLENGTH = 22, INPUT = app_uuid_parser, OUTPUT = app_uuid_formatter)

创建基本类型需要超级用户,而这在云托管数据库中是受限的。此外,这种方式让“数据是如何呈现的”决定了“数据是如何存储的”,这将是反向的。