Commit 4bcf5c6a authored by Vincent Pelletier's avatar Vincent Pelletier

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 713b4191
...@@ -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:
if flag_list[-1] == flag_name:
flag_bitmap |= flag
pop()
if not flag_list:
break
else:
continue
else:
# BBB: legacy related key format
default_string = 'default_'
related_string = 'related_'
prefix = key
if prefix.startswith(default_string):
prefix = prefix[len(default_string):]
if prefix.startswith(STRICT_METHOD_NAME):
prefix = prefix[len(STRICT_METHOD_NAME):]
flag_bitmap |= DYNAMIC_RELATED_KEY_FLAG_STRICT
if prefix.startswith(PARENT_METHOD_NAME):
prefix = prefix[len(PARENT_METHOD_NAME):]
flag_bitmap |= DYNAMIC_RELATED_KEY_FLAG_PARENT
split_key = prefix.split('_')
for i in xrange(len(split_key) - 1, 0, -1): for i in xrange(len(split_key) - 1, 0, -1):
expected_base_cat_id = '_'.join(split_key[0:i]) base_category_id = '_'.join(split_key[0:i])
if expected_base_cat_id in base_cat_id_set or ( if base_category_id in base_category_id_set or (
i == len(split_key) - 1 and expected_base_cat_id == IGNORE_BASE_CATEGORY_UID i == len(split_key) - 1 and base_category_id == IGNORE_BASE_CATEGORY_UID
): ):
# We have found a base_category # We have found a base_category
end_key = '_'.join(split_key[i:]) column_id = '_'.join(split_key[i:])
related = end_key.startswith(related_string) related = column_id.startswith(related_string)
if related: if related:
end_key = end_key[len(related_string):] column_id = column_id[len(related_string):]
# XXX: joining with non-catalog tables is not trivial and requires # XXX: joining with non-catalog tables is not trivial and requires
# ZSQLCatalog's ColumnMapper cooperation, so only allow catalog # ZSQLCatalog's ColumnMapper cooperation, so only allow catalog
# columns. # columns.
if 'catalog' in column_map.get(end_key, ()): if 'catalog' in column_map.get(column_id, ()):
is_uid = end_key == 'uid' break
else:
continue
is_uid = column_id == 'uid'
if is_uid: if is_uid:
end_key = 'uid' if related else 'category_uid' column_id = 'uid' if related else 'category_uid'
related_key_list.append( related_key_list.append(
prefix + key + ' | category' + key + ' | ' +
'category' +
('' if is_uid else ',catalog') + ('' if is_uid else ',catalog') +
'/' + '/' +
end_key + column_id +
'/' + DYNAMIC_METHOD_NAME + '/' + DYNAMIC_METHOD_NAME +
(STRICT_METHOD_NAME if strict else '') + (STRICT_METHOD_NAME if flag_bitmap & DYNAMIC_RELATED_KEY_FLAG_STRICT else '') +
(PARENT_METHOD_NAME if parent else '') + (PARENT_METHOD_NAME if flag_bitmap & DYNAMIC_RELATED_KEY_FLAG_PARENT else '') +
expected_base_cat_id + base_category_id +
(RELATED_DYNAMIC_METHOD_NAME if related else '') (RELATED_DYNAMIC_METHOD_NAME if related else '')
) )
break
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