【问题标题】:Filter by a nested jsonb array column against the array of the same signature通过嵌套的 jsonb 数组列对相同签名的数组进行过滤
【发布时间】:2021-10-22 14:28:07
【问题描述】:

我的参加者表有一个 jsonb 数组字段,名为 eventfilters

鉴于与eventfilters 具有相同签名(表单)的查询过滤器数组,我需要通过查询过滤器值选择针对此eventfilters 字段过滤的与会者。


以下是查询过滤器和eventfilters 字段的示例:

eventfilters 字段如下所示:

[
    {
      "field": "Org Type",
      "selected": ["B2C", "Both", "Nonprofit"]
    },
    {
      "field": "Job Role",
      "selected": ["Customer Experience", "Digital Marketing"]
    },
    {
      "field": "Industry Sector",
      "selected": ["Advertising", "Construction / Development"]
    }
]

查询过滤器可能如下所示:

[
    {
      "field": "Org Type",
      "selected": ["B2C", "Nonprofit"]
    },
    {
      "field": "Industry Sector",
      "selected": ["Advertising"]
    }
]

因此,eventfilters 字段和查询过滤器始终具有相同的签名:

Array<{"field": text, "selected": text[]}>

给定上面的查询过滤器和eventfilters,过滤逻辑如下:

  • 选择具有eventfilters 字段的所有与会者,例如:
    • selected 数组和与会者的field: "Org Type" (eventfilters) 包含selected 数组中存在的任何值以及查询过滤器的“组织类型”字段;
    • selected 数组和与会者的field: "Industry Sector" (eventfilters) 包含selected 数组中存在的任何值以及查询过滤器的“行业部门”字段。

查询过滤器数组可以有不同的长度和不同的元素,但总是具有相同的签名(形式)。


我能想到的是上述逻辑,但不是and 用于查询过滤器中的每个元素,而是or

select distinct attendee.id,
                attendee.email,
                attendee.eventfilters
from attendee cross join lateral jsonb_array_elements(attendee.eventfilters) single_filter
where (
    ((single_filter ->> 'field')::text = 'Org Type' and (single_filter ->> 'selected')::jsonb ?| array ['B2C', 'Nonprofit'])
    or ((single_filter ->> 'field')::text = 'Industry Sector' and (single_filter ->> 'selected')::jsonb ?| array ['Advertising'])
);

基本上我需要将上面查询中where 子句中的or 更改为and,但这显然行不通。

where 子句将动态生成。

这是我现在如何生成它的示例(它是javascript,但我希望你能掌握这个想法):

  function buildEventFiltersWhereSql(eventFilters) {
    return eventFilters.map((filter) => {
      const selectedArray = filter.selected.map((s) => `'${s}'`).join(', ');
      return `((single_filter ->> 'field')::text = '${filter.field}' and (single_filter ->> 'selected')::jsonb ?| array[${selectedArray}])`;
    }).join('\nor ');
  }

orand 在逻辑上的简单交换,在实现上似乎有很大的不同。我想使用jsonpath 来实现它会更容易,但我的 postgres 版本是 11 :(

如何实现这样的过滤?


PS:create tableinsert 复制代码:https://pastebin.com/1tsHyJV0

【问题讨论】:

    标签: postgresql jsonb postgresql-11 cross-join postgresql-json


    【解决方案1】:

    所以,我稍微摸索了一下,得到了以下查询:

    with unnested_filters as (
        select distinct attendee.id,
                        jsonb_agg((single_filter ->> 'selected')::jsonb) filter (where (single_filter ->> 'field')::text = 'Org Type') over (partition by attendee.id) as "Org Type",
                        jsonb_agg((single_filter ->> 'selected')::jsonb) filter (where (single_filter ->> 'field')::text = 'Industry Sector') over (partition by attendee.id) as "Industry Sector",
                        attendee.email,
                        attendee.eventid,
                        attendee.eventfilters
        from attendee
                 cross join lateral jsonb_array_elements(attendee.eventfilters) single_filter
        where eventid = 1
    ) select * from unnested_filters where (
        (unnested_filters."Org Type" #>> '{0}')::jsonb ?| array['B2C', 'Both', 'Nonprofit']
        and (unnested_filters."Industry Sector" #>> '{0}')::jsonb ?| array['Advertising']
    );
    

    这有点奇怪,尤其是jsonb_agg 的部分,但它似乎有效。

    【讨论】:

      猜你喜欢
      • 2020-09-17
      • 1970-01-01
      • 2019-02-17
      • 2021-10-23
      • 2022-01-02
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多