Skip to content

os-query-digest

Human-readable, loggable digests and stable fingerprints for OpenSearch DSL queries.

A search request in a log file is a wall of nested braces nobody reads. This turns it into one line you can paste into Dashboards, plus a stable fingerprint you can terms aggregate on.

Before.

{
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { "gte": "now-15m", "lt": "now" } } },
        { "term": { "service": "api" } }
      ],
      "must_not": [ { "term": { "status": 200 } } ]
    }
  },
  "size": 50,
  "sort": [ { "@timestamp": "desc" } ],
  "_source": [ "@timestamp", "message", "status" ]
}

After. The same request, three ways:

text  logs-* | q=(@timestamp >= now-15m and @timestamp < now and not status:200 and service:api) | size=50 sort=@timestamp:desc
sig   logs-* | q=(@timestamp >= ? and @timestamp < ? and not status:? and service:?) | size=50 sort=@timestamp:desc
hash  q5:fe168406e702
what it is what it is for
text the query in DQL, with real values paste it into the Dashboards search bar
signature the same line with literals erased read the shape at a glance
hash versioned fingerprint of the signature terms aggregate on it

Try it on your own query Getting started

The playground runs this library in your browser — PHP compiled to WebAssembly, no server, nothing leaves the page.

The third one is the point. Log the hash next to took, and your log index answers questions your dashboards cannot: which kind of query got slow this afternoon, which shape runs a thousand times an hour, which one appeared the day the incident started.


Start here

  • Two runtime dependencies: php and ext-json. Nothing else is installed, so nothing else can break.
  • ~30 µs per request, against a search that takes milliseconds. lazy() costs 0.2 µs, so a debug record your handler drops parses nothing at all.
  • PHP 7.4 through 8.5, tested against every one of them.
  • Certified against real clusters — OpenSearch 2.19.6 and 3.8.0, not inferred from a specification.

Install

composer require mr-dlef/os-query-digest
use MrDlef\OsQueryDigest\Formatter;

$digest = Formatter::create()->describe($request, 'logs-2026.08.13');

$logger->info('opensearch.search', [
    'q'    => $digest->text(),
    'hash' => $digest->hash(),
    'took' => $response['took'],
]);

That is the whole of it. Getting started takes it from there.