Skip to main content

Migrating from Elasticsearch

If you are coming from Elasticsearch or OpenSearch, most search features map onto SereneDB's inverted index and plain SQL. This page maps the concepts side by side; each Elasticsearch feature links to its reference. The biggest shift is that search and analytics are both just SQL — you filter with @@ and aggregate with GROUP BY in the same query, against the same database that holds your relational data.

Key differences​

AspectElasticsearchSereneDB
Query languageQuery DSL (JSON)SQL — @@, ORDER BY, GROUP BY
Data modelDocuments in indicesRows in tables, with an inverted index beside the columnar data
Schema & typesDynamic field mappings, JSON typesTyped SQL columns; per-column operator classes pick how each is indexed
Search typesSeparate field types — text, dense_vector, geo_*One inverted index spans full-text, vector and geospatial
Analyzer configAnalyzers, tokenizers, token filtersText search dictionaries — templates + pipeline
RelevanceBM25 by defaultBM25 by default, plus other scorers
Aggregations + hitsOne request, two result treesOne SQL query — filter + GROUP BY / window
TransactionsNone across documentsFull ACID
JoinsLimited (nested / parent-child)Native SQL joins
DeploymentDistributed JVM clusterSingle binary, PostgreSQL wire protocol

Migration tips​

Query capabilities​

ElasticsearchSereneDBNotes
match✅col @@ 'terms' (Full-Text Search)
match_phrase / proximity✅ts_phrase, ## operator
prefix / wildcard / regexp✅ts_starts_with, ts_like, ts_regexp
fuzzy✅ts_levenshtein; plus ts_ngram n-gram similarity (no ES equivalent)
bool✅&& || !!, ts_compound
term / terms✅verbatim columns; ts_any / ts_all
range✅ts_between, ts_lt/le/gt/ge
query_string✅to_tsquery, websearch_to_tsquery
more_like_this⚠️No direct function; use the minhash scalar function or vector similarity

Function mapping​

The detailed mapping from each Elasticsearch query to the specific SereneDB function — the left column links to the Elasticsearch reference, the right to the SereneDB function reference:

Elasticsearch querySereneDB function
matcha bare string, or ts_tokenize
match (operator: and)plainto_tsquery
match_phrasets_phrase / phraseto_tsquery
match_phrase (slop)ts_phrase with slop := N or ::slop(N)
prefixts_starts_with
wildcardts_like
regexpts_regexp
fuzzyts_levenshtein
(no ES equivalent)ts_ngram — n-gram similarity
term / termsa verbatim token, ts_any / ts_all
terms_setts_any with min_match
rangets_between, ts_lt / ts_le / ts_gt / ts_ge
existsPlain SQL IS NOT NULL / IS NULL — the index claims both on indexed columns.
boolts_compound, or && / || / !!
query_stringto_tsquery
simple_query_stringwebsearch_to_tsquery
boost (^)^ operator
_score (BM25)BM25 and other scorers
highlightingts_highlight, ts_offsets
kNN<-> / <=> / <#> + ORDER BY … LIMIT
geo queriesST_Intersects, ST_Contains, ST_Distance_*
analyzer testts_lexize

Text analysis​

ElasticsearchSereneDBNotes
Tokenizers✅split_text, generate_ngrams, split_text_csv, split_text_icu, … templates
Token filters (lowercase / stemming / stopwords)✅split_text template options + stem_words / remove_stopwords templates
Accent folding✅accent = false
n-gram / edge n-gram✅generate_ngrams (mode = 'only_prefix' for edge n-grams), generate_sparse_ngrams
Shingles✅generate_shingles wraps another template and emits word n-grams
Synonyms✅expand_solr_synonyms, expand_wordnet_synonyms
Custom analyzers✅compose templates with pipeline
Separate search analyzer✅Symmetric by default; override per query with ts_tokenize(text, 'dict') or 'text'::tokenize('dict')

Scoring and relevance​

