Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions src/cache/articles_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@
namespace real_medium::cache::articles_cache {

userver::storages::postgres::Query ArticlesCachePolicy::kQuery =
userver::storages::postgres::Query(real_medium::sql::kSelectFullArticleInfo.data());
userver::storages::postgres::Query(real_medium::sql::kSelectFullArticleInfo.c_str());

void ArticlesCacheContainer::insert_or_assign(Key&& key, Article&& article) {
auto articlePtr = std::make_shared<const Article>(std::move(article));
auto oldValue = articleByKey_.find(key);
if (oldValue != articleByKey_.end()) {
articleBySlug_.erase(oldValue->second->slug);
for (const auto& oldFollower : oldValue->second->authorFollowedByUsersIds)
if (articlePtr->authorFollowedByUsersIds.find(oldFollower) == articlePtr->authorFollowedByUsersIds.end())
articlesByFollower_[oldFollower].erase(articlePtr->articleId);
auto article_ptr = std::make_shared<const Article>(std::move(article));
auto old_value = article_by_key_.find(key);
if (old_value != article_by_key_.end()) {
article_by_slug_.erase(old_value->second->slug);
for (const auto& oldFollower : old_value->second->authorFollowedByUsersIds)
if (article_ptr->authorFollowedByUsersIds.find(oldFollower) == article_ptr->authorFollowedByUsersIds.end())
articles_by_follower_[oldFollower].erase(article_ptr->articleId);
}

articleByKey_.insert_or_assign(key, articlePtr);
articleBySlug_.insert_or_assign(articlePtr->slug, articlePtr);
for (const auto& follower : articlePtr->authorFollowedByUsersIds)
articlesByFollower_[follower].insert_or_assign(articlePtr->articleId, articlePtr);
recentArticles_.insert_or_assign({articlePtr->createdAt, articlePtr->articleId}, articlePtr);
article_by_key_.insert_or_assign(key, article_ptr);
article_by_slug_.insert_or_assign(article_ptr->slug, article_ptr);
for (const auto& follower : article_ptr->authorFollowedByUsersIds)
articles_by_follower_[follower].insert_or_assign(article_ptr->articleId, article_ptr);
recent_articles_.insert_or_assign({article_ptr->createdAt, article_ptr->articleId}, article_ptr);
}

size_t ArticlesCacheContainer::size() const { return articleByKey_.size(); }
size_t ArticlesCacheContainer::size() const { return article_by_key_.size(); }

ArticlesCacheContainer::ArticlePtr ArticlesCacheContainer::findArticleBySlug(const Slug& slug) const {
auto it = articleBySlug_.find(slug);
if (it == articleBySlug_.end()) return nullptr;
auto it = article_by_slug_.find(slug);
if (it == article_by_slug_.end()) return nullptr;
return it->second;
}

Expand All @@ -37,7 +37,7 @@ std::vector<ArticlesCacheContainer::ArticlePtr> ArticlesCacheContainer::getRecen
) const {
std::vector<ArticlePtr> articles;
int offset = 0;
for (const auto& it : recentArticles_) {
for (const auto& it : recent_articles_) {
if (filter.limit && articles.size() >= userver::utils::numeric_cast<std::size_t>(filter.limit)) break;

const auto& tags = it.second->tags;
Expand All @@ -57,16 +57,16 @@ std::vector<ArticlesCacheContainer::ArticlePtr> ArticlesCacheContainer::getRecen
return articles;
}
std::vector<ArticlesCacheContainer::ArticlePtr>
ArticlesCacheContainer::getFeed(real_medium::handlers::FeedArticleFilterDTO& filter, UserId authId) const {
auto followedArticlesUMap = articlesByFollower_.find(authId);
if (followedArticlesUMap == articlesByFollower_.end()) return {};
ArticlesCacheContainer::getFeed(real_medium::handlers::FeedArticleFilterDTO& filter, UserId auth_id) const {
auto followed_articles_umap = articles_by_follower_.find(auth_id);
if (followed_articles_umap == articles_by_follower_.end()) return {};

RecentArticlesMap followedArticlesOrdered;
for (const auto& it : followedArticlesUMap->second)
for (const auto& it : followed_articles_umap->second)
followedArticlesOrdered.insert_or_assign({it.second->createdAt, it.second->articleId}, it.second);

std::vector<ArticlePtr> articles;
;

int offset = 0;
for (const auto& it : followedArticlesOrdered) {
if (filter.limit && articles.size() >= userver::utils::numeric_cast<std::size_t>(filter.limit)) break;
Expand Down
14 changes: 10 additions & 4 deletions src/cache/articles_cache.hpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#pragma once

#include <docs/api/api.hpp>
#include <memory>
#include <unordered_map>

#include <userver/cache/base_postgres_cache.hpp>
#include <userver/storages/postgres/io/chrono.hpp>

#include "db/sql.hpp"
#include "dto/filter.hpp"
#include "models/article.hpp"

namespace real_medium::cache::articles_cache {

class ArticlesCacheContainer {
using Timepoint = userver::storages::postgres::TimePointTz;
using Slug = std::string;
Expand Down Expand Up @@ -40,10 +44,10 @@ class ArticlesCacheContainer {
};

using RecentArticlesMap = std::map<TimepointedArticle /*created_at*/, ArticlePtr, std::greater<TimepointedArticle>>;
std::unordered_map<Key, ArticlePtr> articleByKey_;
std::unordered_map<Slug, ArticlePtr> articleBySlug_;
std::unordered_map<UserId /*follower*/, std::unordered_map<Key, ArticlePtr>> articlesByFollower_;
RecentArticlesMap recentArticles_;
std::unordered_map<Key, ArticlePtr> article_by_key_;
std::unordered_map<Slug, ArticlePtr> article_by_slug_;
std::unordered_map<UserId /*follower*/, std::unordered_map<Key, ArticlePtr>> articles_by_follower_;
RecentArticlesMap recent_articles_;
};

struct ArticlesCachePolicy {
Expand All @@ -55,5 +59,7 @@ struct ArticlesCachePolicy {
static constexpr auto kUpdatedField = "updated_at";
using UpdatedFieldType = userver::storages::postgres::TimePointTz;
};

using ArticlesCache = ::userver::components::PostgreCache<ArticlesCachePolicy>;

} // namespace real_medium::cache::articles_cache
22 changes: 11 additions & 11 deletions src/cache/comments_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@
namespace real_medium::cache::comments_cache {

userver::storages::postgres::Query CommentCachePolicy::kQuery =
userver::storages::postgres::Query(real_medium::sql::kSelectCachedComments.data());
userver::storages::postgres::Query(real_medium::sql::kSelectCachedComments.c_str());

void CommentsCacheContainer::insert_or_assign(
real_medium::cache::comments_cache::CommentsCacheContainer::Key&& commentId,
real_medium::cache::comments_cache::CommentsCacheContainer::Key&& comment_id,
real_medium::cache::comments_cache::CommentsCacheContainer::Comment&& comment
) {
auto commentPtr = std::make_shared<const Comment>(std::move(comment));
if (comment_to_key_.count(commentId)) {
auto& oldSlug = comment_to_key_[commentId]->slug;
if (oldSlug != commentPtr->slug) {
auto& comments = comments_to_slug_[oldSlug];
comments_to_slug_[commentPtr->slug] = comments;
comments_to_slug_.erase(oldSlug);
auto comment_ptr = std::make_shared<const Comment>(std::move(comment));
if (comment_to_key_.count(comment_id)) {
auto& old_slug = comment_to_key_[comment_id]->slug;
if (old_slug != comment_ptr->slug) {
auto& comments = comments_to_slug_[old_slug];
comments_to_slug_[comment_ptr->slug] = comments;
comments_to_slug_.erase(old_slug);
}
}
comment_to_key_.insert_or_assign(commentId, commentPtr);
comments_to_slug_[commentPtr->slug].insert_or_assign(commentId, commentPtr);
comment_to_key_.insert_or_assign(comment_id, comment_ptr);
comments_to_slug_[comment_ptr->slug].insert_or_assign(comment_id, comment_ptr);
};

size_t CommentsCacheContainer::size() const { return comment_to_key_.size(); }
Expand Down
60 changes: 30 additions & 30 deletions src/db/sql.hpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
#pragma once

#include <string_view>
#include <userver/utils/zstring_view.hpp>

namespace real_medium::sql {

inline constexpr std::string_view kInsertUser = R"~(
inline constexpr userver::utils::zstring_view kInsertUser = R"~(
INSERT INTO real_medium.users(username, email, bio, image, password_hash, salt)
VALUES($1, $2, $3, $4, $5, $6)
RETURNING *
)~";

inline constexpr std::string_view kSelectUserByEmailAndPassword = R"~(
inline constexpr userver::utils::zstring_view kSelectUserByEmailAndPassword = R"~(
SELECT * FROM real_medium.users
WHERE email = $1 AND password_hash = $2
)~";

inline constexpr std::string_view kUpdateUser = R"~(
inline constexpr userver::utils::zstring_view kUpdateUser = R"~(
UPDATE real_medium.users SET
username = COALESCE($2, username),
email = COALESCE($3, email),
Expand All @@ -27,16 +27,16 @@ WHERE user_id = $1
RETURNING *
)~";

inline constexpr std::string_view kFindUserById = R"~(
inline constexpr userver::utils::zstring_view kFindUserById = R"~(
SELECT * FROM real_medium.users WHERE user_id = $1
)~";

inline constexpr std::string_view kFindUserIDByUsername = R"~(
inline constexpr userver::utils::zstring_view kFindUserIDByUsername = R"~(
SELECT user_id FROM real_medium.users WHERE username = $1
)~";

// Comments
inline constexpr std::string_view kFindCommentByIdAndSlug = R"~(
inline constexpr userver::utils::zstring_view kFindCommentByIdAndSlug = R"~(
WITH article AS (
SELECT article_id FROM real_medium.articles WHERE slug = $2
)
Expand All @@ -45,7 +45,7 @@ JOIN article ON article.article_id = real_medium.comments.article_id
WHERE comment_id = $1
)~";

inline constexpr std::string_view kFindCommentsByArticleId = R"~(
inline constexpr userver::utils::zstring_view kFindCommentsByArticleId = R"~(
WITH comments AS (
SELECT * FROM real_medium.comments WHERE article_id = $1
)
Expand All @@ -66,12 +66,12 @@ SELECT
FROM comments
)~";

inline constexpr std::string_view kDeleteCommentById = R"~(
inline constexpr userver::utils::zstring_view kDeleteCommentById = R"~(
DELETE FROM real_medium.comments WHERE comment_id = $1 AND user_id = $2
RETURNING *
)~";

inline constexpr std::string_view kAddComment = R"~(
inline constexpr userver::utils::zstring_view kAddComment = R"~(
WITH comment AS (
INSERT INTO real_medium.comments(body, user_id, article_id)
VALUES ($1, $2, $3)
Expand All @@ -91,16 +91,16 @@ SELECT
FROM comment
)~";

inline constexpr std::string_view kFindIdArticleBySlug = R"~(
inline constexpr userver::utils::zstring_view kFindIdArticleBySlug = R"~(
SELECT article_id FROM real_medium.articles WHERE slug = $1
)~";

inline constexpr std::string_view kIsProfileFollowing = R"~(
inline constexpr userver::utils::zstring_view kIsProfileFollowing = R"~(
RETURN EXISTS (SELECT 1 FROM real_medium.followers WHERE follower_user_id = $1 AND followed_user_id = $2);
)~";

// TODO: reuse common kIsProfileFollowing
inline constexpr std::string_view kGetProfileByUsername = R"~(
inline constexpr userver::utils::zstring_view kGetProfileByUsername = R"~(
WITH profile AS (
SELECT * FROM real_medium.users WHERE username = $1
)
Expand All @@ -112,11 +112,11 @@ SELECT profile.username, profile.bio, profile.image,
FROM profile
)~";

