---
title: 0.23.0
noindex: true
---

## New Features 🎉

- Aggregate-on-JOIN support: a full pipeline for running aggregates over join results, including:
  - 3+ table join support and `DISTINCT` aggregates in AggregateScan.
  - `LEFT`/`RIGHT`/`FULL JOIN` and `COUNT(DISTINCT)`.
  - `HAVING` clause and per-aggregate `FILTER` clause.
  - `ORDER BY` within `STRING_AGG` and `ARRAY_AGG`.
  - JSON sub-field `GROUP BY` in the DataFusion aggregate path.
  - Date/Timestamp projection and `STDDEV`/`VARIANCE` aggregates.
  - Extended aggregate TopK detection to `GROUP BY` column and `MIN`/`MAX` ordering.
  - TopK optimization for aggregate queries via Tantivy and DataFusion.
- New `edge_ngram` tokenizer for search-as-you-type use cases.
- Support for `IS NULL`/`IS NOT NULL` predicates in Top-K JoinScan.
- Per-field tunable BM25 `k1` and `b` parameters via typmod.
- Explicit control of prefix behavior for fuzzy queries.
- `keep_whitespace` option for Lindera tokenizers.
- Support for the `citext` Postgres type.

## Performance Improvements 🚀

- Lazily set up tokenizers, avoiding unnecessary initialization overhead.
- Conditionally enable scoring in row estimation for faster planning.
- Cache `anyelement_search_opoids` to reduce repeated catalog lookups.
- Cache `score`/`snippet` function OIDs.
- Cache `segment_id` to `segment_ordinal` mapping during `SearchIndexReader` construction.

## Stability Improvements 💪

- Suppress LIMIT pushdown when non-pushable post-filters are present.
- Fix Top K for prepared statements.
- Recognize identity expressions (e.g. `id + 0`) in JoinScan `ORDER BY` matching.
- Re-add meaningful term filter.
- Fix boolean comparison edge cases.
- Allow unaliased, tokenized index fields to be selected by name when the same column is indexed multiple times.
- Support `varchar`, `text[]`, and `citext` parameters in operators with generic plans.

The full changelog is available [on the GitHub Release](https://github.com/paradedb/paradedb/releases/tag/v0.23.0).
