从 JSON 数组中删除整个 json 对象

Remove entire json object from JSON array

我正在尝试通过删除用户输入要求删除的任何联系人来更新联系人列表。换句话说,试图通过 Node.js 脚本从我的 PostgreSQL 数据库中的 JSON 数组中删除整个 JSON 对象,但我得到错误

error: null value in column "info" of relation "user_emails" violates not-null constraint

我仔细检查了一下,值和一切都在那里。当我在这里 online 尝试时,它可以工作,但在我的服务器上 returns 出现错误。我该如何解决这个问题?

    DROP table if exists user_emails;
CREATE table user_emails (
  id serial not null PRIMARY KEY,
  info jsonb NOT NULL
  );
  insert into user_emails(info) values('{
  "userid": "4",
  "mailbox": "johndoe@example.com",
  "contacts": [
    {
      "id": "ghr3gk8dez4",
      "email": "janedoe@gmail.com",
      "last_name": "Doe",
      "first_name": "Jane",
      "date_created": "2022-05-08T20:52:47.967Z"
    },
    {
      "id": "th2lypvoxpr1652045110763",
      "email": "aldoe@gmail.com",
      "last_name": "Doe",
      "first_name": "Al",
      "date_created": "2022-05-08T21:25:10.763Z"
    },
    {
      "id": "ld123tqicmj1652045372671",
      "email": "stdoe@gmail.com",
      "last_name": "Doe",
      "first_name": "Stella",
      "date_created": "2022-05-08T21:29:32.671Z"
    },
    {
      "id": "1ltbrpbj8xf1652045768004",
      "email": "mdoe@mail.com",
      "last_name": "Doe",
      "first_name": "Marta",
      "date_created": "2022-05-08T21:36:08.004Z"
    },
    {
      "id": "1dgntfwvsmf1652045832589",
      "email": "nala@mail.com",
      "last_name": "La",
      "first_name": "Na",
      "date_created": "2022-05-08T21:37:12.589Z"
    },
    {
      "id": "ll3z1n0jkhc1652045984538",
      "email": "bdoe@mail.com",
      "last_name": "doe",
      "first_name": "bruno",
      "date_created": "2022-05-08T21:39:44.538Z"
    },
    {
      "id": "kzr996xxxt1652046050118",
      "email": "pp@mail.com",
      "last_name": "Perf",
      "first_name": "Perf",
      "date_created": "2022-05-08T21:40:50.118Z"
    },
    {
      "id": "41bovnvsihq1652046121940",
      "email": "mmd@mm.com",
      "last_name": "Doe",
      "first_name": "Melinda",
      "date_created": "2022-05-08T21:42:01.940Z"
    },
    {
      "id": "tnjlj4dcg2b1652046154937",
      "email": "keke@j.com",
      "last_name": "Kee",
      "first_name": "Kee",
      "date_created": "2022-05-08T21:42:34.937Z"
    },
    {
      "id": "hor0wafkuj1652046684582",
      "email": "jojo@mail.com",
      "last_name": "Jo",
      "first_name": "Jo",
      "date_created": "2022-05-08T21:51:24.582Z"
    }
  ],
  "auto_reply": false,
  "email_name": "johndoe",
  "signatures": [],
  "domain_name": "example.com",
  "date_created": "2022-05-08T20:39:54.881Z",
  "forward_email": [],
  "auto_reply_messages": []
}');

这是我的更新

UPDATE user_emails SET info = (SELECT jsonb_agg(j) 
                               FROM jsonb_array_elements(user_emails.info->'contacts') as t(j) 
                               WHERE j ->> 'id' not in ('ghr3gk8dez4'));
                               
SELECT * FROM user_emails;

jsonb_agg,像许多其他聚合函数一样,returns NULL 如果没有要聚合的行。您可能正在寻找 COALESCE 它到一个空数组:

UPDATE user_emails
SET info = jsonb_set(
  user_emails.info,
  '{contacts}',
  COALESCE(
    (SELECT jsonb_agg(j) 
      FROM jsonb_array_elements(user_emails.info->'contacts') as t(j) 
      WHERE j ->> 'id' not in ('ghr3gk8dez4')
    ),
    '[]'::jsonb
  )
);

我认为没有一种有效的方法可以只使用 built-in 个函数来做到这一点。

有一个运算符#-通过指定路径删除元素,例如info #- '{contacts, 0}' 会做你想做的事。但是,直接构建这样一个“路径数组”并不简单。

我会编写一个函数来查找要删除的联系人的索引并生成路径数组:

create or replace function find_entry(p_info jsonb, p_id text)
  returns text[]
as
$$
  select array['contacts', (idx - 1)::text]
  from jsonb_array_elements(p_info -> 'contacts') with ordinality as t(element, idx)
  where t.element ->> 'id' = p_id
  limit 1;
$$
language sql;

-1 是必需的,因为 SQL 使用从 1 开始的编号,但在 JSON 中数组从零开始。

使用该函数,您可以执行以下操作:

update user_emails 
   set info #- find_entry(info, 'ghr3gk8dez4')
where ...