inline constexpr std::string_view kGetSaltByEmail = R"~(
inline constexpr userver::utils::zstring_view kGetSaltByEmail = R"~(
SELECT salt FROM real_medium.users WHERE email = $1
)~";

inline constexpr std::string_view KFollowingUser = R"~(
inline constexpr userver::utils::zstring_view KFollowingUser = R"~(
WITH profile AS (
SELECT * FROM real_medium.users WHERE user_id = $1
), following AS (
Expand All @@ -132,7 +132,7 @@ SELECT
FROM profile
)~";

inline constexpr std::string_view KUnFollowingUser = R"~(
inline constexpr userver::utils::zstring_view KUnFollowingUser = R"~(
WITH profile AS (
SELECT * FROM real_medium.users WHERE user_id = $1
), following AS (
Expand All @@ -147,19 +147,19 @@ SELECT
FROM profile
)~";

inline constexpr std::string_view kCreateArticle{R"~(
inline constexpr userver::utils::zstring_view kCreateArticle{R"~(
SELECT real_medium.create_article($1, $2, $3, $4, $5, $6)
)~"};

inline constexpr std::string_view kGetArticleWithAuthorProfile{R"~(
inline constexpr userver::utils::zstring_view kGetArticleWithAuthorProfile{R"~(
SELECT real_medium.get_article_with_author_profile($1, $2)
)~"};

inline constexpr std::string_view kGetArticleWithAuthorProfileBySlug{R"~(
inline constexpr userver::utils::zstring_view kGetArticleWithAuthorProfileBySlug{R"~(
SELECT real_medium.get_article_with_author_profile_by_slug($1, $2)
)~"};

inline constexpr std::string_view kInsertFavoritePair = R"~(
inline constexpr userver::utils::zstring_view kInsertFavoritePair = R"~(
WITH tmp(article_id, user_id) AS (
SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2
)
Expand All @@ -168,13 +168,13 @@ ON CONFLICT DO NOTHING
RETURNING article_id
)~";

inline constexpr std::string_view kIncrementFavoritesCount = R"~(
inline constexpr userver::utils::zstring_view kIncrementFavoritesCount = R"~(
UPDATE real_medium.articles
SET favorites_count=favorites_count + 1
WHERE article_id=$1
)~";

inline constexpr std::string_view kDeleteFavoritePair = R"~(
inline constexpr userver::utils::zstring_view kDeleteFavoritePair = R"~(
WITH tmp(article_id, user_id) AS (
SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2
)
Expand All @@ -183,33 +183,33 @@ WHERE (article_id, user_id) IN (SELECT article_id, user_id FROM tmp)
RETURNING article_id
)~";

