KB66 Post: Add scope field.

The scope field defines whether a post is a top-level post or a
comment and is used to select posts for the main feed.

Change-Id: I44363e3e67acbecff9844730a513ddb1d554afaf
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
index 346b71f..3a16868 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/Post.java
@@ -2,6 +2,7 @@
 
 import static java.util.stream.Collectors.toList;
 
+import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType;
 import eu.mulk.mulkcms2.benki.accesscontrol.Role;
 import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
 import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
@@ -28,6 +29,8 @@
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
@@ -48,15 +51,23 @@
 import javax.persistence.criteria.JoinType;
 import javax.persistence.criteria.Predicate;
 import org.hibernate.Session;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
 import org.jboss.logging.Logger;
 
 @Entity
 @Table(name = "posts", schema = "benki")
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+@TypeDef(name = "pg_enum", typeClass = PostgreSQLEnumType.class)
 public abstract class Post<Text extends PostText<?>> extends PanacheEntityBase {
 
   private static final Logger log = Logger.getLogger(Post.class);
 
+  public enum Scope {
+    top_level,
+    comment
+  }
+
   @Id
   @SequenceGenerator(
       allocationSize = 1,
@@ -71,6 +82,11 @@
   @CheckForNull
   public OffsetDateTime date;
 
+  @Column(nullable = false)
+  @Enumerated(EnumType.STRING)
+  @Type(type = "pg_enum")
+  public Scope scope = Scope.top_level;
+
   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "newsletter", referencedColumnName = "id", nullable = true)
   @CheckForNull
@@ -204,6 +220,8 @@
       conditions.add(cb.or(localizedSearches));
     }
 
+    conditions.add(cb.equal(post.get(Post_.scope), Scope.top_level));
+
     query.where(conditions.toArray(new Predicate[0]));
 
     return query;
@@ -223,6 +241,10 @@
     return text.getDescriptionHtml();
   }
 
+  public final boolean isTopLevel() {
+    return scope == Scope.top_level;
+  }
+
   public static class PostPage<T extends Post<? extends PostText>> {
     public @CheckForNull final Integer prevCursor;
     public @CheckForNull final Integer cursor;
diff --git a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
index 59cacee..7ecfcba 100644
--- a/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
+++ b/src/main/java/eu/mulk/mulkcms2/benki/posts/PostResource.java
@@ -310,6 +310,7 @@
 
     feed.setEntries(
         posts.stream()
+            .filter(Post::isTopLevel)
             .map(
                 post -> {
                   var entry = new Entry();
diff --git a/src/main/resources/db/changeLog-1.8.xml b/src/main/resources/db/changeLog-1.8.xml
new file mode 100644
index 0000000..2359001
--- /dev/null
+++ b/src/main/resources/db/changeLog-1.8.xml
@@ -0,0 +1,58 @@
+<?xml version="1.1" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog
+  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="
+    http://www.liquibase.org/xml/ns/dbchangelog
+    http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.10.xsd">
+
+  <changeSet author="mulk" id="1.8-1">
+    <sql>
+      CREATE TYPE benki.post_scope AS ENUM ('top_level', 'comment');
+    </sql>
+  </changeSet>
+
+  <changeSet author="mulk" id="1.8-2">
+    <addColumn tableName="bookmarks" schemaName="benki">
+      <column name="scope" type="benki.post_scope" defaultValue="top_level">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+
+    <addColumn tableName="lazychat_messages" schemaName="benki">
+      <column name="scope" type="benki.post_scope" defaultValue="top_level">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+
+    <addColumn tableName="posts" schemaName="benki">
+      <column name="scope" type="benki.post_scope" defaultValue="top_level">
+        <constraints nullable="false"/>
+      </column>
+    </addColumn>
+
+    <createIndex tableName="posts"
+                 schemaName="benki"
+                 indexName="post_scope_id_idx">
+      <column name="scope"/>
+      <column name="id"/>
+    </createIndex>
+  </changeSet>
+
+  <changeSet id="1.8-3" author="mulk">
+    <createIndex tableName="bookmarks"
+                 schemaName="benki"
+                 indexName="bookmarks_scope_id_idx">
+      <column name="scope"/>
+      <column name="id"/>
+    </createIndex>
+
+    <createIndex tableName="lazychat_messages"
+                 schemaName="benki"
+                 indexName="lazychat_messages_scope_id_idx">
+      <column name="scope"/>
+      <column name="id"/>
+    </createIndex>
+  </changeSet>
+
+</databaseChangeLog>
diff --git a/src/main/resources/db/changeLog.xml b/src/main/resources/db/changeLog.xml
index 7b4b700..88937a3 100644
--- a/src/main/resources/db/changeLog.xml
+++ b/src/main/resources/db/changeLog.xml
@@ -14,5 +14,6 @@
   <include file="db/changeLog-1.5.xml"/>
   <include file="db/changeLog-1.6.xml"/>
   <include file="db/changeLog-1.7.xml"/>
+  <include file="db/changeLog-1.8.xml"/>
 
 </databaseChangeLog>