Class: ForestLiana::ResourcesGetter

Inherits:
BaseGetter show all
Defined in:
app/services/forest_liana/resources_getter.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseGetter

#get_collection, #get_resource

Constructor Details

#initialize(resource, params, forest_user) ⇒ ResourcesGetter

Returns a new instance of ResourcesGetter.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'app/services/forest_liana/resources_getter.rb', line 5

def initialize(resource, params, forest_user)
  @resource = resource
  @params = params
  @user = forest_user
  @count_needs_includes = false
  @collection_name = ForestLiana.name_for(@resource)
  @collection = get_collection(@collection_name)
  @fields_to_serialize = get_fields_to_serialize
  @field_names_requested = field_names_requested
  @segment = get_segment
  compute_includes
  @search_query_builder = SearchQueryBuilder.new(@params, @includes, @collection, @user)

  prepare_query
  @base_records_for_batch = @records
end

Instance Attribute Details

#includesObject (readonly)

Returns the value of attribute includes.



3
4
5
# File 'app/services/forest_liana/resources_getter.rb', line 3

def includes
  @includes
end

#records_countObject (readonly)

Returns the value of attribute records_count.



3
4
5
# File 'app/services/forest_liana/resources_getter.rb', line 3

def records_count
  @records_count
end

#search_query_builderObject (readonly)

Returns the value of attribute search_query_builder.



3
4
5
# File 'app/services/forest_liana/resources_getter.rb', line 3

def search_query_builder
  @search_query_builder
end

Class Method Details

.fetch_ids(resources_getter) ⇒ Object



261
262
263
264
265
266
# File 'app/services/forest_liana/resources_getter.rb', line 261

def self.fetch_ids(resources_getter)
  ids = []
  resources_getter.query_for_batch.find_in_batches { |records| ids += records.map(&:id) }

  ids
end

.filter_excluded_ids(ids, ids_excluded) ⇒ Object



268
269
270
# File 'app/services/forest_liana/resources_getter.rb', line 268

def self.filter_excluded_ids(ids, ids_excluded)
  ids_excluded ? ids.reject { |id| ids_excluded.map(&:to_s).include?(id.to_s) } : ids
end

.get_ids_from_request(params, user) ⇒ Object



22
23
24
25
26
27
28
29
30
31
# File 'app/services/forest_liana/resources_getter.rb', line 22

def self.get_ids_from_request(params, user)
  attributes = params.dig('data', 'attributes')
  return attributes[:ids] if attributes&.fetch(:all_records, false) == false && attributes[:ids]

  attributes = merge_subset_query(attributes)
  resources_getter = initialize_resources_getter(attributes, user)
  ids = fetch_ids(resources_getter)

  filter_excluded_ids(ids, attributes[:all_records_ids_excluded])
end

.initialize_resources_getter(attributes, user) ⇒ Object



233
234
235
236
237
238
239
# File 'app/services/forest_liana/resources_getter.rb', line 233

def self.initialize_resources_getter(attributes, user)
  if related_data?(attributes)
    HasManyGetter.new(*related_data_params(attributes, user))
  else
    ResourcesGetter.new(SchemaUtils.find_model_from_collection_name(attributes[:collection_name]), attributes, user)
  end
end

.merge_subset_query(attributes) ⇒ Object



229
230
231
# File 'app/services/forest_liana/resources_getter.rb', line 229

def self.merge_subset_query(attributes)
  attributes.merge(attributes[:all_records_subset_query].dup.to_unsafe_h)
end

Returns:

  • (Boolean)


241
242
243
# File 'app/services/forest_liana/resources_getter.rb', line 241

def self.related_data?(attributes)
  attributes[:parent_collection_id] && attributes[:parent_collection_name] && attributes[:parent_association_name]
end


245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'app/services/forest_liana/resources_getter.rb', line 245

