https://github.com/dannote/arel_maniac
The missing PostgreSQL features for ActiveRecord — no raw SQL allowed
https://github.com/dannote/arel_maniac
activerecord cte distinct-on lateral-join postgresql rails ruby tablesample
Last synced: 21 days ago
JSON representation
The missing PostgreSQL features for ActiveRecord — no raw SQL allowed
- Host: GitHub
- URL: https://github.com/dannote/arel_maniac
- Owner: dannote
- License: mit
- Created: 2026-02-11T22:26:10.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-11T23:48:37.000Z (5 months ago)
- Last Synced: 2026-04-27T16:38:59.409Z (2 months ago)
- Topics: activerecord, cte, distinct-on, lateral-join, postgresql, rails, ruby, tablesample
- Language: Ruby
- Size: 17.6 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# ArelManiac
The missing PostgreSQL features for ActiveRecord — no raw SQL allowed.
One gem to replace `activerecord-cte`, `active_record_distinct_on`, `fast_count`, and half of `activerecord_extended`.
## Installation
```ruby
gem "arel_maniac"
```
## Features
### DISTINCT ON
```ruby
Land.distinct_on(:cadastral_number_id)
Land.distinct_on(:cadastral_number_id, :land_category)
# With associations
Land.distinct_on(cadastral_number: :region)
```
### LATERAL Joins
```ruby
# Inner join
Author.lateral_join(
Book.where("books.author_id = authors.id").order(published_at: :desc).limit(3),
as: "recent_books"
)
# Left join
Author.lateral_join(subquery, as: "recent_books", type: :left)
```
### TABLESAMPLE
```ruby
# Bernoulli sampling (row-level, default)
Land.tablesample(5)
# System sampling (block-level, faster)
Land.tablesample(10, method: :system)
# Repeatable results
Land.tablesample(1, seed: 42)
```
### Set Operations
```ruby
Land.where(region: "77").union(Land.where(region: "50"))
Land.where(region: "77").union_all(Land.where(region: "50"))
Land.where(region: "77").union_except(Land.where(land_category: "industrial"))
Land.where(region: "77").union_intersect(Land.where("area_sq_m > 1000"))
```
### OR Conditions
```ruby
Land.any_of(
Land.where(land_category: "residential"),
Land.where("area_sq_m > ?", 1000)
)
Land.none_of(
Land.where(land_category: "industrial"),
Land.where(ownership_type: "state")
)
```
### Window Functions
```ruby
Land
.define_window(:w).partition_by(:land_category, order_by: { area_sq_m: :desc })
.select_window(:row_number, over: :w, as: :rank)
```
### Fast Count
```ruby
Land.fast_count # estimated count from pg_class (handles partitioned tables)
Land.fast_count(10_000) # fallback to COUNT(*) if estimate < threshold
```
### Estimated Count
```ruby
Land.where(region: "77").estimated_count # EXPLAIN-based row estimate, no full scan
```
### Aggregate Functions
```ruby
ArelManiac.string_agg(Zone.arel_table[:name], ", ")
ArelManiac.array_agg(Land.arel_table[:id], distinct: true)
ArelManiac.generate_series(1, 10)
ArelManiac.coalesce(Land.arel_table[:area_sq_m], Arel::Nodes.build_quoted(0))
```
## Optional Extensions
### PostGIS (`require "arel_maniac/ext/postgis"`)
Supplements rgeo-activerecord with missing spatial functions:
```ruby
geometry_node.st_dwithin(point, 0.001)
geometry_node.st_tileenvelope(14, 9799, 5373)
geometry_node.st_transform(4326)
geometry_node.st_makeenvelope(-74, 40, -73, 41, 4326)
```
### ParadeDB (`require "arel_maniac/ext/paradedb"`)
```ruby
Land.arel_table[:id].paradedb_score # paradedb.score() for BM25 ranking
```
## Rails Compatibility
- Rails 7.1+
- Ruby 3.2+
- PostgreSQL 14+
## Acknowledgments
Incorporates ideas and code from:
- [active_record_distinct_on](https://github.com/aha-app/active_record_distinct_on) (MIT)
- [ActiveRecordExtended](https://github.com/GeorgeKaraszi/ActiveRecordExtended) (MIT)
- [fast_count](https://github.com/fatkodima/fast_count) (MIT)
## License
MIT