通过嵌套的 jsonb 数组列对相同签名的数组进行过滤
Filter by a nested jsonb array column against the array of the same signature
我的与会者 table 有一个名为 eventfilters
的 jsonb
数组字段。
鉴于与 eventfilters
具有相同签名(表单)的查询过滤器数组,我需要 select 通过查询过滤器值针对此 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
,过滤逻辑如下:
- Select 所有具有
eventfilters
字段的与会者:
- 具有与会者 (
eventfilters
) field: "Org Type"
的 selected
数组包含 selected
数组中具有字段“Org查询过滤器的类型”;
和
- 具有与会者 (
eventfilters
) field: "Industry Sector"
的 selected
数组包含 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 ');
}
or
和and
逻辑上的简单交换,在实现上似乎大不相同。我想使用 jsonpath
来实现它会更容易,但我的 postgres 版本是 11 :(
如何实现这样的过滤?
PS: create table
and insert
复制代码: https://pastebin.com/1tsHyJV0
所以,我四处寻找了一下,得到了以下查询:
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
的那部分,但看起来是可行的。
我的与会者 table 有一个名为 eventfilters
的 jsonb
数组字段。
鉴于与 eventfilters
具有相同签名(表单)的查询过滤器数组,我需要 select 通过查询过滤器值针对此 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
,过滤逻辑如下:
- Select 所有具有
eventfilters
字段的与会者:- 具有与会者 (
eventfilters
)field: "Org Type"
的selected
数组包含selected
数组中具有字段“Org查询过滤器的类型”; 和 - 具有与会者 (
eventfilters
)field: "Industry Sector"
的selected
数组包含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 ');
}
or
和and
逻辑上的简单交换,在实现上似乎大不相同。我想使用 jsonpath
来实现它会更容易,但我的 postgres 版本是 11 :(
如何实现这样的过滤?
PS: create table
and insert
复制代码: https://pastebin.com/1tsHyJV0
所以,我四处寻找了一下,得到了以下查询:
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
的那部分,但看起来是可行的。