Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision f5df17ca

Von Sven Schöling vor mehr als 7 Jahren hinzugefügt

  • ID f5df17caa4a22bcb8abe4910b837ec86a84ba9f0
  • Vorgänger 2a5c1eb3
  • Nachfolger f1c3810f

LinkedRecord: Batch mode

Unterschiede anzeigen:

SL/DB/Helper/LinkedRecords.pm
7 7
our @EXPORT = qw(linked_records link_to_record);
8 8

  
9 9
use Carp;
10
use List::MoreUtils qw(any);
11
use List::UtilsBy qw(uniq_by);
10 12
use Sort::Naturally;
11 13
use SL::DBUtils;
12 14

  
......
50 52
    my %from_to    = ( from => delete($params{from}) || $both,
51 53
                       to   => delete($params{to})   || $both);
52 54

  
53
    my @records    = (@{ _linked_records_implementation($self, %params, direction => 'from', from => $from_to{from}) },
54
                      @{ _linked_records_implementation($self, %params, direction => 'to',   to   => $from_to{to}  ) });
55
    if ($params{batch} && $params{by_id}) {
56
      my %results;
57
      my @links = (
58
        _linked_records_implementation($self, %params, direction => 'from', from => $from_to{from}),
59
        _linked_records_implementation($self, %params, direction => 'to',   to   => $from_to{to}  ),
60
      );
61

  
62
      for my $by_id (@links) {
63
        for (keys %$by_id) {
64
          $results{$_} = defined $results{$_}
65
                       ? [ uniq_by { $_->id } @{ $results{$_} }, @{ $by_id->{$_} } ]
66
                       : $by_id->{$_};
67
        }
68
      }
69

  
70
      return \%results;
71
    } else {
72
      my @records    = (@{ _linked_records_implementation($self, %params, direction => 'from', from => $from_to{from}) },
73
                        @{ _linked_records_implementation($self, %params, direction => 'to',   to   => $from_to{to}  ) });
55 74

  
56
    my %record_map = map { ( ref($_) . $_->id => $_ ) } @records;
75
      my %record_map = map { ( ref($_) . $_->id => $_ ) } @records;
57 76

  
58
    return [ values %record_map ];
77
      return [ values %record_map ];
78
    }
59 79
  }
