From b20b1239a0b6aba0f847c3bed228b2145c9491c9 Mon Sep 17 00:00:00 2001
From: Tom Zhao <tom.zhao@dsv.su.se>
Date: Wed, 18 Dec 2024 13:23:58 +0100
Subject: [PATCH] 3470: Refactor how message counting works:

1. Now we retrieve at first all threads, then use another database call to get count of threads which have unread posts. It makes possible to reuse the logic of unread-post-counting somewhere else.
2. We use QueryDSL instead of native SQL, also left join from thread table instead of exists().
---
 .../dsv/scipro/forum/BasicForumService.java   |  1 +
 .../scipro/forum/BasicForumServiceImpl.java   |  5 +++
 .../dsv/scipro/forum/ForumPostRepository.java |  3 ++
 .../scipro/forum/ForumPostRepositoryImpl.java | 19 +++++++++++
 .../scipro/forum/ProjectForumServiceImpl.java | 15 +++------
 .../scipro/forum/ProjectThreadRepository.java |  7 ++--
 .../forum/ProjectThreadRepositoryImpl.java    | 33 +++----------------
 7 files changed, 38 insertions(+), 45 deletions(-)

diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
index a6cff8bdae..b1001c6a3b 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumService.java
@@ -23,4 +23,5 @@ public interface BasicForumService extends Serializable {
 
     ForumThread createThread(String subject);
 
+    long countUnreadThreads(List<ForumThread> forumThreadList, User user);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
index 0dae3383bb..6877c784ae 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/BasicForumServiceImpl.java
@@ -88,6 +88,11 @@ public class BasicForumServiceImpl implements BasicForumService {
         return threadRepository.save(forumThread);
     }
 
+    @Override
+    public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
+        return postRepository.countUnreadThreads(forumThreadList, user);
+    }
+
     private ForumPostReadState getReadState(final User user, final ForumPost post) {
         ForumPostReadState state = readStateRepository.find(user, post);
         if (state == null) {
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
index 92e3afb1c8..8e965c015b 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepository.java
@@ -7,6 +7,7 @@ import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Pair;
 
 import java.util.List;
@@ -17,4 +18,6 @@ public interface ForumPostRepository extends JpaRepository<ForumPost, Long>, Que
     List<ForumPost> findByThread(ForumThread forumThread);
 
     List<Pair<ProjectThread, ForumPost>> latestPost(Project project, int amount);
+
+    long countUnreadThreads(List<ForumThread> forumThreadList, User user);
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
index e4536e7582..e806c717d6 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ForumPostRepositoryImpl.java
@@ -1,15 +1,19 @@
 package se.su.dsv.scipro.forum;
 
+import com.querydsl.jpa.JPAExpressions;
 import com.querydsl.jpa.impl.JPAQuery;
 import jakarta.persistence.EntityManager;
+import jakarta.persistence.Query;
 import se.su.dsv.scipro.forum.dataobjects.ForumPost;
 import se.su.dsv.scipro.forum.dataobjects.ForumThread;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.forum.dataobjects.QForumPost;
+import se.su.dsv.scipro.forum.dataobjects.QForumPostReadState;
 import se.su.dsv.scipro.forum.dataobjects.QForumThread;
 import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.system.GenericRepo;
+import se.su.dsv.scipro.system.User;
 import se.su.dsv.scipro.util.Pair;
 
 import jakarta.inject.Inject;
@@ -46,4 +50,19 @@ public class ForumPostRepositoryImpl extends GenericRepo<ForumPost, Long> implem
                         tuple.get(QForumPost.forumPost)))
                 .toList();
     }
