limit-offset-patch

This commit is contained in:
tonyrewin 2022-09-14 12:45:31 +03:00
parent 90439d931d
commit 66cd20514e
6 changed files with 79 additions and 91 deletions

View File

@ -6,11 +6,12 @@ from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import TopicFollower from orm.topic import TopicFollower
from orm.user import AuthorFollower from orm.user import AuthorFollower
from typing import List from typing import List
from services.zine.shoutscache import prepare_shouts
@query.field("shoutsForFeed") @query.field("shoutsForFeed")
@login_required @login_required
def get_user_feed(_, info, page, size) -> List[Shout]: def get_user_feed(_, info, offset, limit) -> List[Shout]:
user = info.context["request"].user user = info.context["request"].user
shouts = [] shouts = []
with local_session() as session: with local_session() as session:
@ -28,23 +29,23 @@ def get_user_feed(_, info, page, size) -> List[Shout]:
.where(TopicFollower.follower == user.slug) .where(TopicFollower.follower == user.slug)
.order_by(desc(Shout.createdAt)) .order_by(desc(Shout.createdAt))
) )
shouts = shouts.union(topicrows).limit(size).offset(page * size).all() shouts = shouts.union(topicrows).limit(limit).offset(offset).all()
return shouts return shouts
@query.field("myCandidates") @query.field("myCandidates")
@login_required @login_required
async def user_unpublished_shouts(_, info, page=1, size=10) -> List[Shout]: async def user_unpublished_shouts(_, info, offset, limit) -> List[Shout]:
user = info.context["request"].user user = info.context["request"].user
shouts = [] shouts = []
with local_session() as session: with local_session() as session:
shouts = ( shouts = prepare_shouts(
session.query(Shout) session.query(Shout)
.join(ShoutAuthor) .join(ShoutAuthor)
.where(and_(not bool(Shout.publishedAt), ShoutAuthor.user == user.slug)) .where(and_(not bool(Shout.publishedAt), ShoutAuthor.user == user.slug))
.order_by(desc(Shout.createdAt)) .order_by(desc(Shout.createdAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
.all() .all()
) )
return shouts return shouts

View File

@ -16,7 +16,7 @@ from typing import List
@query.field("userReactedShouts") @query.field("userReactedShouts")
async def get_user_reacted_shouts(_, info, slug, page, size) -> List[Shout]: async def get_user_reacted_shouts(_, _info, slug, offset, limit) -> List[Shout]:
user = await UserStorage.get_user_by_slug(slug) user = await UserStorage.get_user_by_slug(slug)
if not user: if not user:
return [] return []
@ -26,8 +26,8 @@ async def get_user_reacted_shouts(_, info, slug, page, size) -> List[Shout]:
.join(Reaction) .join(Reaction)
.where(Reaction.createdBy == user.slug) .where(Reaction.createdBy == user.slug)
.order_by(desc(Reaction.createdAt)) .order_by(desc(Reaction.createdAt))
.limit(size) .limit(limit)
.offset(page * size) .offset()
.all() .all()
) )
return shouts return shouts
@ -99,7 +99,7 @@ async def get_current_user(_, info):
@query.field("getUsersBySlugs") @query.field("getUsersBySlugs")
async def get_users_by_slugs(_, info, slugs): async def get_users_by_slugs(_, _info, slugs):
with local_session() as session: with local_session() as session:
users = ( users = (
session.query(User) session.query(User)
@ -111,7 +111,7 @@ async def get_users_by_slugs(_, info, slugs):
@query.field("getUserRoles") @query.field("getUserRoles")
async def get_user_roles(_, info, slug): async def get_user_roles(_, _info, slug):
with local_session() as session: with local_session() as session:
user = session.query(User).where(User.slug == slug).first() user = session.query(User).where(User.slug == slug).first()
roles = ( roles = (
@ -183,14 +183,10 @@ def author_unfollow(user, slug):
@query.field("authorsAll") @query.field("authorsAll")
def get_authors_all(_, info, page, size): def get_authors_all(_, _info, offset, limit):
end = page * size return list(UserStorage.get_all_users())[offset : offset + limit] # type: ignore
start = end - size
return list(UserStorage.get_all_users())[start:end] # type: ignore
@query.field("topAuthors") @query.field("topAuthors")
def get_top_authors(_, info, page, size): def get_top_authors(_, _info, offset, limit):
end = page * size return list(UserStorage.get_top_users())[offset : offset + limit] # type: ignore
start = end - size
return list(UserStorage.get_top_users())[start:end] # type: ignore

View File

@ -113,7 +113,7 @@ async def delete_reaction(_, info, id):
@query.field("reactionsByShout") @query.field("reactionsByShout")
async def get_shout_reactions(_, info, slug, page, size): async def get_shout_reactions(_, info, slug, offset, limit):
offset = page * size offset = page * size
reactions = [] reactions = []
with local_session() as session: with local_session() as session:
@ -130,7 +130,7 @@ async def get_shout_reactions(_, info, slug, page, size):
@query.field("reactionsForShouts") @query.field("reactionsForShouts")
async def get_reactions_for_shouts(_, info, shouts, page, size): async def get_reactions_for_shouts(_, info, shouts, offset, limit):
offset = page * size offset = page * size
reactions = [] reactions = []
with local_session() as session: with local_session() as session:

View File

@ -4,7 +4,7 @@ from orm.topic import Topic
from base.orm import local_session from base.orm import local_session
from base.resolvers import mutation, query from base.resolvers import mutation, query
from services.zine.shoutauthor import ShoutAuthorStorage from services.zine.shoutauthor import ShoutAuthorStorage
from services.zine.shoutscache import ShoutsCache from services.zine.shoutscache import ShoutsCache, prepare_shouts
from services.stat.viewed import ViewedStorage from services.stat.viewed import ViewedStorage
from resolvers.profile import author_follow, author_unfollow from resolvers.profile import author_follow, author_unfollow
from resolvers.topics import topic_follow, topic_unfollow from resolvers.topics import topic_follow, topic_unfollow
@ -22,39 +22,39 @@ async def increment_view(_, _info, shout):
@query.field("topViewed") @query.field("topViewed")
async def top_viewed(_, _info, page, size): async def top_viewed(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.top_viewed[((page - 1) * size) : (page * size)] return ShoutsCache.top_viewed[offset : offset + limit]
@query.field("topMonth") @query.field("topMonth")
async def top_month(_, _info, page, size): async def top_month(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.top_month[((page - 1) * size) : (page * size)] return ShoutsCache.top_month[offset : offset + limit]
@query.field("topOverall") @query.field("topOverall")
async def top_overall(_, _info, page, size): async def top_overall(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.top_overall[((page - 1) * size) : (page * size)] return ShoutsCache.top_overall[offset : offset + limit]
@query.field("recentPublished") @query.field("recentPublished")
async def recent_published(_, _info, page, size): async def recent_published(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.recent_published[((page - 1) * size) : (page * size)] return ShoutsCache.top_overall[offset : offset + limit]
@query.field("recentAll") @query.field("recentAll")
async def recent_all(_, _info, page, size): async def recent_all(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.recent_all[((page - 1) * size) : (page * size)] return ShoutsCache.recent_all[offset : offset + limit]
@query.field("recentReacted") @query.field("recentReacted")
async def recent_reacted(_, _info, page, size): async def recent_reacted(_, _info, offset, limit):
async with ShoutsCache.lock: async with ShoutsCache.lock:
return ShoutsCache.recent_reacted[((page - 1) * size) : (page * size)] return ShoutsCache.recent_all[offset : offset + limit]
@mutation.field("viewShout") @mutation.field("viewShout")
@ -70,7 +70,6 @@ async def get_shout_by_slug(_, info, slug):
] ]
selected_fields = set(["authors", "topics"]).intersection(all_fields) selected_fields = set(["authors", "topics"]).intersection(all_fields)
select_options = [selectinload(getattr(Shout, field)) for field in selected_fields] select_options = [selectinload(getattr(Shout, field)) for field in selected_fields]
shout = {}
with local_session() as session: with local_session() as session:
# s = text(open("src/queries/shout-by-slug.sql", "r").read() % slug) # s = text(open("src/queries/shout-by-slug.sql", "r").read() % slug)
shout = ( shout = (
@ -90,18 +89,17 @@ async def get_shout_by_slug(_, info, slug):
@query.field("searchQuery") @query.field("searchQuery")
async def get_search_results(_, _info, query, page, size): async def get_search_results(_, _info, query, offset, limit):
# TODO: remove the copy of searchByTopics # TODO: remove the copy of searchByTopics
# with search ranking query # with search ranking query
page = page - 1
with local_session() as session: with local_session() as session:
shouts = ( shouts = (
session.query(Shout) session.query(Shout)
.join(ShoutTopic) .join(ShoutTopic)
.where(and_(ShoutTopic.topic.in_(query), bool(Shout.publishedAt))) .where(and_(ShoutTopic.topic.in_(query), bool(Shout.publishedAt)))
.order_by(desc(Shout.publishedAt)) .order_by(desc(Shout.publishedAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
) )
for s in shouts: for s in shouts:
@ -112,16 +110,15 @@ async def get_search_results(_, _info, query, page, size):
@query.field("shoutsByTopics") @query.field("shoutsByTopics")
async def shouts_by_topics(_, _info, slugs, page, size): async def shouts_by_topics(_, _info, slugs, offset, limit):
page = page - 1
with local_session() as session: with local_session() as session:
shouts = ( shouts = prepare_shouts(
session.query(Shout) session.query(Shout)
.join(ShoutTopic) .join(ShoutTopic)
.where(and_(ShoutTopic.topic.in_(slugs), bool(Shout.publishedAt))) .where(and_(ShoutTopic.topic.in_(slugs), bool(Shout.publishedAt)))
.order_by(desc(Shout.publishedAt)) .order_by(desc(Shout.publishedAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
) )
for s in shouts: for s in shouts:
@ -131,17 +128,15 @@ async def shouts_by_topics(_, _info, slugs, page, size):
@query.field("shoutsByCollection") @query.field("shoutsByCollection")
async def shouts_by_collection(_, _info, collection, page, size): async def shouts_by_collection(_, _info, collection, offset, limit):
page = page - 1
shouts = []
with local_session() as session: with local_session() as session:
shouts = ( shouts = prepare_shouts(
session.query(Shout) session.query(Shout)
.join(ShoutCollection, ShoutCollection.collection == collection) .join(ShoutCollection, ShoutCollection.collection == collection)
.where(and_(ShoutCollection.shout == Shout.slug, bool(Shout.publishedAt))) .where(and_(ShoutCollection.shout == Shout.slug, bool(Shout.publishedAt)))
.order_by(desc(Shout.publishedAt)) .order_by(desc(Shout.publishedAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
) )
for s in shouts: for s in shouts:
for a in s.authors: for a in s.authors:
@ -150,17 +145,15 @@ async def shouts_by_collection(_, _info, collection, page, size):
@query.field("shoutsByAuthors") @query.field("shoutsByAuthors")
async def shouts_by_authors(_, _info, slugs, page, size): async def shouts_by_authors(_, _info, slugs, offset, limit):
page = page - 1
with local_session() as session: with local_session() as session:
shouts = ( shouts = (
session.query(Shout) session.query(Shout)
.join(ShoutAuthor) .join(ShoutAuthor)
.where(and_(ShoutAuthor.user.in_(slugs), bool(Shout.publishedAt))) .where(and_(ShoutAuthor.user.in_(slugs), bool(Shout.publishedAt)))
.order_by(desc(Shout.publishedAt)) .order_by(desc(Shout.publishedAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
) )
for s in shouts: for s in shouts:
@ -173,11 +166,10 @@ SINGLE_COMMUNITY = True
@query.field("shoutsByCommunities") @query.field("shoutsByCommunities")
async def shouts_by_communities(_, info, slugs, page, size): async def shouts_by_communities(_, info, slugs, offset, limit):
if SINGLE_COMMUNITY: if SINGLE_COMMUNITY:
return recent_published(_, info, page, size) return recent_published(_, info, offset, limit)
else: else:
page = page - 1
with local_session() as session: with local_session() as session:
# TODO fix postgres high load # TODO fix postgres high load
shouts = ( shouts = (
@ -193,8 +185,8 @@ async def shouts_by_communities(_, info, slugs, page, size):
) )
) )
.order_by(desc(Shout.publishedAt)) .order_by(desc(Shout.publishedAt))
.limit(size) .limit(limit)
.offset(page * size) .offset(offset)
) )
for s in shouts: for s in shouts:

View File

@ -223,28 +223,28 @@ type Query {
userFollowedCommunities(slug: String!): [Community]! userFollowedCommunities(slug: String!): [Community]!
userReactedShouts(slug: String!): [Shout]! # test userReactedShouts(slug: String!): [Shout]! # test
getUserRoles(slug: String!): [Role]! getUserRoles(slug: String!): [Role]!
authorsAll(page: Int!, size: Int!): [User]! authorsAll(offset: Int!, limit: Int!): [User]!
# shouts # shouts
getShoutBySlug(slug: String!): Shout! getShoutBySlug(slug: String!): Shout!
shoutsForFeed(page: Int!, size: Int!): [Shout]! # test shoutsForFeed(offset: Int!, limit: Int!): [Shout]! # test
shoutsByTopics(slugs: [String]!, page: Int!, size: Int!): [Shout]! shoutsByTopics(slugs: [String]!, offset: Int!, limit: Int!): [Shout]!
shoutsByAuthors(slugs: [String]!, page: Int!, size: Int!): [Shout]! shoutsByAuthors(slugs: [String]!, offset: Int!, limit: Int!): [Shout]!
shoutsByCommunities(slugs: [String]!, page: Int!, size: Int!): [Shout]! shoutsByCommunities(slugs: [String]!, offset: Int!, limit: Int!): [Shout]!
myCandidates(page: Int!, size: Int!): [Shout]! # test myCandidates(offset: Int!, limit: Int!): [Shout]! # test
topViewed(page: Int!, size: Int!): [Shout]! topViewed(offset: Int!, limit: Int!): [Shout]!
# topReacted(page: Int!, size: Int!): [Shout]! # topReacted(offset: Int!, limit: Int!): [Shout]!
topAuthors(page: Int!, size: Int!): [Author]! topAuthors(offset: Int!, limit: Int!): [Author]!
topMonth(page: Int!, size: Int!): [Shout]! topMonth(offset: Int!, limit: Int!): [Shout]!
topOverall(page: Int!, size: Int!): [Shout]! topOverall(offset: Int!, limit: Int!): [Shout]!
recentPublished(page: Int!, size: Int!): [Shout]! # homepage recentPublished(offset: Int!, limit: Int!): [Shout]! # homepage
recentReacted(page: Int!, size: Int!): [Shout]! # test recentReacted(offset: Int!, limit: Int!): [Shout]! # test
recentAll(page: Int!, size: Int!): [Shout]! recentAll(offset: Int!, limit: Int!): [Shout]!
# reactons # reactons
reactionsByAuthor(slug: String!, page: Int!, size: Int!): [Reaction]! reactionsByAuthor(slug: String!, offset: Int!, limit: Int!): [Reaction]!
reactionsByShout(slug: String!, page: Int!, size: Int!): [Reaction]! reactionsByShout(slug: String!, offset: Int!, limit: Int!): [Reaction]!
reactionsForShouts(shouts: [String]!, page: Int!, size: Int!): [Reaction]! reactionsForShouts(shouts: [String]!, offset: Int!, limit: Int!): [Reaction]!
# collab # collab
getCollabs: [Collab]! getCollabs: [Collab]!
@ -258,14 +258,14 @@ type Query {
# collections # collections
collectionsAll: [Collection]! collectionsAll: [Collection]!
getUserCollections(author: String!): [Collection]! getUserCollections(author: String!): [Collection]!
shoutsByCollection(collection: String!, page: Int, size: Int): [Shout]! shoutsByCollection(collection: String!, offset: Int!, limit: Int!): [Shout]!
# communities # communities
getCommunity(slug: String): Community! getCommunity(slug: String): Community!
getCommunities: [Community]! # all getCommunities: [Community]! # all
# search # search
searchQuery(q: String, page: Int, size: Int): [Shout] searchQuery(q: String, offset: Int!, limit: Int!): [Shout]
} }
############################################ Subscription ############################################ Subscription

View File

@ -8,11 +8,10 @@ from orm.shout import Shout, ShoutAuthor, ShoutTopic
from services.stat.viewed import ViewedByDay from services.stat.viewed import ViewedByDay
def shoutify(session, stmt): async def prepare_shouts(session, stmt):
shouts = [] shouts = []
for s in list(map(lambda r: r.Shout, session.execute(stmt))): for s in list(map(lambda r: r.Shout, session.execute(stmt))):
if not bool(s.stat): s.stats = await s.stat
raise Exception
shouts.append(s) shouts.append(s)
return shouts return shouts
@ -35,7 +34,7 @@ class ShoutsCache:
@staticmethod @staticmethod
async def prepare_recent_published(): async def prepare_recent_published():
with local_session() as session: with local_session() as session:
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout) select(Shout)
.options(selectinload(Shout.authors), selectinload(Shout.topics)) .options(selectinload(Shout.authors), selectinload(Shout.topics))
.where(bool(Shout.publishedAt)) .where(bool(Shout.publishedAt))
@ -49,7 +48,7 @@ class ShoutsCache:
@staticmethod @staticmethod
async def prepare_recent_all(): async def prepare_recent_all():
with local_session() as session: with local_session() as session:
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout) select(Shout)
.options(selectinload(Shout.authors), selectinload(Shout.topics)) .options(selectinload(Shout.authors), selectinload(Shout.topics))
.order_by(desc("createdAt")) .order_by(desc("createdAt"))
@ -62,7 +61,7 @@ class ShoutsCache:
@staticmethod @staticmethod
async def prepare_recent_reacted(): async def prepare_recent_reacted():
with local_session() as session: with local_session() as session:
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout, func.max(Reaction.createdAt).label("reactionCreatedAt")) select(Shout, func.max(Reaction.createdAt).label("reactionCreatedAt"))
.options( .options(
selectinload(Shout.authors), selectinload(Shout.authors),
@ -82,7 +81,7 @@ class ShoutsCache:
async def prepare_top_overall(): async def prepare_top_overall():
with local_session() as session: with local_session() as session:
# with reacted times counter # with reacted times counter
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout, func.count(Reaction.id).label("reacted")) select(Shout, func.count(Reaction.id).label("reacted"))
.options( .options(
selectinload(Shout.authors), selectinload(Shout.authors),
@ -95,7 +94,7 @@ class ShoutsCache:
.order_by(desc("reacted")) .order_by(desc("reacted"))
.limit(ShoutsCache.limit) .limit(ShoutsCache.limit)
)) ))
shouts.sort(key=lambda s: s.stat['rating'], reverse=True) shouts.sort(key=lambda s: s.stats['rating'], reverse=True)
async with ShoutsCache.lock: async with ShoutsCache.lock:
print("[zine.cache] %d top shouts " % len(shouts)) print("[zine.cache] %d top shouts " % len(shouts))
ShoutsCache.top_overall = shouts ShoutsCache.top_overall = shouts
@ -104,7 +103,7 @@ class ShoutsCache:
async def prepare_top_month(): async def prepare_top_month():
month_ago = datetime.now() - timedelta(days=30) month_ago = datetime.now() - timedelta(days=30)
with local_session() as session: with local_session() as session:
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout, func.count(Reaction.id).label("reacted")) select(Shout, func.count(Reaction.id).label("reacted"))
.options(selectinload(Shout.authors), selectinload(Shout.topics)) .options(selectinload(Shout.authors), selectinload(Shout.topics))
.join(Reaction) .join(Reaction)
@ -113,7 +112,7 @@ class ShoutsCache:
.order_by(desc("reacted")) .order_by(desc("reacted"))
.limit(ShoutsCache.limit) .limit(ShoutsCache.limit)
)) ))
shouts.sort(key=lambda s: s.stat['rating'], reverse=True) shouts.sort(key=lambda s: s.stats['rating'], reverse=True)
async with ShoutsCache.lock: async with ShoutsCache.lock:
print("[zine.cache] %d top month shouts " % len(shouts)) print("[zine.cache] %d top month shouts " % len(shouts))
ShoutsCache.top_month = shouts ShoutsCache.top_month = shouts
@ -122,7 +121,7 @@ class ShoutsCache:
async def prepare_top_viewed(): async def prepare_top_viewed():
month_ago = datetime.now() - timedelta(days=30) month_ago = datetime.now() - timedelta(days=30)
with local_session() as session: with local_session() as session:
shouts = shoutify(session, ( shouts = await prepare_shouts(session, (
select(Shout, func.sum(ViewedByDay.value).label("viewed")) select(Shout, func.sum(ViewedByDay.value).label("viewed"))
.options(selectinload(Shout.authors), selectinload(Shout.topics)) .options(selectinload(Shout.authors), selectinload(Shout.topics))
.join(ViewedByDay) .join(ViewedByDay)
@ -131,7 +130,7 @@ class ShoutsCache:
.order_by(desc("viewed")) .order_by(desc("viewed"))
.limit(ShoutsCache.limit) .limit(ShoutsCache.limit)
)) ))
shouts.sort(key=lambda s: s.stat['viewed'], reverse=True) shouts.sort(key=lambda s: s.stats['viewed'], reverse=True)
async with ShoutsCache.lock: async with ShoutsCache.lock:
print("[zine.cache] %d top viewed shouts " % len(shouts)) print("[zine.cache] %d top viewed shouts " % len(shouts))
ShoutsCache.top_viewed = shouts ShoutsCache.top_viewed = shouts