Skip to content

Commit 878d4f5

Browse files
author
bosd
committed
[ADD] attribute_set_jsonb: JSONB optimization for attribute_set
This module provides JSONB optimization for attribute_set by integrating with base_sparse_field_jsonb from OCA/server-tools. Features: - Automatic migration of x_custom_json_attrs columns to JSONB - GIN index creation for fast key/value lookups - Per-attribute expression indexes for frequently filtered attributes - Server action to regenerate all indexes This module is the bridge between: - base_sparse_field_jsonb (generic JSONB field in server-tools) - attribute_set (functional module in odoo-pim) Auto-installs when both attribute_set and base_sparse_field_jsonb are present.
1 parent 3dd4f17 commit 878d4f5

File tree

12 files changed

+1120
-0
lines changed

12 files changed

+1120
-0
lines changed

attribute_set_jsonb/README.rst

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
.. image:: https://odoo-community.org/readme-banner-image
2+
:target: https://odoo-community.org/get-involved?utm_source=readme
3+
:alt: Odoo Community Association
4+
5+
===================
6+
Attribute Set JSONB
7+
===================
8+
9+
..
10+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
11+
!! This file is generated by oca-gen-addon-readme !!
12+
!! changes will be overwritten. !!
13+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
14+
!! source digest: sha256:d286206bd64f4c1b478ec8b2ae6c5c943bd6a1b0d786362ef06055dcb606b2b7
15+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
16+
17+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
18+
:target: https://odoo-community.org/page/development-status
19+
:alt: Beta
20+
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
21+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
22+
:alt: License: AGPL-3
23+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fodoo--pim-lightgray.png?logo=github
24+
:target: https://github.com/OCA/odoo-pim/tree/19.0/attribute_set_jsonb
25+
:alt: OCA/odoo-pim
26+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
27+
:target: https://translation.odoo-community.org/projects/odoo-pim-19-0/odoo-pim-19-0-attribute_set_jsonb
28+
:alt: Translate me on Weblate
29+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
30+
:target: https://runboat.odoo-community.org/builds?repo=OCA/odoo-pim&target_branch=19.0
31+
:alt: Try me on Runboat
32+
33+
|badge1| |badge2| |badge3| |badge4| |badge5|
34+
35+
This module provides JSONB optimization for the ``attribute_set`` module
36+
by integrating with ``base_sparse_field_jsonb`` from OCA/server-tools.
37+
38+
Features
39+
--------
40+
41+
Automatic JSONB Migration
42+
~~~~~~~~~~~~~~~~~~~~~~~~~
43+
44+
On installation, the module automatically migrates ``attribute_set``'s
45+
``x_custom_json_attrs`` columns from TEXT to PostgreSQL JSONB format,
46+
providing:
47+
48+
- **Faster filtering**: GIN indexes enable efficient key/value lookups
49+
- **Native JSON operators**: Database-level filtering instead of Python
50+
- **Better storage**: Binary format with automatic compression
51+
52+
Expression-Based Indexing
53+
~~~~~~~~~~~~~~~~~~~~~~~~~
54+
55+
For frequently filtered attributes, you can enable per-attribute
56+
expression indexes that provide even faster filtering performance:
57+
58+
1. Go to PIM → Attributes → Product Attributes
59+
2. Edit an attribute and check "Create Expression Index"
60+
3. The module creates an optimized partial index for that specific
61+
attribute
62+
63+
This is recommended for attributes used heavily in e-commerce filtering
64+
(e.g., brand, color, material).
65+
66+
When to Use
67+
~~~~~~~~~~~
68+
69+
Install this module if you:
70+
71+
- Have many serialized attributes (100+)
72+
- Use attribute filtering on e-commerce pages
73+
- Need faster attribute-based searches
74+
75+
Technical Details
76+
~~~~~~~~~~~~~~~~~
77+
78+
The module creates two types of indexes:
79+
80+
1. **GIN index on JSONB column**: Enables fast key existence checks and
81+
value lookups across all attributes in the column
82+
2. **Expression indexes (optional)**: Per-attribute indexes for specific
83+
value extraction, optimal for equality queries on high-traffic
84+
attributes
85+
86+
**Table of contents**
87+
88+
.. contents::
89+
:local:
90+
91+
Bug Tracker
92+
===========
93+
94+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/odoo-pim/issues>`_.
95+
In case of trouble, please check there if your issue has already been reported.
96+
If you spotted it first, help us to smash it by providing a detailed and welcomed
97+
`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**>`_.
98+
99+
Do not contact contributors directly about support or help with technical issues.
100+
101+
Credits
102+
=======
103+
104+
Contributors
105+
------------
106+
107+
- Stefcy hello@stefcy.com
108+
109+
Maintainers
110+
-----------
111+
112+
This module is maintained by the OCA.
113+
114+
.. image:: https://odoo-community.org/logo.png
115+
:alt: Odoo Community Association
116+
:target: https://odoo-community.org
117+
118+
OCA, or the Odoo Community Association, is a nonprofit organization whose
119+
mission is to support the collaborative development of Odoo features and
120+
promote its widespread use.
121+
122+
This module is part of the `OCA/odoo-pim <https://github.com/OCA/odoo-pim/tree/19.0/attribute_set_jsonb>`_ project on GitHub.
123+
124+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

