string_agg
Some checks failed
Deploy on push / deploy (push) Failing after 11s

This commit is contained in:
Untone 2024-08-07 17:45:22 +03:00
parent f9a91e3a66
commit 9c374d789e
3 changed files with 42 additions and 25 deletions

View File

@ -38,7 +38,7 @@ class Reaction(Base):
deleted_by = Column(ForeignKey("author.id"), nullable=True) deleted_by = Column(ForeignKey("author.id"), nullable=True)
reply_to = Column(ForeignKey("reaction.id"), nullable=True) reply_to = Column(ForeignKey("reaction.id"), nullable=True)
quote = Column(String, nullable=True, comment="Original quoted text") quote = Column(String, nullable=True, comment="Original quoted text")
shout = Column(ForeignKey("shout.id"), nullable=False) shout = Column(ForeignKey("shout.id"), nullable=False, index=True)
created_by = Column(ForeignKey("author.id"), nullable=False) created_by = Column(ForeignKey("author.id"), nullable=False)
kind = Column(String, nullable=False, index=True) kind = Column(String, nullable=False, index=True)

View File

@ -27,43 +27,49 @@ from services.viewed import ViewedStorage
def query_shouts(): def query_shouts():
""" """
Базовый запрос для получения публикаций с подзапросами статистики, авторов и тем. Базовый запрос для получения публикаций с подзапросами статистики, авторов и тем,
с агрегацией в строку вместо JSON.
:param session: Сессия SQLAlchemy для выполнения запроса.
:return: Запрос для получения публикаций. :return: Запрос для получения публикаций.
""" """
# Создаем алиасы для таблиц для избежания конфликтов имен # Создаем алиасы для таблиц для избежания конфликтов имен
aliased_reaction = aliased(Reaction) aliased_reaction = aliased(Reaction)
# Подзапрос для уникальных авторов # Подзапрос для уникальных авторов, объединенных в строку
authors_subquery = ( authors_subquery = (
select( select(
ShoutAuthor.shout.label("shout_id"), ShoutAuthor.shout.label("shout_id"),
func.json_agg( func.string_agg(
func.json_build_object( func.concat_ws(
'id', Author.id, ";",
'name', Author.name, func.concat("id:", Author.id),
'slug', Author.slug, func.concat("name:", Author.name),
'pic', Author.pic func.concat("slug:", Author.slug),
) func.concat("pic:", Author.pic),
).label("authors") ),
", ",
).label("authors"),
) )
.join(Author, ShoutAuthor.author == Author.id) .join(Author, ShoutAuthor.author == Author.id)
.group_by(ShoutAuthor.shout) .group_by(ShoutAuthor.shout)
.subquery() .subquery()
) )
# Подзапрос для уникальных тем # Подзапрос для уникальных тем, объединенных в строку
topics_subquery = ( topics_subquery = (
select( select(
ShoutTopic.shout.label("shout_id"), ShoutTopic.shout.label("shout_id"),
func.json_agg( func.string_agg(
func.json_build_object( func.concat_ws(
'id', Topic.id, ";",
'title', Topic.title, func.concat("id:", Topic.id),
'body', Topic.body, func.concat("title:", Topic.title),
'slug', Topic.slug func.concat("body:", Topic.body),
) func.concat("slug:", Topic.slug),
).label("topics") ),
", ",
).label("topics"),
) )
.join(Topic, ShoutTopic.topic == Topic.id) .join(Topic, ShoutTopic.topic == Topic.id)
.group_by(ShoutTopic.shout) .group_by(ShoutTopic.shout)
@ -95,6 +101,7 @@ def query_shouts():
return q, aliased_reaction return q, aliased_reaction
def get_shouts_with_stats(q, limit, offset=0, author_id=None): def get_shouts_with_stats(q, limit, offset=0, author_id=None):
""" """
Получение публикаций со статистикой, и подзапросами авторов и тем. Получение публикаций со статистикой, и подзапросами авторов и тем.

View File

@ -12,21 +12,23 @@ from cache.memorycache import cache_region
from services.schema import mutation, query from services.schema import mutation, query
# Запрос на получение всех тем
@query.field("get_topics_all") @query.field("get_topics_all")
def get_topics_all(_, _info): def get_topics_all(_, _info):
cache_key = "get_topics_all" cache_key = "get_topics_all" # Ключ для кеша
@cache_region.cache_on_arguments(cache_key) @cache_region.cache_on_arguments(cache_key)
def _get_topics_all(): def _get_topics_all():
topics_query = select(Topic) topics_query = select(Topic)
return get_with_stat(topics_query) return get_with_stat(topics_query) # Получение тем с учетом статистики
return _get_topics_all() return _get_topics_all()
# Запрос на получение тем по сообществу
@query.field("get_topics_by_community") @query.field("get_topics_by_community")
def get_topics_by_community(_, _info, community_id: int): def get_topics_by_community(_, _info, community_id: int):
cache_key = f"get_topics_by_community_{community_id}" cache_key = f"get_topics_by_community_{community_id}" # Ключ для кеша
@cache_region.cache_on_arguments(cache_key) @cache_region.cache_on_arguments(cache_key)
def _get_topics_by_community(): def _get_topics_by_community():
@ -36,6 +38,7 @@ def get_topics_by_community(_, _info, community_id: int):
return _get_topics_by_community() return _get_topics_by_community()
# Запрос на получение тем по автору
@query.field("get_topics_by_author") @query.field("get_topics_by_author")
async def get_topics_by_author(_, _info, author_id=0, slug="", user=""): async def get_topics_by_author(_, _info, author_id=0, slug="", user=""):
topics_by_author_query = select(Topic) topics_by_author_query = select(Topic)
@ -49,6 +52,7 @@ async def get_topics_by_author(_, _info, author_id=0, slug="", user=""):
return get_with_stat(topics_by_author_query) return get_with_stat(topics_by_author_query)
# Запрос на получение одной темы по её slug
@query.field("get_topic") @query.field("get_topic")
async def get_topic(_, _info, slug: str): async def get_topic(_, _info, slug: str):
topic = await get_cached_topic_by_slug(slug, get_with_stat) topic = await get_cached_topic_by_slug(slug, get_with_stat)
@ -56,12 +60,13 @@ async def get_topic(_, _info, slug: str):
return topic return topic
# Мутация для создания новой темы
@mutation.field("create_topic") @mutation.field("create_topic")
@login_required @login_required
async def create_topic(_, _info, inp): async def create_topic(_, _info, inp):
with local_session() as session: with local_session() as session:
# TODO: check user permissions to create topic for exact community # TODO: проверить права пользователя на создание темы для конкретного сообщества
# and actor is permitted to craete it # и разрешение на создание
new_topic = Topic(**inp) new_topic = Topic(**inp)
session.add(new_topic) session.add(new_topic)
session.commit() session.commit()
@ -69,6 +74,7 @@ async def create_topic(_, _info, inp):
return {"topic": new_topic} return {"topic": new_topic}
# Мутация для обновления темы
@mutation.field("update_topic") @mutation.field("update_topic")
@login_required @login_required
async def update_topic(_, _info, inp): async def update_topic(_, _info, inp):
@ -85,6 +91,7 @@ async def update_topic(_, _info, inp):
return {"topic": topic} return {"topic": topic}
# Мутация для удаления темы
@mutation.field("delete_topic") @mutation.field("delete_topic")
@login_required @login_required
async def delete_topic(_, info, slug: str): async def delete_topic(_, info, slug: str):
@ -105,6 +112,7 @@ async def delete_topic(_, info, slug: str):
return {"error": "access denied"} return {"error": "access denied"}
# Запрос на получение случайных тем
@query.field("get_topics_random") @query.field("get_topics_random")
def get_topics_random(_, _info, amount=12): def get_topics_random(_, _info, amount=12):
q = select(Topic) q = select(Topic)
@ -121,6 +129,7 @@ def get_topics_random(_, _info, amount=12):
return topics return topics
# Запрос на получение подписчиков темы
@query.field("get_topic_followers") @query.field("get_topic_followers")
async def get_topic_followers(_, _info, slug: str): async def get_topic_followers(_, _info, slug: str):
logger.debug(f"getting followers for @{slug}") logger.debug(f"getting followers for @{slug}")
@ -130,6 +139,7 @@ async def get_topic_followers(_, _info, slug: str):
return followers return followers
# Запрос на получение авторов темы
@query.field("get_topic_authors") @query.field("get_topic_authors")
async def get_topic_authors(_, _info, slug: str): async def get_topic_authors(_, _info, slug: str):
logger.debug(f"getting authors for @{slug}") logger.debug(f"getting authors for @{slug}")