jsonruby-on-railspostgresqlarel

How to extract JSON element within an Arel query on Postgres (Rails 5.2.4)?


I defined a helper to feed a drop down select field in a HTML form. I have a quite simple query, except that the name field has the JSON data type. It contains a name translated in several languages:

{"en":"New", "fr":"Nouveau", "de":"Neue"}

With PostgreSQL, extracting the desired translation is strait forward. The case here is simple, and the ->> operator, when passed the JSON key, returns directly a text data :

SELECT  parameters.id, 
parameters.code, 
parameters.sort_code, 
parameters.property, 
name ->> 'en' as name 
FROM dqm_app.parameters 
WHERE parameters.parameters_list_id = 3

I tried to reproduce the query with Arel syntax:

options = Parameter.
            select(:id, 
                   :code, 
                   :sort_code, 
                   :property, 
                   Arel::Nodes::SqlLiteral.new("name ->> 'en' as name")).
            where("parameters_list_id = ?", 3)

Unfortunately, even though the generated query is identical to the SQL query, this returns a null value for the name field, or no value if I omit the as name statement.

Note: When writing the query without Arel statement, the result is the same:

options = Parameter.
            select("id, code, sort_code, property, (name ->> 'en') as name").
            where("parameters_list_id = ?", 3)

I am suspecting that the model is waiting for a JSON data, but gets a text for the name field, and doesn't know how to manage it. How can I solve this?

Thanks a lot!


Solution

  • According to the PostgreSQL documentation, extracting a text from a simple JSON uses the operator ->> in the form:

    json ->> text → text

    jsonb ->> text → text

    Extracts JSON object field with the given key, as text.

    '{"a":1,"b":2}'::json ->> 'b' → 2

    But in the case described above, where Rails expects a JSON field, the syntax that works is name -> 'en'. The value returned by this form has the JSON data type from the PostgreSQL point of view, and results in the expected String attribute after Rails processing the returned value.

    The solution to my issue is:

    options = Parameter.
                select("id, code, sort_code, property, (name -> 'en') as name").
                where("parameters_list_id = ?", 3)