将mongo对象插入perl数组

问题描述 投票:0回答:2

如果这是一个新手问题,请道歉。我对perl很新。

我有一系列从DB读取的包含查询的字符串,其中一些用于mongodb并且是json格式(松散地)。 我试图通过修改现有的perl脚本来读取这些字符串并在相应的DB上运行它们,这些脚本执行与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解码部分甚至不起作用。

mongodb perl
2个回答
0
投票

我本以为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

0
投票

谢谢xdg,我也很惊讶没有为遍历找到一些东西。

在perlmonks的家伙的帮助下,我也写了一些东西(见下文)。

就像我在评论中提到的更好的解决方案是将'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 } 
© www.soinside.com 2019 - 2024. All rights reserved.