File Coverage

blib/lib/Aniki/Schema/Relationship/Fetcher.pm
Criterion Covered Total %
statement 82 93 88.1
branch 16 34 47.0
condition 1 3 33.3
subroutine 14 16 87.5
pod 0 1 0.0
total 113 147 76.8


line stmt bran cond sub pod time code
1             package Aniki::Schema::Relationship::Fetcher;
2 30     30   469 use 5.014002;
  30         105  
3              
4 30     30   161 use namespace::autoclean;
  30         108  
  30         567  
5 30     30   1903 use Mouse v2.4.5;
  30         375  
  30         167  
6              
7             has relationship => (
8             is => 'ro',
9             weak_ref => 1,
10             required => 1,
11             );
12              
13 30     30   23872 use List::MoreUtils qw/pairwise notall/;
  30         214293  
  30         317  
14 30     30   35412 use List::UtilsBy qw/partition_by/;
  30         37562  
  30         2303  
15 30     30   236 use Scalar::Util qw/weaken/;
  30         98  
  30         1340  
16 30     30   12371 use SQL::QueryMaker;
  30         60870  
  30         25632  
17              
18             sub execute {
19 29     29 0 80 my ($self, $handler, $rows, $prefetch) = @_;
20 29 50       87 return unless @$rows;
21              
22 29         53 my %where;
23 29 100       101 if (ref $prefetch eq 'HASH') {
24 2         4 my %prefetch;
25 2         6 for my $key (keys %$prefetch) {
26 2 100       12 if ($key =~ /^\./) {
27 1         5 my $column = $key =~ s/^\.//r;
28 1         4 $where{$column} = $prefetch->{$key};
29             }
30             else {
31 1         4 $prefetch{$key} = $prefetch->{$key};
32             }
33             }
34 2         4 $prefetch = \%prefetch;
35             }
36              
37 29         95 my $relationship = $self->relationship;
38 29         96 my $name = $relationship->name;
39 29         94 my $table_name = $relationship->dest_table_name;
40 29         86 my $has_many = $relationship->has_many;
41 29         50 my @src_columns = @{ $relationship->src_columns };
  29         125  
42 29         54 my @dest_columns = @{ $relationship->dest_columns };
  29         105  
43              
44 29 50 33     172 if (@src_columns == 1 and @dest_columns == 1) {
45 29         62 my $src_column = $src_columns[0];
46 29         55 my $dest_column = $dest_columns[0];
47              
48 29         66 my @src_values = grep defined, map { $_->get_column($src_column) } @$rows;
  50         153  
49 29 50       94 unless (@src_values) {
50             # set empty value
51 0         0 for my $row (@$rows) {
52 0 0       0 $row->relay_data->{$name} = $has_many ? [] : undef;
53             }
54 0         0 return;
55             }
56              
57 29         139 my @related_rows = $handler->select($table_name => {
58             %where,
59             $dest_column => sql_in(\@src_values),
60             }, { prefetch => $prefetch })->all;
61              
62 29     72   220 my %related_rows_map = partition_by { $_->get_column($dest_column) } @related_rows;
  72         431  
63 29         262 for my $row (@$rows) {
64 50         139 my $src_value = $row->get_column($src_column);
65 50 50       134 unless (defined $src_value) {
66             # set empty value
67 0 0       0 $row->relay_data->{$name} = $has_many ? [] : undef;
68 0         0 next;
69             }
70              
71 50         92 my $related_rows = $related_rows_map{$src_value};
72 50 50       205 $row->relay_data->{$name} = $has_many ? $related_rows : $related_rows->[0];
73             }
74              
75 29         102 $self->_execute_inverse(\@related_rows => $rows);
76             }
77             else {
78             # follow slow case...
79 0         0 for my $row (@$rows) {
80 0 0   0   0 next if notall { defined $row->get_column($_) } @src_columns;
  0         0  
81             my @related_rows = $handler->select($table_name => {
82             %where,
83 0     0   0 pairwise { $a => $row->get_column($b) } @dest_columns, @src_columns
  0         0  
84             }, { prefetch => $prefetch })->all;
85 0 0       0 $row->relay_data->{$name} = $has_many ? \@related_rows : $related_rows[0];
86             }
87             }
88             }
89              
90             sub _execute_inverse {
91 29     29   73 my ($self, $src_rows, $dest_rows) = @_;
92 29 100       112 return unless @$src_rows;
93 25 50       60 return unless @$dest_rows;
94              
95 25         104 for my $relationship ($self->relationship->get_inverse_relationships) {
96 25         68 my $name = $relationship->name;
97 25         65 my $has_many = $relationship->has_many;
98 25         41 my @src_columns = @{ $relationship->src_columns };
  25         89  
99 25         47 my @dest_columns = @{ $relationship->dest_columns };
  25         69  
100              
101             my $src_keygen = sub {
102 72     72   123 my $src_row = shift;
103 72 50       122 return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $src_row->get_column($_) } @src_columns;
  72         263  
  72         158  
104 25         92 };
105             my $dest_keygen = sub {
106 44     44   68 my $dest_row = shift;
107 44 50       84 return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $dest_row->get_column($_) } @dest_columns;
  44         233  
  44         145  
108 25         71 };
109              
110 25     44   85 my %dest_rows_map = partition_by { $dest_keygen->($_) } @$dest_rows;
  44         259  
111 25         168 for my $src_row (@$src_rows) {
112 72 50   72   270 next if notall { defined $src_row->get_column($_) } @src_columns;
  72         472  
113 72         459 my $dest_rows = $dest_rows_map{$src_keygen->($src_row)};
114 72 50       234 $src_row->relay_data->{$name} = $has_many ? $dest_rows : $dest_rows->[0];
115 72         434 weaken($src_row->relay_data->{$name});
116             }
117             }
118             }
119              
120             __PACKAGE__->meta->make_immutable();
121             __END__