attribute_set_jsonb/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import hooks
2+
from . import models
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "Attribute Set JSONB",
3+
"version": "19.0.1.0.0",
4+
"category": "Technical",
5+
"summary": "JSONB optimization and expression indexing for attribute_set",
6+
"author": "Odoo Community Association (OCA)",
7+
"website": "https://github.com/OCA/odoo-pim",
8+
"license": "AGPL-3",
9+
"depends": [
10+
"attribute_set",
11+
"base_sparse_field_jsonb",
12+
],
13+
"data": [
14+
"views/attribute_attribute_views.xml",
15+
],
16+
"post_init_hook": "post_init_hook",
17+
"installable": True,
18+
"auto_install": True,
19+
}

attribute_set_jsonb/hooks.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"""Installation hooks for attribute_set_jsonb.
2+
3+
The post_init_hook handles migration of attribute_set's serialized field
4+
columns from TEXT to JSONB and creates GIN indexes for filtering.
5+
"""
6+
7+
import logging
8+
9+
from psycopg2 import Error as Psycopg2Error
10+
from psycopg2 import sql
11+
12+
_logger = logging.getLogger(__name__)
13+
14+
15+
def post_init_hook(env):
16+
"""Post-installation hook to migrate attribute_set columns to JSONB.
17+
18+
This hook specifically targets the x_custom_json_attrs columns used by
19+
attribute_set for serialized attribute storage.
20+
"""
21+
cr = env.cr
22+
23+
_logger.info("attribute_set_jsonb: Starting post-install migration...")
24+
25+
# Find all x_custom_json_attrs columns (attribute_set's serialization field)
26+
cr.execute(
27+
"""
28+
SELECT table_name, column_name, data_type
29+
FROM information_schema.columns
30+
WHERE column_name = 'x_custom_json_attrs'
31+
ORDER BY table_name
32+
"""
33+
)
34+
columns_to_migrate = cr.fetchall()
35+
36+
migrated_count = 0
37+
index_count = 0
38+
39+
for table_name, column_name, data_type in columns_to_migrate:
40+
# Migrate TEXT to JSONB if needed
41+
if data_type == "text":
42+
_logger.info(
43+
"Migrating %s.%s from TEXT to JSONB...", table_name, column_name
44+
)
45+
try:
46+
alter_query = sql.SQL(
47+
"""
48+
ALTER TABLE {table}
49+
ALTER COLUMN {column}
50+
TYPE jsonb
51+
USING CASE
52+
WHEN {column} IS NULL THEN NULL
53+
WHEN {column} = '' THEN '{{}}'::jsonb
54+
ELSE {column}::jsonb
55+
END
56+
"""
57+
).format(
58+
table=sql.Identifier(table_name),
59+
column=sql.Identifier(column_name),
60+
)
61+
cr.execute(alter_query)
62+
migrated_count += 1
63+
_logger.info(
64+
"Successfully migrated %s.%s to JSONB", table_name, column_name
65+
)
66+
except Psycopg2Error as e:
67+
_logger.warning(
68+
"Could not migrate %s.%s to JSONB: %s", table_name, column_name, e
69+
)
70+
cr.rollback()
71+
continue
72+
73+
# Create GIN index if not exists
74+
index_name = f"idx_{table_name}_{column_name}_gin"
75+
cr.execute(
76+
"""
77+
SELECT 1 FROM pg_indexes
78+
WHERE tablename = %s AND indexname = %s
79+
""",
80+
(table_name, index_name),
81+
)
82+
if not cr.fetchone():
83+
_logger.info("Creating GIN index on %s.%s...", table_name, column_name)
84+
try:
85+
create_index_query = sql.SQL(
86+
"""
87+
CREATE INDEX IF NOT EXISTS {index}
88+
ON {table} USING GIN ({column})
89+
"""
90+
).format(
91+
index=sql.Identifier(index_name),
92+
table=sql.Identifier(table_name),
93+
column=sql.Identifier(column_name),
94+
)
95+
cr.execute(create_index_query)
96+
index_count += 1
97+
_logger.info("Created GIN index %s", index_name)
98+
except Psycopg2Error as e:
99+
_logger.warning(
100+
"Could not create GIN index on %s.%s: %s",
101+
table_name,
102+
column_name,
103+
e,
104+
)
105+
106+
_logger.info(
107+
"attribute_set_jsonb: Migration complete. "
108+
"Migrated %d columns, created %d GIN indexes.",
109+
migrated_count,
110+
index_count,
111+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import attribute_attribute

0 commit comments

Comments
 (0)