diff --git a/src/main/java/de/thm/arsnova/config/PersistenceConfig.java b/src/main/java/de/thm/arsnova/config/PersistenceConfig.java
index 1f831776d279908dbed4613813d590ae813b7b2b..f8e6bdf5501ada9a641c2700ca3bf5e1865fe5cf 100644
--- a/src/main/java/de/thm/arsnova/config/PersistenceConfig.java
+++ b/src/main/java/de/thm/arsnova/config/PersistenceConfig.java
@@ -32,6 +32,7 @@ import de.thm.arsnova.config.properties.CouchDbProperties;
 import de.thm.arsnova.model.serialization.CouchDbObjectMapperFactory;
 import de.thm.arsnova.persistence.AnswerRepository;
 import de.thm.arsnova.persistence.CommentRepository;
+import de.thm.arsnova.persistence.ContentGroupRepository;
 import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.LogEntryRepository;
 import de.thm.arsnova.persistence.MotdRepository;
@@ -41,6 +42,7 @@ import de.thm.arsnova.persistence.StatisticsRepository;
 import de.thm.arsnova.persistence.UserRepository;
 import de.thm.arsnova.persistence.couchdb.CouchDbAnswerRepository;
 import de.thm.arsnova.persistence.couchdb.CouchDbCommentRepository;
+import de.thm.arsnova.persistence.couchdb.CouchDbContentGroupRepository;
 import de.thm.arsnova.persistence.couchdb.CouchDbContentRepository;
 import de.thm.arsnova.persistence.couchdb.CouchDbLogEntryRepository;
 import de.thm.arsnova.persistence.couchdb.CouchDbMotdRepository;
@@ -140,6 +142,11 @@ public class PersistenceConfig {
 		return new CouchDbContentRepository(couchDbConnector(), false);
 	}
 
