如何在Postgres中从JSON选择查询

时间:2019-10-19 22:08:06

标签: json postgresql

我在字段hotel_data中有这样的JSON数据:

{ 
   "title":"foo",
   "description":[ 
      { 
         "locale":"pt",
         "content":"pt text"
      },
      { 
         "locale":"fr",
         "content":"fr text"
      }
   ]
}

我只想选择description个描述。可以使用Postgres,如何使用?

我正尝试使用fr,但是它不起作用...

->>

注意: 我不想使用SELECT hotel_data->'description'->>'locale' = 'fr' AS description FROM hotel LIMIT 1; ...

例外的输出:SELECT *

2 个答案:

答案 0 :(得分:1)

您可以使用横向联接和json_to_recordset将json数组扩展为一组记录。然后,您可以对生成的记录中的列locale进行过滤,最后将新的json对象重组为预期的结果:

select json_build_object('description', d.content) hotel_data_descr_fr
from 
    mytable,
    json_to_recordset(hotel_data->'description') as d("locale" text, "content" text)
where d.locale = 'fr'

Demo on DB Fiddle

with mytable as (
    select '{ 
   "title":"foo",
   "description":[ 
      { 
         "locale":"pt",
         "content":"pt text"
      },
      { 
         "locale":"fr",
         "content":"fr text"
      }
   ]
}'::json hotel_data
)
select json_build_object('description', d.content) hotel_data_descr_fr
from 
    mytable,
    json_to_recordset(hotel_data->'description') as d("locale" text, "content" text)
where d.locale = 'fr'
| hotel_data_descr_fr        |
| :------------------------- |
| {"description": "fr text"} |

答案 1 :(得分:1)

可以使用@>运算符完成过滤,该运算符可以使用hotel_data列上的GIN索引。通常,这比扩展阵列要快。

select ...
from hotel
where hotel_data @> '{"description": [{"locale":"fr"}] }';

这也可以扩展为包括更多属性:

select ...
from hotel
where hotel_data @> '{"description": [{"locale":"fr", "headline": "nice view'}] }';

但是您只能用它在键/值对上表示相等条件。无法使用LIKE。如果要执行此操作,则必须扩展数组,并在WHERE子句中应用条件-请参见GMB的答案。

要提取该描述,我将使用标量子查询:

select (select jsonb_build_object('description', t.descr ->> 'content')
        from jsonb_array_elements(h.hotel_data -> 'description') as t(descr)
        where t.descr ->> 'locale' = 'fr'
        limit 1)
from hotel h
where h.hotel_data @> '{"description": [{"locale":"fr"}] }';

这样一来,您无需扩展数组即可进行过滤,如果只有少数几家酒店符合该条件,我希望这样做会更快。但是它的缺点是您需要在子选择中的语言环境上重复条件。

如果您有多个法语描述,则限制1仅是一个安全网。如果您从未拥有过它,也不会受伤


使用Postgres 12,这更容易:

select jsonb_build_object(
         'description', 
         jsonb_path_query_first(hotel_data, '$.description ? (@.locale == "fr")') -> 'content'
       )
from hotel
where hotel_data @> '{"description": [{"locale":"fr"}] }'

以上所有假设hotel_data是一个jsonb列,如果不是(应该是),则需要对其进行强制转换:hotel_data::jsonb