ClickHouse vs SereneDB: one engine for ranked search and analytics
ClickHouse is a columnar database whose text index, generally available since version 26.2, matches tokens and phrases but does not rank results by relevance. SereneDB is an Apache 2.0 database that ranks matches with BM25 and aggregates them in the same PostgreSQL-compatible SQL query, over its own tables or over ClickHouse tables where they already are.
median hot query time · the 70 queries both finish
Why teams look for a ClickHouse alternative
Teams look past ClickHouse when log search has to rank results rather than filter them, when applications expect PostgreSQL, or when rows change after they land. SereneDB ranks with BM25, joins and aggregates in one PostgreSQL-compatible SQL engine.
> Relevance ranking in SQL
SereneDB ranks matches with BM25, TF-IDF or a language-model scorer in the statement that filters them. ClickHouse's text index does not rank matches by relevance, so 21 of SearchBench's 92 queries, the BM25 top-k ones, cannot be written in ClickHouse SQL.
> Fuzzy, phrase, synonym and geo search from the index
SereneDB's inverted index answers phrases with gaps and a slop, edit distance, n-grams, regular expressions, wildcards, synonyms, boosts, minimum-should-match and geo shapes, and highlights what matched. ClickHouse 26.10's text index serves LIKE and prefix patterns but no edit distance or phrase slop, and skips blocks for a regular expression only when the pattern holds whole words.
> PostgreSQL SQL, updates and transactions
SereneDB speaks PostgreSQL's dialect and wire protocol, so psql, the PostgreSQL drivers and Grafana's PostgreSQL data source connect as they are, and UPDATE and DELETE run in multi-statement ACID transactions. ClickHouse accepts PostgreSQL wire-protocol connections too, but parses the queries as ClickHouse SQL by default, and its multi-statement transactions are experimental.
SereneDB vs ClickHouse, feature by feature
Both store data in columns and index text. They differ in whether search results can be ranked, which SQL dialect you write, and how rows change after they land.
| Capability | SereneDB | ClickHouse |
|---|---|---|
| Storage | Yes: Columnar row groups with automatic min-max zonemaps, plus inverted indexes for search | Yes: MergeTree columnar parts sorted by the ORDER BY key, with a sparse primary index and skip indexes |
| Full-text search | Yes: Inverted index with per-column text analysis: tokenizers, stemming, stop words and synonyms | Yes: Text index, generally available since 26.2: hasToken, hasAnyTokens, hasAllTokens and hasPhrase, with tokenizers, a preprocessor and stemming |
| Relevance ranking | Yes: BM25, TF-IDF and language-model scorers in SQL, with top-k pruning | No: No relevance ranking: the text index announcement says it implements neither TF-IDF nor BM25 |
| Phrase and proximity | Yes: ts_phrase with a slop: the words within N positions | Partly: hasPhrase (since 26.4) needs the words in order with nothing between them; no slop |
| Fuzzy, regex and prefix | Yes: Edit distance, regular expressions, prefixes and wildcards from the index | Partly: LIKE, startsWith and endsWith from the index dictionary; a regular expression skips blocks only on whole words; no index for edit distance |
| Highlighting | Yes: ts_highlight marks the tokens the query matched and picks the best-scoring snippets | Partly: highlight() wraps the terms you pass in tags across the whole text, matched ASCII case-insensitively |
| Synonyms | Yes: Solr and WordNet synonym lists expand words in the indexed text and in the query | Partly: An experimental synonyms() function looks up a word in a plain or WordNet synonym file |
| Vector and hybrid search | Yes: HNSW and IVF in the same inverted index; a text match and a vector order in one query | Partly: HNSW vector similarity index, generally available since 25.8; no text relevance score to blend with distance |
| SQL dialect and clients | Yes: PostgreSQL dialect and wire protocol: psql, the PostgreSQL drivers, Grafana's PostgreSQL data source | Partly: ClickHouse SQL; a PostgreSQL wire-protocol interface accepts psql, and the queries are ClickHouse SQL by default |
| Joins | Yes: Inner, outer, semi, anti and lateral joins across tables, indexes, files and attached databases | Yes: INNER, OUTER, SEMI, ANTI, ANY and ASOF; outer-join misses get the type's default unless join_use_nulls = 1 |
| Updates, deletes and transactions | Yes: UPDATE, DELETE and multi-statement ACID transactions with snapshot isolation | Partly: Lightweight DELETE, and lightweight UPDATE (generally available in 26.10) on tables with block-number columns; BEGIN and COMMIT experimental |
| Kafka ingest | Yes: Kafka Connect's PostgreSQL sinks write to it over the PostgreSQL wire protocol; also COPY, INSERT, OTLP over HTTP or a subscription to a Postgres publication | Yes: Kafka table engine |
| OpenTelemetry ingest | Yes: A built-in OTLP/HTTP receiver writes logs, traces and metrics to search tables | Yes: Through the OpenTelemetry Collector's ClickHouse exporter |
| Data in place | Yes: Indexes files and tables where they are: Parquet, CSV, JSON and Iceberg on S3 or disk, attached ClickHouse and PostgreSQL | Yes: Queries S3, Iceberg, Delta Lake and PostgreSQL in place through table functions; a text index needs a MergeTree table |
| License | Yes: Apache 2.0 | Yes: Apache 2.0 |
✓ supported · ~ supported with limits · ✕ not supported. ClickHouse facts from ClickHouse's documentation for 26.10 and runs on 26.10.2, SereneDB facts from its documentation and runs on 26.10.7. Last reviewed October 31, 2026.
The same question in ClickHouse SQL and SereneDB SQL
Each tab asks one question of the same logs in both dialects. The SereneDB SQL ran on SereneDB 26.10.7 and the ClickHouse SQL on ClickHouse 26.10.2, both on October 31, 2026.
Log search
SELECT ts, service, message
FROM logs
WHERE hasToken(message, 'timeout')
AND service = 'checkout'
AND ts > now() - INTERVAL 15 MINUTE
ORDER BY ts DESC
LIMIT 50;SELECT ts, service, message
FROM logs
WHERE message @@ 'timeout'
AND service = 'checkout'
AND ts > now() - INTERVAL '15 minutes'
ORDER BY ts DESC
LIMIT 50;In ClickHouse, logs is a MergeTree table with a text index on message; in SereneDB it is a search table with an inverted index on message. Both answer the token match from the index and return the same rows.
Fuzzy match
SELECT ts, service, message
FROM logs
WHERE arrayExists(
t -> editDistance(t, 'conection') <= 1,
splitByNonAlpha(lower(message)))
ORDER BY ts DESC
LIMIT 50;SELECT ts, service, message
FROM logs
WHERE message @@ ts_levenshtein('conection', 1)
ORDER BY ts DESC
LIMIT 50;ts_levenshtein('conection', 1) finds every word within one edit of the typo in the index's term dictionary. ClickHouse's text index does not serve edit distance, so its query compares every word of every row; EXPLAIN lists no skip index for it. Both return the same six rows.
BM25 ranking
-- No relevance score: the text index finds
-- the rows, so they can only be sorted by a column
SELECT ts, service, message
FROM logs
WHERE hasAnyTokens(message, ['connection', 'reset', 'refused'])
ORDER BY ts DESC
LIMIT 10;SELECT ts, service, message,
BM25(logs.tableoid) AS score
FROM logs
WHERE message @@ ts_any(['connection', 'reset', 'refused'])
ORDER BY score DESC, ts DESC
LIMIT 10;Both queries find the same seven rows. SereneDB orders them by BM25 relevance; ClickHouse has no score to order by, so its query sorts them by time.
Search ClickHouse in place
-- The table stays in ClickHouse as it is
CREATE TABLE logs (
id UInt64,
ts DateTime,
service LowCardinality(String),
level LowCardinality(String),
message String,
INDEX message_idx message
TYPE text(tokenizer = splitByNonAlpha,
preprocessor = lower(message))
) ENGINE = MergeTree
ORDER BY (service, ts);CREATE SERVER ch FOREIGN DATA WRAPPER clickhouse_fdw
OPTIONS (host 'clickhouse', port '9000');
CREATE VIEW ch_logs AS SELECT * FROM ch.default.logs;
CREATE TEXT SEARCH DICTIONARY words
AS split_text(case := 'lower') WITH (frequency, position, norm);
CREATE INDEX ch_logs_idx ON ch_logs
USING inverted (message words, service, ts)
WITH (key_columns = 'id');
SELECT ts, service, message,
BM25(ch_logs_idx.tableoid) AS score
FROM ch_logs_idx
WHERE message @@ ts_any(['connection', 'reset', 'refused'])
ORDER BY score DESC, ts DESC
LIMIT 10;SereneDB reaches ClickHouse over its native protocol through a foreign server and indexes the table where it is: the rows stay in ClickHouse and SereneDB keeps the postings, keyed on the unique id column. The index is a snapshot; REINDEX INDEX ch_logs_idx or the reindex_interval option brings in new rows.
Move search off ClickHouse in three steps
Run SereneDB next to ClickHouse, search the ClickHouse tables where they are, compare the results on the same data, then move queries and dashboards. Indexing external data covers the statements.
Start SereneDB
Run it next to ClickHouse and connect with psql or any PostgreSQL driver.
$ docker run -d --name serenedb -p 7890:7890 -e POSTGRES_PASSWORD=<password> serenedb/serenedbConnect ClickHouse and index it in place
A foreign server reaches ClickHouse over its native protocol. Index a view of a table as in the last tab above, or copy the rows with
CREATE TABLE … AS SELECT.$ psql -h localhost -p 7890 -U postgres -c "CREATE SERVER ch FOREIGN DATA WRAPPER clickhouse_fdw OPTIONS (host '<clickhouse-host>', port '9000', user '<user>', password '<password>')"Port queries and dashboards
Rewrite
hasToken,hasAnyTokensandhasAllTokensas@@matches, add BM25 where results should be ranked, rebuild dashboards on Grafana's PostgreSQL data source, and move traffic once the results match.$ psql -h localhost -p 7890 -U postgres -c "SELECT count(*) FROM ch_logs_idx WHERE message @@ 'timeout'"
No. ClickHouse's text index, generally available since 26.2, matches tokens and phrases but does not rank them: the announcement of its general availability says it implements neither TF-IDF nor BM25, and 26.10 adds no relevance scoring. SereneDB ranks matches with BM25 or another scorer in SQL, for example ORDER BY BM25(logs.tableoid) DESC.
ClickHouse's text index filters rows but does not rank them by relevance, serves no edit-distance matching, and matches phrases only without a slop. Multi-statement transactions are experimental, and lightweight UPDATE, generally available since 26.10, needs the table's block-number columns enabled. SereneDB serves ranked, fuzzy, regex and phrase search from one inverted index and changes rows in ACID transactions.
SereneDB combines both: PostgreSQL's dialect, wire protocol and clients, with a columnar engine and BM25 search. On SearchBench's 92 log search and analytics queries over 1B logs, SereneDB 26.09.1 finished all 92 within 60 seconds, ClickHouse 26.8.2.7 finished 70, and PostgreSQL 18.4 with tsvector and a GIN index finished 12.
Yes. Lightweight DELETE hides rows that later merges remove, and lightweight UPDATE, generally available since 26.10, works on tables with block-number columns enabled and is meant for up to about 10% of a table. ALTER TABLE … UPDATE and DELETE are asynchronous mutations that rewrite data parts and cannot be rolled back, and multi-statement transactions are experimental. In SereneDB, UPDATE and DELETE run in ACID transactions with snapshot isolation, and the search index sees the change within about a second.
On SearchBench, SereneDB. Over 1B OpenTelemetry logs, SereneDB 26.09.1 had a 13× lower median hot query time than ClickHouse 26.8.2.7 on the 70 queries both finish (26.5 ms against 349 ms) and answered the 22 that ClickHouse cannot express. ClickHouse was faster on 5 of the 70, and on cold runs SereneDB's lead is 3.3×. ClickHouse 26.10 has not been measured yet.
Yes. SereneDB connects to ClickHouse over its native protocol with CREATE SERVER and builds an inverted index over a view of a ClickHouse table. The rows stay in ClickHouse, SereneDB stores the postings, and one SQL query ranks and aggregates the matches. The index is a snapshot that REINDEX or the reindex_interval option refreshes, keyed on a unique column named with key_columns.
Yes. SereneDB is licensed under Apache 2.0 and its source is on GitHub. The ClickHouse server is licensed under Apache 2.0 too.
SearchBench measured all three engines on the same 92 queries over 1B OpenTelemetry logs. SereneDB answered all 92 and runs the BM25 search and the aggregation in one SQL query; its median query time was 13× lower than ClickHouse 26.8.2.7 on the 70 queries both finish and 3.0× lower than Elasticsearch 9.5 on the 88 both finish. The Elasticsearch comparison covers SereneDB against Elasticsearch.