Search code examples
ruby-on-railsjsonpostgresqlarel

Select from database a specific JSON node using Rails/AREL?


Pretend I have a table called books and it has a json type column called misc where some miscellaneous data has been saved. Using Rails/AREL I would like to extract just id and the number of pages (num_pages) from all the books.

In psql I can do the following:

SELECT id, misc->'num_pages' FROM books ;
  id   |   ?column?    
-------+---------------
 23562 | "220"
 22188 | "355"

(Note: the above query produces the same results with --> as with ->).

I expected this to work:

pry> Book.select("id, misc->>'num_pages'")
  Book Load(1.7ms)  SELECT  id, etc-->'num_pages' FROM "books"
  Book Load(1.5ms)  SELECT  id, etc-->'num_pages' FROM "books"
=> #<Book::ActiveRecord_Relation:0x3ff7e934d8f4>

pry> Book.select("id, misc->'num_pages'")
  Book Load(1.1ms)  SELECT  id, etc->'num_pages' FROM "books"
=> [#<Book:0x00007fefd3c11a68 id: 23562>, #<Book:0x00007fefd3c116a8 id: 22188>]

...when I remove the ->'num_pages' part, it returns the entire misc field (so I know it's almost working):

pry> Book.select("id, misc")
  Book Load(0.9ms)  SELECT  id, etc FROM "books"
=> [#<Book:0x00007fefe4e99fb8
  id: 23562,
  etc:
   {"num_pages"=>"220",
...

Solution

  • I can't get it done with .select but .pluck works in this form:

    Book.pluck("id", "misc -> 'num_pages'")
      => [[23562, "220"], [22188,"355"]]
    

    However, this will produce a deprecation warning:

    DEPRECATION WARNING: Dangerous query method (method whose arguments are used 
    as raw SQL) called with non-attribute argument(s): "misc->'num_pages'". 
    Non-attribute arguments will be disallowed in Rails 6.0. This method should
    not be called with user-provided values, such as request parameters or model
    attributes. Known-safe values can be passed by wrapping them in Arel.sql().
    

    So use Arel.sql() as suggested:

    Book.pluck("id", Arel.sql("misc -> 'num_pages'"))
      => [[23562, "220"], [22188,"355"]]