TT#147151 fast loading/paging/searching panel datatables

query refactoring an rowcount clipping for UI datatables
that are slow when using millions of subscribers:

 contacts
 customers
 contracts
 subscribers
 billing profiles
 billing networks
 billing profile packages

Change-Id: Ia50e3aa52684772548569b6908f0cbc08395a5a7
mr10.2
Rene Krenn 4 years ago
parent 26bccf25d3
commit 133bd43df4

@ -32,7 +32,6 @@ sub profile_list :Chained('/') :PathPart('billing') :CaptureArgs(0) :Does(ACL) :
{ name => "id", "search" => 1, "title" => $c->loc("#") },
{ name => "name", "search" => 1, "title" => $c->loc("Name") },
{ name => "reseller.name", "search" => 1, "title" => $c->loc("Reseller") },
#{ name => "v_count_used", "search" => 0, "title" => $c->loc("Used") },
NGCP::Panel::Utils::Billing::get_datatable_cols($c),
]);
@ -48,7 +47,7 @@ sub _profile_resultset_admin {
my $rs = $c->model('DB')->resultset('billing_profiles')->search({
'me.status' => { '!=' => 'terminated' },
},
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
});
@ -62,7 +61,7 @@ sub _profile_resultset_reseller {
->search_rs({
'me.status' => { '!=' => 'terminated' },
},
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
});

@ -31,11 +31,11 @@ sub list_contact :Chained('/') :PathPart('contact') :CaptureArgs(0) :Does(ACL) :
$c->stash(template => 'contact/list.tt');
$c->stash->{contact_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "id", search => 1, title => $c->loc("#") },
{ name => "reseller.name", search => 1, title => $c->loc("Reseller") },
{ name => "firstname", search => 1, title => $c->loc("First Name") },
{ name => "lastname", search => 1, title => $c->loc("Last Name") },
{ name => "company", search => 1, title => $c->loc("Company") },
{ name => "id", int_search => 1, title => $c->loc("#") },
{ name => "reseller.name", search => 0, title => $c->loc("Reseller") },
{ name => "firstname", search => 0, title => $c->loc("First Name") },
{ name => "lastname", search => 0, title => $c->loc("Last Name") },
{ name => "company", search => 0, title => $c->loc("Company") },
{ name => "email", search => 1, title => $c->loc("Email") },
]);
}
@ -311,9 +311,7 @@ sub ajax_list_contacts{
$c,
$c->stash->{contacts}->search_rs(
$reseller_query->[0],
$reseller_query->[1] ? $reseller_query->[1] : {
prefetch=>"contracts"
}
$reseller_query->[1],
),
$c->stash->{contact_dt_columns},
sub {
@ -328,6 +326,7 @@ sub ajax_list_contacts{
my %data = (deletable => ($contract_rs->first or $subscriber_rs->first) ? 0 : 1);
return %data
},
{ 'count_limit' => 1000, },
);
}

