【问题标题】:insert mongo objects into perl array将 mongo 对象插入 perl 数组
【发布时间】:2018-02-12 16:02:08
【问题描述】:

抱歉,如果这是一个新手问题。我对 perl 很陌生。

我从包含查询的数据库中读取了一系列字符串,其中一些用于 mongodb 并且采用 json 格式(松散)。
我正在尝试通过修改现有的 perl 脚本来读取这些字符串并在适当的数据库上运行它们,该脚本对 MySQL 查询执行非常相似的操作。

即读取json字符串,解码成数组,然后在mongodb中运行。

简单的案例工作正常。 问题是某些数据类型(例如 ISODate)需要在运行查询之前转换为 perl 对象。例如:

my $qstr = 'MONGOQUERY[
    {
        $match: {
         "lastModifiedDate": "ISODate("2016-08-10T04:55:46.053+0000")"
        }
    },
    {
        $project: {
           name:1
        }
    },
]';

$qstr=~s/MONGOQUERY//gs;

my $qarrayref = JSON::PP->new->allow_nonref->relaxed->allow_barekey->loose->decode($qstr);

my $mongores = $collection->aggregate( $qarrayref,{ allowDiskUse => 1 } );

我的解决方法是在运行查询之前用适当的匹配 perl 对象查找并替换数组中那些指定数据类型(Timestamp、ISODate、NumberDecimal、NumberLong、NumberInt、Symbol)的所有实例,但因为数组不一定一致的深度,我不知道该怎么做。有什么想法或替代解决方法吗?

并不是说它会更好,但请注意,如果我删除引号并有:

"lastModifiedDate": ISODate("2016-08-10T04:55:46.053+0000")

json 解码部分甚至不起作用。

【问题讨论】:

  • 为什么不能简单地将值作为字符串传递? IIRC 的 ISODate 函数应该是 MongoDB 理解的吧?
  • 我试过了,但是当我使用时:"lastModifiedDate":"ISODate("2016-08-10T04:55:46.053+0000")" 似乎试图将它与字符串进行比较"ISODate("2016-08-10T04:55:46.053+0000")" 而不是实际日期。如果这可行,那将是理想的,但如以下链接所示,它不stackoverflow.com/questions/23709428/query-isodate-time-in-perl
  • 即使您使用标量引用?如\qq{ISODate(2016-08-10T04:55:46.053+0000")} ?
  • ISODate() 是 shell 中 Javascript 中的辅助函数。它不被数据库解析。对于 Perl,您需要创建一个受支持的日期时间对象:请参阅 MongoDB::DataTypes
  • @xdg,我认为是这种情况(尽管我希望有一个替代方案),但是我如何遍历数组以用新的日期时间对象替换 ISODate 的每个实例?你能提供一个示例代码吗?

标签: mongodb perl


【解决方案1】:

我原以为 CPAN 上有一个简单的模块可以递归地遍历结构并让您转换值,但由于我没有找到,我写了一个新模块:Data::Visitor::Tiny

use 5.010;
use strict;
use warnings;

use Data::Dumper;
use Data::Visitor::Tiny;
use Time::Moment;

my $ref = [
    {
        '$match' => { "lastModifiedDate" => "ISODate(\"2016-08-10T04:55:46.053+0000\")", }
    },
    { '$project' => { name => 1 } },
];

my $visitor = sub {
    my (undef, $valueref) = @_;

    # skip over values that are just nested hash/array refs
    return if ref;

    # match values you want to replace
    return unless /^ISODate\("([^"]+)"\)$/;

    # convert captured string to Time::Moment
    $$valueref = Time::Moment->from_string($1, lenient => 1);
};

visit( $ref, $visitor);

say Dumper($ref);
say "Date parsed as: " . $ref->[0]{'$match'}{lastModifiedDate};

结果:

$VAR1 = [
          {
            '$match' => {
                          'lastModifiedDate' => bless( do{\(my $o = 'R¸=œ���@∑(����')}, 'Time::Moment' )
                        }
          },
          {
            '$project' => {
                            'name' => 1
                          }
          }
        ];

Date parsed as: 2016-08-10T04:55:46.053Z

【讨论】:

    【解决方案2】:

    感谢 xdg,我也很惊讶没有找到用于遍历的东西。

    在 perlmonks 的人的帮助下,我还写了一些东西(见下文)。

    就像我在 cmets 中提到的那样,更好的解决方案是将 'ISODate(...)' 替换为 '{$dateFromString: {dateString:...}}' 因为这可以在字符串本身中完成,但是作为一个更通用的解决方案,也适用于旧版本的 mongo:

    #get rid of comments
    $qstr=~s/[\/][\/](.*?)$//gms;
    #handle specially declared datatypes
    $qstr=~s/MONGOQUERY//gs;
    $qstr=~s/ISODate\("([^)]*?)\)/"ISODate$1/gs;
    $qstr=~s/NumberDecimal\(([^)]*?)\)/$1/gs;
    $qstr=~s/NumberLong\(([^)]*?)\)/$1/gs;
    $qstr=~s/NumberInt\(([^)]*?)\)/$1/gs;
    #add qr flag for regexes
    $qstr=~s/[\/]([\.\^].*?)([^\\])[\/](i?)/"qr\/$1$2\/$3"/gs;
    
    my $qarrayref = JSON::PP->new->allow_nonref->relaxed->allow_barekey->loose->decode($qry);
    my $rlimit=50;
    repl_mongo(\@{$qarrayref},$rlimit);
    
        sub repl_mongo{
            if($_[1]>0){
                if(ref($_[0]) eq 'ARRAY'){
                    foreach my $item(@{$_[0]}){
                        repl_mongo($item,$_[1]-1 );
                    }
                }elsif(ref($_[0]) eq 'HASH'){
                    foreach my $key (keys %{$_[0]}){
                        repl_mongo($_[0]->{$key},$_[1]-1 );
                    }
                }else{
                    if(index($_[0],"ISODate") >= 0){
                        $_[0] =~s/ISODate//gs;
                        if(index($_[0],"Z") >= 0 || index($_[0],"+")){
                            $_[0] =Time::Moment->from_string($_[0], lenient => 1);
                        }else{
                            $_[0] =Time::Moment->from_string($_[0] . 'Z', lenient => 1);
                        }
                    }
                    #clean up regexes
                    if(index($_[0],"qr/") >= 0){ 
                        $_[0] =~s/qr[\/](.*?)([^\\])[\/](i?)$/$1$2/s;
                        if($3=='i'){
                            $_[0] = qr/$_[0]/i;
                        }else{
                            $_[0] = qr/$_[0]/;
                        }
                    }
                }
            }
        }
    
    my $mongores = $collection->aggregate( $qarrayref,{ allowDiskUse => 1 } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      • 2019-06-28
      • 2013-09-20
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多