NAME
Typesense::Client::Analytics - popular queries, no-hits queries and click events
SYNOPSIS
my $a = $ts->analytics;
# Aggregate what people search for into its own collection.
$a->upsert_rule('popular_products', {
type => 'popular_queries',
params => {
source => { collections => ['products'] },
destination => { collection => 'product_queries' },
limit => 1000,
expand_query => \1, # store the full term, not the prefix
},
});
# And what they search for and do not find.
$a->upsert_rule('nohits_products', {
type => 'nohits_queries',
params => {
source => { collections => ['products'] },
destination => { collection => 'product_nohits' },
limit => 1000,
},
});
# Feed real popularity back into ranking.
$a->upsert_rule('product_popularity', {
type => 'counter',
params => {
source => { collections => ['products'],
events => [ { type => 'click', weight => 1,
name => 'product_click' } ] },
destination => { collection => 'products', counter_field => 'popularity' },
},
});
## The other half of the pairing: the same id on the search itself.
$ts->search('products', { q => 'laptop', query_by => 'name' },
headers => { 'x-typesense-user-id' => $session_id });
$a->click('product_click', { doc_id => '1024', user_id => $session_id });
my $top = $a->top_queries('product_queries', limit => 20);
DESCRIPTION
Typesense can aggregate search traffic on its own. That matters most for a type-ahead, where logging every keystroke server-side is both expensive and useless: Typesense only counts a query after a four-second pause, so what lands in the destination collection is what the person actually finished typing, not the prefixes on the way there.
The server has to be started for it
None of this works unless the server runs with:
--enable-search-analytics=true
--analytics-dir=/data/analytics
--analytics-flush-interval=300 # seconds; the default is 3600
Without those flags rules can be created and will simply never produce anything, which is a confusing way to fail. Check $ts->debug or the server log if a destination collection stays empty.
Reading the results
There is no endpoint that returns aggregated analytics. The rules write into ordinary collections, which you search like any other - that is all "top_queries" does.
Attributing events to people
Pass the same identifier as user_id on events and as the x-typesense-user-id header (or X-TYPESENSE-USER-ID parameter) on searches. Without it Typesense falls back to the client IP, and behind a reverse proxy that makes every visitor look like one very busy person.
METHODS
rules
GET /analytics/rules.
rule
GET /analytics/rules/{name}.
upsert_rule
PUT /analytics/rules/{name}. type is popular_queries, nohits_queries or counter.
delete_rule
DELETE /analytics/rules/{name}. Not an error if absent.
event
$a->event($type, $name, \%data);
POST /analytics/events. $name must match the event name declared in a counter rule for it to be counted.
click, conversion, visit
Shorthands for the three event types.
top_queries
my $r = $a->top_queries($destination_collection, limit => 20);
Searches a popular_queries or nohits_queries destination collection, ordered by count. Convenience only - it is a plain search.
SEE ALSO
https://typesense.org/docs/latest/api/analytics-query-suggestions.html
AUTHOR
SeHarrys
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by SeHarrys.
This is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.