@ -25,13 +25,13 @@ sub contract_list :Chained('/') :PathPart('contract') :CaptureArgs(0) {
my $now = NGCP::Panel::Utils::DateTime::current_local;
$c->stash->{contract_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "id", search => 1, title => $c->loc("#") },
{ name => "external_id", search => 1, title => $c->loc("External #") },
{ name => "contact.email", search => 1, title => $c->loc("Contact Email") },
{ name => "product.name", search => 1, title => $c->loc("Product") },
{ name => "id", int_search => 1, title => $c->loc("#") },
{ name => "external_id", strict_search => 1, title => $c->loc("External #") },
{ name => "contact.email", search => 0, title => $c->loc("Contact Email") },
{ name => "product.name", search => 0, title => $c->loc("Product") },
{ name => 'billing_profile_name', accessor => "billing_profile_name", search => 0, title => $c->loc('Billing Profile'),
literal_sql => NGCP::Panel::Utils::BillingMappings::get_actual_billing_mapping_stmt(c => $c, now => $now, projection => 'billing_profile.name' ) },
{ name => "status", search => 1, title => $c->loc("Status") },
{ name => "status", search => 0, title => $c->loc("Status") },
]);
my $rs = NGCP::Panel::Utils::Contract::get_contract_rs(
@ -50,13 +50,11 @@ sub contract_list :Chained('/') :PathPart('contract') :CaptureArgs(0) {
join => 'contact',
});
}
my @product_ids = map { $_->id; } $c->model('DB')->resultset('products')->search_rs({ 'class' => ['pstnpeering','sippeering','reseller'] })->all;
$rs = $rs->search({
'-or' => [
'product.class' => 'pstnpeering',
'product.class' => 'sippeering',
'product.class' => 'reseller',
],
});
'product_id' => { -in => [ @product_ids ] },
});
$c->stash(contract_select_rs => $rs);
$c->stash(contract_select_all_rs => $rs_all);
$c->stash(now => $now);
@ -297,7 +295,18 @@ sub ajax :Chained('contract_list') :PathPart('ajax') :Args(0) {
my ($self, $c) = @_;
my $res = $c->stash->{contract_select_rs};
NGCP::Panel::Utils::Datatables::process($c, $res, $c->stash->{contract_dt_columns});
NGCP::Panel::Utils::Datatables::process($c, $res, $c->stash->{contract_dt_columns}, undef, {
'count_limit' => 1000,
'extra_or' => [ {
'me.id' => { in => [ map { $_->id } $res->search({
'contact.email' => { like => [ NGCP::Panel::Utils::Datatables::get_search_string_pattern($c) ]->[0] },
},{
rows => 1001,
page => 1,
join => 'contact',
})->all ] },
}, ],
});
$c->detach( $c->view("JSON") );
}

@ -45,18 +45,18 @@ sub list_customer :Chained('/') :PathPart('customer') :CaptureArgs(0) {
my $now = NGCP::Panel::Utils::DateTime::current_local;
$c->stash->{contract_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "id", search => 1, title => $c->loc("#") },
{ name => "external_id", search => 1, title => $c->loc("External #") },
{ name => "contact.reseller.name", search => 1, title => $c->loc("Reseller") },
{ name => "contact.email", search => 1, title => $c->loc("Contact Email") },
{ name => "contact.firstname", search => 1, title => '' },
{ name => "contact.lastname", search => 1, title => $c->loc("Name"),
{ name => "id", int_search => 1, title => $c->loc("#") },
{ name => "external_id", strict_search => 1, title => $c->loc("External #") },
{ name => "contact.reseller.name", search => 0, title => $c->loc("Reseller") },
{ name => "contact.email", search => 0, title => $c->loc("Contact Email") },
{ name => "contact.firstname", search => 0, title => '' },
{ name => "contact.lastname", search => 0, title => $c->loc("Name"),
custom_renderer => 'function ( data, type, full, opt ) { var sep = (full.contact_firstname && full.contact_lastname) ? " " : ""; return (full.contact_firstname || "") + sep + (full.contact_lastname || ""); }' },
{ name => "product.name", search => 1, title => $c->loc("Product") },
{ name => "product.name", search => 0, title => $c->loc("Product") },
{ name => 'billing_profile_name', accessor => "billing_profile_name", search => 0, title => $c->loc('Billing Profile'),
literal_sql => '""' },
{ name => "status", search => 1, title => $c->loc("Status") },
{ name => "max_subscribers", search => 1, title => $c->loc("Max. Subscribers") },
{ name => "status", search => 0, title => $c->loc("Status") },
{ name => "max_subscribers", search => 0, title => $c->loc("Max. Subscribers") },
]);
my $rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); #, now => $now);
@ -88,7 +88,18 @@ sub ajax :Chained('list_customer') :PathPart('ajax') :Args(0) {
my $bm_actual = NGCP::Panel::Utils::BillingMappings::get_actual_billing_mapping(c => $c, contract => $item, now => $now, );
$result{'billing_profile_name'} = $bm_actual->billing_profile->name if $bm_actual;
return %result;
},);
}, {
'count_limit' => 1000,
'extra_or' => [ {
'me.id' => { in => [ map { $_->id } $res->search({
'contact.email' => { like => [ NGCP::Panel::Utils::Datatables::get_search_string_pattern($c) ]->[0] },
},{
rows => 1001,
page => 1,
join => 'contact',
})->all ] },
}, ],
});
$c->detach( $c->view("JSON") );
}

