Class: OccamsRecord::Merge

Inherits:
Object
  • Object
show all
Defined in:
lib/occams-record/merge.rb

Overview

Represents a merge operation to be performed. Merges are always “left” merges. You initialize the Merge with the “left” records, and the name of the attribute into which “right” records will be placed.

After initializing, perform a specific type of merge by calling the appropriate *! method.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target_rows, assoc_attr) ⇒ Merge

Initialize a new Merge operation.

Parameters:

  • target_rows (Array<OccamsRecord::Results::Row] the rows into which associated rows should be merged)

    arget_rows [Array<OccamsRecord::Results::Row] the rows into which associated rows should be merged

  • assoc_attr (String|Symbol)

    name of the attribute where associated rows will be put



18
19
20
21
# File 'lib/occams-record/merge.rb', line 18

def initialize(target_rows, assoc_attr)
  @target_rows = target_rows
  @assign = "#{assoc_attr}="
end

Instance Attribute Details

#target_rowsArray<OccamsRecord::Results::Row> (readonly)

Returns the rows into which associated rows will be merged.

Returns:



10
11
12
# File 'lib/occams-record/merge.rb', line 10

def target_rows
  @target_rows
end

Instance Method Details

#many!(assoc_rows, mapping) ⇒ Object

Merge an array of assoc_rows into the target_rows. Some target_rows may end up with 0 matching associations, and they’ll be assigned empty arrays.

for the associated rows.

Parameters:

  • assoc_rows (Array<OccamsRecord::Results::Row>)

    rows to merge into target_rows

  • mapping (Hash)

    The fields that should match up. The keys are for the target rows and the values



88
89
90
91
92
93
94
95
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
130
131
132
133
# File 'lib/occams-record/merge.rb', line 88

def many!(assoc_rows, mapping)
  target_attrs = mapping.keys
  assoc_attrs = mapping.values

  # Optimized for merges where there's a single mapping key pair (which is the vast majority)
  if mapping.size == 1
    target_attr, assoc_attr = target_attrs[0], assoc_attrs[0]
    begin
      assoc_rows_by_attr = assoc_rows.group_by { |r|
        r.send assoc_attr
      }
    rescue NoMethodError => e
      raise MissingColumnError.new(assoc_rows[0], e.name)
    end

    target_rows.each do |row|
      begin
        pkey = row.send target_attr
      rescue NoMethodError => e
        raise MissingColumnError.new(row, e.name)
      end
      row.send(@assign, assoc_rows_by_attr[pkey] || [])
    end

  # Slower but works with any number of mapping key pairs
  else
    begin
      assoc_rows_by_attrs = assoc_rows.group_by { |r|
        assoc_attrs.map { |attr| r.send attr }
      }
    rescue NoMethodError => e
      raise MissingColumnError.new(assoc_rows[0], e.name)
    end

    target_rows.each do |row|
      begin
        pkeys = target_attrs.map { |attr| row.send attr }
      rescue NoMethodError => e
        raise MissingColumnError.new(row, e.name)
      end
      row.send(@assign, assoc_rows_by_attrs[pkeys] || [])
    end
  end

  nil
end

#single!(assoc_rows, mapping) ⇒ Object

Merge a single assoc_row into each target_rows (or nil if one can’t be found). target_attr and assoc_attr are the matching keys on target_rows and assoc_rows, respectively.

for the associated rows.

Parameters:

  • assoc_rows (Array<OccamsRecord::Results::Row>)

    rows to merge into target_rows

  • mapping (Hash)

    The fields that should match up. The keys are for the target rows and the values



31
32
33
34
35
36
37
38
39
40
41
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
# File 'lib/occams-record/merge.rb', line 31

def single!(assoc_rows, mapping)
  target_attrs = mapping.keys
  assoc_attrs = mapping.values

  # Optimized for merges where there's a single mapping key pair (which is the vast majority)
  if mapping.size == 1
    target_attr, assoc_attr = target_attrs[0], assoc_attrs[0]
    assoc_rows_by_ids = assoc_rows.each_with_object({}) { |assoc_row, acc|
      begin
        id = assoc_row.send assoc_attr
      rescue NoMethodError => e
        raise MissingColumnError.new(assoc_row, e.name)
      end
      acc[id] ||= assoc_row
    }

    target_rows.each do |row|
      begin
        attr = row.send target_attr
      rescue NoMethodError => e
        raise MissingColumnError.new(row, e.name)
      end
      row.send(@assign, attr ? assoc_rows_by_ids[attr] : nil)
    end

  # Slower but works with any number of mapping key pairs
  else
    assoc_rows_by_ids = assoc_rows.each_with_object({}) { |assoc_row, acc|
      begin
        ids = assoc_attrs.map { |attr| assoc_row.send attr }
      rescue NoMethodError => e
        raise MissingColumnError.new(assoc_row, e.name)
      end
      acc[ids] ||= assoc_row
    }

    target_rows.each do |row|
      begin
        attrs = target_attrs.map { |attr| row.send attr }
      rescue NoMethodError => e
        raise MissingColumnError.new(row, e.name)
      end
      row.send(@assign, attrs.any? ? assoc_rows_by_ids[attrs] : nil)
    end
  end

  nil
end