inline constexpr std::string_view kDecrementFavoritesCount = R"~(
inline constexpr userver::utils::zstring_view kDecrementFavoritesCount = R"~(
UPDATE real_medium.articles
SET favorites_count=favorites_count - 1
WHERE article_id=$1
)~";

inline constexpr std::string_view kFindArticlesByFollowedUsers = R"~(
inline constexpr userver::utils::zstring_view kFindArticlesByFollowedUsers = R"~(
SELECT real_medium.get_feed_articles($1, $2, $3)
)~";

inline constexpr std::string_view kGetArticleIdBySlug{R"~(
inline constexpr userver::utils::zstring_view kGetArticleIdBySlug{R"~(
SELECT real_medium.get_article_id_by_slug($1)
)~"};

inline constexpr std::string_view kUpdateArticleBySlug{R"~(
inline constexpr userver::utils::zstring_view kUpdateArticleBySlug{R"~(
SELECT real_medium.update_article_by_slug($1, $2, $3, $4, $5, $6)
)~"};

inline constexpr std::string_view kDeleteArticleBySlug{R"~(
inline constexpr userver::utils::zstring_view kDeleteArticleBySlug{R"~(
SELECT real_medium.delete_article_by_slug($1, $2)
)~"};

inline constexpr std::string_view kFindArticlesByFilters{R"~(
inline constexpr userver::utils::zstring_view kFindArticlesByFilters{R"~(
SELECT real_medium.get_articles_by_filters($1, $2, $3, $4, $5, $6)
)~"};

inline constexpr std::string_view kSelectFullArticleInfo = {R"~(
inline constexpr userver::utils::zstring_view kSelectFullArticleInfo = {R"~(
SELECT a.article_id AS articleId,
a.title,
a.slug,
Expand Down Expand Up @@ -245,7 +245,7 @@ FROM real_medium.articles a
JOIN real_medium.users u ON a.user_id = u.user_id
)~"};

inline constexpr std::string_view kSelectCachedComments = {R"~(
inline constexpr userver::utils::zstring_view kSelectCachedComments = {R"~(
SELECT c.comment_id,
c.created_at AS createdAt,
c.updated_at AS updatedAt,
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/articles/articles_favorite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow(
userver::storages::postgres::Transaction::RW
);

auto res = transaction.Execute(sql::kInsertFavoritePair.data(), user_id, slug);
auto res = transaction.Execute(sql::kInsertFavoritePair.c_str(), user_id, slug);

if (!res.IsEmpty()) {
auto article_id = res.AsSingleRow<std::string>();

transaction.Execute(sql::kIncrementFavoritesCount.data(), article_id);
transaction.Execute(sql::kIncrementFavoritesCount.c_str(), article_id);
transaction.Commit();
}

const auto get_article_res = GetPg().Execute(
userver::storages::postgres::ClusterHostType::kSlave,
real_medium::sql::kGetArticleWithAuthorProfileBySlug.data(),
real_medium::sql::kGetArticleWithAuthorProfileBySlug.c_str(),
slug,
user_id
);
Expand Down
6 changes: 3 additions & 3 deletions src/handlers/articles/articles_get.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow(

auto user_id = context.GetData<std::optional<std::string>>("id");
auto data = GetArticlesCache().Get();
auto recentArticles = data->getRecent(filter);
auto recent_articles = data->getRecent(filter);
userver::formats::json::ValueBuilder builder;
builder["articles"] = userver::formats::common::Type::kArray;
for (auto& article : recentArticles) builder["articles"].PushBack(dto::Article::Parse(*article, user_id));
builder["articlesCount"] = recentArticles.size();
for (auto& article : recent_articles) builder["articles"].PushBack(dto::Article::Parse(*article, user_id));
builder["articlesCount"] = recent_articles.size();
return builder.ExtractValue();
}

Expand Down
Loading