@ -47,7 +47,7 @@ sub _network_resultset_admin {
})->search_rs({
'me.status' => { '!=' => 'terminated' },
},
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
});
@ -65,7 +65,7 @@ sub _network_resultset_reseller {
})->search_rs({
'me.status' => { '!=' => 'terminated' },
},
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
});

@ -47,7 +47,7 @@ sub _package_resultset_admin {
{ group_by => 'me.id',
})->search_rs(
undef,
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' },
],
});
@ -64,7 +64,7 @@ sub _package_resultset_reseller {
{ group_by => 'me.id',
})->search_rs(
undef,
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' },
{ '+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' },
],
});

@ -91,16 +91,16 @@ sub sub_list :Chained('/') :PathPart('subscriber') :CaptureArgs(0) {
}
$c->stash->{dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "id", search => 1, title => $c->loc('#') },
{ name => "contract_id", search => 1, title => $c->loc('Contract #') },
{ name => "contract.contact.email", search => 1, title => $c->loc('Contact Email') },
{ name => "username", search => 1, title => $c->loc('Username') },
{ name => "domain.domain", search => 1, title => $c->loc('Domain') },
{ name => "uuid", search => 1, title => $c->loc('UUID') },
{ name => "status", search => 1, title => $c->loc('Status') },
{ name => "id", int_search => 1, title => $c->loc('#') },
{ name => "contract_id", int_search => 1, title => $c->loc('Contract #') },
{ name => "contract.contact.email", search => 0, title => $c->loc('Contact Email') },
{ name => "username", strict_search => 1, title => $c->loc('Username') },
{ name => "domain.domain", search => 0, title => $c->loc('Domain') },
{ name => "uuid", strict_search => 1, title => $c->loc('UUID') },
{ name => "status", search => 0, title => $c->loc('Status') },
{ name => "number", search => 0, title => $c->loc('Number'), literal_sql => "concat(primary_number.cc, primary_number.ac, primary_number.sn)" },
#{ name => "provisioning_voip_subscriber.voip_dbaliases.username", search => 1, 'no_column' => 1 },
{ name => "provisioning_voip_subscriber.voip_subscriber_profile.name", search => 1, title => $c->loc('Profile') },
{ name => "provisioning_voip_subscriber.voip_subscriber_profile.name", search => 0, title => $c->loc('Profile') },
]);
}
@ -365,7 +365,18 @@ sub ajax :Chained('sub_list') :PathPart('ajax') :Args(0) :Does(ACL) :ACLDetachTo
my ($self, $c) = @_;
my $resultset = $c->stash->{subscribers_rs};
NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{dt_columns}, undef, { 'count_limit' => 1000, });
NGCP::Panel::Utils::Datatables::process($c, $resultset, $c->stash->{dt_columns}, undef, {
'count_limit' => 1000,
'extra_or' => [ {
'me.id' => { in => [ map { $_->id } $resultset->search({
'contact.email' => { like => [ NGCP::Panel::Utils::Datatables::get_search_string_pattern($c) ]->[0] },
},{
rows => 1001,
page => 1,
join => { 'contract' => 'contact'},
})->all ] },
}, ],
});
$c->detach( $c->view("JSON") );
}

