Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions attribute_set_jsonb/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

===================
Attribute Set JSONB
===================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:d286206bd64f4c1b478ec8b2ae6c5c943bd6a1b0d786362ef06055dcb606b2b7
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--pim-lightgray.png?logo=github
:target: https://github.com/OCA/odoo-pim/tree/19.0/attribute_set_jsonb
:alt: OCA/odoo-pim
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/odoo-pim-19-0/odoo-pim-19-0-attribute_set_jsonb
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/odoo-pim&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module provides JSONB optimization for the ``attribute_set`` module
by integrating with ``base_sparse_field_jsonb`` from OCA/server-tools.

Features
--------

Automatic JSONB Migration
~~~~~~~~~~~~~~~~~~~~~~~~~

On installation, the module automatically migrates ``attribute_set``'s
``x_custom_json_attrs`` columns from TEXT to PostgreSQL JSONB format,
providing:

- **Faster filtering**: GIN indexes enable efficient key/value lookups
- **Native JSON operators**: Database-level filtering instead of Python
- **Better storage**: Binary format with automatic compression

Expression-Based Indexing
~~~~~~~~~~~~~~~~~~~~~~~~~

For frequently filtered attributes, you can enable per-attribute
expression indexes that provide even faster filtering performance:

1. Go to PIM → Attributes → Product Attributes
2. Edit an attribute and check "Create Expression Index"
3. The module creates an optimized partial index for that specific
attribute

This is recommended for attributes used heavily in e-commerce filtering
(e.g., brand, color, material).

When to Use
~~~~~~~~~~~

Install this module if you:

- Have many serialized attributes (100+)
- Use attribute filtering on e-commerce pages
- Need faster attribute-based searches

Technical Details
~~~~~~~~~~~~~~~~~

The module creates two types of indexes:

1. **GIN index on JSONB column**: Enables fast key existence checks and
value lookups across all attributes in the column
2. **Expression indexes (optional)**: Per-attribute indexes for specific
value extraction, optimal for equality queries on high-traffic
attributes

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/odoo-pim/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/odoo-pim/issues/new?body=module:%20attribute_set_jsonb%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Contributors
------------

- Stefcy [email protected]

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/odoo-pim <https://github.com/OCA/odoo-pim/tree/19.0/attribute_set_jsonb>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions attribute_set_jsonb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .hooks import post_init_hook
from . import models
19 changes: 19 additions & 0 deletions attribute_set_jsonb/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "Attribute Set JSONB",
"version": "19.0.1.0.0",
"category": "Technical",
"summary": "JSONB optimization and expression indexing for attribute_set",
"author": "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/odoo-pim",
"license": "AGPL-3",
"depends": [
"attribute_set",
"base_sparse_field_jsonb",
],
"data": [
"views/attribute_attribute_views.xml",
],
"post_init_hook": "post_init_hook",
"installable": True,
"auto_install": True,
}
111 changes: 111 additions & 0 deletions attribute_set_jsonb/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""Installation hooks for attribute_set_jsonb.

The post_init_hook handles migration of attribute_set's serialized field
columns from TEXT to JSONB and creates GIN indexes for filtering.
"""

import logging

from psycopg2 import Error as Psycopg2Error
from psycopg2 import sql

_logger = logging.getLogger(__name__)


def post_init_hook(env):
"""Post-installation hook to migrate attribute_set columns to JSONB.

This hook specifically targets the x_custom_json_attrs columns used by
attribute_set for serialized attribute storage.
"""
cr = env.cr

_logger.info("attribute_set_jsonb: Starting post-install migration...")

# Find all x_custom_json_attrs columns (attribute_set's serialization field)
cr.execute(
"""
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE column_name = 'x_custom_json_attrs'
ORDER BY table_name
"""
)
columns_to_migrate = cr.fetchall()

migrated_count = 0
index_count = 0

for table_name, column_name, data_type in columns_to_migrate:
# Migrate TEXT to JSONB if needed
if data_type == "text":
_logger.info(
"Migrating %s.%s from TEXT to JSONB...", table_name, column_name
)
try:
alter_query = sql.SQL(
"""
ALTER TABLE {table}
ALTER COLUMN {column}
TYPE jsonb
USING CASE
WHEN {column} IS NULL THEN NULL
WHEN {column} = '' THEN '{{}}'::jsonb
ELSE {column}::jsonb
END
"""
).format(
table=sql.Identifier(table_name),
column=sql.Identifier(column_name),
)
cr.execute(alter_query)
migrated_count += 1
_logger.info(
"Successfully migrated %s.%s to JSONB", table_name, column_name
)
except Psycopg2Error as e:
_logger.warning(
"Could not migrate %s.%s to JSONB: %s", table_name, column_name, e
)
cr.rollback()
continue

# Create GIN index if not exists
index_name = f"idx_{table_name}_{column_name}_gin"
cr.execute(
"""
SELECT 1 FROM pg_indexes
WHERE tablename = %s AND indexname = %s
""",
(table_name, index_name),
)
if not cr.fetchone():
_logger.info("Creating GIN index on %s.%s...", table_name, column_name)
try:
create_index_query = sql.SQL(
"""
CREATE INDEX IF NOT EXISTS {index}
ON {table} USING GIN ({column})
"""
).format(
index=sql.Identifier(index_name),
table=sql.Identifier(table_name),
column=sql.Identifier(column_name),
)
cr.execute(create_index_query)
index_count += 1
_logger.info("Created GIN index %s", index_name)
except Psycopg2Error as e:
_logger.warning(
"Could not create GIN index on %s.%s: %s",
table_name,
column_name,
e,
)

_logger.info(
"attribute_set_jsonb: Migration complete. "
"Migrated %d columns, created %d GIN indexes.",
migrated_count,
index_count,
)
1 change: 1 addition & 0 deletions attribute_set_jsonb/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import attribute_attribute
Loading
Loading