如果这是一个新手问题,请道歉。我对perl很新。
我从DB中读取了一系列包含查询的字符串,其中一些用于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解码部分甚至不起作用。
答案 0 :(得分: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
答案 1 :(得分: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 }