Commit 8d751ead authored by Vincent Pelletier's avatar Vincent Pelletier Committed by Eteri

CatalogTool: make getCategory{,ValueDict}ParameterDict return a query tree

This way it is easier to further extend it without risking key conflicts
in catalog parameters, which in turn would either lead to simple TypeErrors
("multiple values for parameter ...") or to criterion being accidentally
ignored.
Update callers.
These methods are young enough that no other callers should exist yet.
parent 9e307ebb
...@@ -1372,11 +1372,14 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1372,11 +1372,14 @@ class CategoryTool( UniqueObject, Folder, Base ):
portal_catalog = context.getPortalObject().portal_catalog portal_catalog = context.getPortalObject().portal_catalog
def search(category_list, portal_type, strict_membership): def search(category_list, portal_type, strict_membership):
catalog_kw = portal_catalog.getCategoryParameterDict( inner_join_list = []
catalog_kw = {
'query': portal_catalog.getCategoryParameterDict(
category_list=category_list, category_list=category_list,
strict_membership=strict_membership, strict_membership=strict_membership,
) onJoin=inner_join_list.append,
inner_join_list = catalog_kw.keys() ),
}
if portal_type is not None: if portal_type is not None:
catalog_kw['portal_type'] = portal_type catalog_kw['portal_type'] = portal_type
return portal_catalog.unrestrictedSearchResults( return portal_catalog.unrestrictedSearchResults(
......
...@@ -183,24 +183,29 @@ class DomainTool(BaseTool): ...@@ -183,24 +183,29 @@ class DomainTool(BaseTool):
# BBB: ValueError would seem more appropriate here, but original code # BBB: ValueError would seem more appropriate here, but original code
# was raising TypeError - and this is explicitely tested for. # was raising TypeError - and this is explicitely tested for.
raise TypeError('Unknown category: %r' % (category, )) 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 # Base category is part of preferred predicate categories, predicates
# which ignore it are indexed with category_uid=0. # 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, # Base category is not part of preferred predicate categories,
# predicates which ignore it get no predicate_category row inserted # predicates which ignore it get no predicate_category row inserted
# for it, so an SQL NULL appears, translating to None. # for it, so an SQL NULL appears, translating to None.
(left_join_category_list, left_join_list, None), return SimpleQuery(**{column_name: None})
): query_list.append(portal_catalog.getCategoryParameterDict(
category_parameter_kw = portal_catalog.getCategoryParameterDict( left_join_category_list,
join_category_list,
category_table='predicate_category', category_table='predicate_category',
onMissing=onMissing, onMissing=onMissing,
) onJoin=onLeftJoin,
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)
else: else:
# No category to match against, so predicates expecting any relation # No category to match against, so predicates expecting any relation
# would not apply, so we can exclude these. # would not apply, so we can exclude these.
......
...@@ -79,6 +79,7 @@ DYNAMIC_RELATED_KEY_FLAG_LIST = ( ...@@ -79,6 +79,7 @@ DYNAMIC_RELATED_KEY_FLAG_LIST = (
('strict', DYNAMIC_RELATED_KEY_FLAG_STRICT), ('strict', DYNAMIC_RELATED_KEY_FLAG_STRICT),
('predicate', DYNAMIC_RELATED_KEY_FLAG_PREDICATE), ('predicate', DYNAMIC_RELATED_KEY_FLAG_PREDICATE),
) )
EMPTY_SET = ()
class IndexableObjectWrapper(object): class IndexableObjectWrapper(object):
__security_parameter_cache = None __security_parameter_cache = None
...@@ -1036,12 +1037,11 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): ...@@ -1036,12 +1037,11 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
return related_key_list return related_key_list
security.declarePublic('getCategoryValueDictParameterDict') 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 From a mapping from base category ids to lists of documents, produce a
catalog keyword argument dictionary testing (strict or not, forward or query tree testing (strict or not, forward or reverse relation)
reverse relation) membership to these documents with their respective membership to these documents with their respective base categories.
base categories.
base_category_dict (dict with base category ids as keys and document sets base_category_dict (dict with base category ids as keys and document sets
as values) as values)
...@@ -1054,9 +1054,17 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): ...@@ -1054,9 +1054,17 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
forward (bool) forward (bool)
Whether document being looked up bears the relation (true) or is its Whether document being looked up bears the relation (true) or is its
target (false). target (false).
onJoin(column_name) -> None or query
Return a dictionnary whose keys are catalog parameter names and values Called for each generated query which imply a join. Specifically, this
are sets of uids. 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 = [] flag_list = []
if category_table == 'predicate_category': if category_table == 'predicate_category':
...@@ -1067,23 +1075,36 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): ...@@ -1067,23 +1075,36 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
flag_list.append('strict') flag_list.append('strict')
prefix = ('_'.join(flag_list) + '__') if flag_list else '' prefix = ('_'.join(flag_list) + '__') if flag_list else ''
suffix = ('' if forward else '__related') + '__uid' suffix = ('' if forward else '__related') + '__uid'
parent_uid_set = base_category_dict.pop('parent', None) parent_document_set = base_category_dict.pop('parent', None)
result = { query_list = []
prefix + base_category_id + suffix: { for base_category_id, document_set in base_category_dict.iteritems():
document.getUid() for document in document_set column = prefix + base_category_id + suffix
} category_query = SimpleQuery(**{
for base_category_id, document_set in base_category_dict.iteritems() column: {document.getUid() for document in document_set},
} })
if parent_uid_set is not None: extra_query = onJoin(column)
result['parent_uid'] = parent_uid_set if extra_query is not None:
return result 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') security.declarePublic('getCategoryParameterDict')
def getCategoryParameterDict(self, category_list, onMissing=lambda category: True, **kw): def getCategoryParameterDict(self, category_list, onMissing=lambda category: True, **kw):
""" """
From a list of categories, produce a catalog keyword argument dictionary From a list of categories, produce a query tree testing (strict or not,
testing (strict or not, forward or reverse relation) membership to these forward or reverse relation) membership to these documents with their
categories. respective base categories.
category_list (list of category relative urls with their base categories) category_list (list of category relative urls with their base categories)
onMissing (callable) onMissing (callable)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment