Commit 0a6bfe12 authored by Vincent Pelletier's avatar Vincent Pelletier Committed by Eteri

CatalogTool: Introduce new dynamic related key syntax

Less ambiguous than previous syntax while keeping names short.
Allows defining more flags without limiting base_category namespace: the
only forbidden base_category id is "related", which was already reserved
(ie, not working) in previous syntax.
parent 159f91d7
...@@ -69,6 +69,15 @@ IGNORE_BASE_CATEGORY_UID = 'any' ...@@ -69,6 +69,15 @@ IGNORE_BASE_CATEGORY_UID = 'any'
SECURITY_QUERY_ARGUMENT_NAME = 'ERP5Catalog_security_query' SECURITY_QUERY_ARGUMENT_NAME = 'ERP5Catalog_security_query'
DYNAMIC_RELATED_KEY_FLAG_PARENT = 1 << 0
DYNAMIC_RELATED_KEY_FLAG_STRICT = 1 << 1
# Note: parsing flags backward as "pop()" is O(1), so this list contains flags
# in right to left order.
DYNAMIC_RELATED_KEY_FLAG_LIST = (
('parent', DYNAMIC_RELATED_KEY_FLAG_PARENT),
('strict', DYNAMIC_RELATED_KEY_FLAG_STRICT),
)
class IndexableObjectWrapper(object): class IndexableObjectWrapper(object):
__security_parameter_cache = None __security_parameter_cache = None
__local_role_cache = None __local_role_cache = None
...@@ -955,79 +964,93 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject): ...@@ -955,79 +964,93 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
This method will try to automatically generate new related key This method will try to automatically generate new related key
by looking at the category tree. by looking at the category tree.
For exemple it will generate:
destination_title | category,catalog/title/z_related_destination
default_destination_title | category,catalog/title/z_related_destination
strict_destination_title | category,catalog/title/z_related_strict_destination
strict_ related keys only returns documents which are strictly member of
the category.
Syntax: Syntax:
[default_][strict_][parent_]<base category id>[related_]<column id> [[strict_][parent_]_]<base category id>__[related__]<column id>
"default_": No effect, useful to avoid static related keys, which would shadow desired dynamic related key. "strict": Match only strict relation members, otherwise match non-strict too.
"strict_": Match only strict relation members, otherwise match non-strict too. "parent": Search for documents whose parent have described relation, otherwise search for their immediate relations.
"parent_": Search for documents whose parent have described relation, otherwise search for their immediate relations.
<base_category_id>: The id of an existing Base Category document, or "any" to not restrict by relation type. <base_category_id>: The id of an existing Base Category document, or "any" to not restrict by relation type.
"related_": Search for reverse relationships, otherwise search for direct relationships. "related": Search for reverse relationships, otherwise search for direct relationships.
<column_id>: The name of the column to compare values against. <column_id>: The name of the column to compare values against.
Old syntax is supported for backward-compatibility, but will not receive
further extensions:
[default_][strict_][parent_]<base category id>_[related_]<column id>
""" """
related_key_list = [] base_category_id_set = set(
base_cat_id_set = set(
self.getPortalObject().portal_categories.getBaseCategoryList() self.getPortalObject().portal_categories.getBaseCategoryList()
) )
base_cat_id_set.discard('parent') base_category_id_set.discard('parent')
default_string = 'default_'
strict_string = STRICT_METHOD_NAME
parent_string = PARENT_METHOD_NAME
related_string = 'related_'
column_map = self.getSQLCatalog(sql_catalog_id).getColumnMap() column_map = self.getSQLCatalog(sql_catalog_id).getColumnMap()
related_key_list = []
for key in key_list: for key in key_list:
prefix = '' flag_bitmap = 0
strict = 0 if '__' in key:
parent = 0 split_key = key.split('__')
if key.startswith(default_string): column_id = split_key.pop()
key = key[len(default_string):] if 'catalog' not in column_map.get(column_id, ()):
prefix = default_string continue
if key.startswith(strict_string): base_category_id = split_key.pop()
strict = 1 related = base_category_id == 'related'
key = key[len(strict_string):] if related:
prefix += strict_string base_category_id = split_key.pop()
if key.startswith(parent_string): if split_key:
parent = 1 flag_string, = split_key
key = key[len(parent_string):] flag_list = flag_string.split('_')
prefix += parent_string pop = flag_list.pop
split_key = key.split('_') for flag_name, flag in DYNAMIC_RELATED_KEY_FLAG_LIST:
for i in xrange(len(split_key) - 1, 0, -1): if flag_list[-1] == flag_name:
expected_base_cat_id = '_'.join(split_key[0:i]) flag_bitmap |= flag
if expected_base_cat_id in base_cat_id_set or ( pop()
i == len(split_key) - 1 and expected_base_cat_id == IGNORE_BASE_CATEGORY_UID if not flag_list:
): break
# We have found a base_category else:
end_key = '_'.join(split_key[i:]) continue
related = end_key.startswith(related_string) else:
if related: # BBB: legacy related key format
end_key = end_key[len(related_string):] default_string = 'default_'
# XXX: joining with non-catalog tables is not trivial and requires related_string = 'related_'
# ZSQLCatalog's ColumnMapper cooperation, so only allow catalog prefix = key
# columns. if prefix.startswith(default_string):
if 'catalog' in column_map.get(end_key, ()): prefix = prefix[len(default_string):]
is_uid = end_key == 'uid' if prefix.startswith(STRICT_METHOD_NAME):
if is_uid: prefix = prefix[len(STRICT_METHOD_NAME):]
end_key = 'uid' if related else 'category_uid' flag_bitmap |= DYNAMIC_RELATED_KEY_FLAG_STRICT
related_key_list.append( if prefix.startswith(PARENT_METHOD_NAME):
prefix + key + ' | category' + prefix = prefix[len(PARENT_METHOD_NAME):]
('' if is_uid else ',catalog') + flag_bitmap |= DYNAMIC_RELATED_KEY_FLAG_PARENT
'/' + split_key = prefix.split('_')
end_key + for i in xrange(len(split_key) - 1, 0, -1):
'/' + DYNAMIC_METHOD_NAME + base_category_id = '_'.join(split_key[0:i])
(STRICT_METHOD_NAME if strict else '') + if base_category_id in base_category_id_set or (
(PARENT_METHOD_NAME if parent else '') + i == len(split_key) - 1 and base_category_id == IGNORE_BASE_CATEGORY_UID
expected_base_cat_id + ):
(RELATED_DYNAMIC_METHOD_NAME if related else '') # We have found a base_category
) column_id = '_'.join(split_key[i:])
break related = column_id.startswith(related_string)
if related:
column_id = column_id[len(related_string):]
# XXX: joining with non-catalog tables is not trivial and requires
# ZSQLCatalog's ColumnMapper cooperation, so only allow catalog
# columns.
if 'catalog' in column_map.get(column_id, ()):
break
else:
continue
is_uid = column_id == 'uid'
if is_uid:
column_id = 'uid' if related else 'category_uid'
related_key_list.append(
key + ' | ' +
'category' +
('' if is_uid else ',catalog') +
'/' +
column_id +
'/' + DYNAMIC_METHOD_NAME +
(STRICT_METHOD_NAME if flag_bitmap & DYNAMIC_RELATED_KEY_FLAG_STRICT else '') +
(PARENT_METHOD_NAME if flag_bitmap & DYNAMIC_RELATED_KEY_FLAG_PARENT else '') +
base_category_id +
(RELATED_DYNAMIC_METHOD_NAME if related else '')
)
return related_key_list return related_key_list
def _aq_dynamic(self, name): def _aq_dynamic(self, name):
......
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