From 657f10fcc7f9ccd4beba17792c3bccf2d3af9260 Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Sat, 17 Aug 2019 14:29:45 +0200
Subject: [PATCH] Refactor legacy code for /v2 to support content groups

Use updated content group handling and remove all references to the
old `variant` property of contents/answers.

The following /v2 endpoints are now fully implemented:
* Deleting all contents of a group (/)
* Retrieving ids of unanswered content in a group (/unanswered)
* Deleting all answers of contents in a group (/answers)
* Counting answers of a group (/answercount)
---
 .../controller/v2/ContentController.java      |  47 +++---
 .../arsnova/persistence/AnswerRepository.java |   9 +-
 .../persistence/ContentRepository.java        |  29 +---
 .../couchdb/CouchDbAnswerRepository.java      |  30 ++--
 .../couchdb/CouchDbContentRepository.java     | 142 ++----------------
 .../arsnova/service/AnswerServiceImpl.java    |  31 ++--
 .../arsnova/service/ContentGroupService.java  |   6 +-
 .../arsnova/service/ContentServiceImpl.java   |  45 +++---
 src/main/resources/couchdb/Answer.design.js   |  25 +--
 src/main/resources/couchdb/Content.design.js  |   4 +-
 10 files changed, 111 insertions(+), 257 deletions(-)

