我正在尝试编写一个查询来在 Ruby on Rails 5 中搜索 jsonb 字段,我需要查找在日期范围内出现的所有记录。
例如。查找在
2017-08-01T12:00:00
和 2017-09-01T12:00:00
之间出现的所有记录。
{
"repeats": {
"rrule": "FREQ=WEEKLY;DTSTART=20170904T030000Z;COUNT=20;INTERVAL=1;WKST=SU;BYDAY=MO",
"occurrences": [
{
"uuid": "5445c067-1377-4636-9844-16b159caff1b",
"date_range": {
"end": "2017-09-04T14:00:00+10:00",
"start": "2017-09-04T13:00:00+10:00",
"timezone": "Australia/Melbourne"
}
},
{
"uuid": "9a9a58ef-d697-4941-94d0-623915af87f3",
"date_range": {
"end": "2017-11-13T14:00:00+11:00",
"start": "2017-11-13T13:00:00+11:00",
"timezone": "Australia/Melbourne"
}
},
{
"uuid": "4a310678-997a-4eb9-8bb9-faef80836c9f",
"date_range": {
"end": "2017-11-27T14:00:00+11:00",
"start": "2017-11-27T13:00:00+11:00",
"timezone": "Australia/Melbourne"
}
}
]
},
"tickets": {
"url": "",
"title": ""
},
}
我很迷失从哪里开始,我所能够实现的就是获得一条记录,其中第一次出现的开始不为空(不是很有用!):
posts = EventPost.where("document -> 'repeats' #> '{occurrences,0}' -> 'date_range' -> 'start' IS NOT NULL")
你可以尝试这样做:
start_datetime = DateTime.new(2017, 9, 4, 13)
end_datetime = DateTime.new(2017, 9, 4, 14)
EventPost.where(%{
EXISTS (
SELECT * FROM jsonb_array_elements(document #> '{repeats,occurrences}') occurrences
WHERE (occurrences -> 'date_range' ->> 'start')::timestamp >= ?
AND (occurrences -> 'date_range' ->> 'end')::timestamp <= ?
)
}, start_datetime, end_datetime)
这将产生以下 SQL:
SELECT "event_posts".* FROM "event_posts" WHERE (
EXISTS (
SELECT * FROM jsonb_array_elements(document #> '{repeats,occurrences}') occurrences
WHERE (occurrences -> 'date_range'->> 'start')::timestamp >= '2017-09-04 13:00:00'
AND (occurrences -> 'date_range' ->> 'end')::timestamp <= '2017-09-04 14:00:00'
)
)
jsonb_array_elements()
将 JSON 数组扩展为行集。在 psql 中,它看起来像这样:
{"uuid": "5445c067-1377-4636-9844-16b159caff1b", "date_range": {"end": "2017-09-04T14:00:00+10:00", "start": "2017-09-04T13:00:00+10:00", "timezone": "Australia/Melbou
rne"}}
{"uuid": "9a9a58ef-d697-4941-94d0-623915af87f3", "date_range": {"end": "2017-11-13T14:00:00+11:00", "start": "2017-11-13T13:00:00+11:00", "timezone": "Australia/Melbou
rne"}}
{"uuid": "4a310678-997a-4eb9-8bb9-faef80836c9f", "date_range": {"end": "2017-11-27T14:00:00+11:00", "start": "2017-11-27T13:00:00+11:00", "timezone": "Australia/Melbou
rne"}}
(3 rows)
从这些行中,我们仅选择那些满足条件的行,如果有,则选择
EventPost
。
但我不认为它是性能上最好的解决方案,因为有子查询、类型转换和 jsonb 处理。 @EJ2015 和 @Michael Chaney 给出了一个很好的建议,不要将
start
和 end
存储在 jsonb 中。