sqljsonpostgresqljsonbto-char

Use day name from DATE in another field as the key in a JSONB query in postgresql


I have a department table that has a jsonb field that give the hours of operation that looks like:

{
            "Friday": {
                "closeTime": "1970-01-01T17:00:00.000Z",
                "isOpen": true,
                "isOpen24Hours": false,
                "openTime": "1970-01-01T08:00:00.000Z"
            },
            "Monday": {
                "isOpen": false
            },
            "Saturday": {
                "isOpen": false
            },
            "Sunday": {
                "isOpen": false
            },
            "Thursday": {
                "closeTime": "1970-01-01T17:00:00.000Z",
                "isOpen": true,
                "isOpen24Hours": false,
                "openTime": "1970-01-01T08:00:00.000Z"
            },
            "Tuesday": {
                "closeTime": "1970-01-01T17:00:00.000Z",
                "isOpen": true,
                "isOpen24Hours": false,
                "openTime": "1970-01-01T08:00:00.000Z"
            },
            "Wednesday": {
                "closeTime": "1970-01-01T17:00:00.000Z",
                "isOpen": true,
                "isOpen24Hours": false,
                "openTime": "1970-01-01T08:00:00.000Z"
            }
        }

I have another table that associates some activity on a day with a department and I want to know if on the day that the activity happened is the department was open. So how do I query this jsonb field with the day of week of the date from the other table?

I have tried something like:

select a.activity_date, a.department_id, 
d.hours_of_operation->to_char(a.activity_date, 'Day')->>'isOpen' as isOpen
from activity a
join department d on a.department_id = d.id

but that only returns an isOpen value when the activity_date is a Wednesday (which is the last key in the hours_of_operation jsonb)


Solution

  • You probably overlooked blank-padding that comes with Day template:

    DAY full upper case day name (blank-padded to 9 chars)
    Day full capitalized day name (blank-padded to 9 chars)
    day full lower case day name (blank-padded to 9 chars)

    Which means only Wednesday doesn't get any spaces, because it's 9 chars long already. The rest end up as keys that don't match what's in your jsonb structure, because of the additional whitespace characters. The -> returns null in that case. Demo at db<>fiddle:

    select quote_literal(to_char(a.activity_date, 'Day'))
          ,a.activity_date
          ,a.department_id
          ,(d.hours_of_operation->to_char(a.activity_date, 'Day'))->>'isOpen' as isOpen
    from activity a
    join department d on a.department_id = d.id
    
    quote_literal activity_date department_id isopen
    'Wednesday' 2024-04-24 1 true
    'Tuesday ' 2024-04-23 1 null
    'Monday ' 2024-04-22 1 null
    'Sunday ' 2024-04-21 1 null
    'Saturday ' 2024-04-20 1 null
    'Friday ' 2024-04-19 1 null
    'Thursday ' 2024-04-18 1 null
    'Wednesday' 2024-04-17 1 true

    Add a trim() and it'll be fine:

    select quote_literal(trim(to_char(a.activity_date, 'Day')))
          ,a.activity_date
          ,a.department_id
          ,(d.hours_of_operation->trim(to_char(a.activity_date, 'Day')))->>'isOpen' as isOpen
    from activity a
    join department d on a.department_id = d.id
    

    Or add a fill mode FM prefix in your to_char() template:

    Modifier Description Example
    FM prefix fill mode (suppress trailing zeroes and padding blanks) FM99.99
    select quote_literal(to_char(a.activity_date, 'FMDay'))
          ,a.activity_date
          ,a.department_id
          ,(d.hours_of_operation->to_char(a.activity_date, 'FMDay'))->>'isOpen' as isOpen
    from activity a
    join department d on a.department_id = d.id
    
    quote_literal activity_date department_id isopen
    'Wednesday' 2024-04-24 1 true
    'Tuesday' 2024-04-23 1 true
    'Monday' 2024-04-22 1 false
    'Sunday' 2024-04-21 1 false
    'Saturday' 2024-04-20 1 false
    'Friday' 2024-04-19 1 true
    'Thursday' 2024-04-18 1 true
    'Wednesday' 2024-04-17 1 true