Rails 5 在 jsonb 嵌套数组中搜索 2 个其他日期之间的日期

问题描述 投票:0回答:1

我正在尝试编写一个查询来在 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")
ruby-on-rails postgresql jsonb
1个回答
2
投票

你可以尝试这样做:

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 中。

© www.soinside.com 2019 - 2024. All rights reserved.