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 )