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   493 use 5.014002;
  30         112  
3              
4 30     30   179 use namespace::autoclean;
  30         95  
  30         190  
5 30     30   2137 use Mouse v2.4.5;
  30         392  
  30         521  
6              
7             has relationship => (
8             is => 'ro',
9             weak_ref => 1,
10             required => 1,
11             );
12              
13 30     30   27453 use List::MoreUtils qw/pairwise notall/;
  30         249955  
  30         371  
14 30     30   40986 use List::UtilsBy qw/partition_by/;
  30         43681  
  30         2584  
15 30     30   271 use Scalar::Util qw/weaken/;
  30         115  
  30         1560  
16 30     30   13717 use SQL::QueryMaker;
  30         67820  
  30         28603  
17              
18             sub execute {
19 29     29 0 72 my ($self, $handler, $rows, $prefetch) = @_;
20 29 50       72 return unless @$rows;
21              
22 29         56 my %where;
23 29 100       82 if (ref $prefetch eq 'HASH') {
24 2         4 my %prefetch;
25 2         6 for my $key (keys %$prefetch) {
26 2 100       11 if ($key =~ /^\./) {
27 1         6 my $column = $key =~ s/^\.//r;
28 1         3 $where{$column} = $prefetch->{$key};
29             }
30             else {
31 1         5 $prefetch{$key} = $prefetch->{$key};
32             }
33             }
34 2         6 $prefetch = \%prefetch;
35             }
36              
37 29         76 my $relationship = $self->relationship;
38 29         84 my $name = $relationship->name;
39 29         79 my $table_name = $relationship->dest_table_name;
40 29         81 my $has_many = $relationship->has_many;
41 29         49 my @src_columns = @{ $relationship->src_columns };
  29         101  
42 29         51 my @dest_columns = @{ $relationship->dest_columns };
  29         87  
43              
44 29 50 33     162 if (@src_columns == 1 and @dest_columns == 1) {
45 29         57 my $src_column = $src_columns[0];
46 29         48 my $dest_column = $dest_columns[0];
47              
48 29         58 my @src_values = grep defined, map { $_->get_column($src_column) } @$rows;
  50         141  
49 29 50       80 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         140 my @related_rows = $handler->select($table_name => {
58             %where,
59             $dest_column => sql_in(\@src_values),
60             }, { prefetch => $prefetch })->all;
61              
62 29     72   181 my %related_rows_map = partition_by { $_->get_column($dest_column) } @related_rows;
  72         458  
63 29         242 for my $row (@$rows) {
64 50         124 my $src_value = $row->get_column($src_column);
65 50 50       115 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         96 my $related_rows = $related_rows_map{$src_value};
72 50 50       175 $row->relay_data->{$name} = $has_many ? $related_rows : $related_rows->[0];
73             }
74              
75 29         91 $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   59 my ($self, $src_rows, $dest_rows) = @_;
92 29 100       87 return unless @$src_rows;
93 25 50       60 return unless @$dest_rows;
94              
95 25         105 for my $relationship ($self->relationship->get_inverse_relationships) {
96 25         65 my $name = $relationship->name;
97 25         63 my $has_many = $relationship->has_many;
98 25         45 my @src_columns = @{ $relationship->src_columns };
  25         76  
99 25         42 my @dest_columns = @{ $relationship->dest_columns };
  25         68  
100              
101             my $src_keygen = sub {
102 72     72   110 my $src_row = shift;
103 72 50       126 return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $src_row->get_column($_) } @src_columns;
  72         234  
  72         150  
104 25         86 };
105             my $dest_keygen = sub {
106 44     44   65 my $dest_row = shift;
107 44 50       87 return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $dest_row->get_column($_) } @dest_columns;
  44         219  
  44         105  
108 25         64 };
109              
110 25     44   85 my %dest_rows_map = partition_by { $dest_keygen->($_) } @$dest_rows;
  44         243  
111 25         170 for my $src_row (@$src_rows) {
112 72 50   72   260 next if notall { defined $src_row->get_column($_) } @src_columns;
  72         469  
113 72         405 my $dest_rows = $dest_rows_map{$src_keygen->($src_row)};
114 72 50       227 $src_row->relay_data->{$name} = $has_many ? $dest_rows : $dest_rows->[0];
115 72         409 weaken($src_row->relay_data->{$name});
116             }
117             }
118             }
119              
120             __PACKAGE__->meta->make_immutable();
121             __END__