+
+    @Override
+    public long countUnreadThreads(List<ForumThread> forumThreadList, User user) {
+        return new JPAQuery<>(em())
+                .select(QForumThread.forumThread.id.countDistinct())
+                .from(QForumThread.forumThread)
+                .leftJoin(QForumThread.forumThread.posts, QForumPost.forumPost)
+                .where(QForumPost.forumPost.notIn(
+                        JPAExpressions.select(QForumPostReadState.forumPostReadState.id.post)
+                                .from(QForumPostReadState.forumPostReadState)
+                                .where(QForumPostReadState.forumPostReadState.read.isTrue(),
+                                        QForumPostReadState.forumPostReadState.id.user.eq(user))
+                        ), QForumThread.forumThread.in(forumThreadList)
+                ).fetchOne();
+    }
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
index 467b092b70..305943b062 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java
@@ -108,18 +108,11 @@ public class ProjectForumServiceImpl implements ProjectForumService {
 
     @Override
     public long getUnreadThreadsCount(Project project, User user) {
-        return projectThreadRepository.getUnreadThreadsCount(project, user);
-        /*
         List<ProjectThread> threads = getThreads(project);
-        int count = 0;
-        for (ProjectThread thread : threads) {
-            if (!basicForumService.isThreadRead(user, thread.getForumThread())) {
-                count++;
-            }
-        }
-        return count;
-
-         */
+        List<ForumThread> list = threads.stream()
+                .map(ProjectThread::getForumThread)
+                .toList();
+        return basicForumService.countUnreadThreads(list, user);
     }
 
     @Override
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
index 189ffed09b..83720860fe 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepository.java
@@ -1,11 +1,10 @@
 package se.su.dsv.scipro.forum;
 
 import jakarta.transaction.Transactional;
-import se.su.dsv.scipro.system.JpaRepository;
-import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
 import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.system.JpaRepository;
+import se.su.dsv.scipro.system.QueryDslPredicateExecutor;
 
 import java.util.List;
 
@@ -14,6 +13,4 @@ public interface ProjectThreadRepository extends JpaRepository<ProjectThread, Lo
 
     List<ProjectThread> findByProject(Project project);
 
-    long getUnreadThreadsCount(Project project, User user);
-
 }
diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepositoryImpl.java
index 26286262e6..cf5988cfde 100644
--- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepositoryImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectThreadRepositoryImpl.java
@@ -1,15 +1,12 @@
 package se.su.dsv.scipro.forum;
 
-import jakarta.persistence.Query;
-import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
-import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
-import se.su.dsv.scipro.project.Project;
-import se.su.dsv.scipro.system.GenericRepo;
-
 import jakarta.inject.Inject;
 import jakarta.inject.Provider;
 import jakarta.persistence.EntityManager;
-import se.su.dsv.scipro.system.User;
+import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
+import se.su.dsv.scipro.forum.dataobjects.QProjectThread;
+import se.su.dsv.scipro.project.Project;
+import se.su.dsv.scipro.system.GenericRepo;
 
 import java.util.List;
 
@@ -23,26 +20,4 @@ public class ProjectThreadRepositoryImpl extends GenericRepo<ProjectThread, Long
     public List<ProjectThread> findByProject(Project project) {
         return findAll(QProjectThread.projectThread.project.eq(project));
     }
-
-    @Override
-    public long getUnreadThreadsCount(Project project, User user) {
-        StringBuilder sql = new StringBuilder()
-                .append("select count(distinct(thread_id)) result ")
-                .append("  from (select fp.thread_id as thread_id, fp.id as post_id, ")
-                .append("               exists (select `read` ")
-                .append("                         from forum_post_read_state fprs ")
-                .append("                        where fprs.forum_post_id = fp.id and ")
-                .append("                              fprs.user_id = :uid and ")
-                .append("                              fprs.read = 1) as post_read ")
-                .append("          from project_thread pt, forum_post fp ")
-                .append("         where pt.thread_id = fp.thread_id and ")
-                .append("               pt.project_id = :pid) tbl_ex ")
-                .append(" where post_read = 0");
-        Query query = em().createNativeQuery(sql.toString());
-        query.setParameter("uid", user.getId())
-             .setParameter("pid", project.getId());
-
-        return (Long)query.getSingleResult();
-    }
-
 }