diff --git a/product/CMFCategory/CategoryTool.py b/product/CMFCategory/CategoryTool.py index 90759ed9a272556bd9a25e12a4ffc96b9d981c0a..0c1a472591467676955e1e3ad295a78fccc79847 100644 --- a/product/CMFCategory/CategoryTool.py +++ b/product/CMFCategory/CategoryTool.py @@ -1372,11 +1372,14 @@ class CategoryTool( UniqueObject, Folder, Base ): portal_catalog = context.getPortalObject().portal_catalog def search(category_list, portal_type, strict_membership): - catalog_kw = portal_catalog.getCategoryParameterDict( - category_list=category_list, - strict_membership=strict_membership, - ) - inner_join_list = catalog_kw.keys() + inner_join_list = [] + catalog_kw = { + 'query': portal_catalog.getCategoryParameterDict( + category_list=category_list, + strict_membership=strict_membership, + onJoin=inner_join_list.append, + ), + } if portal_type is not None: catalog_kw['portal_type'] = portal_type return portal_catalog.unrestrictedSearchResults( diff --git a/product/ERP5/Tool/DomainTool.py b/product/ERP5/Tool/DomainTool.py index 9b9f061d4117a2a599e44e91dcf9efd45a256b4e..7ed2ea48d13e99caea59db3a8c981f76a2dd3b4e 100644 --- a/product/ERP5/Tool/DomainTool.py +++ b/product/ERP5/Tool/DomainTool.py @@ -183,24 +183,29 @@ class DomainTool(BaseTool): # BBB: ValueError would seem more appropriate here, but original code # was raising TypeError - and this is explicitely tested for. raise TypeError('Unknown category: %r' % (category, )) - for join_category_list, join_list, predicate_has_no_condition_value in ( + def onInnerJoin(column_name): + inner_join_list.append(column_name) # Base category is part of preferred predicate categories, predicates # which ignore it are indexed with category_uid=0. - (inner_join_category_list, inner_join_list, 0), + return SimpleQuery(**{column_name: 0}) + query_list.append(portal_catalog.getCategoryParameterDict( + inner_join_category_list, + category_table='predicate_category', + onMissing=onMissing, + onJoin=onInnerJoin, + )) + def onLeftJoin(column_name): + left_join_list.append(column_name) # Base category is not part of preferred predicate categories, # predicates which ignore it get no predicate_category row inserted # for it, so an SQL NULL appears, translating to None. - (left_join_category_list, left_join_list, None), - ): - category_parameter_kw = portal_catalog.getCategoryParameterDict( - join_category_list, - category_table='predicate_category', - onMissing=onMissing, - ) - for relation, uid_set in category_parameter_kw.iteritems(): - join_list.append(relation) - uid_set.add(predicate_has_no_condition_value) - kw.update(category_parameter_kw) + return SimpleQuery(**{column_name: None}) + query_list.append(portal_catalog.getCategoryParameterDict( + left_join_category_list, + category_table='predicate_category', + onMissing=onMissing, + onJoin=onLeftJoin, + )) else: # No category to match against, so predicates expecting any relation # would not apply, so we can exclude these. diff --git a/product/ERP5Catalog/CatalogTool.py b/product/ERP5Catalog/CatalogTool.py index 5e0ed78d7d6756060931218fa404cade0a68f34d..85fa4d38f699372fdd27f1484a733edae4d26ac7 100644 --- a/product/ERP5Catalog/CatalogTool.py +++ b/product/ERP5Catalog/CatalogTool.py @@ -79,6 +79,7 @@ DYNAMIC_RELATED_KEY_FLAG_LIST = ( ('strict', DYNAMIC_RELATED_KEY_FLAG_STRICT), ('predicate', DYNAMIC_RELATED_KEY_FLAG_PREDICATE), ) +EMPTY_SET = () class IndexableObjectWrapper(object): __security_parameter_cache = None @@ -1036,12 +1037,11 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): return related_key_list security.declarePublic('getCategoryValueDictParameterDict') - def getCategoryValueDictParameterDict(self, base_category_dict, category_table='category', strict_membership=True, forward=True): + def getCategoryValueDictParameterDict(self, base_category_dict, category_table='category', strict_membership=True, forward=True, onJoin=lambda x: None): """ From a mapping from base category ids to lists of documents, produce a - catalog keyword argument dictionary testing (strict or not, forward or - reverse relation) membership to these documents with their respective - base categories. + query tree testing (strict or not, forward or reverse relation) + membership to these documents with their respective base categories. base_category_dict (dict with base category ids as keys and document sets as values) @@ -1054,9 +1054,17 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): forward (bool) Whether document being looked up bears the relation (true) or is its target (false). - - Return a dictionnary whose keys are catalog parameter names and values - are sets of uids. + onJoin(column_name) -> None or query + Called for each generated query which imply a join. Specifically, this + will not be called for "parent" relation, as it does not involve a + join. + Receives pseudo-column name of the relation as argument. + If return value is not None, it must be a query tree, OR-ed with + existing conditions for given pseudo-column. + This last form should very rarely be needed (ex: when joining with + predicate_category table as it contains non-standard uid values). + + Return a query tree. """ flag_list = [] if category_table == 'predicate_category': @@ -1067,23 +1075,36 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): flag_list.append('strict') prefix = ('_'.join(flag_list) + '__') if flag_list else '' suffix = ('' if forward else '__related') + '__uid' - parent_uid_set = base_category_dict.pop('parent', None) - result = { - prefix + base_category_id + suffix: { - document.getUid() for document in document_set - } - for base_category_id, document_set in base_category_dict.iteritems() - } - if parent_uid_set is not None: - result['parent_uid'] = parent_uid_set - return result + parent_document_set = base_category_dict.pop('parent', None) + query_list = [] + for base_category_id, document_set in base_category_dict.iteritems(): + column = prefix + base_category_id + suffix + category_query = SimpleQuery(**{ + column: {document.getUid() for document in document_set}, + }) + extra_query = onJoin(column) + if extra_query is not None: + category_query = ComplexQuery( + category_query, + extra_query, + logical_operator='OR', + ) + query_list.append(category_query) + if parent_document_set is not None: + query_list.append(SimpleQuery( + parent_uid={ + document.getUid() + for document in parent_document_set + }, + )) + return ComplexQuery(query_list) security.declarePublic('getCategoryParameterDict') def getCategoryParameterDict(self, category_list, onMissing=lambda category: True, **kw): """ - From a list of categories, produce a catalog keyword argument dictionary - testing (strict or not, forward or reverse relation) membership to these - categories. + From a list of categories, produce a query tree testing (strict or not, + forward or reverse relation) membership to these documents with their + respective base categories. category_list (list of category relative urls with their base categories) onMissing (callable)