有一个单页应用程序使用JSON中的有效负载发送POST HTTP请求:
{"product": { "name": "product A", "quantity": 100 }}
有一个Postgres数据库,它有表和存储过程:
create table product {
product_id serial primary key,
name text,
quantity numeric,
description text
}
create function insert_product (product product) returns product as $$
-- This function accepts a product type as an argument
是否有任何语言的解决方案可以放在服务器上,处理HTTP请求,调用存储过程并自动将JSON对象转换为正确的Postgres行类型?
在伪Express.js
中app.post('/product', (req, res) =>
db.query('select insert_product($1)', [convertToPostgresPlease(req.body.product)])
我不考虑解决方案:
'(' + product.name + ',' + ...
我知道存储过程经常不受欢迎,但对于小项目,老实说我认为它们很棒。 SQL是一种用于处理数据的令人惊叹的DSL,Postgres足以处理任何与数据相关的任务。
在任何情况下,将JSON HTTP请求与正确的SQL RDBMS连接的最简单方法是什么?
找到解决方案(差不多):
答案 0 :(得分:4)
正如Abelisto在评论中提到的,您可以使用json_populate_record
/ jsonb_populate_record
将数据库函数中的JSON / JSONB参数转换为特定的表行。另一种方法是直接使用json变量使用->
和->>
运算符来检索其内容。这样做的缺点是每个表的维护都有相当多的编码。
您也可以从RESTful界面中受益(例如https://github.com/QBisConsult/psql-api)。
基于JSON的大量解决方案的另一个选择是简化大量小型表的操作,这些小型表每个都不会超过几百个记录。会有性能损失,但对于几行,它可能会忽略不计。
以下举例说明了PostgreSQL中JSON数据类型的强大功能以及支持JSON运算符的GIN索引。您仍然可以使用普通表和专用函数来处理需要最高性能的数据。
示例:
CREATE TABLE public.jtables (
table_id serial NOT NULL PRIMARY KEY,
table_name text NOT NULL UNIQUE,
fields jsonb
);
INSERT INTO public.jtables VALUES (default, 'product', '{"id": "number", "name": "string", "quantity": "number", "description": "string"}'::jsonb);
CREATE TABLE public.jdata (
table_id int NOT NULL REFERENCES jtables,
data jsonb NOT NULL
);
CREATE UNIQUE INDEX ON public.jdata USING BTREE (table_id, (data->>'id'));
CREATE INDEX ON public.jdata USING GIN (data);
您可以创建以通用JSON方式操作数据的函数,例如:
CREATE FUNCTION public.jdata_insert(_table text, _data jsonb) RETURNS text AS
$BODY$
INSERT INTO public.jdata
SELECT table_id, $2
FROM public.jtables
WHERE table_name = $1
RETURNING (data)->>'id';
$BODY$ LANGUAGE sql;
CREATE FUNCTION public.jdata_update(_table text, _id text, _data jsonb) RETURNS text AS
$BODY$
UPDATE public.jdata d SET data = jsonb_strip_nulls(d.data || $3)
FROM public.jtables t
WHERE d.table_id = t.table_id AND t.table_name = $1 AND (d.data->>'id') = $2
RETURNING (d.data)->>'id';
$BODY$ LANGUAGE sql;
然后可以使用这些通用函数插入行:
SELECT public.jdata_insert('product', '{"id": 1, "name": "Product 1", "quantity": 10, "description": "no description"}'::jsonb);
SELECT public.jdata_insert('product', '{"id": 2, "name": "Product 2", "quantity": 5}'::jsonb);
SELECT public.jdata_update('product', '1', '{"description": "test product"}'::jsonb);
可以通过各种方式查询他们的数据,这些方式利用了现有的索引:
SELECT * FROM public.jdata WHERE table_id = 1 AND (data->>'id') = '1';
SELECT * FROM public.jdata WHERE table_id = 1 AND data @> '{"quantity": 5}'::jsonb;
SELECT * FROM public.jdata WHERE table_id = 1 AND data ? 'description';
视图可以简化查询:
CREATE VIEW public.vjdata AS
SELECT d.table_id, t.table_name, (d.data->>'id') AS id, d.data
FROM jtables t
JOIN jdata d USING (table_id);
CREATE OR REPLACE FUNCTION public.vjdata_upsert() RETURNS trigger AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
PERFORM public.jdata_insert(NEW.table_name, NEW.data);
ELSE
PERFORM public.jdata_update(NEW.table_name, NEW.id, NEW.data);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER vjdata_upsert_trigger INSTEAD OF INSERT OR UPDATE
ON public.vjdata FOR EACH ROW EXECUTE PROCEDURE public.vjdata_upsert();
UPDATE public.vjdata SET
data = data || jsonb_build_object('quantity', (data->>'quantity')::int + 2)
WHERE table_name = 'product' AND id = '2'
SELECT * FROM public.vjdata WHERE table_name = 'product' AND id = '2';
答案 1 :(得分:0)
退回PostgREST?,顺便说一句,我不知道为什么有人会不喜欢存储的proc。与数据库交互的正确方法是通过视图和功能/过程。在过去的15年中,用纯代码编写SQL只是一件简单的事,这仅仅是由于便利和SQL技能的丧失。对于大多数人来说,设置操作要比过程处理难。