ElasticsearchSereneDBNotes
BM25✅BM25(idx.tableoid) (Ranking)
Other scorers✅TFIDF, lm_jm, lm_dirichlet, dfi, more
boosting / boost✅^ operator (Relevance Tuning)
function_score✅compose the scorer in SQL arithmetic, e.g. BM25(...) * 2
Top-K acceleration✅optimize_top_k (WAND)
Reciprocal Rank Fusion✅Hybrid Search + RRF

Highlighting​

ElasticsearchSereneDBNotes
Snippets / fragments✅ts_highlight
Custom tags✅StartSel / StopSel
Match offsets✅ts_offsets

Suggesters​

Elasticsearch suggesters read a field's terms to complete and correct what a user types. SereneDB serves the same from the inverted index dictionary with the ts_dict_* aggregates, so there is no separate suggester structure to build or keep in sync. You query the dictionary directly and rank however you like.

ElasticsearchSereneDBNotes
completion suggester✅prefix match over the dictionary, ranked by document frequency (Autocomplete)
term suggester✅ts_levenshtein over the dictionary, ranked by ts_dict_score (Spell Correction)
phrase suggester⚠️correct each term with ts_levenshtein; no whole-phrase language model (Spell Correction)
context suggester✅autocomplete with a WHERE filter on another indexed column

Vector and geospatial​

ElasticsearchSereneDBNotes
Dense vector / kNN (IVF)✅Vector Search
Geo queries✅Geospatial Search (ST_*)

Aggregations​

This is where SereneDB's SQL model shines: every Elasticsearch aggregation maps to a SQL construct, run in the same query as the search filter. Elasticsearch's three families map as follows.

Bucket aggregations​

Bucket aggregations group documents into buckets — SereneDB's GROUP BY clause:

ElasticsearchSereneDB
terms / multi_termsGROUP BY col (one or more columns)
histogramGROUP BY width_bucket(col, …)
date_histogramGROUP BY date_trunc('month', col)
range / date_range / ip_rangeGROUP BY CASE …
filterWHERE …
filterscount(*) with a FILTER clause per branch
missingWHERE col IS NULL
rare_termsGROUP BY col with HAVING count(*) <= n
nestedUNNEST / LATERAL

Metric aggregations​

Metric aggregations compute a value over each bucket — SereneDB's aggregate functions:

ElasticsearchSereneDB
avg / sum / min / max / value_countavg / sum / min / max / count
stats / extended_statsthose together (+ stddev / variance)
cardinalityapprox_count_distinct(col) (or count(DISTINCT col))
percentilesquantile_cont(col, p)
percentile_rankscount(*) FILTER (WHERE col <= v) / count(*)
weighted_avgsum(w*x) / sum(w)
top_hitsDISTINCT ON / windowed row_number()

Pipeline aggregations​

Pipeline aggregations post-process the output of other aggregations — SereneDB's window functions:

ElasticsearchSereneDB
cumulative_sumsum(x) OVER (ORDER BY …)
derivative / serial_diffx - lag(x) OVER (…)
moving_fnavg(x) OVER (… ROWS BETWEEN …)
bucket_scriptarithmetic over aggregated columns
bucket_selectorHAVING
bucket_sortORDER BY … LIMIT

Aggregates run over the inverted index itself — GROUP BY and aggregate functions over indexed (and INCLUDEd) columns are answered without materializing the base table. Add columns you frequently aggregate to the index. On a keyword column a terms aggregation goes further and reads the term dictionary directly, so facet counts cost a dictionary walk rather than a scan; see Faceted Search. The query below filters with @@ and buckets the matches by category in one statement — the Elasticsearch equivalent of a terms aggregation inside a query:

Query
SELECT category, count(*) AS nFROM catalog_docs_idxWHERE body @@ 'galaxy'GROUP BY categoryORDER BY category;
Result
 category | n----------+--- guide    | 1 sci-fi   | 2

Index management and operations​

ElasticsearchSereneDBNotes
Create / delete index✅CREATE INDEX … USING inverted / DROP INDEX
Reindex✅DROP INDEX + CREATE INDEX
Refresh✅VACUUM (REFRESH_TABLE) (Maintenance)
Force merge✅VACUUM (COMPACT_TABLE)
Aliases✅Use a view
Pagination (from/size, search_after)✅LIMIT / OFFSET, keyset pagination

See also​