From 39aeff6f974d2ab33651ea5a88046f298ef8cafc Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Thu, 19 Aug 2021 13:34:38 +0100 Subject: [PATCH] [IMP] base_search_fuzzy: correct patching, remove similarity order - [Server-wide patching should be done in `post_load` hook][1], and there's where it's done now. - Remove similarity order, as it had no use in the wild and was buggy. - Refactor monkey patch to remove some nonsense. - Move tests to at_install mode, now that the patch is installed correctly. @Tecnativa TT31444 --- base_search_fuzzy/README.rst | 8 -- base_search_fuzzy/__init__.py | 1 + base_search_fuzzy/__manifest__.py | 3 +- base_search_fuzzy/hooks.py | 45 ++++++++++ base_search_fuzzy/models/__init__.py | 1 - base_search_fuzzy/models/ir_model.py | 86 ------------------- base_search_fuzzy/readme/USAGE.rst | 8 -- .../static/description/index.html | 9 +- .../tests/test_query_generation.py | 10 +-- 9 files changed, 50 insertions(+), 121 deletions(-) create mode 100644 base_search_fuzzy/hooks.py delete mode 100644 base_search_fuzzy/models/ir_model.py diff --git a/base_search_fuzzy/README.rst b/base_search_fuzzy/README.rst index 09e320c90..3f77f3928 100644 --- a/base_search_fuzzy/README.rst +++ b/base_search_fuzzy/README.rst @@ -70,14 +70,6 @@ Usage ``self.env.cr.execute("SELECT set_limit(0.2);")`` -#. Another interesting feature is the use of ``similarity(column, 'text')`` - function in the ``order`` parameter to order by similarity. This module just - contains a basic implementation which doesn't perform validations and has to - start with this function. For example you can define the function as - followed: - - ``similarity(%s.name, 'John Mil') DESC" % self.env['res.partner']._table`` - For further questions read the Documentation of the `pg_trgm `_ module. diff --git a/base_search_fuzzy/__init__.py b/base_search_fuzzy/__init__.py index 14b61a7d5..98d1a08c6 100644 --- a/base_search_fuzzy/__init__.py +++ b/base_search_fuzzy/__init__.py @@ -3,3 +3,4 @@ # Copyright 2020 NextERP SRL. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models +from .hooks import post_load diff --git a/base_search_fuzzy/__manifest__.py b/base_search_fuzzy/__manifest__.py index 7f2a77229..f8fe753a1 100644 --- a/base_search_fuzzy/__manifest__.py +++ b/base_search_fuzzy/__manifest__.py @@ -5,7 +5,7 @@ "name": "Fuzzy Search", "summary": "Fuzzy search with the PostgreSQL trigram extension", "category": "Uncategorized", - "version": "14.0.1.0.0", + "version": "14.0.1.0.1", "website": "https://github.com/OCA/server-tools", "author": "bloopark systems GmbH & Co. KG, " "ForgeFlow, " @@ -16,4 +16,5 @@ "data": ["views/trgm_index.xml", "security/ir.model.access.csv"], "demo": ["demo/res_partner_demo.xml", "demo/TrgmIndex_demo.xml"], "installable": True, + "post_load": "post_load", } diff --git a/base_search_fuzzy/hooks.py b/base_search_fuzzy/hooks.py new file mode 100644 index 000000000..fb83ba4e9 --- /dev/null +++ b/base_search_fuzzy/hooks.py @@ -0,0 +1,45 @@ +# Copyright 2016 Eficent Business and IT Consulting Services S.L. +# Copyright 2016 Serpent Consulting Services Pvt. Ltd. +# Copyright 2017 LasLabs Inc. +# Copyright 2021 Tecnativa - Jairo Llopis +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from functools import wraps + +from odoo.osv import expression + + +def patch_leaf_trgm(original): + @wraps(original) + def _wrapper(self, leaf, model, alias): + left, operator, right = leaf + # No overload for other operators... + if operator != "%": + # Except translation "inselect" queries + if operator == "inselect": + right = (right[0].replace(" % ", " %% "), right[1]) + leaf = (left, operator, right) + return original(self, leaf, model, alias) + # The field must exist + if left not in model._fields: + raise ValueError( + "Invalid field {!r} in domain term {!r}".format(left, leaf) + ) + # Generate correct WHERE clause part + query = '("{}"."{}" %% {})'.format( + alias, + left, + model._fields[left].column_format, + ) + params = [right] + return query, params + + return _wrapper + + +def post_load(): + """Patch expression generators to enable the fuzzy search operator.""" + expression.TERM_OPERATORS += ("%",) + expression.expression._expression__leaf_to_sql = patch_leaf_trgm( + expression.expression._expression__leaf_to_sql + ) diff --git a/base_search_fuzzy/models/__init__.py b/base_search_fuzzy/models/__init__.py index d4b09c313..a9fcb9ffc 100644 --- a/base_search_fuzzy/models/__init__.py +++ b/base_search_fuzzy/models/__init__.py @@ -2,6 +2,5 @@ # Copyright 2016 Serpent Consulting Services Pvt. Ltd. # Copyright 2020 NextERP SRL. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import ir_model from . import trgm_index from . import query diff --git a/base_search_fuzzy/models/ir_model.py b/base_search_fuzzy/models/ir_model.py deleted file mode 100644 index d17776de7..000000000 --- a/base_search_fuzzy/models/ir_model.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2016 ForgeFlow S.L. -# Copyright 2016 Serpent Consulting Services Pvt. Ltd. -# Copyright 2017 LasLabs Inc. -# Copyright 2020 NextERP SRL. -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import logging - -from odoo import _, api, models -from odoo.osv import expression - -_logger = logging.getLogger(__name__) - - -def patch_leaf_trgm(method): - def decorate_leaf_to_sql(self, leaf, model, alias): - left, operator, right = leaf - table_alias = '"%s"' % alias - if operator == "%": - - sql_operator = "%%" - params = [] - - if left in model._fields: - column = "{}.{}".format(table_alias, expression._quote(left)) - query = "({} {} {})".format( - column, - sql_operator, - model._fields[left].column_format, - ) - elif left in models.MAGIC_COLUMNS: - query = '({}."{}" {} %s)'.format(table_alias, left, sql_operator) - params = right - else: # Must not happen - raise ValueError( - _("Invalid field {!r} in domain term {!r}".format(left, leaf)) - ) - - if left in model._fields: - params = str(right) - - if isinstance(params, str): - params = [params] - return query, params - elif operator == "inselect": - right = (right[0].replace(" % ", " %% "), right[1]) - leaf = (left, operator, right) - - return method(self, leaf, model, alias) - - decorate_leaf_to_sql.__decorated__ = True - - return decorate_leaf_to_sql - - -def patch_generate_order_by(method): - @api.model - def decorate_generate_order_by(self, order_spec, query): - if order_spec and order_spec.startswith("similarity("): - return " ORDER BY " + order_spec - return method(self, order_spec, query) - - decorate_generate_order_by.__decorated__ = True - - return decorate_generate_order_by - - -class IrModel(models.Model): - - _inherit = "ir.model" - - def _register_hook(self): - # We have to prevent wrapping the function twice to avoid recursion - # errors - if not hasattr(expression.expression._expression__leaf_to_sql, "__decorated__"): - expression.expression._expression__leaf_to_sql = patch_leaf_trgm( - expression.expression._expression__leaf_to_sql - ) - - if "%" not in expression.TERM_OPERATORS: - expression.TERM_OPERATORS += ("%",) - - if not hasattr(models.BaseModel._generate_order_by, "__decorated__"): - models.BaseModel._generate_order_by = patch_generate_order_by( - models.BaseModel._generate_order_by - ) - return super()._register_hook() diff --git a/base_search_fuzzy/readme/USAGE.rst b/base_search_fuzzy/readme/USAGE.rst index 04f30223d..b990ad4bf 100644 --- a/base_search_fuzzy/readme/USAGE.rst +++ b/base_search_fuzzy/readme/USAGE.rst @@ -12,14 +12,6 @@ ``self.env.cr.execute("SELECT set_limit(0.2);")`` -#. Another interesting feature is the use of ``similarity(column, 'text')`` - function in the ``order`` parameter to order by similarity. This module just - contains a basic implementation which doesn't perform validations and has to - start with this function. For example you can define the function as - followed: - - ``similarity(%s.name, 'John Mil') DESC" % self.env['res.partner']._table`` - For further questions read the Documentation of the `pg_trgm `_ module. diff --git a/base_search_fuzzy/static/description/index.html b/base_search_fuzzy/static/description/index.html index 4b4b271bc..2ecdf57cc 100644 --- a/base_search_fuzzy/static/description/index.html +++ b/base_search_fuzzy/static/description/index.html @@ -3,7 +3,7 @@ - + Fuzzy Search