diff --git a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
index cd26dbbd7..ad1ea3d6f 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
@@ -356,14 +356,16 @@ public class ContentController extends PaginationController {
 			nickname = "deleteContents")
 	@DeleteMapping("/")
 	public void deleteContents(
-			@RequestParam(value = "sessionkey") final String roomShortId,
-			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") boolean lectureContentsOnly,
-			@RequestParam(value = "flashcardsonly", defaultValue = "false") boolean flashcardsOnly,
-			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") boolean preparationContentsOnly,
+			@RequestParam(value = "sessionkey")
+			final String roomShortId,
+			@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
+			final boolean lectureContentsOnly,
+			@RequestParam(value = "flashcardsonly", defaultValue = "false")
+			final boolean flashcardsOnly,
+			@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
+			final boolean preparationContentsOnly,
 			final HttpServletResponse response) {
 		final String roomId = roomService.getIdByShortId(roomShortId);
-		/* FIXME: Content variant is ignored for now */
-		lectureContentsOnly = preparationContentsOnly = flashcardsOnly = false;
 		if (lectureContentsOnly) {
 			contentService.deleteLectureContents(roomId);
 		} else if (preparationContentsOnly) {
@@ -414,13 +416,14 @@ public class ContentController extends PaginationController {
 	@Deprecated
 	@GetMapping("/unanswered")
 	public List<String> getUnAnsweredContentIds(
-			@RequestParam(value = "sessionkey") final String roomShortId,
-			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") boolean lectureContentsOnly,
-			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") boolean preparationContentsOnly) {
+			@RequestParam(value = "sessionkey")
+			final String roomShortId,
+			@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
+			final boolean lectureContentsOnly,
+			@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
+			final boolean preparationContentsOnly) {
 		final String roomId = roomService.getIdByShortId(roomShortId);
 		final List<String> answers;
-		/* FIXME: Content variant is ignored for now */
-		lectureContentsOnly = preparationContentsOnly = false;
 		if (lectureContentsOnly) {
 			answers = contentService.getUnAnsweredLectureContentIds(roomId);
 		} else if (preparationContentsOnly) {
@@ -589,13 +592,14 @@ public class ContentController extends PaginationController {
 			nickname = "deleteAllContentsAnswers")
 	@DeleteMapping("/answers")
 	public void deleteAllContentsAnswers(
-			@RequestParam(value = "sessionkey") final String roomShortId,
-			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") boolean lectureContentsOnly,
-			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") boolean preparationContentsOnly,
+			@RequestParam(value = "sessionkey")
+			final String roomShortId,
+			@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
+			final boolean lectureContentsOnly,
+			@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
+			final boolean preparationContentsOnly,
 			final HttpServletResponse response) {
 		final String roomId = roomService.getIdByShortId(roomShortId);
-		/* FIXME: Content variant is ignored for now */
-		lectureContentsOnly = preparationContentsOnly = false;
 		if (lectureContentsOnly) {
 			contentService.deleteAllLectureAnswers(roomId);
 		} else if (preparationContentsOnly) {
@@ -685,13 +689,14 @@ public class ContentController extends PaginationController {
 	@Deprecated
 	@GetMapping(value = "/answercount", produces = MediaType.TEXT_PLAIN_VALUE)
 	public String getTotalAnswerCount(
-			@RequestParam(value = "sessionkey") final String roomShortId,
-			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") boolean lectureContentsOnly,
-			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") boolean preparationContentsOnly) {
+			@RequestParam(value = "sessionkey")
+			final String roomShortId,
+			@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
+			final boolean lectureContentsOnly,
+			@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
+			final boolean preparationContentsOnly) {
 		final String roomId = roomService.getIdByShortId(roomShortId);
 		int count = 0;
-		/* FIXME: Content variant is ignored for now */
-		lectureContentsOnly = preparationContentsOnly = false;
 		if (lectureContentsOnly) {
 			count = answerService.countLectureContentAnswers(roomId);
 		} else if (preparationContentsOnly) {
diff --git a/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java b/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java
index f7260fe56..eebde69a8 100644
--- a/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java
+++ b/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java
@@ -18,6 +18,7 @@
 
 package de.thm.arsnova.persistence;
 
+import java.util.Collection;
 import java.util.List;
 
 import de.thm.arsnova.model.Answer;
@@ -32,17 +33,15 @@ public interface AnswerRepository extends CrudRepository<Answer, String> {
 
 	int countByContentId(String contentId);
 
+	int countByContentIds(Collection<String> contentIds);
+
 	<T extends Answer> List<T> findByContentId(String contentId, Class<T> type, int start, int limit);
 
 	List<Answer> findByUserIdRoomId(String userId, String roomId);
 
 	Iterable<Answer> findStubsByContentId(String contentId);
 
-	Iterable<Answer> findStubsByContentIds(List<String> contentId);
+	Iterable<Answer> findStubsByContentIds(Collection<String> contentId);
 
 	int countByRoomId(String roomId);
-
-	int countByRoomIdOnlyLectureVariant(String roomId);
-
-	int countByRoomIdOnlyPreparationVariant(String roomId);
 }
diff --git a/src/main/java/de/thm/arsnova/persistence/ContentRepository.java b/src/main/java/de/thm/arsnova/persistence/ContentRepository.java
index 9b2ca2558..320d031f0 100644
--- a/src/main/java/de/thm/arsnova/persistence/ContentRepository.java
+++ b/src/main/java/de/thm/arsnova/persistence/ContentRepository.java
@@ -1,6 +1,7 @@
 package de.thm.arsnova.persistence;
 
 import java.util.List;
+import java.util.Set;
 
 import de.thm.arsnova.model.Content;
 
@@ -15,37 +16,13 @@ public interface ContentRepository extends CrudRepository<Content, String> {
 
 	List<String> findIdsByRoomId(String roomId);
 
-	Iterable<Content> findStubsByRoomId(final String roomId);
+	Iterable<Content> findStubsByIds(Set<String> ids);
 
-	Iterable<Content> findStubsByRoomIdAndVariant(String roomId, String variant);
+	Iterable<Content> findStubsByRoomId(final String roomId);
 
 	List<String> findUnansweredIdsByRoomIdAndUser(String roomId, String userId);
 
-	List<Content> findByRoomIdOnlyLectureVariantAndActive(String roomId);
-
-	List<Content> findByRoomIdOnlyLectureVariant(String roomId);
-
 	List<Content> findByRoomIdOnlyFlashcardVariantAndActive(String roomId);
 
-	List<Content> findByRoomIdOnlyFlashcardVariant(String roomId);
-
-	List<Content> findByRoomIdOnlyPreparationVariantAndActive(String roomId);
-
-	List<Content> findByRoomIdOnlyPreparationVariant(String roomId);
-
 	List<Content> findByRoomId(String roomId);
-
-	int countLectureVariantByRoomId(String roomId);
-
-	int countFlashcardVariantRoomId(String roomId);
-
-	int countPreparationVariantByRoomId(String roomId);
-
-	List<String> findIdsByRoomIdAndVariantAndSubject(String roomId, String questionVariant, String subject);
-
-	List<String> findSubjectsByRoomIdAndVariant(String roomId, String questionVariant);
-
-	List<String> findUnansweredIdsByRoomIdAndUserOnlyLectureVariant(String roomId, String userId);
-
-	List<String> findUnansweredIdsByRoomIdAndUserOnlyPreparationVariant(String roomId, String userId);
 }
diff --git a/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbAnswerRepository.java b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbAnswerRepository.java
index a6b70c5d3..17773478d 100644
--- a/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbAnswerRepository.java
+++ b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbAnswerRepository.java
@@ -21,6 +21,7 @@ package de.thm.arsnova.persistence.couchdb;
 import com.fasterxml.jackson.databind.JsonNode;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -67,7 +68,7 @@ public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer>
 	}
 
 	@Override
-	public Iterable<Answer> findStubsByContentIds(final List<String> contentIds) {
+	public Iterable<Answer> findStubsByContentIds(final Collection<String> contentIds) {
 		return createEntityStubs(db.queryView(createQuery("by_contentid").keys(contentIds)));
 	}
 
@@ -144,6 +145,16 @@ public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer>
 		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
 	}
 
+	@Override
+	public int countByContentIds(final Collection<String> contentIds) {
+		final ViewResult result = db.queryView(createQuery("by_contentid")
+				.reduce(true)
+				.group(true)
+				.keys(contentIds));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
 	@Override
 	public <T extends Answer> List<T> findByContentId(
 			final String contentId, final Class<T> type, final int start, final int limit) {
@@ -175,21 +186,4 @@ public class CouchDbAnswerRepository extends CouchDbCrudRepository<Answer>
 
 		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
 	}
-
-	@Override
-	public int countByRoomIdOnlyLectureVariant(final String roomId) {
-		return countBySessionIdVariant(roomId, "lecture");
-	}
-
-	@Override
-	public int countByRoomIdOnlyPreparationVariant(final String roomId) {
-		return countBySessionIdVariant(roomId, "preparation");
-	}
-
-	private int countBySessionIdVariant(final String sessionId, final String variant) {
-		final ViewResult result = db.queryView(createQuery("by_roomid_variant")
-				.key(ComplexKey.of(sessionId, variant)));
-
-		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentRepository.java b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentRepository.java
index b1eabaf7b..0fea9e856 100644
--- a/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentRepository.java
+++ b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentRepository.java
@@ -20,8 +20,6 @@ package de.thm.arsnova.persistence.couchdb;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -66,7 +64,7 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 
 	@Override
 	public int countByRoomId(final String roomId) {
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
+		final ViewResult result = db.queryView(createQuery("by_roomid_locked")
 				.startKey(ComplexKey.of(roomId))
 				.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
 				.reduce(true));
@@ -76,25 +74,24 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 
 	@Override
 	public List<String> findIdsByRoomId(final String roomId) {
-		return collectQuestionIds(db.queryView(createQuery("by_roomid_group_locked")
+		return collectQuestionIds(db.queryView(createQuery("by_roomid_locked")
 				.startKey(ComplexKey.of(roomId))
 				.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
 				.reduce(false)));
 	}
 
 	@Override
-	public Iterable<Content> findStubsByRoomId(final String roomId) {
-		return createEntityStubs(db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId))
-				.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
+	public Iterable<Content> findStubsByIds(final Set<String> ids) {
+		return createEntityStubs(db.queryView(createQuery("by_id")
+				.keys(ids)
 				.reduce(false)));
 	}
 
 	@Override
-	public Iterable<Content> findStubsByRoomIdAndVariant(final String roomId, final String variant) {
-		return createEntityStubs(db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, variant))
-				.endKey(ComplexKey.of(roomId, variant, ComplexKey.emptyObject()))
+	public Iterable<Content> findStubsByRoomId(final String roomId) {
+		return createEntityStubs(db.queryView(createQuery("by_roomid_locked")
+				.startKey(ComplexKey.of(roomId))
+				.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
 				.reduce(false)));
 	}
 
@@ -104,7 +101,7 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 
 	@Override
 	public List<String> findUnansweredIdsByRoomIdAndUser(final String roomId, final String userId) {
-		final ViewResult result = db.queryView(createQuery("contentid_by_creatorid_roomid_variant")
+		final ViewResult result = db.queryView(createQuery("contentid_by_creatorid_roomid")
 				.designDocId("_design/Answer")
 				.startKey(ComplexKey.of(userId, roomId))
 				.endKey(ComplexKey.of(userId, roomId, ComplexKey.emptyObject())));
@@ -112,45 +109,8 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 		for (final ViewResult.Row row : result.getRows()) {
 			answeredIds.add(row.getId());
 		}
-		return collectUnansweredQuestionIds(findIdsByRoomId(roomId), answeredIds);
-	}
-
-	@Override
-	public List<String> findUnansweredIdsByRoomIdAndUserOnlyLectureVariant(final String roomId, final String userId) {
-		final ViewResult result = db.queryView(createQuery("contentid_round_by_creatorid_roomid_variant")
-				.designDocId("_design/Answer")
-				.key(ComplexKey.of(userId, roomId, "lecture")));
-		final Map<String, Integer> answeredQuestions = new HashMap<>();
-		for (final ViewResult.Row row : result.getRows()) {
-			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
-		}
-
-		return collectUnansweredQuestionIdsByPiRound(findByRoomIdOnlyLectureVariantAndActive(roomId), answeredQuestions);
-	}
-
-	@Override
-	public List<String> findUnansweredIdsByRoomIdAndUserOnlyPreparationVariant(
-			final String roomId, final String userId) {
-		final ViewResult result = db.queryView(createQuery("contentid_round_by_creatorid_roomid_variant")
-				.designDocId("_design/Answer")
-				.key(ComplexKey.of(userId, roomId, "preparation")));
-		final Map<String, Integer> answeredQuestions = new HashMap<>();
-		for (final ViewResult.Row row : result.getRows()) {
-			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
-		}
-
-		return collectUnansweredQuestionIdsByPiRound(
-				findByRoomIdOnlyPreparationVariantAndActive(roomId), answeredQuestions);
-	}
 
-	@Override
-	public List<Content> findByRoomIdOnlyLectureVariantAndActive(final String roomId) {
-		return findByRoomIdAndVariantAndActive(roomId, "lecture", true);
-	}
-
-	@Override
-	public List<Content> findByRoomIdOnlyLectureVariant(final String roomId) {
-		return findByRoomIdAndVariantAndActive(roomId, "lecture");
+		return collectUnansweredQuestionIds(findIdsByRoomId(roomId), answeredIds);
 	}
 
 	@Override
@@ -158,21 +118,6 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 		return findByRoomIdAndVariantAndActive(roomId, "flashcard", true);
 	}
 
-	@Override
-	public List<Content> findByRoomIdOnlyFlashcardVariant(final String roomId) {
-		return findByRoomIdAndVariantAndActive(roomId, "flashcard");
-	}
-
-	@Override
-	public List<Content> findByRoomIdOnlyPreparationVariantAndActive(final String roomId) {
-		return findByRoomIdAndVariantAndActive(roomId, "preparation", true);
-	}
-
-	@Override
-	public List<Content> findByRoomIdOnlyPreparationVariant(final String roomId) {
-		return findByRoomIdAndVariantAndActive(roomId, "preparation");
-	}
-
 	@Override
 	public List<Content> findByRoomId(final String roomId) {
 		return findByRoomIdAndVariantAndActive(roomId);
@@ -183,7 +128,7 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 		final Object[] endKeys = Arrays.copyOf(keys, keys.length + 1);
 		endKeys[keys.length] = ComplexKey.emptyObject();
 
-		return db.queryView(createQuery("by_roomid_group_locked")
+		return db.queryView(createQuery("by_roomid_locked")
 						.includeDocs(true)
 						.reduce(false)
 						.startKey(ComplexKey.of(keys))
@@ -191,36 +136,6 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 				Content.class);
 	}
 
-	@Override
-	public int countLectureVariantByRoomId(final String roomId) {
-		/* TODO: reduce code duplication */
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, "lecture"))
-				.endKey(ComplexKey.of(roomId, "lecture", ComplexKey.emptyObject())));
-
-		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
-	}
-
-	@Override
-	public int countFlashcardVariantRoomId(final String roomId) {
-		/* TODO: reduce code duplication */
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, "flashcard"))
-				.endKey(ComplexKey.of(roomId, "flashcard", ComplexKey.emptyObject())));
-
-		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
-	}
-
-	@Override
-	public int countPreparationVariantByRoomId(final String roomId) {
-		/* TODO: reduce code duplication */
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, "preparation"))
-				.endKey(ComplexKey.of(roomId, "preparation", ComplexKey.emptyObject())));
-
-		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
-	}
-
 	private List<String> collectUnansweredQuestionIds(
 			final List<String> contentIds,
 			final List<String> answeredContentIds) {
@@ -257,37 +172,4 @@ public class CouchDbContentRepository extends CouchDbCrudRepository<Content> imp
 		}
 		return ids;
 	}
-
-	/* TODO: remove if this method is no longer used */
-	@Override
-	public List<String> findIdsByRoomIdAndVariantAndSubject(
-			final String roomId, final String questionVariant, final String subject) {
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, questionVariant, false, subject))
-				.endKey(ComplexKey.of(roomId, questionVariant, false, subject, ComplexKey.emptyObject())));
-
-		final List<String> qids = new ArrayList<>();
-
-		for (final ViewResult.Row row : result.getRows()) {
-			final String s = row.getId();
-			qids.add(s);
-		}
-
-		return qids;
-	}
-
-	@Override
-	public List<String> findSubjectsByRoomIdAndVariant(final String roomId, final String questionVariant) {
-		final ViewResult result = db.queryView(createQuery("by_roomid_group_locked")
-				.startKey(ComplexKey.of(roomId, questionVariant))
-				.endKey(ComplexKey.of(roomId, questionVariant, ComplexKey.emptyObject())));
-
-		final Set<String> uniqueSubjects = new HashSet<>();
-
-		for (final ViewResult.Row row : result.getRows()) {
-			uniqueSubjects.add(row.getKeyAsNode().get(3).asText());
-		}
-
-		return new ArrayList<>(uniqueSubjects);
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
index c231d311b..db181211c 100644
--- a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
@@ -23,6 +23,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Queue;
+import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import org.ektorp.DbAccessException;
 import org.slf4j.Logger;
@@ -62,6 +63,7 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 
 	private RoomService roomService;
 	private ContentService contentService;
+	private ContentGroupService contentGroupService;
 	private AnswerRepository answerRepository;
 	private UserService userService;
 
@@ -83,6 +85,11 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 		this.contentService = contentService;
 	}
 
+	@Autowired
+	public void setContentGroupService(final ContentGroupService contentGroupService) {
+		this.contentGroupService = contentGroupService;
+	}
+
 	@Scheduled(fixedDelay = 5000)
 	public void flushAnswerQueue() {
 		if (answerQueue.isEmpty()) {
@@ -377,15 +384,6 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 		answer.setRoomId(room.getId());
 	}
 
-	/*
-	 * The "internal" suffix means it is called by internal services that have no authentication!
-	 * TODO: Find a better way of doing this...
-	 */
-	@Override
-	public int countLectureQuestionAnswersInternal(final String roomId) {
-		return answerRepository.countByRoomIdOnlyLectureVariant(roomService.get(roomId).getId());
-	}
-
 	@Override
 	public Map<String, Object> countAnswersAndAbstentionsInternal(final String contentId) {
 		final Content content = contentService.get(contentId);
@@ -414,13 +412,26 @@ public class AnswerServiceImpl extends DefaultEntityServiceImpl<Answer> implemen
 		return this.countPreparationQuestionAnswersInternal(roomId);
 	}
 
+	/*
+	 * The "internal" suffix means it is called by internal services that have no authentication!
+	 * TODO: Find a better way of doing this...
+	 */
+	@Override
+	public int countLectureQuestionAnswersInternal(final String roomId) {
+		final Set<String> contentIds =
+				contentGroupService.getByRoomIdAndName(roomId, "lecture").getContentIds();
+		return answerRepository.countByContentIds(contentIds);
+	}
+
 	/*
 	 * The "internal" suffix means it is called by internal services that have no authentication!
 	 * TODO: Find a better way of doing this...
 	 */
 	@Override
 	public int countPreparationQuestionAnswersInternal(final String roomId) {
-		return answerRepository.countByRoomIdOnlyPreparationVariant(roomService.get(roomId).getId());
+		final Set<String> contentIds =
+				contentGroupService.getByRoomIdAndName(roomId, "preparation").getContentIds();
+		return answerRepository.countByContentIds(contentIds);
 	}
 
 	@EventListener
diff --git a/src/main/java/de/thm/arsnova/service/ContentGroupService.java b/src/main/java/de/thm/arsnova/service/ContentGroupService.java
index a227a984f..74b6e84e8 100644
--- a/src/main/java/de/thm/arsnova/service/ContentGroupService.java
+++ b/src/main/java/de/thm/arsnova/service/ContentGroupService.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.event.EventListener;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@@ -42,12 +43,15 @@ public class ContentGroupService extends DefaultEntityServiceImpl<ContentGroup>
 
 	public ContentGroupService(
 			final ContentGroupRepository repository,
-			final ContentService contentService,
 			@Qualifier("defaultJsonMessageConverter")
 			final MappingJackson2HttpMessageConverter jackson2HttpMessageConverter,
 			final Validator validator) {
 		super(ContentGroup.class, repository, jackson2HttpMessageConverter.getObjectMapper(), validator);
 		this.contentGroupRepository = repository;
+	}
+
+	@Autowired
+	public void setContentService(final ContentService contentService) {
 		this.contentService = contentService;
 	}
 
diff --git a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
index 78208b53d..898cc636c 100644
--- a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
@@ -239,43 +239,42 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	}
 
 	@PreAuthorize("isAuthenticated()")
-	private void deleteBySessionAndVariant(final Room room, final String variant) {
+	private void deleteByRoomAndGroupName(final Room room, final String groupName) {
 		final Iterable<Content> contents;
-		if ("all".equals(variant)) {
-			contents = contentRepository.findStubsByRoomId(room.getId());
+		if ("all".equals(groupName)) {
+			delete(contentRepository.findStubsByRoomId(room.getId()));
 		} else {
-			contents = contentRepository.findStubsByRoomIdAndVariant(room.getId(), variant);
+			final Set<String> ids = contentGroupService.getByRoomIdAndName(room.getId(), groupName).getContentIds();
+			delete(contentRepository.findStubsByIds(ids));
 		}
-
-		delete(contents);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAllContents(final String roomId) {
 		final Room room = getRoomWithAuthCheck(roomId);
-		deleteBySessionAndVariant(room, "all");
+		deleteByRoomAndGroupName(room, "all");
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteLectureContents(final String roomId) {
 		final Room room = getRoomWithAuthCheck(roomId);
-		deleteBySessionAndVariant(room, "lecture");
+		deleteByRoomAndGroupName(room, "lecture");
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deletePreparationContents(final String roomId) {
 		final Room room = getRoomWithAuthCheck(roomId);
-		deleteBySessionAndVariant(room, "preparation");
+		deleteByRoomAndGroupName(room, "preparation");
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteFlashcards(final String roomId) {
 		final Room room = getRoomWithAuthCheck(roomId);
-		deleteBySessionAndVariant(room, "flashcard");
+		deleteByRoomAndGroupName(room, "flashcard");
 	}
 
 	@Override
@@ -358,7 +357,10 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public List<String> getUnAnsweredLectureContentIds(final String roomId, final String userId) {
-		return contentRepository.findUnansweredIdsByRoomIdAndUserOnlyLectureVariant(roomId, userId);
+		final List<String> ids = contentRepository.findUnansweredIdsByRoomIdAndUser(roomId, userId);
+		ids.retainAll(contentGroupService.getByRoomIdAndName(roomId, "lecture").getContentIds());
+
+		return ids;
 	}
 
 	@Override
@@ -370,7 +372,10 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public List<String> getUnAnsweredPreparationContentIds(final String roomId, final String userId) {
-		return contentRepository.findUnansweredIdsByRoomIdAndUserOnlyPreparationVariant(roomId, userId);
+		final List<String> ids = contentRepository.findUnansweredIdsByRoomIdAndUser(roomId, userId);
+		ids.retainAll(contentGroupService.getByRoomIdAndName(roomId, "preparation").getContentIds());
+
+		return ids;
 	}
 
 	@Override
@@ -422,10 +427,9 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAllPreparationAnswers(final String roomId) {
 		final Room room = roomService.get(roomId);
-
-		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "preparation");
-		resetContentsRoundState(room.getId(), contents);
-		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		contentGroupService.getByRoomIdAndName(roomId, "preparation").getContentIds();
+		final Set<String> contentIds = contentGroupService.getByRoomIdAndName(roomId, "preparation").getContentIds();
+		resetContentsRoundState(room.getId(), get(contentIds));
 		answerService.delete(answerRepository.findStubsByContentIds(contentIds));
 	}
 
@@ -433,10 +437,9 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAllLectureAnswers(final String roomId) {
 		final Room room = roomService.get(roomId);
-
-		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "lecture");
-		resetContentsRoundState(room.getId(), contents);
-		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		contentGroupService.getByRoomIdAndName(roomId, "lecture").getContentIds();
+		final Set<String> contentIds = contentGroupService.getByRoomIdAndName(roomId, "lecture").getContentIds();
+		resetContentsRoundState(room.getId(), get(contentIds));
 		answerService.delete(answerRepository.findStubsByContentIds(contentIds));
 	}
 
@@ -446,7 +449,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			@CacheEvict(value = "lecturecontentlists", key = "#roomId"),
 			@CacheEvict(value = "preparationcontentlists", key = "#roomId"),
 			@CacheEvict(value = "flashcardcontentlists", key = "#roomId") })
-	private void resetContentsRoundState(final String roomId, final List<Content> contents) {
+	private void resetContentsRoundState(final String roomId, final Iterable<Content> contents) {
 		for (final Content q : contents) {
 			/* TODO: Check if setting the sessionId is necessary. */
 			q.setRoomId(roomId);
diff --git a/src/main/resources/couchdb/Answer.design.js b/src/main/resources/couchdb/Answer.design.js
index 3e89d47b1..ba5814d6a 100644
--- a/src/main/resources/couchdb/Answer.design.js
+++ b/src/main/resources/couchdb/Answer.design.js
@@ -15,7 +15,8 @@ var designDoc = {
 				if (doc.type === "Answer") {
 					emit(doc.contentId, {_rev: doc._rev});
 				}
-			}
+			},
+            "reduce": "_count"
 		},
 		"by_contentid_round_body_subject": {
 			"map": function (doc) {
@@ -55,34 +56,12 @@ var designDoc = {
 			},
 			"reduce": "_count"
 		},
-		"by_roomid_variant": {
-			"map": function (doc) {
-				if (doc.type === "Answer") {
-					emit([doc.roomId, doc.questionVariant], {_rev: doc._rev});
-				}
-			},
-			"reduce": "_count"
-		},
 		"by_creatorid_roomid": {
 			"map": function (doc) {
 				if (doc.type === "Answer") {
 					emit([doc.creatorId, doc.roomId], {_rev: doc._rev});
 				}
 			}
-		},
-		"contentid_by_creatorid_roomid_variant": {
-			"map": function (doc) {
-				if (doc.type === "Answer") {
-					emit([doc.user, doc.roomId, doc.questionVariant], doc.contentId);
-				}
-			}
-		},
-		"contentid_round_by_creatorid_roomid_variant": {
-			"map": function (doc) {
-				if (doc.type === "Answer") {
-					emit([doc.creatorId, doc.roomId, doc.questionVariant], [doc.contentId, doc.round]);
-				}
-			}
 		}
 	}
 };
diff --git a/src/main/resources/couchdb/Content.design.js b/src/main/resources/couchdb/Content.design.js
index 5b09c23f6..82b773ef6 100644
--- a/src/main/resources/couchdb/Content.design.js
+++ b/src/main/resources/couchdb/Content.design.js
@@ -18,10 +18,10 @@ var designDoc = {
 			},
 			"reduce": "_count"
 		},
-		"by_roomid_group_locked": {
+		"by_roomid_locked": {
 			"map": function (doc) {
 				if (doc.type === "Content") {
-					emit([doc.roomId, doc.group, doc.locked, doc.subject, doc.body.substr(0, 16)], {_rev: doc._rev});
+					emit([doc.roomId, doc.locked, doc.subject, doc.body.substr(0, 16)], {_rev: doc._rev});
 				}
 			},
 			"reduce": "_count"
-- 
GitLab