Aniki
view release on metacpan or search on metacpan
lib/Aniki/Schema/Relationship/Fetcher.pm view on Meta::CPAN
package Aniki::Schema::Relationship::Fetcher;
use 5.014002;
use namespace::autoclean;
use Mouse v2.4.5;
has relationship => (
is => 'ro',
weak_ref => 1,
required => 1,
);
use List::MoreUtils qw/pairwise notall/;
use List::UtilsBy qw/partition_by/;
use Scalar::Util qw/weaken/;
use SQL::QueryMaker;
sub execute {
my ($self, $handler, $rows, $prefetch) = @_;
return unless @$rows;
my %where;
if (ref $prefetch eq 'HASH') {
my %prefetch;
for my $key (keys %$prefetch) {
if ($key =~ /^\./) {
my $column = $key =~ s/^\.//r;
$where{$column} = $prefetch->{$key};
}
else {
$prefetch{$key} = $prefetch->{$key};
}
}
$prefetch = \%prefetch;
}
my $relationship = $self->relationship;
my $name = $relationship->name;
my $table_name = $relationship->dest_table_name;
my $has_many = $relationship->has_many;
my @src_columns = @{ $relationship->src_columns };
my @dest_columns = @{ $relationship->dest_columns };
if (@src_columns == 1 and @dest_columns == 1) {
my $src_column = $src_columns[0];
my $dest_column = $dest_columns[0];
my @src_values = grep defined, map { $_->get_column($src_column) } @$rows;
unless (@src_values) {
# set empty value
for my $row (@$rows) {
$row->relay_data->{$name} = $has_many ? [] : undef;
}
return;
}
my @related_rows = $handler->select($table_name => {
%where,
$dest_column => sql_in(\@src_values),
}, { prefetch => $prefetch })->all;
my %related_rows_map = partition_by { $_->get_column($dest_column) } @related_rows;
for my $row (@$rows) {
my $src_value = $row->get_column($src_column);
unless (defined $src_value) {
# set empty value
$row->relay_data->{$name} = $has_many ? [] : undef;
next;
}
my $related_rows = $related_rows_map{$src_value};
$row->relay_data->{$name} = $has_many ? $related_rows : $related_rows->[0];
}
$self->_execute_inverse(\@related_rows => $rows);
}
else {
# follow slow case...
for my $row (@$rows) {
next if notall { defined $row->get_column($_) } @src_columns;
my @related_rows = $handler->select($table_name => {
%where,
pairwise { $a => $row->get_column($b) } @dest_columns, @src_columns
}, { prefetch => $prefetch })->all;
$row->relay_data->{$name} = $has_many ? \@related_rows : $related_rows[0];
}
}
}
sub _execute_inverse {
my ($self, $src_rows, $dest_rows) = @_;
return unless @$src_rows;
return unless @$dest_rows;
for my $relationship ($self->relationship->get_inverse_relationships) {
my $name = $relationship->name;
my $has_many = $relationship->has_many;
my @src_columns = @{ $relationship->src_columns };
my @dest_columns = @{ $relationship->dest_columns };
my $src_keygen = sub {
my $src_row = shift;
return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $src_row->get_column($_) } @src_columns;
};
my $dest_keygen = sub {
my $dest_row = shift;
return join '|', map { defined $_ ? quotemeta $_ : '(NULL)' } map { $dest_row->get_column($_) } @dest_columns;
};
my %dest_rows_map = partition_by { $dest_keygen->($_) } @$dest_rows;
for my $src_row (@$src_rows) {
next if notall { defined $src_row->get_column($_) } @src_columns;
my $dest_rows = $dest_rows_map{$src_keygen->($src_row)};
$src_row->relay_data->{$name} = $has_many ? $dest_rows : $dest_rows->[0];
weaken($src_row->relay_data->{$name});
}
}
}
__PACKAGE__->meta->make_immutable();
__END__
( run in 1.355 second using v1.01-cache-2.11-cpan-99c4e6809bf )