@ -70,18 +70,25 @@ sub hal_from_item {
sub _item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('billing_networks')->search_rs({ 'me.status' => { '!=' => 'terminated' } });
my $item_rs = $c->model('DB')->resultset('billing_networks')->search_rs();
my $search_xtra = {
'+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt() ] , -as => 'contract_cnt' },
'+select' => [ { '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::BillingNetworks::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
};
if($c->user->roles eq "admin") {
$item_rs = $item_rs->search(undef,
$search_xtra);
$item_rs = $item_rs->search({
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({ reseller_id => $c->user->reseller_id },
$search_xtra);
$item_rs = $item_rs->search({
reseller_id => $c->user->reseller_id,
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
} else {
$item_rs = $item_rs->search({
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
}
return $item_rs;
}

@ -19,21 +19,24 @@ sub _item_rs {
my $item_rs = $c->model('DB')->resultset('billing_profiles');
my $search_xtra = {
'+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt() ] , -as => 'contract_cnt' },
'+select' => [ { '' => \[ NGCP::Panel::Utils::Billing::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_contract_exists_stmt() ] , -as => 'contract_exists' },
{ '' => \[ NGCP::Panel::Utils::Billing::get_package_count_stmt() ] , -as => 'package_cnt' }, ],
};
if($c->user->roles eq "admin") {
$item_rs = $item_rs->search({ 'me.status' => { '!=' => 'terminated' } },
$search_xtra);
$item_rs = $item_rs->search({
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({ reseller_id => $c->user->reseller_id,
'me.status' => { '!=' => 'terminated' } },
$search_xtra);
$item_rs = $item_rs->search({
reseller_id => $c->user->reseller_id,
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
} else {
$item_rs = $item_rs->search({ reseller_id => $c->user->contract->contact->reseller_id,
'me.status' => { '!=' => 'terminated' } },
$search_xtra);
$item_rs = $item_rs->search({
reseller_id => $c->user->contract->contact->reseller_id,
'me.status' => { '!=' => 'terminated' },
}, $search_xtra);
}
return $item_rs;
}

@ -90,9 +90,9 @@ sub hal_from_item {
sub _item_rs {
my ($self, $c) = @_;
my $item_rs = $c->model('DB')->resultset('profile_packages')->search_rs(); #{ 'me.status' => { '!=' => 'terminated' } });
my $item_rs = $c->model('DB')->resultset('profile_packages')->search_rs();
my $search_xtra = {
'+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt() ] , -as => 'contract_cnt' },
'+select' => [ { '' => \[ NGCP::Panel::Utils::ProfilePackages::get_contract_count_stmt(1000) ] , -as => 'contract_cnt' },
{ '' => \[ NGCP::Panel::Utils::ProfilePackages::get_voucher_count_stmt() ] , -as => 'voucher_cnt' },
],
};
@ -102,6 +102,9 @@ sub _item_rs {
} elsif($c->user->roles eq "reseller") {
$item_rs = $item_rs->search({ 'me.reseller_id' => $c->user->reseller_id },
$search_xtra);
} else {
$item_rs = $item_rs->search(undef,
$search_xtra);
}
return $item_rs;
}

@ -474,16 +474,15 @@ EOS
sub get_contract_count_stmt {
my $count_limit = shift;
if (defined $count_limit and $count_limit > 0) {
$count_limit = $count_limit + 1;
} else {
$count_limit = -1;
}
return <<EOS;
select
count(c.id)
from billing.contracts_billing_profile_network_schedule cbpns
join billing.contracts_billing_profile_network cbpn on cbpns.profile_network_id = cbpn.id
join billing._v_actual_effective_start_time est on est.contract_id = cbpn.contract_id and cbpns.effective_start_time = est.effective_start_time
join billing.contracts as c on est.contract_id = c.id
where
cbpn.billing_profile_id = me.id
and c.status != 'terminated'
billing.get_billing_profile_contract_cnt(me.id,$count_limit)
EOS
}
@ -507,7 +506,8 @@ sub get_datatable_cols {
return (
{ name => "prepaid", "search" => 0, "title" => $c->loc("Prepaid"),
custom_renderer => 'function ( data, type, full, opt ) { opt.escapeHtml = false; return \'<input type="checkbox" disabled="disabled"\' + (full.prepaid == 1 ? \' checked="checked"\': \'\') + \'/>\'; }' },
{ name => "contract_cnt", "search" => 0, "title" => $c->loc("Used (contracts)"), },
{ name => "contract_cnt", "search" => 0, "title" => $c->loc("Used (contracts)"),
custom_renderer => 'function ( data, type, full, opt ) { if(full.contract_cnt > 1000){return \'1000+\'}return full.contract_cnt; }' },
{ name => "package_cnt", "search" => 0, "title" => $c->loc("Used (packages)"), },
);

@ -168,15 +168,15 @@ EOS
sub get_contract_count_stmt {
my $count_limit = shift;
if (defined $count_limit and $count_limit > 0) {
$count_limit = $count_limit + 1;
} else {
$count_limit = -1;
}
return <<EOS;
select
count(distinct c.id)
from `billing`.`contracts_billing_profile_network` cbpn
join `billing`.`contracts` c on c.id = cbpn.contract_id
where
cbpn.`billing_network_id` = `me`.`id`
and c.status != 'terminated'
and (cbpn.end_date is null or cbpn.end_date >= now())
billing.get_billing_network_contract_cnt(me.id,$count_limit)
EOS
}
@ -200,7 +200,8 @@ sub get_datatable_cols {
my $grp_stmt = "group_concat(if(`billing_network_blocks`.`mask` is null,`billing_network_blocks`.`ip`,concat(`billing_network_blocks`.`ip`,'/',`billing_network_blocks`.`mask`)) separator ', ')";
my $grp_len = 30;
return (
{ name => "contract_cnt", "search" => 0, "title" => $c->loc("Used (contracts)"), },
{ name => "contract_cnt", "search" => 0, "title" => $c->loc("Used (contracts)"),
custom_renderer => 'function ( data, type, full, opt ) { if(full.contract_cnt > 1000){return \'1000+\'}return full.contract_cnt; }' },
{ name => "package_cnt", "search" => 0, "title" => $c->loc("Used (packages)"), },
{ name => 'blocks_grp', accessor => "blocks_grp", search => 0, title => $c->loc('Network Blocks'), literal_sql =>
"if(length(".$grp_stmt.") > ".$grp_len.", concat(left(".$grp_stmt.", ".$grp_len."), '...'), ".$grp_stmt.")" },

@ -131,9 +131,7 @@ sub get_contract_rs {
$schema //= $c->model('DB');
my $rs = $schema->resultset('contracts')->search({
$include_terminated ? () : ('me.status' => { '!=' => 'terminated' }), ## no critic (ProhibitCommaSeparatedStatements)
},{
join => 'product',
});
}, undef);
return $rs;
}
@ -142,12 +140,15 @@ sub get_customer_rs {
my %params = @_;
my ($c,$schema,$include_terminated) = @params{qw/c schema include_terminated/};
$schema //= $c->model('DB');
my @product_ids = map { $_->id; } $schema->resultset('products')->search_rs({ 'class' => ['sipaccount','pbxaccount'] })->all;
my $rs = get_contract_rs(
c => $c,
schema => $schema,
include_terminated => $include_terminated,
)->search_rs({
'product.class' => { -in => [ 'sipaccount', 'pbxaccount' ] },
'product_id' => { -in => [ @product_ids ] },
},{
join => 'contact',
});

@ -24,7 +24,6 @@ sub process {
my $user_tz = $c->session->{user_tz};
# check if we need to join more tables
# TODO: can we nest it deeper than once level?
set_columns($c, $cols);
@ -36,10 +35,10 @@ sub process {
### Search processing section
($rs,my @searchColumns) = _apply_search_filters($c,$rs,$cols,$use_rs_cb);
($rs,my @searchColumns) = _apply_search_filters($c,$rs,$cols,$use_rs_cb,$params->{extra_or});
my $is_set_operations = 0;
($displayRecords, $displayRecordCountClipped, $is_set_operations) = _get_count_safe($c,$rs,$params) if(!$use_rs_cb);
($displayRecords, $displayRecordCountClipped, $is_set_operations) = _get_count_safe($c,$rs,$params) if (!$use_rs_cb);
#my $footer = ((scalar @$aggregate_cols) > 0 and $displayRecordCountClipped);
#$aggregate_cols = [] if $displayRecordCountClipped;
if(@$aggregate_cols){
@ -198,9 +197,9 @@ sub process {
}
sub apply_dt_joins_filters {
my ($c,$rs, $cols) = @_;
my ($c,$rs, $cols, $extra_or) = @_;
$rs = _resolve_joins($rs, $cols, undef, 1, 1);
($rs,my @searchColumns) = _apply_search_filters($c,$rs,$cols);
($rs,my @searchColumns) = _apply_search_filters($c, $rs, $cols, $extra_or);
return $rs;
}
@ -237,33 +236,43 @@ sub _resolve_joins {
}
sub get_search_string_pattern {
my $c = shift;
my $searchString = $c->request->params->{sSearch} // "";
my $is_pattern = 0;
my $searchString_escaped = join('',map {
my $token = $_;
if ($token ne '\\\\') {
$token =~ s/%/\\%/g;
$token =~ s/_/\\_/g;
if ($token =~ s/(?<!\\)\*/%/g) {
$is_pattern = 1;
}
$token =~ s/\\\*/*/g;
}
$token;
} split(/(\\\\)/,$searchString,-1));
return ($searchString_escaped,$is_pattern);
}
sub _apply_search_filters {
my ($c,$rs,$cols,$use_rs_cb) = @_;
my ($c,$rs,$cols,$use_rs_cb,$extra_or) = @_;
# generic searching
my @searchColumns = ();
my %conjunctSearchColumns = ();
#processing single search input - group1 from groups to be joined by 'AND'
my $searchString = $c->request->params->{sSearch} // "";
if ($searchString && !$use_rs_cb) {
if (length($searchString) && !$use_rs_cb) {
#for search string from one search input we need to check all columns which contain the 'search' spec (now: qw/search search_lower_column search_upper_column/). so, for example user entered into search input ip address - we don't know that it is ip address, so we check that name like search OR id like search OR search is between network_lower_value and network upper value
foreach my $col(@{ $cols }) {
my ($name,$search_value,$op,$convert);
# avoid amigious column names if we have the same column in different joined tables
if($col->{search} or $col->{strict_search} or $col->{int_search}){
my $is_pattern = 0;
my $searchString_escaped = join('',map {
my $token = $_;
if ($token ne '\\\\') {
$token =~ s/%/\\%/g;
$token =~ s/_/\\_/g;
if ($token =~ s/(?<!\\)\*/%/g) {
$is_pattern = 1;
}
$token =~ s/\\\*/*/g;
}
$token;
} split(/(\\\\)/,$searchString,-1));
(my $searchString_escaped, $is_pattern) = get_search_string_pattern($c);
if ($is_pattern) {
$op = 'like';
$search_value = $searchString_escaped;
@ -320,6 +329,7 @@ sub _apply_search_filters {
#...things in arrays are OR'ed, and things in hashes are AND'ed
push @{$searchColumns[0]}, { map { %{$_} } @{$conjunctSearchColumns{$conjunct_column}} };
}
push @{$searchColumns[0]}, @{$extra_or} if $extra_or;
}
#/processing single search input
#processing dates search input - group2 from groups to be joined by 'AND'

Loading…
Cancel
Save