App-ElasticSearch-Utilities
view release on metacpan or search on metacpan
lib/App/ElasticSearch/Utilities/Query.pm view on Meta::CPAN
eval {
debug({color=>'yellow'}, "query() - retrieving section '$k'");
## no critic
no strict 'refs';
$v = $self->$k();
## user critic
debug_var({color=>'cyan'},$v) if defined $v and ref $v;
1;
} or do {
debug({color=>'red'}, "query() - Failed to retrieve '$k'");
};
$bool{$k} = clone $v if defined $v;
if(my $stash = $self->stash($k)) {
push @{ $bool{$k} }, is_arrayref($stash) ? @{ $stash } : $stash;
}
delete $bool{$k} if exists $bool{$k} and is_arrayref($bool{$k}) and not @{ $bool{$k} };
}
$qref = { bool => \%bool };
}
return $qref;
}
sub add_aggregations {
my $self = shift;
my %aggs = @_;
my $aggs = $self->aggregations();
$aggs ||= {};
foreach my $agg (keys %aggs) {
debug("aggregation[$agg] added to query");
$aggs->{$agg} = $aggs{$agg};
}
$self->set_aggregations($aggs);
$self->set_size(0);
$self->set_scroll(undef);
}
sub wrap_aggregations {
my $self = shift;
my %wrapper = @_;
my $aggs = $self->aggregations;
if( keys %{ $aggs } ) {
foreach my $a (keys %wrapper) {
$wrapper{$a}->{$AGG_KEY} = clone $aggs;
}
}
$self->set_aggregations(\%wrapper);
}
sub aggregations_by {
my ($self,$dir,$aggs) = @_;
my @sort = ();
my %aggs = ();
foreach my $def (@{ $aggs }) {
my ($name,$agg) = %{ expand_aggregate_string($def) };
next unless is_single_stat(keys %{ $agg });
$aggs{$name} = $agg;
push @sort, { $name => $dir };
}
if( @sort ) {
push @sort, { '_count' => 'desc' };
my $ref_aggs = $self->aggregations;
foreach my $name ( keys %{ $ref_aggs } ) {
foreach my $k ( keys %{ $ref_aggs->{$name} } ) {
next if $k eq $AGG_KEY;
$ref_aggs->{$name}{$k}{order} = \@sort;
foreach my $agg (keys %aggs) {
$ref_aggs->{$name}{$AGG_KEY}{$agg} = $aggs{$agg};
}
}
}
$self->set_aggregations( $ref_aggs );
}
}
# Support Short-hand like ES
*aggs = \&aggregations;
*set_aggs = \&set_aggregations;
*add_aggs = \&add_aggregations;
*wrap_aggs = \&wrap_aggregations;
*aggs_by = \&aggregations_by;
sub set_scan_scroll {
my ($self,$ctxt_life) = @_;
# Validate Context Lifetime
if( !is_TimeConstant( $ctxt_life) ) {
undef($ctxt_life);
}
$ctxt_life ||= '1m';
$self->set_sort( [qw(_doc)] );
$self->set_scroll( $ctxt_life );
$self;
}
sub set_match_all {
my ($self) = @_;
# Reset the relevant pieces of the query
$self->set_must_not([]);
$self->set_filter([]);
$self->set_should([]);
# Set the match_all bits
$self->set_must({match_all=>{}});
$self;
}
sub add_bool {
my $self = shift;
my %bools = @_;
lib/App/ElasticSearch/Utilities/Query.pm view on Meta::CPAN
=head2 add_aggregations( name => { ... } )
Takes one or more key-value pairs. The key is the name of the aggregation.
The value being the hash reference representation of the aggregation itself.
It will silently replace a previously named aggregation with the most recent
call.
Calling this function overrides the L<size> element to B<0> and disables L<scroll>.
Aliased as B<add_aggs>.
=head2 wrap_aggregations( name => { ... } )
Use this to wrap an aggregation in another aggregation. For example:
$q->add_aggregations(ip => { terms => { field => src_ip } });
Creates:
{
"aggs": {
"ip": {
"terms": {
"field": "src_ip"
}
}
}
}
Would give you the top IP for the whole query set. To wrap that aggregation to get top IPs per hour, you could:
$q->wrap_aggregations( hourly => { date_histogram => { field => 'timestamp', interval => '1h' } } );
Which translates the query into:
{
"aggs": {
"hourly": {
"date_histogram": {
"field": "timestamp",
"interval": "1h"
}
"aggs": {
"ip": {
"terms": {
"field": "src_ip"
}
}
}
}
}
}
=head2 aggregations_by( [asc | desc] => aggregation_string )
Applies a sort to all aggregations at the current level based on the
aggregation string.
Aggregation strings are parsed with the
L<App::ElasticSearch::Utilities::Aggregations> C<expand_aggregate_string()>
functions.
Examples:
$q->aggregations_by( desc => [ qw( sum:bytes ) ] );
$q->aggregations_by( desc => [ qw( sum:bytes cardinality:user_agent ) ] );
=head2 set_scan_scroll($ctxt_life)
This function emulates the old scan scroll feature in early version of Elasticsearch. It takes
an optional L<ElasticSearch time constant|https://www.elastic.co/guide/en/elasticsearch/reference/master/common-options.html#time-units>,
but defaults to '1m'. It is the same as calling:
$self->set_sort( [qw(_doc)] );
$self->set_scroll( $ctxt_life );
=head2 set_match_all()
This method clears all filters and query elements to and sets the must to match_all.
It will not reset other parameters like size, sort, and aggregations.
=head2 add_bool( section => conditions .. )
Appends a search condition to a section in the query body. Valid query body
points are: must, must_not, should, and filter.
$q->add_bool( must => { term => { http_status => 200 } } );
# or
$q->add_bool(
must => [
{ term => { http_method => 'GET' } }
{ term => { client_ip => '10.10.10.1' } }
]
must_not => { term => { http_status => 400 } },
);
=head2 stash( section => condition )
Allows a replaceable query element to exist in the query body sections: must, must_not,
should, and/or filter. This is useful for moving through a data-set preserving everthing in a query
except one piece that shifts. Imagine:
my $query = App::ElasticSearch::Utilities::Query->new();
$query->add_bool(must => { terms => {src_ip => [qw(1.2.3.4)]} });
$query->add_bool(must => { range => { attack_score => { gt => 10 }} });
while( 1 ) {
$query->stash( must => { range => { timestamp => { gt => time() } } } );
my @results = make_es_request( $query->request_body, $query->uri_params );
# Long processing
}
This allows re-use of the query object inside of loops like this.
=for Pod::Coverage aggs
=for Pod::Coverage set_aggs
=for Pod::Coverage add_aggs
( run in 0.462 second using v1.01-cache-2.11-cpan-5623c5533a1 )