60 80

  
61 81
  if ($params{via}) {
......
68 88

  
69 89
  my $sub_wanted_table = "${wanted}_table";
70 90
  my $sub_wanted_id    = "${wanted}_id";
91
  my $sub_myself_id    = "${myself}_id";
71 92

  
72 93
  my ($wanted_classes, $wanted_tables);
73 94
  if ($params{$wanted}) {
......
77 98

  
78 99
  my @get_objects_query = ref($params{query}) eq 'ARRAY' ? @{ $params{query} } : ();
79 100
  my $get_objects       = sub {
80
    my ($link)        = @_;
81
    my $manager_class = SL::DB::Helper::Mappings::get_manager_package_for_table($link->$sub_wanted_table);
82
    my $object_class  = SL::DB::Helper::Mappings::get_package_for_table($link->$sub_wanted_table);
83
    eval "require " . $object_class . "; 1;";
84
    return map {
85
      $_->{_record_link_direction} = $wanted;
86
      $_->{_record_link}           = $link;
87
      $_
88
    } @{
89
      $manager_class->get_all(
90
        query         => [ id => $link->$sub_wanted_id, @get_objects_query ],
101
    my ($links)        = @_;
102
    return [] unless @$links;
103

  
104
    my %classes;
105
    push @{ $classes{ $_->$sub_wanted_table } //= [] }, $_->$sub_wanted_id for @$links;
106

  
107
    my @objs;
108
    for (keys %classes) {
109
      my $manager_class = SL::DB::Helper::Mappings::get_manager_package_for_table($_);
110
      my $object_class  = SL::DB::Helper::Mappings::get_package_for_table($_);
111
      eval "require " . $object_class . "; 1;";
112

  
113
      push @objs, @{ $manager_class->get_all(
114
        query         => [ id => $classes{$_}, @get_objects_query ],
91 115
        (with_objects => $params{with_objects}) x !!$params{with_objects},
92 116
        inject_results => 1,
93
      )
94
    };
117
      ) };
118
    }
119

  
120
    my %objs_by_id = map { $_->id => $_ } @objs;
121

  
122
    for (@$links) {
123
      if ('ARRAY' eq ref $objs_by_id{$_->$sub_wanted_id}->{_record_link}) {
124
        push @{ $objs_by_id{$_->$sub_wanted_id}->{_record_link_direction} }, $wanted;
125
        push @{ $objs_by_id{$_->$sub_wanted_id}->{_record_link          } }, $_;
126
      } elsif ($objs_by_id{$_->$sub_wanted_id}->{_record_link}) {
127
        $objs_by_id{$_->$sub_wanted_id}->{_record_link_direction} = [
128
          $objs_by_id{$_->$sub_wanted_id}->{_record_link_direction},
129
          $wanted,
130
        ];
131
        $objs_by_id{$_->$sub_wanted_id}->{_record_link}           = [
132
          $objs_by_id{$_->$sub_wanted_id}->{_record_link},
133
          $_,
134
        ];
135
      } else {
136
        $objs_by_id{$_->$sub_wanted_id}->{_record_link_direction} = $wanted;
137
        $objs_by_id{$_->$sub_wanted_id}->{_record_link}           = $_;
138
      }
139
    }
140

  
141
    return \@objs;
95 142
  };
96 143

  
97 144
  # If no 'via' is given then use a simple(r) method for querying the wanted objects.
98 145
  if (!$params{via} && !$params{recursive}) {
99 146
    my @query = ( "${myself}_table" => $my_table,
100
                  "${myself}_id"    => $self->id );
147
                  "${myself}_id"    => $params{batch} ? $params{batch} : $self->id );
101 148
    push @query, ( "${wanted}_table" => $wanted_tables ) if $wanted_tables;
102 149

  
103
    return [ map { $get_objects->($_) } @{ SL::DB::Manager::RecordLink->get_all(query => [ and => \@query ]) } ];
150
    my $links = SL::DB::Manager::RecordLink->get_all(query => [ and => \@query ]);
151
    my $objs  = $get_objects->($links);
152

  
153
    if ($params{batch} && $params{by_id}) {
154
      return {
155
        map {
156
          my $id = $_;
157
          $_ => [
158
            grep {
159
              $_->{_record_link}->$sub_myself_id == $id
160
            } @$objs
161
          ]
162
        } @{ $params{batch} }
163
      }
164
    } else {
165
      return $objs;
166
    }
104 167
  }
105 168

  
106 169
  # More complex handling for the 'via' case.
107 170
  if ($params{via}) {
171
    die 'batch mode is not supported with via' if $params{batch};
172

  
108 173
    my @sources = ( $self );
109 174
    my @targets = map { SL::DB::Helper::Mappings::get_table_for_package($_) } @{ ref($params{via}) ? $params{via} : [ $params{via} ] };
110 175
    push @targets, @{ $wanted_tables } if $wanted_tables;
......
118 183
                      "${myself}_id"    => $src->id,
119 184
                      "${wanted}_table" => \@targets );
120 185
        push @new_sources,
121
             map  { $get_objects->($_) }
122
             grep { !$seen{$_->$sub_wanted_table . $_->$sub_wanted_id} }
123
             @{ SL::DB::Manager::RecordLink->get_all(query => [ and => \@query ]) };
186
             @{ $get_objects->([
187
               grep { !$seen{$_->$sub_wanted_table . $_->$sub_wanted_id} }
188
               @{ SL::DB::Manager::RecordLink->get_all(query => [ and => \@query ]) }
189
             ]) };
124 190
      }
125 191

  
126 192
      @sources = @new_sources;
......
134 200

  
135 201
  # And lastly recursive mode
136 202
  if ($params{recursive}) {
203
    my ($id_token, @ids);
204
    if ($params{batch}) {
205
      $id_token = sprintf 'IN (%s)', join ', ', ('?') x @{ $params{batch} };
206
      @ids      = @{ $params{batch} };
207
    } else {
208
      $id_token = '= ?';
209
      @ids      = ($self->id);
210
    }
211

  
137 212
    # don't use rose retrieval here. too slow.
138 213
    # instead use recursive sql to get all the linked record_links entrys, and retrieve the objects from there
139 214
    my $query = <<"";
......
141 216
        SELECT id, from_table, from_id, to_table, to_id,
142 217
          1, ARRAY[id], false
143 218
        FROM record_links
144
        WHERE ${myself}_id = ? and ${myself}_table = ?
219
        WHERE ${myself}_id $id_token and ${myself}_table = ?
145 220
      UNION ALL
146 221
        SELECT rl.id, rl.from_table, rl.from_id, rl.to_table, rl.to_id,
147 222
          rlr.depth + 1, path || rl.id, rl.id = ANY(path)
......
153 228
      WHERE NOT cycle
154 229
      ORDER BY ${wanted}_table, ${wanted}_id, depth ASC;
155 230

  
156
    my $links     = selectall_hashref_query($::form, $::form->get_standard_dbh, $query, $self->id, $self->meta->table);
231
    my $links     = selectall_hashref_query($::form, $::form->get_standard_dbh, $query, @ids, $self->meta->table);
157 232

  
158
    return [] unless @$links;
233
    if (!@$links) {
234
      return $params{by_id} ? {} : [];
235
    }
159 236

  
160 237
    my $link_objs = SL::DB::Manager::RecordLink->get_all(query => [ id => [ map { $_->{id} } @$links ] ]);
161
    my @objects = map { $get_objects->($_) } @$link_objs;
238
    my $objects = $get_objects->($link_objs);
239

  
240
    my %links_by_id = map { $_->{id} => $_ } @$links;
162 241

  
163 242
    if ($params{save_path}) {
164
       my %links_by_id = map { $_->{id} => $_ } @$links;
165
       for (@objects) {
166
         my $link = $links_by_id{$_->{_record_link}->id};
167
         my $intermediate_links = SL::DB::Manager::RecordLink->get_all(query => [ id => $link->{path} ]);
168
         $_->{_record_link_path}     = $link->{path};
169
         $_->{_record_link_obj_path} = [ map { $get_objects->($_) } @$intermediate_links ];
170
         $_->{_record_link_depth}    = $link->{depth};
243
       for (@$objects) {
244
         for my $record_link ('ARRAY' eq ref $_->{_record_link} ? @{ $_->{_record_link} } : $_->{_record_link}) {
245
           my $link = $links_by_id{$record_link->id};
246
           my $intermediate_links = SL::DB::Manager::RecordLink->get_all(query => [ id => $link->{path} ]);
247
           $_->{_record_link_path}     = $link->{path};
248
           $_->{_record_link_obj_path} = $get_objects->($intermediate_links);
249
           $_->{_record_link_depth}    = $link->{depth};
250
         }
171 251
       }
172 252
    }
173 253

  
174
    return \@objects;
254
    if ($params{batch} && $params{by_id}) {
255
      my %link_obj_by_id = map { $_->id => $_ } @$link_objs;
256
      return +{
257
        map {
258
         my $id = $_;
259
         $id => [
260
           grep {
261
             any {
262
               $link_obj_by_id{
263
                 $links_by_id{$_->id}->{path}->[0]
264
                }->$sub_myself_id == $id
265
             } 'ARRAY' eq $_->{_record_link} ? @{ $_->{_record_link} } : $_->{_record_link}
266
           } @$objects
267
         ]
268
        } @{ $params{batch} }
269
      };
270
    } else {
271
      return $objects;
272
    }
175 273
  }
176 274
}
177 275

  
......
470 568

  
471 569
=back
472 570

  
571
Since record_links is comparatively expensive to call, you will want to cache
572
the results for multiple objects if you know in advance you'll need them.
573

  
574
You can pass the optional argument C<batch> with an array ref of ids which will
575
be used instead of the id of the invocant. You still need to call it as a
576
method on a valid object, because table information is inferred from there.
577

  
578
C<batch> mode will currenty not work with C<via>.
579

  
580
The optional flag C<by_id> will return the objects sorted into a hash instead
581
of a plain array. Calling C<<recursive => 1, batch => [1,2], by_id => 1>> on
582
 order 1:
583

  
584
  order 1 --> delivery order 1 --> invoice 1
585
  order 2 --> delivery order 2 --> invoice 2
586

  
587
will give you:
588

  
589
  { 1 => [ delivery order 1, invoice 1 ],
590
    2 => [ delivery order 2, invoice 1 ], }
591

  
592
you may then cache these as you see fit.
593

  
473 594

  
474 595
The optional parameters C<$params{sort_by}> and C<$params{sort_dir}>
475 596
can be used in order to sort the result. If C<$params{sort_by}> is

Auch abrufbar als: Unified diff