Module: ActiveRpc::Rpc::Concerns::QueryBuilder

Extended by:
ActiveSupport::Concern
Includes:
Includable, Paginatable, Ransackable, Scopable, Sortable
Included in:
BaseController, ResourceController
Defined in:
lib/active_rpc/rpc/concerns/query_builder.rb

Overview

The QueryBuilder concern combines all query-related concerns and provides high-level methods for building and executing queries in gRPC controllers.

Examples:

def list_users
  process_request do
    execute_query(User, request.message,
      response_class: ::Core::UserListResponse,
      transformer: :to_rpc_response
    )
  end
end

Instance Method Summary collapse

Methods included from Sortable

#apply_sorting

Methods included from Paginatable

#apply_pagination, #pagination_metadata

Methods included from Includable

#apply_includes

Methods included from Scopable

#apply_scopes

Methods included from Ransackable

#apply_ransack

Instance Method Details

#build_query(base_query, params) ⇒ Object

Build a query with all parameters



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/active_rpc/rpc/concerns/query_builder.rb', line 26

def build_query(base_query, params)
  query = base_query

  # Apply filters in a specific order
  query = apply_where_filters(query, params)
  query = apply_ransack(query, params)
  query = apply_scopes(query, params)
  query = apply_includes(query, params)
  apply_sorting(query, params)

  # TEMPORARILY DISABLE PAGINATION TO TEST BULK LOADING
  # Apply pagination last
  # query = apply_pagination(query, params)
end

#execute_query(query_or_class, request_payload, options = {}) ⇒ Object

Execute a query and return results with pagination metadata



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/active_rpc/rpc/concerns/query_builder.rb', line 42

def execute_query(query_or_class, request_payload, options = {})
  # Determine the base query
  base_query = if query_or_class.is_a?(Class)
                 # If a class is provided, start with all records
                 options[:base_query] || query_or_class.all
  else
                 # If a query is provided, use it directly
                 query_or_class
  end

  # Filter by specific IDs if provided
  if request_payload.respond_to?(:ids) && request_payload.ids.present?
    Rails.logger.info("[DEBUG] IDs found: #{request_payload.ids.inspect} (#{request_payload.ids.class})")

    # Convert Google::Protobuf::RepeatedField to Ruby Array
    ids_array = if request_payload.ids.is_a?(Google::Protobuf::RepeatedField)
                  request_payload.ids.to_a
    else
                  request_payload.ids
    end

    Rails.logger.info("[DEBUG] Converted IDs: #{ids_array.inspect} (#{ids_array.class})")
    base_query = base_query.where(id: ids_array)
  else
    Rails.logger.info("[DEBUG] No IDs found. respond_to?(:ids): #{request_payload.respond_to?(:ids)}, ids.present?: #{request_payload.respond_to?(:ids) ? request_payload.ids.present? : 'N/A'}")
    Rails.logger.info("[DEBUG] Request payload: #{request_payload.inspect}")
  end

  # Apply additional filters if provided
  base_query = options[:additional_filters].call(base_query) if options[:additional_filters].is_a?(Proc)

  # Apply includes from options if provided
  # Note: includes are applied BEFORE scopes to avoid conflicts
  base_query = base_query.includes(options[:includes]) if options[:includes].present?

  # Build the query with all parameters
  query = build_query(base_query, request_payload)

  # Execute the query
  records = query.to_a

  # Transform records if a transformer is provided
  if options[:transformer].is_a?(Proc)
    records = records.map(&options[:transformer])
  elsif options[:transformer] == :to_rpc_response
    records = records.map(&:to_rpc_response)
  end

  # Return the response with pagination metadata
  response_class = options[:response_class]
  if response_class
    paginated_response(response_class, records, query, request_payload, records.size)
  else
    {
      records: records,
      metadata: (query, request_payload, records.size)
    }
  end
end

#paginated_response(response_class, items, query, params, total_count = nil) ⇒ Object

Get the response with pagination metadata



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/active_rpc/rpc/concerns/query_builder.rb', line 103

def paginated_response(response_class, items, query, params, total_count = nil)
  # Get pagination metadata
   = (query, params, total_count)

  # Check if pagination was requested
  pagination_requested = params.respond_to?(:page) && params.respond_to?(:per_page) &&
                         params.page.present? && params.per_page.present?

  # Create the response with items and pagination metadata
  response_class.new(
    items: items,
    total_count: [:total_count],
    page: pagination_requested ? [:page] : 1,
    per_page: pagination_requested ? [:per_page] : [:total_count],
    total_pages: pagination_requested ? [:total_pages] : 1
  )
end