Ponyorm: фильтрация объектов по нескольким объединенным значениям в JOIN - PullRequest
0 голосов
/ 24 февраля 2020

Я пытаюсь реализовать механизм фильтрации тегов, где элементы могут иметь произвольное количество тегов, связанных с ними, и я хочу иметь возможность находить элементы, которые имеют один или все запрошенные теги.

Моя схема выглядит примерно так:

class Item(db.Entity):
    tags = orm.Set("ItemTag")

class ItemTag(db.Entity):
    item = orm.Required(Item)
    tag = orm.Required(str)
    orm.composite_key(item, tag)
    orm.composite_key(tag, item)

Я могу легко фильтровать существующие запросы по одному тегу:

def filter_query_tag(query, tag:str):
    return orm.select(i for i in query for t in i.tags if t.key == tag)

и могу найти элементы с любым тегов легко:

def filter_query_any_tag(query, tags:typing.Union(set,list,tuple)):
    return orm.select(i for i in query for t in i.tags if t.key in tags)

, но попытка создать запрос, который находит все тегов, выдает ошибку:

def filter_query_all_tags(query, tags:typing.Union(set,list,tuple)):
    for tag in tags:
        query = orm.select(i for i in query for t in i.tags if t.key == tag)
    return query

приводит к :

  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5562, in select
    return make_query(args, frame_depth=cut_traceback_depth+1)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5558, in make_query
    return Query(code_key, tree.code, globals, locals, cells, left_join)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5717, in __init__
    translator = translator_cls(tree_copy, None, code_key, filter_num, extractors, vars, vartypes.copy(), left_join=left_join)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 222, in __init__
    translator.init(tree, parent_translator, code_key, filter_num, extractors, vars, vartypes, left_join, optimize)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 396, in init
    tableref = translator.sqlquery.add_tableref(name_path, parent_tableref, attr)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 1211, in add_tableref
    assert name_path not in sqlquery.tablerefs
AssertionError

Вероятно, проблема в том, что возникают проблемы с отображением нескольких имен соединений в одну и ту же таблицу, что, возможно, является ошибкой в ​​ponyorm. Тем не менее, я подозреваю, что, возможно, есть лучший способ построить запрос без необходимости выполнять несколько объединений и фильтров.

Я попытался использовать orm.left_join вместо orm.select, например:

def filter_query_all_tags(query, tags:typing.Union(set,list,tuple)):
    for tag in tags:
        query = orm.left_join(i for i in query for t in i.tags if t.key == tag)
    return query

В этом случае я могу фильтровать по двум тегам одновременно, но если я добавлю третий тег, я получу:

  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5566, in left_join
    return make_query(args, frame_depth=cut_traceback_depth+1, left_join=True)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5558, in make_query
    return Query(code_key, tree.code, globals, locals, cells, left_join)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/core.py", line 5717, in __init__
    translator = translator_cls(tree_copy, None, code_key, filter_num, extractors, vars, vartypes.copy(), left_join=left_join)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 222, in __init__
    translator.init(tree, parent_translator, code_key, filter_num, extractors, vars, vartypes, left_join, optimize)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 343, in init
    names, try_extend_prev_query=not i)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 598, in process_query_qual
    subquery_ast = prev_translator.construct_subquery_ast(prev_limit, prev_offset, aliases=aliases)
  File "/Users/fluffy/.local/share/virtualenvs/Publ-FUB3ZG92/lib/python3.7/site-packages/pony/orm/sqltranslation.py", line 625, in construct_subquery_ast
    assert not star and len(aliases) == len(select_ast) - 1
AssertionError

Есть ли примитив или idiomati c выражение в пониоме, которое позволяет мне указать, что все значения набора должны присутствовать в ItemTag значениях для любого данного Item?

1 Ответ

0 голосов
/ 24 февраля 2020

Это похоже на работу:

def filter_query_all_tags(query, tags:typing.Union(set,list,tuple)):
    for tag in tags:
        query = query.filter(lambda i: orm.exists(t for t in i.tags if t.key == tag))
    return query

Однако, похоже, что это могло бы быть и лучше.

...