【问题标题】:Why assigning list containing array to array itself causes recursion in perl?为什么将包含数组的列表分配给数组本身会导致 perl 中的递归?
【发布时间】:2018-05-15 20:36:40
【问题描述】:

我正在使用 DBIX::Class 并为这样的搜索生成条件:

my @array;
push @array, { condition1 => 'value1' };
push @array, [ { condition2 => 'value2' }, { condition3 => 'value3' } ];

必须使用 AND 运算符检查所有这些条件,这就是我写这个的原因:

@array = ( -and => @array );

在我的虚拟机上运行具有此类条件的代码后,进程开始使用多达 8 Gb 内存。我认为这是递归问题,我没有弄错。我查看了日志并查看了关于深度递归的记录,但我在互联网上找不到任何关于我的案例的信息。

将包含数组的列表分配给数组本身有问题吗? 或者可能是 DBIX::Class (SQL::Abstract) 的问题?为什么会导致深度递归?

更新。这是项目的真实代码:

sub faq {
    my ( $self ) = @_;
    my @cond;

    if ( $self->param('faq_type') ) {
        push @cond,
        {
            'me.faq_type' => $self->param('faq_type'),
        };
    }

    if ( my $search = $self->param('search') ) {
        push @cond,
        [
            'me.title' => { ilike => "%$search%" },
            'me.text'  => { ilike => "%$search%" },
        ];
    }

    @cond = ( -and => @cond );

    my %attr = (
        join         => 'page_category',
        rows         => $self->param('limit'),
        offset       => $self->param('offset'),
        order_by     => { -desc => 'id' },
        result_class => 'BUX::Util::HashRefInflator',
        '+select'    => [ qw( page_category.name ) ],
        '+as'        => [ qw( category_name ) ],
    );

    my @pages       = BUX::DB->rs('Page')->search( \@cond, \%attr )->all;
    my $total_count = BUX::DB->rs('Page')->count( \@cond );

    return $self->render(json => {
        pages => \@pages,
        count => $total_count
    });
}

以及日志记录:

在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 719 行对子例程“SQL::Abstract::_SWITCH_refkind”进行深度递归。 在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 546 行对子例程“SQL::Abstract::_recurse_where”进行深度递归。 在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 687 行对子例程“SQL::Abstract::_where_ARRAYREF”进行深度递归。 在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 493 行对子例程“SQL::Abstract::_where_HASHREF”进行深度递归。 在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 596 行对子例程“SQL::Abstract::_where_unary_op”进行深度递归。 在 /opt/perlbrew/perls/perl-5.14.4/lib/site_perl/5.14.4/SQL/Abstract.pm 第 645 行对子例程“SQL::Abstract::_where_op_ANDOR”进行深度递归。

附: BUX::DBDBIx::Class 的子类,rsresultset 的快捷方式。

【问题讨论】:

  • 请显示更多您的代码。 @array = (-and => @array);@array = ('-and', @array); 相同,只是将字符串-and 前置到@array;它不是递归的。 (我不知道这对您对 operator and987654333@ 的需求有何帮助)
  • 我的意思是你需要展示更多的代码。正如我所说,@array = (-and => @array); itself'-and' 添加到数组中,没有递归。如您所说,该数组确实被展平为列表。如果您分配\@array,那么这就是它添加的内容。但我们看不到其余代码的作用。
  • 在我看来,您想要推送{ condition2 => 'value2' }, ...,而不是[{condition2 => ...}, {...}] 来获取hashrefs 数组。然后,您应该指定 search({ -and => \@array }) 并且一切顺利。
  • 您可能在循环中使用了该代码,因此您的数组越来越多。请edit 向我们展示更多信息,否则我们无法为您提供帮助。
  • 'me.faq_type' => $self->param('faq_type') 看起来可能容易被注入。 param 来自哪里?

标签: arrays list perl recursion


【解决方案1】:

当指定搜索时应满足的多个条件时 对于 DBIx::Class,通常的方法是传递一个 hashref 以列名作为键,条件作为值。

虽然可以使用 '-and' 关键字指定 hashrefs 的 arrayref,但这通常是不必要的 - 特别是如果您只有一个条件要指定!

注意:我不确定 { -and => @cond } 是否符合您的要求,您是否尝试将其替换为 { -and => \@cond }(注意数组ref)?这可能是 SQL::Abstract 感到困惑的原因,尽管我不确定这最终会如何成为递归。

第二个注意事项:我发现@cond = ( -and => \@cond ) 令人困惑,它可能会造成麻烦。我建议使用传递给搜索的 hashref,因为它应该被调用,并通过调整我的第一个示例来设置 -and 键。

这就是我指定条件的方式:

my $cond;

if ( my $faq_type = $self->param('faq_type') ){
    $cond->{'me.faq_type'} = $faq_type;
}

if ( my $search = $self->param('search') ){
    $cond->{-or} = [
        { $cond->{'me.title'} = { ilike => '%$search%' }, },
        { $cond->{'me.text' } = { ilike => '%$search%' }, },
    ];
}

要考虑的替代方法是,首先指定 'faq_type' 搜索并存储结果 rs,然后根据需要进一步细化,这似乎更符合 DBIx::Class 的精神:

my $pages_rs = BUX::DB->rs('Page');

if ( my $faq_type = $self->param('faq_type') ){
    $pages_rs = $pages_rs->search({ 'me.faq_type' => $faq_type });
}

if ( my $search = $self->param('search') ){
    $pages_rs = $pages_rs->search({
        -or => [
            'me.title' => { ilike => "%$search%" },
            'me.text'  => { ilike => "%$search%" },
        ];
    });
}

my %attr = (
        join         => 'page_category',
        rows         => $self->param('limit'),
        offset       => $self->param('offset'),
        order_by     => { -desc => 'id' },
        result_class => 'BUX::Util::HashRefInflator',
        '+select'    => [ qw( page_category.name ) ],
        '+as'        => [ qw( category_name ) ],
);

$pages_rs = $pages_rs->search( undef, \%attr );

my @pages = $pages_rs->all; # This executes the query

请记住,这是未经测试的,因为我目前没有简单的方法来验证这一点。如果这没有帮助,请随时发表评论,我会尝试解决可能出现的问题。

编辑:为了不留下错误的东西,我删除了我输入的(不相关的)页数。

【讨论】:

  • 对不起,但这不是我的问题的答案。只需稍加修改,这段代码就可以完美运行,这不是我第一次看到并使用这种为 DBIx::Class/SQL::Abstract 创建条件的风格。这种情况非常简单,我同意没有必要使用数组作为所有条件的收集器,但在更大的任务中,它成为唯一的解决方案,特别是当我们需要在一个条件下使用相同的字段两次时。
  • 我的实际问题是关于递归的。在我的代码中的上述情况中,似乎不可能与递归有关,但是我遇到了性能问题并找到了有关深度递归的日志记录。重写代码不是问题——我已经做到了。我只是想了解这种情况如何导致递归。
  • 回答您的问题 - # 为什么不标量 @pages,如果您要获取所有内容,无需再次查询? 因为分页。可以看到%attr hash中有rowsoffset属性。所以,你错了,它没有获取所有记录。
  • 对不起,我想我不应该这么晚才写答案——因为你没有对计数进行分页,这很有意义。不过,我看不到查询应该如何导致深度递归。我不知道你的扩展程序是做什么的,但我会尝试不这样做,看看它会去哪里。
  • 我已经写了一个关于@cond 的注释。我认为给出一个列表是不正确的,我很容易看到解析器感到困惑。
猜你喜欢
  • 2020-10-30
  • 1970-01-01
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多