def self.related_data_params(attributes, user)
  parent_model = SchemaUtils.find_model_from_collection_name(attributes[:parent_collection_name])
  model = parent_model.reflect_on_association(attributes[:parent_association_name].to_sym)

  [
    parent_model,
    model,
    attributes.merge(
      collection: attributes[:parent_collection_name],
      id: attributes[:parent_collection_id],
      association_name: attributes[:parent_association_name]
    ),
    user
  ]
end

Instance Method Details

#columns_for_cross_database_association(association_name) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/services/forest_liana/resources_getter.rb', line 81

def columns_for_cross_database_association(association_name)
  association = @resource.reflect_on_association(association_name)

  # Always include all columns of the associated model to avoid missing attribute errors
  columns = association.klass.column_names.map(&:to_sym)

  # Ensure the foreign key is present for manual binding (especially for has_one)
  if association.macro == :has_one
    foreign_keys = Array(association.foreign_key).map(&:to_sym)
    columns.concat(foreign_keys)
  end

  columns.uniq
end

#compute_includesObject



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/services/forest_liana/resources_getter.rb', line 96

def compute_includes
  associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)

  @optional_includes = []
  if @field_names_requested && @params['searchExtended'].to_i != 1
    includes = associations_has_one.map(&:name)

    includes_for_smart_search = []
    if @collection && @collection.search_fields
      includes_for_smart_search = @collection.search_fields
                                             .select { |field| field.include? '.' }
                                             .map { |field| field.split('.').first.to_sym }

      includes_has_many = SchemaUtils.many_associations(@resource)
                                     .select { |association| SchemaUtils.model_included?(association.klass) }
                                     .map(&:name)

      includes_for_smart_search = includes_for_smart_search & includes_has_many
    end

    filter_associations = extract_associations_from_filter
    filter_has_many = filter_associations.select do |assoc_name|
      assoc = @resource.reflect_on_association(assoc_name)
      assoc && [:has_many, :has_and_belongs_to_many].include?(assoc.macro)
    end

    @includes = (includes & @field_names_requested).concat(includes_for_smart_search).concat(filter_has_many).uniq
  else
    @includes = associations_has_one
    # Avoid eager loading has_one associations pointing to a different database as ORM can't join cross databases
                  .reject { |association| separate_database?(@resource, association) }
                  .map(&:name)
  end
end

#countObject



50
51
52
# File 'app/services/forest_liana/resources_getter.rb', line 50

def count
  @records_count = @count_needs_includes ? optimized_count : @records.count
end

#includes_for_serializationObject



131
132
133
# File 'app/services/forest_liana/resources_getter.rb', line 131

def includes_for_serialization
  super & @fields_to_serialize.map(&:to_s)
end

#performObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'app/services/forest_liana/resources_getter.rb', line 33

def perform
  polymorphic_association, preload_loads = analyze_associations(@resource)
  includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes
  has_smart_fields = Array(@params.dig(:fields, @collection_name)&.split(',')).any? do |field|
    ForestLiana::SchemaHelper.is_smart_field?(@resource, field)
  end

  if includes.empty? || has_smart_fields
    @records = optimize_record_loading(@resource, @records, false)
  else
    select = compute_select_fields
    @records = optimize_record_loading(@resource, @records, false).references(includes).select(*select)
  end

  @records
end

#query_for_batchObject



54
55
56
# File 'app/services/forest_liana/resources_getter.rb', line 54

def query_for_batch
  @base_records_for_batch
end

#recordsObject



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'app/services/forest_liana/resources_getter.rb', line 58

def records
  records = @records.offset(offset).limit(limit).to_a
  polymorphic_association, preload_loads = analyze_associations(@resource)

  if polymorphic_association.any? && Rails::VERSION::MAJOR >= 7
    preloader = ActiveRecord::Associations::Preloader.new(records: records, associations: polymorphic_association)
    preloader.loaders
    preloader.branches.each do |branch|
      branch.loaders.each do |loader|
        records_by_owner = loader.records_by_owner
        records_by_owner.each do |record, association|
          record_index =  records.find_index { |r| r.id == record.id }
          records[record_index].define_singleton_method(branch.association) do
            association.first
          end
        end
      end
    end
  end

  records
end