--- title: The btree_gin extension subtitle: Combine GIN and B-tree indexing capabilities for efficient multi-column queries in Postgres enableTableOfContents: true updatedOn: '2025-08-02T10:33:29.228Z' --- The `btree_gin` extension for Postgres provides a specialized set of **GIN operator classes** that allow common, "B-tree-like" data types to be included in **GIN indexes**. This is particularly useful for scenarios where you need to create **multicolumn GIN indexes** that combine complex data types (like arrays or JSONB) with simpler types such as integers, timestamps, or text. Ultimately, `btree_gin` helps you leverage the power of GIN for a broader range of indexing needs, optimizing queries across diverse data structures. Consider a scenario where an application needs to query blog posts based on a set of `tags` (an array) and a `publication_date` (a timestamp). The `btree_gin` extension allows for a single, optimized index to service both conditions, potentially offering significant performance gains over alternative indexing strategies. ## Enable the `btree_gin` extension You can enable the extension by running the following `CREATE EXTENSION` statement in the [Neon SQL Editor](/docs/get-started/query-with-neon-sql-editor) or from a client such as [psql](/docs/connect/query-with-psql-editor) that is connected to your Neon database. ```sql CREATE EXTENSION IF NOT EXISTS btree_gin; ``` **Version availability:** Please refer to the [list of all extensions](/docs/extensions/pg-extensions) available in Neon for up-to-date extension version information. ## `btree_gin`: Bridging index types A common challenge arises when queries require filtering on both B-tree friendly columns (e.g., `status TEXT`, `created_at TIMESTAMP`) and GIN-friendly columns (e.g., `attributes JSONB`, `tags TEXT[]`). While Postgres can use separate B-tree and GIN indexes and combine their results, this is not always the most performant approach. The `btree_gin` extension addresses this by providing GIN **operator classes** for many standard B-tree-indexable data types. These operator classes instruct the GIN indexing mechanism on how to handle these scalar types as if they were native GIN-indexable items. For instance, with `btree_gin`, a single GIN index can be defined on `(order_date TIMESTAMP, product_tags TEXT[])`. ```sql -- Create the table CREATE TABLE orders ( order_id SERIAL PRIMARY KEY, order_date TIMESTAMP, product_tags TEXT[] ); CREATE INDEX idx_orders_date_tags ON orders USING GIN (order_date, product_tags); ``` This composite index can then be leveraged by Postgres to optimize queries filtering on both `order_date` and `product_tags` simultaneously, such as: ```sql SELECT * FROM orders WHERE order_date >= '2025-04-01' AND order_date < '2025-05-01' AND product_tags @> ARRAY['electronics']; ``` Without `btree_gin`, `order_date` could not be directly included in a GIN index in this manner. ## Usage scenarios Let's explore some practical examples of how `btree_gin` can be applied to real-world scenarios, particularly in the context of filtering and querying data efficiently. ### Filtering posts by tags and publication date Consider a `posts` table where queries frequently target posts with specific tags published within a defined timeframe. #### Table schema ```sql CREATE TABLE posts ( post_id SERIAL PRIMARY KEY, title TEXT, content TEXT, tags TEXT[], -- GIN-friendly array published_at TIMESTAMPTZ -- B-tree friendly timestamp ); INSERT INTO posts (title, tags, published_at) VALUES ('Postgres Performance Tuning', '{"postgres", "performance", "database"}', '2025-03-15 10:30:00Z'), ('Advanced Indexing Strategies', '{"sql", "indexes", "optimization"}', '2025-04-02 14:00:00Z'), ('Working with JSONB in Postgres', '{"postgres", "jsonb", "nosql"}', '2025-04-20 09:15:00Z'); ``` #### `btree_gin` index creation A composite GIN index is created to cover both `tags` and `published_at`. ```sql CREATE INDEX idx_posts_tags_published ON posts USING GIN (tags, published_at); ``` #### Example query Retrieve posts tagged 'postgres' published in April 2025. ```sql SELECT title, tags, published_at FROM posts WHERE tags @> '{"postgres"}' AND published_at >= '2025-04-01 00:00:00Z' AND published_at < '2025-05-01 00:00:00Z'; ``` The `idx_posts_tags_published` index enables Postgres to efficiently process both the array containment (`@>`) and timestamp range conditions. ### E-commerce product filtering by attributes and price In an e-commerce context, users often filter products based on dynamic attributes (e.g., stored in `JSONB`) and price ranges. #### Table schema ```sql CREATE TABLE products ( product_id SERIAL PRIMARY KEY, name TEXT, attributes JSONB, -- GIN-friendly JSONB (e.g., {"color": "red", "material": "cotton"}) price NUMERIC(10, 2) -- B-tree friendly numeric ); INSERT INTO products (name, attributes, price) VALUES ('Men''s Cotton T-Shirt', '{"color": "blue", "size": "M", "material": "cotton"}', 29.99), ('Women''s Wool Sweater', '{"color": "red", "size": "S", "material": "wool"}', 89.50), ('Unisex Denim Jeans', '{"color": "black", "size": "32/30", "material": "denim"}', 59.95); ``` #### `btree_gin` index creation ```sql CREATE INDEX idx_products_attributes_price ON products USING GIN (attributes, price); ``` #### Example query Find products made of "cotton" with a price below $50. ```sql SELECT name, attributes, price FROM products WHERE attributes @> '{"material": "cotton"}' AND price < 50.00; ``` The `idx_products_attributes_price` index facilitates efficient resolution of both the JSONB containment check and the numeric inequality. ## Important considerations and Best practices - **Write performance impact:** GIN indexes, due to their structure, generally incur a higher cost for `INSERT`, `UPDATE`, and `DELETE` operations compared to B-tree indexes. This should be a consideration for write-intensive workloads. - **Index storage size:** GIN indexes can be larger on disk than their B-tree counterparts for equivalent data. - **Query selectivity:** The benefits of `btree_gin` are most pronounced when queries filter on multiple columns included in the index, and the combined predicate is reasonably selective. - **Dedicated B-tree indexes:** For queries filtering _solely_ on a B-tree-indexable column, a dedicated B-tree index on that column typically offers superior performance. `btree_gin` is primarily for _combined_ criteria. ## Conclusion The `btree_gin` extension provides a valuable mechanism for optimizing complex queries in Postgres that involve filters across both GIN-indexable and B-tree-indexable column types. By enabling the creation of unified multi-column GIN indexes, `btree_gin` can lead to more efficient query plans, reduced execution times, and a simplified indexing landscape for specific workloads. ## Resources - [PostgreSQL `btree_gin` documentation](https://www.Postgres.org/docs/current/btree-gin.html) - [PostgreSQL Indexes](/postgresql/postgresql-indexes) - [PostgreSQL Index Types](/postgresql/postgresql-indexes/postgresql-index-types)