+	@Bean
+	public ContentGroupRepository contentGroupRepository() throws Exception {
+		return new CouchDbContentGroupRepository(couchDbConnector(), false);
+	}
+
 	@Bean
 	public AnswerRepository answerRepository() throws Exception {
 		return new CouchDbAnswerRepository(couchDbConnector(), false);
diff --git a/src/main/java/de/thm/arsnova/controller/RoomController.java b/src/main/java/de/thm/arsnova/controller/RoomController.java
index 67eb68cbaa54c8c0b3fb3d2503487c209c841c50..5357089b80125af0994470f853d91ac869cabe9f 100644
--- a/src/main/java/de/thm/arsnova/controller/RoomController.java
+++ b/src/main/java/de/thm/arsnova/controller/RoomController.java
@@ -22,12 +22,15 @@ import java.util.Set;
 import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import de.thm.arsnova.model.ContentGroup;
 import de.thm.arsnova.model.Room;
+import de.thm.arsnova.service.ContentGroupService;
 import de.thm.arsnova.service.RoomService;
 
 @RestController
@@ -36,12 +39,16 @@ public class RoomController extends AbstractEntityController<Room> {
 	protected static final String REQUEST_MAPPING = "/room";
 	private static final String GET_MODERATORS_MAPPING = DEFAULT_ID_MAPPING + "/moderator";
 	private static final String MODERATOR_MAPPING = DEFAULT_ID_MAPPING + "/moderator/{userId}";
+	private static final String CONTENTGROUP_MAPPING = DEFAULT_ID_MAPPING + "/contentgroup/{groupName}";
+	private static final String CONTENTGROUP_ADD_MAPPING = CONTENTGROUP_MAPPING + "/{contentId}";
 
 	private RoomService roomService;
+	private ContentGroupService contentGroupService;
 
-	public RoomController(final RoomService roomService) {
+	public RoomController(final RoomService roomService, final ContentGroupService contentGroupService) {
 		super(roomService);
 		this.roomService = roomService;
+		this.contentGroupService = contentGroupService;
 	}
 
 	@Override
@@ -78,4 +85,15 @@ public class RoomController extends AbstractEntityController<Room> {
 		room.getModerators().removeIf(m -> m.getUserId().equals(userId));
 		roomService.update(room);
 	}
+
+	@GetMapping(CONTENTGROUP_MAPPING)
+	public ContentGroup getContentGroup(@PathVariable final String id, @PathVariable final String groupName) {
+		return contentGroupService.getByRoomIdAndName(id, groupName);
+	}
+
+	@PostMapping(CONTENTGROUP_ADD_MAPPING)
+	public void addContentToGroup(@PathVariable final String id, @PathVariable final String groupName,
+			@RequestBody final String contentId) {
+		contentGroupService.addContentToGroup(id, groupName, contentId);
+	}
 }
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 f788bcdec499cb6c0b37a10032c7820c882ee4d3..ad1ea3d6f758ae7a00babbdc5b71806fc8d0a08c 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 import javax.naming.OperationNotSupportedException;
@@ -48,12 +49,14 @@ import org.springframework.web.bind.annotation.RestController;
 import de.thm.arsnova.controller.PaginationController;
 import de.thm.arsnova.model.ChoiceAnswer;
 import de.thm.arsnova.model.ChoiceQuestionContent;
+import de.thm.arsnova.model.ContentGroup;
 import de.thm.arsnova.model.TextAnswer;
 import de.thm.arsnova.model.migration.FromV2Migrator;
 import de.thm.arsnova.model.migration.ToV2Migrator;
 import de.thm.arsnova.model.migration.v2.Answer;
 import de.thm.arsnova.model.migration.v2.Content;
 import de.thm.arsnova.service.AnswerService;
+import de.thm.arsnova.service.ContentGroupService;
 import de.thm.arsnova.service.ContentService;
 import de.thm.arsnova.service.RoomService;
 import de.thm.arsnova.service.TimerService;
@@ -76,6 +79,9 @@ public class ContentController extends PaginationController {
 	@Autowired
 	private ContentService contentService;
 
+	@Autowired
+	private ContentGroupService contentGroupService;
+
 	@Autowired
 	private AnswerService answerService;
 
@@ -100,7 +106,12 @@ public class ContentController extends PaginationController {
 	public Content getContent(@PathVariable final String contentId) {
 		final de.thm.arsnova.model.Content content = contentService.get(contentId);
 		if (content != null) {
-			return toV2Migrator.migrate(content);
+			final Optional<ContentGroup> contentGroup = contentGroupService.getByRoomId(content.getRoomId()).stream()
+					.filter(cg -> cg.getContentIds().contains(contentId)).findFirst();
+			final Content contentV2 = toV2Migrator.migrate(content);
+			contentGroup.ifPresent(cg -> contentV2.setQuestionVariant(cg.getName()));
+
+			return contentV2;
 		}
 
 		throw new NotFoundException();
@@ -118,6 +129,7 @@ public class ContentController extends PaginationController {
 		final String roomId = roomService.getIdByShortId(content.getSessionKeyword());
 		contentV3.setRoomId(roomId);
 		contentService.create(contentV3);
+		contentGroupService.addContentToGroup(roomId, content.getQuestionVariant(), contentV3.getId());
 
 		return toV2Migrator.migrate(contentV3);
 	}
@@ -344,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) {
@@ -402,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) {
@@ -577,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) {
@@ -673,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/model/ContentGroup.java b/src/main/java/de/thm/arsnova/model/ContentGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..25ad8733c95c7bff348e3d3b3d1892aedf56c8a9
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/model/ContentGroup.java
@@ -0,0 +1,122 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2019 The ARSnova Team and Contributors
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.thm.arsnova.model;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import org.springframework.core.style.ToStringCreator;
+
+import de.thm.arsnova.model.serialization.View;
+
+public class ContentGroup extends Entity {
+	@NotEmpty
+	private String roomId;
+
+	@NotBlank
+	private String name;
+
+	private Set<String> contentIds;
+	private boolean autoSort;
+
+	public ContentGroup() {
+
+	}
+
+	public ContentGroup(final String roomId, final String name) {
+		this.roomId = roomId;
+		this.name = name;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRoomId() {
+		return roomId;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRoomId(final String roomId) {
+		this.roomId = roomId;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getName() {
+		return this.name;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setName(final String name) {
+		this.name = name;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public Set<String> getContentIds() {
+		if (contentIds == null) {
+			contentIds = new HashSet<>();
+		}
+
+		return contentIds;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setContentIds(final Set<String> contentIds) {
+		this.contentIds = contentIds;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public boolean isAutoSort() {
+		return autoSort;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setAutoSort(final boolean autoSort) {
+		this.autoSort = autoSort;
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(name, contentIds, autoSort);
+	}
+
+	@Override
+	public boolean equals(final Object o) {
+		if (this == o) {
+			return true;
+		}
+		if (o == null || getClass() != o.getClass()) {
+			return false;
+		}
+		final ContentGroup that = (ContentGroup) o;
+
+		return autoSort == that.autoSort
+			&& Objects.equals(name, that.name)
+			&& Objects.equals(contentIds, that.contentIds);
+	}
+
+	@Override
+	public String toString() {
+		return new ToStringCreator(this)
+			.append("name", name)
+			.append("contentIds", contentIds)
+			.append("autoSort", autoSort)
+			.toString();
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/model/Room.java b/src/main/java/de/thm/arsnova/model/Room.java
index 689eae0a866629bf4ae1dc3fef4a2ee7373baaa3..cf5f69e31f525a78f5f58cc378c76e880547253a 100644
--- a/src/main/java/de/thm/arsnova/model/Room.java
+++ b/src/main/java/de/thm/arsnova/model/Room.java
@@ -23,8 +23,6 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotEmpty;
 import org.springframework.core.style.ToStringCreator;
@@ -32,77 +30,6 @@ import org.springframework.core.style.ToStringCreator;
 import de.thm.arsnova.model.serialization.View;
 
 public class Room extends Entity {
-	public static class ContentGroup {
-		@NotBlank
-		private String name;
-
-		private Set<String> contentIds;
-		private boolean autoSort;
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public String getName() {
-			return this.name;
-		}
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public void setName(final String name) {
-			this.name = name;
-		}
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public Set<String> getContentIds() {
-			if (contentIds == null) {
-				contentIds = new HashSet<>();
-			}
-
-			return contentIds;
-		}
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public void setContentIds(final Set<String> contentIds) {
-			this.contentIds = contentIds;
-		}
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public boolean isAutoSort() {
-			return autoSort;
-		}
-
-		@JsonView({View.Persistence.class, View.Public.class})
-		public void setAutoSort(final boolean autoSort) {
-			this.autoSort = autoSort;
-		}
-
-		@Override
-		public int hashCode() {
-			return Objects.hash(name, contentIds, autoSort);
-		}
-
-		@Override
-		public boolean equals(final Object o) {
-			if (this == o) {
-				return true;
-			}
-			if (o == null || getClass() != o.getClass()) {
-				return false;
-			}
-			final ContentGroup that = (ContentGroup) o;
-
-			return autoSort == that.autoSort
-					&& Objects.equals(name, that.name)
-					&& Objects.equals(contentIds, that.contentIds);
-		}
-
-		@Override
-		public String toString() {
-			return new ToStringCreator(this)
-					.append("name", name)
-					.append("contentIds", contentIds)
-					.append("autoSort", autoSort)
-					.toString();
-		}
-	}
-
 	public static class Moderator {
 		public enum Role {
 			EDITING_MODERATOR,
@@ -468,7 +395,6 @@ public class Room extends Entity {
 
 	private String description;
 	private boolean closed;
-	private Set<ContentGroup> contentGroups;
 	private Set<Moderator> moderators;
 	private Settings settings;
 	private Author author;
@@ -537,28 +463,6 @@ public class Room extends Entity {
 		this.closed = closed;
 	}
 
-	@JsonView({View.Persistence.class, View.Public.class})
-	public Set<ContentGroup> getContentGroups() {
-		if (contentGroups == null) {
-			contentGroups = new HashSet<>();
-		}
-
-		return contentGroups;
-	}
-
-	public Map<String, ContentGroup> getContentGroupsAsMap() {
-		return getContentGroups().stream().collect(Collectors.toMap(ContentGroup::getName, Function.identity()));
-	}
-
-	@JsonView({View.Persistence.class, View.Public.class})
-	public void setContentGroups(final Set<ContentGroup> contentGroups) {
-		this.contentGroups = contentGroups;
-	}
-
-	public void setContentGroupsFromMap(final Map<String, ContentGroup> groups) {
-		this.contentGroups = new HashSet<>(groups.values());
-	}
-
 	@JsonView(View.Persistence.class)
 	public Set<Moderator> getModerators() {
 		if (moderators == null) {
@@ -641,8 +545,8 @@ public class Room extends Entity {
 	 *
 	 * <p>
 	 * The following fields of <tt>Room</tt> are excluded from equality checks:
-	 * {@link #contentGroups}, {@link #settings}, {@link #author}, {@link #poolProperties}, {@link #extensions},
-	 * {@link #attachments}, {@link #statistics}.
+	 * {@link #settings}, {@link #author}, {@link #poolProperties}, {@link #extensions}, {@link #attachments},
+	 * {@link #statistics}.
 	 * </p>
 	 */
 	@Override
@@ -672,7 +576,6 @@ public class Room extends Entity {
 				.append("abbreviation", abbreviation)
 				.append("description", description)
 				.append("closed", closed)
-				.append("contentGroups", contentGroups)
 				.append("settings", settings)
 				.append("author", author)
 				.append("poolProperties", poolProperties)
diff --git a/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java b/src/main/java/de/thm/arsnova/persistence/AnswerRepository.java
index f7260fe5658bc5b780f3f48cbac8aeaea613f08d..eebde69a8e0a8240ae15ebef89adb08bb8374e33 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/ContentGroupRepository.java b/src/main/java/de/thm/arsnova/persistence/ContentGroupRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ee6a87b0b15ebcff3a9d3897f8a538e88587c14
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistence/ContentGroupRepository.java
@@ -0,0 +1,29 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2019 The ARSnova Team and Contributors
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.thm.arsnova.persistence;
+
+import java.util.List;
+
+import de.thm.arsnova.model.ContentGroup;
+
+public interface ContentGroupRepository extends CrudRepository<ContentGroup, String> {
+	ContentGroup findByRoomIdAndName(String roomId, String name);
+
+	List<ContentGroup> findByRoomId(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 9b2ca2558e20f75dec3ac454177007133ad3f31f..320d031f0515e48392a451acbe5a1b7bf0742cf8 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 a6b70c5d398951eda9d9178365fbd33402a6de4d..17773478d87ee9ed30bc75c97a0a9dff6e743071 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/CouchDbContentGroupRepository.java b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentGroupRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..8edebb3ec0a68e5ca629ac5b9d154cf330227b78
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistence/couchdb/CouchDbContentGroupRepository.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2019 The ARSnova Team and Contributors
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.thm.arsnova.persistence.couchdb;
+
+import java.util.List;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+
+import de.thm.arsnova.model.ContentGroup;
+import de.thm.arsnova.persistence.ContentGroupRepository;
+
+public class CouchDbContentGroupRepository extends CouchDbCrudRepository<ContentGroup>
+		implements ContentGroupRepository {
+	public CouchDbContentGroupRepository(final CouchDbConnector db, final boolean createIfNotExists) {
+		super(ContentGroup.class, db, "by_id", createIfNotExists);
+	}
+
+	@Override
+	public ContentGroup findByRoomIdAndName(final String roomId, final String name) {
+		final List<ContentGroup> contentGroupList = db.queryView(createQuery("by_roomid_name")
+						.key(ComplexKey.of(roomId, name))
+						.includeDocs(true)
+						.reduce(false),
+				ContentGroup.class);
+
+		return !contentGroupList.isEmpty() ? contentGroupList.get(0) : null;
+	}
+
+	@Override
+	public List<ContentGroup> findByRoomId(final String roomId) {
+		final List<ContentGroup> contentGroups = db.queryView(createQuery("by_roomid_name")
+						.startKey(ComplexKey.of(roomId))
+						.endKey(ComplexKey.of(roomId, ComplexKey.emptyObject()))
+						.includeDocs(true)
+						.reduce(false),
+				ContentGroup.class);
+
+		return contentGroups;
+	}
+}
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 b1eabaf7b2a1644e365f4f164db994539e77db62..0fea9e85652fa848940b0edff2b4df2225234856 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/security/ApplicationPermissionEvaluator.java b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
index d91e4574794b59554251197e74045f4a2e9f4324..66a50e5bef1b31e8f5b13aced1695b98e12eb08c 100644
--- a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
+++ b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
@@ -35,11 +35,13 @@ import de.thm.arsnova.config.properties.SecurityProperties;
 import de.thm.arsnova.model.Answer;
 import de.thm.arsnova.model.Comment;
 import de.thm.arsnova.model.Content;
+import de.thm.arsnova.model.ContentGroup;
 import de.thm.arsnova.model.Motd;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.model.UserProfile;
 import de.thm.arsnova.persistence.AnswerRepository;
 import de.thm.arsnova.persistence.CommentRepository;
+import de.thm.arsnova.persistence.ContentGroupRepository;
 import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.MotdRepository;
 import de.thm.arsnova.persistence.RoomRepository;
@@ -67,6 +69,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 	@Autowired
 	private ContentRepository contentRepository;
 
+	@Autowired
+	ContentGroupRepository contentGroupRepository;
+
 	@Autowired
 	private AnswerRepository answerRepository;
 
@@ -96,6 +101,8 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 						&& hasRoomPermission(userId, ((Room) targetDomainObject), permission.toString()))
 				|| (targetDomainObject instanceof Content
 						&& hasContentPermission(userId, ((Content) targetDomainObject), permission.toString()))
+				|| (targetDomainObject instanceof ContentGroup
+				&& hasContentGroupPermission(userId, ((ContentGroup) targetDomainObject), permission.toString()))
 				|| (targetDomainObject instanceof Answer
 						&& hasAnswerPermission(userId, ((Answer) targetDomainObject), permission.toString()))
 				|| (targetDomainObject instanceof Comment
@@ -132,6 +139,10 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 			case "content":
 				final Content targetContent = contentRepository.findOne(targetId.toString());
 				return targetContent != null && hasContentPermission(userId, targetContent, permission.toString());
+			case "contentgroup":
+				final ContentGroup targetContentGroup = contentGroupRepository.findOne(targetId.toString());
+				return targetContentGroup != null
+						&& hasContentGroupPermission(userId, targetContentGroup, permission.toString());
 			case "answer":
 				final Answer targetAnswer = answerRepository.findOne(targetId.toString());
 				return targetAnswer != null && hasAnswerPermission(userId, targetAnswer, permission.toString());
@@ -211,6 +222,28 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 		}
 	}
 
+	private boolean hasContentGroupPermission(
+			final String userId,
+			final ContentGroup targetContentGroup,
+			final String permission) {
+		final Room room = roomRepository.findOne(targetContentGroup.getRoomId());
+		if (room == null) {
+			return false;
+		}
+
+		switch (permission) {
+			case "read":
+				return !room.isClosed() || hasUserIdRoomModeratingPermission(room, userId);
+			case "create":
+			case "update":
+			case "delete":
+				return room.getOwnerId().equals(userId)
+						|| hasUserIdRoomModeratorRole(room, userId, Room.Moderator.Role.EDITING_MODERATOR);
+			default:
+				return false;
+		}
+	}
+
 	private boolean hasAnswerPermission(
 			final String userId,
 			final Answer targetAnswer,
diff --git a/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java b/src/main/java/de/thm/arsnova/service/AnswerServiceImpl.java
index c231d311bdf22350cb8e2849427805bedc4b0cbd..db181211c9e33a55eb2b44505ad58671097d9f59 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/ContentFindQueryService.java b/src/main/java/de/thm/arsnova/service/ContentFindQueryService.java
index ad668a81f3f7903fe2724dc8e66abfcaaf81f235..c1df6a20ebf2b699ec266c06b4de4b64cf3462ee 100644
--- a/src/main/java/de/thm/arsnova/service/ContentFindQueryService.java
+++ b/src/main/java/de/thm/arsnova/service/ContentFindQueryService.java
@@ -21,6 +21,7 @@ package de.thm.arsnova.service;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.springframework.stereotype.Service;
 
 import de.thm.arsnova.model.Content;
@@ -30,15 +31,28 @@ import de.thm.arsnova.model.FindQuery;
 public class ContentFindQueryService implements FindQueryService<Content> {
 	private RoomService roomService;
 	private ContentService contentService;
+	private ContentGroupService contentGroupService;
 
-	public ContentFindQueryService(final RoomService roomService, final ContentService contentService) {
+	public ContentFindQueryService(final RoomService roomService, final ContentService contentService,
+			final ContentGroupService contentGroupService) {
 		this.roomService = roomService;
 		this.contentService = contentService;
+		this.contentGroupService = contentGroupService;
 	}
 
 	@Override
 	public Set<String> resolveQuery(final FindQuery<Content> findQuery) {
 		final Set<String> contentIds = new HashSet<>();
+
+		if (findQuery.getExternalFilters().get("notInContentGroupOfRoomId") instanceof String) {
+			final String roomId = (String) findQuery.getExternalFilters().get("notInContentGroupOfRoomId");
+			final Set<String> idsWithGroup = contentGroupService.getByRoomId(roomId).stream()
+					.flatMap(cg -> cg.getContentIds().stream()).collect(Collectors.toSet());
+			final Set<String> idsWithoutGroup = contentService.getByRoomId(roomId).stream()
+					.map(Content::getId).filter(id -> !idsWithGroup.contains(id)).collect(Collectors.toSet());
+			contentIds.addAll(idsWithoutGroup);
+		}
+
 		if (findQuery.getProperties().getRoomId() != null) {
 			final List<Content> contentList = contentService.getByRoomId(findQuery.getProperties().getRoomId());
 			for (final Content c : contentList) {
diff --git a/src/main/java/de/thm/arsnova/service/ContentGroupService.java b/src/main/java/de/thm/arsnova/service/ContentGroupService.java
new file mode 100644
index 0000000000000000000000000000000000000000..74b6e84e857cbc44be193b99a44031f5cd230024
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/service/ContentGroupService.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of ARSnova Backend.
+ * Copyright (C) 2012-2019 The ARSnova Team and Contributors
+ *
+ * ARSnova Backend is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova Backend is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.	 If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.thm.arsnova.service;
+
+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;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.Validator;
+
+import de.thm.arsnova.event.BeforeDeletionEvent;
+import de.thm.arsnova.model.Content;
+import de.thm.arsnova.model.ContentGroup;
+import de.thm.arsnova.model.Room;
+import de.thm.arsnova.persistence.ContentGroupRepository;
+
+@Service
+public class ContentGroupService extends DefaultEntityServiceImpl<ContentGroup> {
+	private ContentGroupRepository contentGroupRepository;
+	private ContentService contentService;
+
+	public ContentGroupService(
+			final ContentGroupRepository repository,
+			@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;
+	}
+
+	public ContentGroup getByRoomIdAndName(final String roomId, final String name) {
+		return contentGroupRepository.findByRoomIdAndName(roomId, name);
+	}
+
+	public List<ContentGroup> getByRoomId(final String roomId) {
+		return contentGroupRepository.findByRoomId(roomId);
+	}
+
+	public void addContentToGroup(final String roomId, final String groupName, final String contentId) {
+		ContentGroup contentGroup = getByRoomIdAndName(roomId, groupName);
+		if (contentGroup == null) {
+			contentGroup = new ContentGroup(roomId, groupName);
+			contentGroup.getContentIds().add(contentId);
+			create(contentGroup);
+		} else {
+			contentGroup.getContentIds().add(contentId);
+			update(contentGroup);
+		}
+	}
+
+	public void updateContentGroup(final ContentGroup contentGroup) {
+		if (contentGroup.getContentIds().isEmpty()) {
+			delete(contentGroup);
+		} else {
+			final Set<String> contentIds = StreamSupport.stream(
+					contentService.get(contentGroup.getContentIds()).spliterator(), false)
+						.filter(c -> c.getRoomId().equals(contentGroup.getRoomId()))
+						.map(Content::getId).collect(Collectors.toSet());
+			update(contentGroup);
+		}
+	}
+
+	@EventListener
+	@Secured({"ROLE_USER", "RUN_AS_SYSTEM"})
+	public void handleRoomDeletion(final BeforeDeletionEvent<Room> event) {
+		final Iterable<ContentGroup> contentGroups = contentGroupRepository.findByRoomId(event.getEntity().getId());
+		delete(contentGroups);
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
index 4321993f857e4ae52f833db4449a0190795e0d6d..898cc636cbe03196912339f59a260d666b1fe67e 100644
--- a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,8 +41,8 @@ import org.springframework.validation.Validator;
 
 import de.thm.arsnova.event.BeforeDeletionEvent;
 import de.thm.arsnova.model.Content;
+import de.thm.arsnova.model.ContentGroup;
 import de.thm.arsnova.model.Room;
-import de.thm.arsnova.model.Room.ContentGroup;
 import de.thm.arsnova.persistence.AnswerRepository;
 import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.LogEntryRepository;
@@ -64,6 +63,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	private ContentRepository contentRepository;
 
+	private ContentGroupService contentGroupService;
+
 	private AnswerService answerService;
 
 	private AnswerRepository answerRepository;
@@ -92,16 +93,17 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		this.answerService = answerService;
 	}
 
+	@Autowired
+	public void setContentGroupService(final ContentGroupService contentGroupService) {
+		this.contentGroupService = contentGroupService;
+	}
+
 	@Override
 	protected void modifyRetrieved(final Content content) {
 		if (content.getFormat() != Content.Format.TEXT && 0 == content.getState().getRound()) {
 			/* needed for legacy questions whose piRound property has not been set */
 			content.getState().setRound(1);
 		}
-
-		final Room room = roomService.get(content.getRoomId());
-		content.setGroups(room.getContentGroups().stream()
-				.map(Room.ContentGroup::getName).filter(g -> !g.isEmpty()).collect(Collectors.toSet()));
 	}
 
 	/* FIXME: caching */
@@ -120,13 +122,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public Iterable<Content> getByRoomIdAndGroup(final String roomId, final String group) {
-		final Room room = roomService.get(roomId);
-		Room.ContentGroup contentGroup = null;
-		for (final Room.ContentGroup cg : room.getContentGroups()) {
-			if (cg.getName().equals(group)) {
-				contentGroup = cg;
-			}
-		}
+		final ContentGroup contentGroup = contentGroupService.getByRoomIdAndName(roomId, group);
+
 		if (contentGroup == null) {
 			throw new NotFoundException("Content group does not exist.");
 		}
@@ -142,13 +139,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	public int countByRoomIdAndGroup(final String roomId, final String group) {
-		final Room room = roomService.get(roomId);
-		Room.ContentGroup contentGroup = null;
-		for (final Room.ContentGroup cg : room.getContentGroups()) {
-			if (cg.getName().equals(group)) {
-				contentGroup = cg;
-			}
-		}
+		final ContentGroup contentGroup = contentGroupService.getByRoomIdAndName(roomId, group);
+
 		if (contentGroup == null) {
 			throw new NotFoundException("Content group does not exist.");
 		}
@@ -179,22 +171,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		*/
 	}
 
-	@Override
-	protected void finalizeCreate(final Content content) {
-		/* Update content groups of room */
-		final Room room = roomService.get(content.getRoomId());
-		final Map<String, Room.ContentGroup> groups = room.getContentGroupsAsMap();
-		for (final String groupName : content.getGroups()) {
-			final Room.ContentGroup group = groups.getOrDefault(groupName, new Room.ContentGroup());
-			groups.put(groupName, group);
-			group.getContentIds().add(content.getId());
-			group.setName(groupName);
-			group.setAutoSort(true);
-		}
-		room.setContentGroupsFromMap(groups);
-		roomService.update(room);
-	}
-
 	@Override
 	protected void prepareUpdate(final Content content) {
 		final User user = userService.getCurrentUser();
@@ -220,25 +196,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	protected void finalizeUpdate(final Content content) {
-		/* Update content groups of room */
-		final Room room = roomService.get(content.getRoomId());
-		final Set<String> contentsGroupNames = content.getGroups();
-		final Set<String> allGroupNames = new HashSet<>(contentsGroupNames);
-		final Map<String, Room.ContentGroup> groups = room.getContentGroupsAsMap();
-		allGroupNames.addAll(groups.keySet());
-		for (final String groupName : allGroupNames) {
-			final Room.ContentGroup group = groups.getOrDefault(groupName, new Room.ContentGroup());
-			if (contentsGroupNames.contains(groupName)) {
-				group.getContentIds().add(content.getId());
-				group.setName(groupName);
-				group.setAutoSort(true);
-			} else {
-				group.getContentIds().remove(content.getId());
-			}
-		}
-		room.setContentGroupsFromMap(groups);
-		roomService.update(room);
-
 		/* TODO: not sure yet how to refactor this code - we need access to the old and new entity
 		if (!oldContent.getState().isVisible() && content.getState().isVisible()) {
 			final UnlockQuestionEvent event = new UnlockQuestionEvent(this, room, content);
@@ -252,11 +209,18 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	protected void prepareDelete(final Content content) {
-		final Room room = roomService.get(content.getRoomId());
-		for (final ContentGroup group : room.getContentGroups()) {
-			group.getContentIds().remove(content.getId());
+		final List<ContentGroup> contentGroups = contentGroupService.getByRoomId(content.getRoomId());
+		for (final ContentGroup contentGroup : contentGroups) {
+			final Set<String> ids = contentGroup.getContentIds();
+			if (ids.contains(content.getId())) {
+				ids.remove(content.getId());
+				if (!ids.isEmpty()) {
+					contentGroupService.update(contentGroup);
+				} else {
+					contentGroupService.delete(contentGroup);
+				}
+			}
 		}
-		roomService.update(room);
 	}
 
 	@Override
@@ -275,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
@@ -394,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
@@ -406,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
@@ -458,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));
 	}
 
@@ -469,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));
 	}
 
@@ -482,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/java/de/thm/arsnova/service/RoomServiceImpl.java b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
index 0ddfa42fb2372025da2f94d915b35e1b9b7dcd2a..2ea4ae30ee8cbeec857bb80c8dc5393c4750fab0 100644
--- a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
@@ -23,9 +23,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import org.slf4j.Logger;
@@ -167,32 +165,6 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 		}
 	}
 
-	/**
-	 * Adds a default content group with contents that have no other content group assigned.
-	 *
-	 * @param room The Room to be modified
-	 */
-	@Override
-	protected void modifyRetrieved(final Room room) {
-		// creates a set from all room content groups
-		final Set<String> cidsWithGroup = room.getContentGroups().stream()
-				.map(Room.ContentGroup::getContentIds)
-				.flatMap(ids -> ids.stream())
-				.collect(Collectors.toSet());
-
-		final Set<String> cids = new HashSet<>(contentRepository.findIdsByRoomId(room.getId()));
-		cids.removeAll(cidsWithGroup);
-
-		if (!cids.isEmpty()) {
-			final Set<Room.ContentGroup> cgs = room.getContentGroups();
-			final Room.ContentGroup defaultGroup = new Room.ContentGroup();
-			defaultGroup.setContentIds(cids);
-			defaultGroup.setAutoSort(true);
-			defaultGroup.setName("");
-			cgs.add(defaultGroup);
-		}
-	}
-
 	@Override
 	public Room join(final String id, final UUID socketId) {
 		final Room room = null != id ? get(id) : null;
diff --git a/src/main/resources/couchdb/Answer.design.js b/src/main/resources/couchdb/Answer.design.js
index 3e89d47b19c6d1875d7b4d134198f78259ad42c4..ba5814d6a4f8bcbb205408a2394dffb3d608622d 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 5b09c23f6fe15cb03a481f2c737ee23d54f8a0d3..82b773ef68c20dbd1c153c19bc20b6ce0c3c812c 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"
diff --git a/src/main/resources/couchdb/ContentGroup.design.js b/src/main/resources/couchdb/ContentGroup.design.js
new file mode 100644
index 0000000000000000000000000000000000000000..8592b2926ba3da698b9aa6325bf68dcc79a6e1b4
--- /dev/null
+++ b/src/main/resources/couchdb/ContentGroup.design.js
@@ -0,0 +1,22 @@
+var designDoc = {
+	"_id": "_design/ContentGroup",
+	"language": "javascript",
+	"views": {
+		"by_id": {
+			"map": function (doc) {
+				if (doc.type === "ContentGroup") {
+					emit(doc._id, {_rev: doc._rev});
+				}
+			},
+			"reduce": "_count"
+		},
+		"by_roomid_name": {
+			"map": function (doc) {
+				if (doc.type === "ContentGroup") {
+					emit([doc.roomId, doc.name], {_rev: doc._rev});
+				}
+			},
+			"reduce": "_count"
+		}
+	}
+};
diff --git a/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java b/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java
index 92e5af312794a0aefe659063d20e5be2425739bf..89974031aa94c8e7172f0a4b168cbf546f2ef9a4 100644
--- a/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java
+++ b/src/test/java/de/thm/arsnova/config/TestPersistanceConfig.java
@@ -26,6 +26,7 @@ import org.springframework.context.annotation.Profile;
 import de.thm.arsnova.persistence.AnswerRepository;
 import de.thm.arsnova.persistence.AttachmentRepository;
 import de.thm.arsnova.persistence.CommentRepository;
+import de.thm.arsnova.persistence.ContentGroupRepository;
 import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.LogEntryRepository;
 import de.thm.arsnova.persistence.MotdRepository;
@@ -62,6 +63,11 @@ public class TestPersistanceConfig {
 		return Mockito.mock(ContentRepository.class);
 	}
 
+	@Bean
+	public ContentGroupRepository contentGroupRepository() {
+		return Mockito.mock(ContentGroupRepository.class);
+	}
+
 	@Bean
 	public AnswerRepository answerRepository() {
 		return Mockito.mock(AnswerRepository.class);