Module: ActiveRecordExtended::QueryMethods::Json

Defined in:
lib/active_record_extended/query_methods/json.rb

Defined Under Namespace

Classes: JsonChain

Constant Summary collapse

JSON_QUERY_METHODS =
[
  :select_row_to_json,
  :json_build_object,
  :jsonb_build_object,
  :json_build_literal,
  :jsonb_build_literal
].freeze

Instance Method Summary collapse

Instance Method Details

#json_build_literal(*args) ⇒ Object

Appends a hash literal to the calling relations response

Arguments: Requires an Array or Hash set of values

Options:

- as: [Symbol or String] (default="results"): What the column will be aliased to

Example:

- Supplying inputs as a Hash
    query = User.json_build_literal(number: 1, last_name: "json", pi: 3.14)
    query.take.results #=> { "number" => 1, "last_name" => "json", "pi" => 3.14 }

- Supplying inputs as an Array

    query = User.json_build_literal(:number, 1, :last_name, "json", :pi, 3.14)
    query.take.results #=> { "number" => 1, "last_name" => "json", "pi" => 3.14 }


318
319
320
# File 'lib/active_record_extended/query_methods/json.rb', line 318

def json_build_literal(*args)
  JsonChain.new(spawn).json_build_literal!(args)
end

#json_build_object(key, from, **options) ⇒ Object

Creates a json response object that will convert all subquery results into a json compatible response

Arguments:

key: [Symbol or String]: What should this response return as
from: [String, Arel, or ActiveRecord::Relation] : A subquery that can be nested into the top-level from clause

Options:

- as: [Symbol or String] (default="results"): What the column will be aliased to

- value: [Symbol or String] (defaults=[key]): How the response should handel the json value return

Example:

 - Generic example:

 subquery = Group.select(:name, :category_id).where("user_id = users.id")
 User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
   #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]

- Setting a custom value:

 Before:
     subquery = User.select(:name).where(id: 100..110).group(:name)
     User.build_json_object(:gang_members, subquery).take.results["gang_members"] #=> nil

 After:
  User.build_json_object(:gang_members, subquery, value: "COALESCE(array_agg(\"gang_members\"), 'BANG!')")
      .take
      .results["gang_members"] #=> "BANG!"


287
288
289
290
291
# File 'lib/active_record_extended/query_methods/json.rb', line 287

def json_build_object(key, from, **options)
  options[:key]  = key
  options[:from] = from
  JsonChain.new(spawn).json_build_object!(options)
end

#jsonb_build_literal(*args) ⇒ Object



322
323
324
# File 'lib/active_record_extended/query_methods/json.rb', line 322

def jsonb_build_literal(*args)
  JsonChain.new(spawn).jsonb_build_literal!(args)
end

#jsonb_build_object(key, from, **options) ⇒ Object



293
294
295
296
297
# File 'lib/active_record_extended/query_methods/json.rb', line 293

def jsonb_build_object(key, from, **options)
  options[:key]  = key
  options[:from] = from
  JsonChain.new(spawn).jsonb_build_object!(options)
end

#select_row_to_json(from = nil, **options, &block) ⇒ Object

Appends a select statement that contains a subquery that is converted to a json response

Arguments:

- from: [String, Arel, or ActiveRecord::Relation] A subquery that can be nested into a ROW_TO_JSON clause

Options:

- as: [Symbol or String] (default="results"): What the column will be aliased to

- key: [Symbol or String] (default=[random letter]) What the row clause will be set as.
      - This is useful if you would like to add additional mid-level clauses (see mid-level scope example)

- cast_with [Symbol or Array of symbols]: Actions to transform your query
  * :to_jsonb
  * :array
  * :array_agg (including just :array with this option will favor :array_agg)
  * :distinct  (auto applies :array_agg & :to_jsonb)

- order_by [Symbol or hash]: Applies an ordering operation (similar to ActiveRecord #order)
  - NOTE: this option will be ignored if you need to order a DISTINCT Aggregated Array,
          since postgres will thrown an error.

Examples:

 subquery = Group.select(:name, :category_id).where("user_id = users.id")
 User.select(:name, email).select_row_to_json(subquery, as: :users_groups, cast_with: :array)
   #=> [<#User name:.., email:.., users_groups: [{ name: .., category_id: .. }, ..]]

- Adding mid-level scopes:

 subquery = Group.select(:name, :category_id)
 User.select_row_to_json(subquery, key: :group, cast_with: :array) do |scope|
   scope.where(group: { name: "Nerd Core" })
 end
  #=>  ```sql
     SELECT ARRAY(
           SELECT ROW_TO_JSON("group")
           FROM(SELECT name, category_id FROM groups) AS group
           WHERE group.name = 'Nerd Core'
     )
  ```
  • Array of JSONB objects

    subquery = Group.select(:name, :category_id) User.select_row_to_json(subquery, key: :group, cast_with: [:array, :to_jsonb]) do |scope|

    scope.where(group: { name: "Nerd Core" })
    

    end #=> “`sql

    SELECT ARRAY(
          SELECT TO_JSONB(ROW_TO_JSON("group"))
          FROM(SELECT name, category_id FROM groups) AS group
          WHERE group.name = 'Nerd Core'
    )
    

    “`

  • Distinct Aggregated Array

    subquery = Group.select(:name, :category_id) User.select_row_to_json(subquery, key: :group, cast_with: [:array_agg, :distinct]) do |scope|

    scope.where(group: { name: "Nerd Core" })
    

    end #=> “`sql

    SELECT ARRAY_AGG(DISTINCT (
          SELECT TO_JSONB(ROW_TO_JSON("group"))
          FROM(SELECT name, category_id FROM groups) AS group
          WHERE group.name = 'Nerd Core'
    ))
    

    “`

  • Ordering a Non-aggregated Array

subquery = Group.select(:name, :category_id)
User.select_row_to_json(subquery, key: :group, cast_with: :array, order_by: { group: { name: :desc } })
#=>  ```sql
   SELECT ARRAY(
         SELECT ROW_TO_JSON("group")
         FROM(SELECT name, category_id FROM groups) AS group
         ORDER BY group.name DESC
   )
```
  • Ordering an Aggregated Array

Subquery = Group.select(:name, :category_id)
User
 .joins(:people_groups)
.select_row_to_json(
   subquery,
   key: :group,
   cast_with: :array_agg,
   order_by: { people_groups: :category_id }
 )
 #=>  ```sql
   SELECT ARRAY_AGG((
         SELECT ROW_TO_JSON("group")
         FROM(SELECT name, category_id FROM groups) AS group
         ORDER BY group.name DESC
   ) ORDER BY people_groups.category_id ASC)
 ```

Raises:

  • (ArgumentError)


248
249
250
251
252
253
254
# File 'lib/active_record_extended/query_methods/json.rb', line 248

def select_row_to_json(from = nil, **options, &block)
  from.is_a?(Hash) ? options.merge!(from) : options.reverse_merge!(from: from)
  options.compact!
  raise ArgumentError.new("Required to provide a non-nilled from clause") unless options.key?(:from)

  JsonChain.new(spawn).row_to_json!(**options, &block)
end