diff --git a/model/src/main/java/lcsb/mapviewer/model/map/Comment.java b/model/src/main/java/lcsb/mapviewer/model/map/Comment.java
index be21199109311f34686493928f6bcb1367f73ed3..fac60ec25baf83375e1897becb406d23c16b7d1f 100644
--- a/model/src/main/java/lcsb/mapviewer/model/map/Comment.java
+++ b/model/src/main/java/lcsb/mapviewer/model/map/Comment.java
@@ -1,6 +1,16 @@
 package lcsb.mapviewer.model.map;
 
-import java.awt.geom.Point2D;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import lcsb.mapviewer.model.MinervaEntity;
+import lcsb.mapviewer.model.map.model.Model;
+import lcsb.mapviewer.model.map.model.ModelData;
+import lcsb.mapviewer.model.map.reaction.Reaction;
+import lcsb.mapviewer.model.map.species.Element;
+import lcsb.mapviewer.model.user.User;
+import lcsb.mapviewer.modelutils.serializer.model.map.ModelAsIdSerializer;
+import org.hibernate.annotations.Type;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -10,32 +20,18 @@ import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.ManyToOne;
 import javax.persistence.Version;
-
-import org.hibernate.annotations.Type;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-import lcsb.mapviewer.model.MinervaEntity;
-import lcsb.mapviewer.model.map.model.Model;
-import lcsb.mapviewer.model.map.model.ModelData;
-import lcsb.mapviewer.model.map.reaction.Reaction;
-import lcsb.mapviewer.model.map.species.Element;
-import lcsb.mapviewer.model.user.User;
-import lcsb.mapviewer.modelutils.serializer.model.map.CommentSerializer;
+import java.awt.geom.Point2D;
 
 /**
  * Class representing comments on the map.
- * 
+ *
  * @author Piotr Gawron
- * 
  */
 @Entity
-@JsonSerialize(using = CommentSerializer.class)
 public class Comment implements MinervaEntity {
 
   /**
-   * 
+   *
    */
   private static final long serialVersionUID = 1L;
 
@@ -48,6 +44,7 @@ public class Comment implements MinervaEntity {
    * What was the reason of removal.
    */
   @Column(length = 255)
+  @JsonIgnore
   private String removeReason = "";
 
   /**
@@ -61,24 +58,28 @@ public class Comment implements MinervaEntity {
    * The model that was commented.
    */
   @ManyToOne(fetch = FetchType.LAZY, optional = false)
+  @JsonSerialize(using = ModelAsIdSerializer.class)
+  @JsonProperty(value = "map")
   private ModelData model;
 
   /**
    * User who gave the feedback (if logged in).
    */
   @ManyToOne(fetch = FetchType.LAZY)
+  @JsonIgnore
   private User user;
 
   /**
    * Email of the user who gave feedback.
    */
   @Column(length = 255)
+  @JsonIgnore
   private String email;
 
   /**
    * Content of the feedback.
    */
-  @Column(columnDefinition = "TEXT")
+  @Column(columnDefinition = "TEXT", nullable = false)
   private String content;
 
   /**
@@ -88,14 +89,17 @@ public class Comment implements MinervaEntity {
   private Point2D coordinates;
 
   @ManyToOne(optional = true)
+  @JsonIgnore
   private Reaction reaction;
 
   @ManyToOne(optional = true)
+  @JsonIgnore
   private Element element;
 
   /**
    * Determines if comment should be visible on the map.
    */
+  @JsonProperty(value = "visible")
   private boolean pinned = false;
 
   @Version
@@ -111,8 +115,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param coordinates
-   *          the coordinates to set
+   * @param coordinates the coordinates to set
    * @see #coordinates
    */
   public void setCoordinates(final Point2D coordinates) {
@@ -128,8 +131,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param content
-   *          the content to set
+   * @param content the content to set
    * @see #content
    */
   public void setContent(final String content) {
@@ -145,8 +147,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param email
-   *          the email to set
+   * @param email the email to set
    * @see #email
    */
   public void setEmail(final String email) {
@@ -162,8 +163,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param user
-   *          the user to set
+   * @param user the user to set
    * @see #user
    */
   public void setUser(final User user) {
@@ -174,13 +174,13 @@ public class Comment implements MinervaEntity {
    * @return the model
    * @see #model
    */
+  @JsonIgnore
   public ModelData getModelData() {
     return model;
   }
 
   /**
-   * @param model
-   *          the model to set
+   * @param model the model to set
    * @see #model
    */
   public void setModelData(final ModelData model) {
@@ -197,8 +197,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param id
-   *          the id to set
+   * @param id the id to set
    * @see #id
    */
   public void setId(final int id) {
@@ -214,8 +213,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param removeReason
-   *          the removeReason to set
+   * @param removeReason the removeReason to set
    * @see #removeReason
    */
   public void setRemoveReason(final String removeReason) {
@@ -231,8 +229,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param deleted
-   *          the deleted to set
+   * @param deleted the deleted to set
    * @see #deleted
    */
   public void setDeleted(final boolean deleted) {
@@ -248,8 +245,7 @@ public class Comment implements MinervaEntity {
   }
 
   /**
-   * @param pinned
-   *          the pinned to set
+   * @param pinned the pinned to set
    * @see #pinned
    */
   public void setPinned(final boolean pinned) {
@@ -258,9 +254,8 @@ public class Comment implements MinervaEntity {
 
   /**
    * Sets model.
-   * 
-   * @param model2
-   *          model to set
+   *
+   * @param model2 model to set
    */
   public void setModel(final Model model2) {
     this.model = model2.getModelData();
diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentDao.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentDao.java
index d807fa424966ea403a352b712ae6240c70091e7d..b330b7af5841419604c1919c2136a8770e6b1895 100644
--- a/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentDao.java
+++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentDao.java
@@ -1,24 +1,29 @@
 package lcsb.mapviewer.persist.dao.map;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.stereotype.Repository;
-
+import lcsb.mapviewer.common.exception.InvalidArgumentException;
+import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.model.map.model.Author;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelData;
 import lcsb.mapviewer.persist.dao.BaseDao;
-import lcsb.mapviewer.persist.dao.MinervaEntityProperty;
+import org.springframework.stereotype.Repository;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Data access object class for Comment objects.
- * 
+ *
  * @author Piotr Gawron
- * 
  */
 @Repository
-public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>> {
+public class CommentDao extends BaseDao<Comment, CommentProperty> {
 
   /**
    * Default constructor.
@@ -30,23 +35,20 @@ public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>>
   /**
    * Returns list of comments for a model given in the parameter. The comments
    * are limited by the parameters.
-   * 
-   * @param model
-   *          in which model we want to find comments
-   * @param pinned
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not visible on the map</li>
-   *          <li>true - comments that are visible on the map</li>
-   *          </ul>
-   * @param deleted
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not deleted</li>
-   *          <li>true - comments that are deleted</li>
-   *          </ul>
+   *
+   * @param model   in which model we want to find comments
+   * @param pinned  this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not visible on the map</li>
+   *                <li>true - comments that are visible on the map</li>
+   *                </ul>
+   * @param deleted this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not deleted</li>
+   *                <li>true - comments that are deleted</li>
+   *                </ul>
    * @return list of comments that fulfill parameters constraints
    */
   public List<Comment> getCommentByModel(final Model model, final Boolean pinned, final Boolean deleted) {
@@ -56,28 +58,25 @@ public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>>
   /**
    * Returns list of comments for a model given in the parameter. The comments
    * are limited by the parameters.
-   * 
-   * @param modelId
-   *          in which model we want to find comments
-   * @param pinned
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not visible on the map</li>
-   *          <li>true - comments that are visible on the map</li>
-   *          </ul>
-   * @param deleted
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not deleted</li>
-   *          <li>true - comments that are deleted</li>
-   *          </ul>
+   *
+   * @param modelId in which model we want to find comments
+   * @param pinned  this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not visible on the map</li>
+   *                <li>true - comments that are visible on the map</li>
+   *                </ul>
+   * @param deleted this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not deleted</li>
+   *                <li>true - comments that are deleted</li>
+   *                </ul>
    * @return list of comments that fulfill parameters constraints
    */
   private List<Comment> getCommentByModel(final int modelId, final Boolean pinned, final Boolean deleted) {
     List<Comment> comments = getElementsByParameter("model_id", modelId);
-    List<Comment> result = new ArrayList<Comment>();
+    List<Comment> result = new ArrayList<>();
     if (deleted != null) {
       for (final Comment comment : comments) {
         if (comment.isDeleted() == deleted) {
@@ -92,7 +91,7 @@ public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>>
       return result;
     }
     comments = result;
-    result = new ArrayList<Comment>();
+    result = new ArrayList<>();
     for (final Comment comment : comments) {
       if (comment.isPinned() == pinned) {
         result.add(comment);
@@ -104,23 +103,20 @@ public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>>
   /**
    * Returns list of comments for a model given in the parameter. The comments
    * are limited by the parameters.
-   * 
-   * @param model
-   *          in which model we want to find comments
-   * @param pinned
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not visible on the map</li>
-   *          <li>true - comments that are visible on the map</li>
-   *          </ul>
-   * @param deleted
-   *          this parameter defines what kind of comments we want to get:
-   *          <ul>
-   *          <li>null - all comments</li>
-   *          <li>false - comments that are not deleted</li>
-   *          <li>true - comments that are deleted</li>
-   *          </ul>
+   *
+   * @param model   in which model we want to find comments
+   * @param pinned  this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not visible on the map</li>
+   *                <li>true - comments that are visible on the map</li>
+   *                </ul>
+   * @param deleted this parameter defines what kind of comments we want to get:
+   *                <ul>
+   *                <li>null - all comments</li>
+   *                <li>false - comments that are not deleted</li>
+   *                <li>true - comments that are deleted</li>
+   *                </ul>
    * @return list of comments that fulfill parameters constraints
    */
   public List<Comment> getCommentByModel(final ModelData model, final Boolean pinned, final Boolean deleted) {
@@ -137,10 +133,40 @@ public class CommentDao extends BaseDao<Comment, MinervaEntityProperty<Comment>>
         .setParameter("project_id", projectId)
         .setParameter("id", id)
         .list();
-    if (list.size() > 0) {
+    if (!list.isEmpty()) {
       return list.get(0);
     }
     return null;
   }
 
+  protected Predicate createPredicate(final Map<CommentProperty, Object> filterOptions, final Root<Comment> root) {
+    CriteriaBuilder builder = getSession().getCriteriaBuilder();
+    List<Predicate> predicates = new ArrayList<>();
+
+    for (CommentProperty key : filterOptions.keySet()) {
+      if (key.equals(CommentProperty.PROJECT_ID)) {
+        Join<Author, ModelData> modelJoin = root.join("model");
+        Join<ModelData, Project> projectJoin = modelJoin.join("project");
+
+        Predicate predicate = builder.and(
+            builder.equal(projectJoin.get("projectId"), filterOptions.get(key)));
+        predicates.add(predicate);
+      } else if (key.equals(CommentProperty.MAP_ID)) {
+        Join<Author, ModelData> modelJoin = root.join("model");
+
+        Predicate predicate = builder.and(
+            builder.equal(modelJoin.get("id"), filterOptions.get(key)));
+        predicates.add(predicate);
+      } else if (key.equals(CommentProperty.ID)) {
+
+        Predicate predicate = builder.and(
+            builder.equal(root.get("id"), filterOptions.get(key)));
+        predicates.add(predicate);
+      } else {
+        throw new InvalidArgumentException("Unknown property: " + key);
+      }
+    }
+    return builder.and(predicates.toArray(new Predicate[0]));
+
+  }
 }
diff --git a/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentProperty.java b/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentProperty.java
new file mode 100644
index 0000000000000000000000000000000000000000..83e70ceb0b7450dfb847b2d34680ca211a81620f
--- /dev/null
+++ b/persist/src/main/java/lcsb/mapviewer/persist/dao/map/CommentProperty.java
@@ -0,0 +1,12 @@
+package lcsb.mapviewer.persist.dao.map;
+
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.persist.dao.MinervaEntityProperty;
+
+public enum CommentProperty implements MinervaEntityProperty<Comment> {
+  PROJECT_ID,
+  MAP_ID,
+  ID,
+  DELETED,
+  VISIBLE,
+}
diff --git a/persist/src/test/java/lcsb/mapviewer/persist/PersistTestFunctionsNoTransaction.java b/persist/src/test/java/lcsb/mapviewer/persist/PersistTestFunctionsNoTransaction.java
index 180be3fee5ab08cf7dd32142341ac393be2817e9..16895ca649e331bd321237789117fd958fd4570f 100644
--- a/persist/src/test/java/lcsb/mapviewer/persist/PersistTestFunctionsNoTransaction.java
+++ b/persist/src/test/java/lcsb/mapviewer/persist/PersistTestFunctionsNoTransaction.java
@@ -228,6 +228,8 @@ public abstract class PersistTestFunctionsNoTransaction extends TestUtils {
   protected Comment createComment(final Model model) {
     final Comment result = new Comment();
     result.setModel(model);
+    result.setUser(model.getProject().getOwner());
+    result.setContent("test content");
     return result;
   }
 
diff --git a/persist/src/test/java/lcsb/mapviewer/persist/dao/map/CommentDaoTest.java b/persist/src/test/java/lcsb/mapviewer/persist/dao/map/CommentDaoTest.java
index a0453a3f9e03b7409ac5080d86bf8da504b85bc8..321df48b231e98a516970b7cdc7f70d37439948e 100644
--- a/persist/src/test/java/lcsb/mapviewer/persist/dao/map/CommentDaoTest.java
+++ b/persist/src/test/java/lcsb/mapviewer/persist/dao/map/CommentDaoTest.java
@@ -1,13 +1,5 @@
 package lcsb.mapviewer.persist.dao.map;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.Comment;
 import lcsb.mapviewer.model.map.MiriamData;
@@ -26,6 +18,13 @@ import lcsb.mapviewer.model.map.species.field.Residue;
 import lcsb.mapviewer.persist.PersistTestFunctions;
 import lcsb.mapviewer.persist.dao.ProjectDao;
 import lcsb.mapviewer.persist.dao.user.UserDao;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
 public class CommentDaoTest extends PersistTestFunctions {
 
@@ -53,15 +52,14 @@ public class CommentDaoTest extends PersistTestFunctions {
     projectDao.add(project);
     return project;
   }
-  
+
   @Test
   public void testGetById() throws Exception {
     Project project = createProject();
 
+
     int counter = (int) commentDao.getCount();
-    Comment comment = new Comment();
-    comment.setUser(project.getOwner());
-    comment.setModel(project.getTopModel());
+    Comment comment = createComment(project.getTopModel());
     commentDao.add(comment);
     int counter2 = (int) commentDao.getCount();
     assertEquals(counter + 1, counter2);
@@ -74,18 +72,16 @@ public class CommentDaoTest extends PersistTestFunctions {
   public void testGetComments() {
     Project project = createProject();
 
-    Comment comment = new Comment();
+    Comment comment = createComment(project.getTopModel());
     comment.setDeleted(true);
-    comment.setModel(project.getTopModel());
     commentDao.add(comment);
 
-    Comment comment2 = new Comment();
+    Comment comment2 = createComment(project.getTopModel());
     comment2.setPinned(true);
-    comment2.setModel(project.getTopModel());
     commentDao.add(comment2);
 
     Model model = project.getTopModel();
-    
+
     assertEquals(0, commentDao.getCommentByModel(model, true, true).size());
     assertEquals(1, commentDao.getCommentByModel(model, false, true).size());
     assertEquals(1, commentDao.getCommentByModel(model, null, true).size());
diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java b/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
index 831426504c3f1f0831d09d8f922da2f9cc23f6ca..55f7f8fac1b0e90462d3f341d4a2335e15cf96e6 100644
--- a/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
+++ b/service/src/main/java/lcsb/mapviewer/services/impl/CommentService.java
@@ -1,17 +1,6 @@
 package lcsb.mapviewer.services.impl;
 
-import java.awt.geom.Point2D;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.hibernate.Hibernate;
-import org.hibernate.HibernateException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
+import lcsb.mapviewer.common.exception.NotImplementedException;
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.BioEntity;
 import lcsb.mapviewer.model.map.Comment;
@@ -21,6 +10,7 @@ import lcsb.mapviewer.model.map.reaction.Reaction;
 import lcsb.mapviewer.model.map.species.Element;
 import lcsb.mapviewer.model.user.User;
 import lcsb.mapviewer.persist.dao.map.CommentDao;
+import lcsb.mapviewer.persist.dao.map.CommentProperty;
 import lcsb.mapviewer.persist.dao.map.ModelDao;
 import lcsb.mapviewer.services.ObjectNotFoundException;
 import lcsb.mapviewer.services.interfaces.ICommentService;
@@ -29,13 +19,26 @@ import lcsb.mapviewer.services.interfaces.IModelService;
 import lcsb.mapviewer.services.interfaces.IUserService;
 import lcsb.mapviewer.services.utils.EmailException;
 import lcsb.mapviewer.services.utils.EmailSender;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * This class is responsible for all services connected with comments
  * functionality.
  *
  * @author Piotr Gawron
- *
  */
 
 @Transactional
@@ -51,14 +54,14 @@ public class CommentService implements ICommentService {
   /**
    * Default class logger.
    */
-  private static Logger logger = LogManager.getLogger();
+  private static final Logger logger = LogManager.getLogger();
 
   /**
    * Data access object fir comments.
    */
   private CommentDao commentDao;
 
-  private ModelDao mapDao;
+  private final ModelDao mapDao;
 
   /**
    * Service for operations on Model object.
@@ -77,10 +80,10 @@ public class CommentService implements ICommentService {
 
   @Autowired
   public CommentService(final CommentDao commentDao,
-      final IModelService modelService,
-      final IUserService userService,
-      final IConfigurationService configurationService,
-      final ModelDao mapDao) {
+                        final IModelService modelService,
+                        final IUserService userService,
+                        final IConfigurationService configurationService,
+                        final ModelDao mapDao) {
     this.commentDao = commentDao;
     this.modelService = modelService;
     this.userService = userService;
@@ -90,7 +93,7 @@ public class CommentService implements ICommentService {
 
   @Override
   public Comment addComment(final String email, final String content, final Point2D coordinates, final BioEntity object,
-      final boolean pinned, final int mapId, final User owner) {
+                            final boolean pinned, final int mapId, final User owner) {
     ModelData map = mapDao.getById(mapId);
     Comment comment = new Comment();
     comment.setEmail(email);
@@ -180,10 +183,11 @@ public class CommentService implements ICommentService {
 
   @Override
   public Comment getCommentById(final String projectId, final String commentId) {
-    int id = -1;
+    int id;
     try {
       id = Integer.parseInt(commentId);
     } catch (final NumberFormatException e) {
+      id = -1;
     }
     return getCommentById(projectId, id);
   }
@@ -202,8 +206,7 @@ public class CommentService implements ICommentService {
   }
 
   /**
-   * @param commentDao
-   *          the commentDao to set
+   * @param commentDao the commentDao to set
    * @see #commentDao
    */
   public void setCommentDao(final CommentDao commentDao) {
@@ -219,8 +222,7 @@ public class CommentService implements ICommentService {
   }
 
   /**
-   * @param modelService
-   *          the modelService to set
+   * @param modelService the modelService to set
    * @see #modelService
    */
   public void setModelService(final IModelService modelService) {
@@ -236,8 +238,7 @@ public class CommentService implements ICommentService {
   }
 
   /**
-   * @param userService
-   *          the userService to set
+   * @param userService the userService to set
    * @see #userService
    */
   public void setUserService(final IUserService userService) {
@@ -253,8 +254,7 @@ public class CommentService implements ICommentService {
   }
 
   /**
-   * @param configurationService
-   *          the configurationService to set
+   * @param configurationService the configurationService to set
    * @see #configurationService
    */
   public void setConfigurationService(final IConfigurationService configurationService) {
@@ -264,12 +264,9 @@ public class CommentService implements ICommentService {
   /**
    * Checks if identifier of the points refer to the same point.
    *
-   * @param uniqueId
-   *          identifier assigned by the java code
-   * @param objectId
-   *          identifier assigned by the javascript code (browser)
-   * @return <code>true</code> if both identifiers refer to the same point,
-   *         <code>false</code> otherwise
+   * @param uniqueId identifier assigned by the java code
+   * @param objectId identifier assigned by the javascript code (browser)
+   * @return <code>true</code> if both identifiers refer to the same point,  <code>false</code> otherwise
    */
   protected boolean equalPoints(final String uniqueId, final String objectId) {
     String tmp = uniqueId.substring(uniqueId.indexOf("[") + 1, uniqueId.indexOf("]"));
@@ -292,14 +289,43 @@ public class CommentService implements ICommentService {
     if (comment == null || comment.getUser() == null) {
       return null;
     }
-    // fetch lazy data
-    comment.getUser().getLogin();
+    Hibernate.initialize(comment.getUser());
     return comment.getUser();
   }
 
   @Override
-  public Comment add(final Comment comment) {
-    return commentDao.add(comment);
+  public Page<Comment> getAll(final Map<CommentProperty, Object> filter, final Pageable pageable) {
+    Page<Comment> page = commentDao.getAll(filter, pageable);
+    for (Comment comment : page.getContent()) {
+      Hibernate.initialize(comment.getUser());
+    }
+    return page;
+  }
+
+  @Override
+  public Comment getById(final int id) {
+    return commentDao.getById(id);
+  }
+
+  @Override
+  public void update(final Comment object) {
+    throw new NotImplementedException();
+  }
+
+  @Override
+  public void remove(final Comment comment) {
+    comment.setDeleted(true);
+    commentDao.update(comment);
+  }
+
+  @Override
+  public long getCount(final Map<CommentProperty, Object> filterOptions) {
+    throw new NotImplementedException();
+  }
+
+  @Override
+  public void add(final Comment comment) {
+    commentDao.add(comment);
   }
 
 }
diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ModelService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ModelService.java
index 7cd68efd59887e4eb2e37fcfea6fd487a6772230..8d4fab3fae365bb56d056d33e9bc9b76b2c9f826 100644
--- a/service/src/main/java/lcsb/mapviewer/services/impl/ModelService.java
+++ b/service/src/main/java/lcsb/mapviewer/services/impl/ModelService.java
@@ -1,6 +1,5 @@
 package lcsb.mapviewer.services.impl;
 
-import lcsb.mapviewer.common.exception.NotImplementedException;
 import lcsb.mapviewer.model.map.MiriamType;
 import lcsb.mapviewer.model.map.compartment.Compartment;
 import lcsb.mapviewer.model.map.model.Model;
@@ -158,7 +157,7 @@ public class ModelService implements IModelService {
 
   @Override
   public ModelData getById(final int id) {
-    throw new NotImplementedException();
+    return modelDao.getById(id);
   }
 
   @Override
diff --git a/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java b/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
index 15488ea297b1bab08542c3f51cd3bef8aa57c16a..c93b7fffa9b0d8bfc29a60ec365a4a0994455202 100644
--- a/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
+++ b/service/src/main/java/lcsb/mapviewer/services/interfaces/ICommentService.java
@@ -1,57 +1,46 @@
 package lcsb.mapviewer.services.interfaces;
 
-import java.awt.geom.Point2D;
-import java.util.List;
-
-import org.hibernate.HibernateException;
-
 import lcsb.mapviewer.model.Project;
 import lcsb.mapviewer.model.map.BioEntity;
 import lcsb.mapviewer.model.map.Comment;
 import lcsb.mapviewer.model.map.model.Model;
 import lcsb.mapviewer.model.map.model.ModelData;
 import lcsb.mapviewer.model.user.User;
+import lcsb.mapviewer.persist.dao.map.CommentProperty;
 import lcsb.mapviewer.services.ObjectNotFoundException;
+import org.hibernate.HibernateException;
+
+import java.awt.geom.Point2D;
+import java.util.List;
 
 /**
  * Service responsible for comments functionality.
- * 
+ *
  * @author Piotr Gawron
- * 
  */
-public interface ICommentService {
+public interface ICommentService extends CrudService<Comment, CommentProperty> {
   /**
    * Adds comment to the map.
-   * 
-   * @param pinned
-   *          parameter that defines if comment should be visible on the map.
-   * @param bioEntity
-   *          determines which object is commented - it could be null; in this
-   *          case it means that a comment is general one.
-   * @param email
-   *          email of the person that comments
-   * @param content
-   *          content of the comment
-   * @param mapId
-   *          on which submodel we comment on
-   * @param coordinates
-   *          where exactly the comment should be placed on
-   * 
+   *
+   * @param pinned      parameter that defines if comment should be visible on the map.
+   * @param bioEntity   determines which object is commented - it could be null; in this
+   *                    case it means that a comment is general one.
+   * @param email       email of the person that comments
+   * @param content     content of the comment
+   * @param mapId       on which submodel we comment on
+   * @param coordinates where exactly the comment should be placed on
    * @return comment object created based on the data provided as parameters
    */
   Comment addComment(final String email, final String content, final Point2D coordinates, final BioEntity bioEntity,
-      final boolean pinned, final int mapId, final User owner);
+                     final boolean pinned, final int mapId, final User owner);
 
   /**
    * This method remove comment. Comment is not removed from the system, only
    * 'deleted' flag is set.
-   * 
-   * @param loggedUser
-   *          user that wants to remove the comment
-   * @param commentId
-   *          identifier of the comment that user wants to remove
-   * @param reason
-   *          why user wants to remove the comment
+   *
+   * @param loggedUser user that wants to remove the comment
+   * @param commentId  identifier of the comment that user wants to remove
+   * @param reason     why user wants to remove the comment
    */
   void deleteComment(final User loggedUser, final String commentId, final String reason);
 
@@ -61,24 +50,22 @@ public interface ICommentService {
 
   /**
    * Returns number of comments in the system.
-   * 
+   *
    * @return number of comments in the system
    */
   long getCommentCount();
 
   /**
    * Removes comments from the model.
-   * 
-   * @param model
-   *          from which model we want to remove comments
+   *
+   * @param model from which model we want to remove comments
    */
   void removeCommentsForModel(final Model model);
 
   /**
    * Removes comments from the model.
-   * 
-   * @param model
-   *          from which model we want to remove comments
+   *
+   * @param model from which model we want to remove comments
    */
   void removeCommentsForModel(final ModelData model);
 
@@ -87,7 +74,4 @@ public interface ICommentService {
   Comment getCommentById(final String projectId, final Integer commentId);
 
   User getOwnerByCommentId(final String projectId, final String commentId);
-
-  Comment add(Comment comment);
-
 }
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalController.java b/web/src/main/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalController.java
index 5924f0f832470eaca1f600bb6170774aace6d187..96c2d61c8140c07649146d13a173176f6a63c63d 100644
--- a/web/src/main/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalController.java
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalController.java
@@ -2,8 +2,10 @@ package lcsb.mapviewer.web.api.project.chemicals;
 
 import lcsb.mapviewer.annotation.data.Chemical;
 import lcsb.mapviewer.annotation.data.MeSH;
+import lcsb.mapviewer.annotation.services.DrugSearchException;
 import lcsb.mapviewer.annotation.services.TaxonomyBackend;
 import lcsb.mapviewer.annotation.services.annotators.AnnotatorException;
+import lcsb.mapviewer.annotation.services.dapi.ChemicalSearchException;
 import lcsb.mapviewer.api.BaseController;
 import lcsb.mapviewer.api.QueryException;
 import lcsb.mapviewer.common.Pair;
@@ -82,7 +84,7 @@ public class NewChemicalController extends BaseController {
     }
 
     List<Chemical> data = new ArrayList<>();
-    if (!query.equals("")) {
+    if (!query.isEmpty()) {
 
       MiriamData organism = project.getOrganism();
       if (organism == null) {
@@ -158,4 +160,15 @@ public class NewChemicalController extends BaseController {
     });
   }
 
+  @PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
+  @GetMapping(value = "chemicals/suggestedQueryList")
+  public List<String> getSuggestedQueryList(final @PathVariable(value = "projectId") String projectId)
+      throws DrugSearchException, ObjectNotFoundException, ChemicalSearchException {
+    final Project project = projectService.getProjectByProjectId(projectId);
+    if (project == null) {
+      throw new ObjectNotFoundException("Project with given id doesn't exist");
+    }
+    return chemicalService.getSuggestedQueryList(project, project.getDisease());
+  }
+
 }
\ No newline at end of file
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/drugs/NewDrugController.java b/web/src/main/java/lcsb/mapviewer/web/api/project/drugs/NewDrugController.java
index 17806b389542251b117dba7b4fbc567b97724d46..585716d533a2992245a7f0c5ceab0727441e874f 100644
--- a/web/src/main/java/lcsb/mapviewer/web/api/project/drugs/NewDrugController.java
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/drugs/NewDrugController.java
@@ -1,6 +1,7 @@
 package lcsb.mapviewer.web.api.project.drugs;
 
 import lcsb.mapviewer.annotation.data.Drug;
+import lcsb.mapviewer.annotation.services.DrugSearchException;
 import lcsb.mapviewer.annotation.services.TaxonomyBackend;
 import lcsb.mapviewer.api.BaseController;
 import lcsb.mapviewer.api.QueryException;
@@ -67,7 +68,7 @@ public class NewDrugController extends BaseController {
     if (project == null) {
       throw new ObjectNotFoundException("Project with given id doesn't exist");
     }
-    if (!query.equals("")) {
+    if (!query.isEmpty()) {
 
       MiriamData organism = project.getOrganism();
       if (organism == null) {
@@ -96,7 +97,6 @@ public class NewDrugController extends BaseController {
 
   private List<Element> getTargets(final String targetType, final Project project, final Integer dbId)
       throws QueryException, ObjectNotFoundException {
-    final List<Element> targets = new ArrayList<>();
     if (targetType.equals(ElementIdentifierType.ALIAS.getJsName())) {
       final Map<ElementProperty, Object> properties = new HashMap<>();
       properties.put(ElementProperty.ID, Collections.singletonList(dbId));
@@ -105,11 +105,21 @@ public class NewDrugController extends BaseController {
       if (elements.getNumberOfElements() == 0) {
         throw new ObjectNotFoundException("Element does not exist");
       }
-      targets.addAll(elements.getContent());
+      return new ArrayList<>(elements.getContent());
     } else {
       throw new QueryException("Targeting for the type not implemented");
     }
-    return targets;
+  }
+
+  @PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
+  @GetMapping(value = "drugs/suggestedQueryList")
+  public List<String> getSuggestedQueryList(final @PathVariable(value = "projectId") String projectId)
+      throws DrugSearchException, ObjectNotFoundException {
+    final Project project = projectService.getProjectByProjectId(projectId);
+    if (project == null) {
+      throw new ObjectNotFoundException("Project with given id doesn't exist");
+    }
+    return drugService.getSuggestedQueryList(project, project.getOrganism());
   }
 
 }
\ No newline at end of file
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/AdminCommentResponseDecorator.java b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/AdminCommentResponseDecorator.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f4f0831707779ac00f1bcc4c72a188a7e00ddd0
--- /dev/null
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/AdminCommentResponseDecorator.java
@@ -0,0 +1,32 @@
+package lcsb.mapviewer.web.api.project.map.comment;
+
+import lcsb.mapviewer.common.Pair;
+import lcsb.mapviewer.model.MinervaEntity;
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.web.api.ResponseDecorator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AdminCommentResponseDecorator implements ResponseDecorator {
+  @Override
+  public List<Pair<String, Object>> getDecoratedValues(final MinervaEntity object) {
+    List<Pair<String, Object>> properties = new ArrayList<>();
+    if (object instanceof Comment) {
+      Comment comment = (Comment) object;
+      String author = null;
+      if (comment.getUser() != null) {
+        author = comment.getUser().getLogin();
+      }
+      properties.add(new Pair<>("author", author));
+      properties.add(new Pair<>("email", comment.getEmail()));
+      properties.add(new Pair<>("removeReason", comment.getRemoveReason()));
+    }
+    return properties;
+  }
+
+  @Override
+  public List<Pair<String, Object>> getDecoratedValuesForRaw(final Object object) {
+    return new ArrayList<>();
+  }
+}
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentController.java b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentController.java
new file mode 100644
index 0000000000000000000000000000000000000000..e657036201a982f373ad988a3bd41485a049dba7
--- /dev/null
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentController.java
@@ -0,0 +1,216 @@
+package lcsb.mapviewer.web.api.project.map.comment;
+
+import lcsb.mapviewer.api.BaseController;
+import lcsb.mapviewer.api.QueryException;
+import lcsb.mapviewer.common.Configuration;
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.model.map.model.ModelData;
+import lcsb.mapviewer.model.security.PrivilegeType;
+import lcsb.mapviewer.model.user.User;
+import lcsb.mapviewer.persist.dao.map.CommentProperty;
+import lcsb.mapviewer.services.ObjectNotFoundException;
+import lcsb.mapviewer.services.interfaces.ICommentService;
+import lcsb.mapviewer.services.interfaces.IModelService;
+import lcsb.mapviewer.services.interfaces.IUserService;
+import lcsb.mapviewer.web.api.NewApiResponseSerializer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.validation.annotation.Validated;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Pattern;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@Validated
+@RequestMapping(
+    value = {
+        "/minerva/new_api/projects/{projectId}/maps/{mapId}/comments",
+    },
+    produces = MediaType.APPLICATION_JSON_VALUE)
+public class NewCommentController extends BaseController {
+
+  private final IUserService userService;
+  private final IModelService modelService;
+  private final ICommentService commentService;
+  private final NewApiResponseSerializer serializer;
+
+  @Autowired
+  public NewCommentController(final IUserService userService,
+                              final IModelService modelService,
+                              final ICommentService commentService,
+                              final NewApiResponseSerializer serializer) {
+    this.userService = userService;
+    this.modelService = modelService;
+    this.commentService = commentService;
+    this.serializer = serializer;
+  }
+
+  @PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
+  @GetMapping
+  public ResponseEntity<?> getComments(
+      final Pageable pageable,
+      final Authentication authentication,
+      final @PathVariable(value = "projectId") String projectId,
+      final @Valid @Pattern(regexp = "^[-+]?\\d+$|^\\*$") @PathVariable(value = "mapId") String mapId,
+      final @RequestParam(value = "removed", required = false) Boolean removed) throws QueryException, ObjectNotFoundException {
+
+    final boolean isAdminView = checkAdminView(authentication, projectId);
+
+    Map<CommentProperty, Object> filterOptions = new HashMap<>();
+    filterOptions.put(CommentProperty.PROJECT_ID, projectId);
+    filterOptions.put(CommentProperty.MAP_ID, mapId);
+
+    if (removed != null) {
+      filterOptions.put(CommentProperty.DELETED, removed);
+    }
+    if (!isAdminView) {
+      filterOptions.put(CommentProperty.VISIBLE, true);
+    }
+
+    Page<Comment> page = commentService.getAll(filterOptions, pageable);
+
+    if (isAdminView) {
+      return serializer.prepareResponse(page, new AdminCommentResponseDecorator());
+    } else {
+      return serializer.prepareResponse(page);
+    }
+  }
+
+  private boolean checkAdminView(final Authentication authentication, final String projectId) {
+    final boolean isAdmin = authentication.getAuthorities()
+        .contains(new SimpleGrantedAuthority(PrivilegeType.IS_ADMIN.name()));
+    final boolean isProjectCurator = authentication.getAuthorities()
+        .contains(new SimpleGrantedAuthority(PrivilegeType.IS_CURATOR.name()))
+        && authentication.getAuthorities()
+        .contains(new SimpleGrantedAuthority(PrivilegeType.READ_PROJECT.name() + ":" + projectId));
+
+    return isAdmin || isProjectCurator;
+  }
+
+  @PreAuthorize("hasAuthority('IS_ADMIN')"
+      + " or hasAuthority('IS_CURATOR') and hasAuthority('WRITE_PROJECT:' + #projectId)"
+      + " or @commentService.getOwnerByCommentId(#projectId, #commentId)?.login == authentication.name")
+  @DeleteMapping(value = "/{commentId}")
+  public Object removeComment(
+      final Authentication authentication,
+      final @RequestHeader(name = "If-Match", required = false) String oldETag,
+      final @RequestBody(required = false) NewRemoveCommentDTO body,
+      final @PathVariable(value = "projectId") String projectId,
+      final @PathVariable(value = "mapId") String mapId,
+      final @PathVariable(value = "commentId") Integer commentId) throws QueryException, ObjectNotFoundException {
+
+    final boolean isAdminView = checkAdminView(authentication, projectId);
+
+    Comment comment = getComment(projectId, mapId, commentId, isAdminView);
+
+    if (comment == null) {
+      throw new ObjectNotFoundException("Comment with given id doesn't exist");
+    }
+
+    serializer.checkETag(oldETag, comment);
+
+    if (body != null) {
+      body.saveToComment(comment);
+    }
+    commentService.remove(comment);
+    if (isAdminView) {
+      return serializer.prepareResponse(comment, new AdminCommentResponseDecorator());
+    } else {
+      return serializer.prepareResponse(comment);
+    }
+  }
+
+  @PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
+  @GetMapping(value = "/{commentId}")
+  public ResponseEntity<?> getComment(
+      final Authentication authentication,
+      final @PathVariable(value = "projectId") String projectId,
+      final @Valid @Pattern(regexp = "^[-+]?\\d+$|^\\*$") @PathVariable(value = "mapId") String mapId,
+      final @PathVariable(value = "commentId") Integer commentId) throws ObjectNotFoundException {
+
+    final boolean isAdminView = checkAdminView(authentication, projectId);
+
+    Comment comment = getComment(projectId, mapId, commentId, isAdminView);
+
+    if (isAdminView) {
+      return serializer.prepareResponse(comment, new AdminCommentResponseDecorator());
+    } else {
+      return serializer.prepareResponse(comment);
+    }
+  }
+
+  private Comment getComment(
+      final String projectId,
+      final String mapId,
+      final Integer commentId,
+      final boolean isAdminView
+  ) throws ObjectNotFoundException {
+    Map<CommentProperty, Object> filterOptions = new HashMap<>();
+    filterOptions.put(CommentProperty.PROJECT_ID, projectId);
+    filterOptions.put(CommentProperty.MAP_ID, mapId);
+    filterOptions.put(CommentProperty.ID, commentId);
+
+    if (!isAdminView) {
+      filterOptions.put(CommentProperty.VISIBLE, true);
+    }
+
+    Page<Comment> page = commentService.getAll(filterOptions, Pageable.unpaged());
+
+    if (page.getNumberOfElements() == 0) {
+      throw new ObjectNotFoundException("No comment found");
+    }
+    return page.getContent().get(0);
+  }
+
+  @PreAuthorize("hasAnyAuthority('IS_ADMIN', 'READ_PROJECT:' + #projectId)")
+  @PostMapping
+  public ResponseEntity<?> addCommentForElement(
+      final @PathVariable(value = "projectId") String projectId,
+      final @PathVariable(value = "mapId") Integer mapId,
+      final Authentication authentication,
+      final @Valid @RequestBody NewCommentDTO data
+  ) throws QueryException, ObjectNotFoundException {
+    final boolean isAdminView = checkAdminView(authentication, projectId);
+
+    User user = userService.getUserByLogin(authentication.getName());
+    if (user.getLogin().equals(Configuration.ANONYMOUS_LOGIN)) {
+      user = null;
+    }
+    final ModelData model = modelService.getModelByMapId(projectId, mapId);
+    if (model == null) {
+      throw new ObjectNotFoundException("Map with given id doesn't exist");
+    }
+
+    Comment comment = new Comment();
+    comment.setUser(user);
+    comment.setModel(model);
+    data.saveToComment(comment);
+
+
+    commentService.add(comment);
+
+    if (isAdminView) {
+      return serializer.prepareResponse(comment, new AdminCommentResponseDecorator());
+    } else {
+      return serializer.prepareResponse(comment);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentDTO.java b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8e0d2483cf4a24e23ba1ee519e2da2ccefdbe14
--- /dev/null
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentDTO.java
@@ -0,0 +1,64 @@
+package lcsb.mapviewer.web.api.project.map.comment;
+
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.web.api.AbstractDTO;
+import org.hibernate.validator.constraints.NotBlank;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.awt.geom.Point2D;
+
+public class NewCommentDTO extends AbstractDTO {
+
+  @NotBlank
+  @Size(max = 255)
+  private String content;
+
+  @Size(max = 255)
+  private String email;
+
+  @NotNull
+  private Point2D coordinates;
+
+  @NotNull
+  private Boolean visible;
+
+  public void saveToComment(final Comment comment) {
+    comment.setContent(content);
+    comment.setEmail(email);
+    comment.setCoordinates(coordinates);
+    comment.setPinned(visible);
+  }
+
+  public Point2D getCoordinates() {
+    return coordinates;
+  }
+
+  public void setCoordinates(final Point2D coordinates) {
+    this.coordinates = coordinates;
+  }
+
+  public Boolean getVisible() {
+    return visible;
+  }
+
+  public void setVisible(final Boolean visible) {
+    this.visible = visible;
+  }
+
+  public String getContent() {
+    return content;
+  }
+
+  public void setContent(final String content) {
+    this.content = content;
+  }
+
+  public String getEmail() {
+    return email;
+  }
+
+  public void setEmail(final String email) {
+    this.email = email;
+  }
+}
diff --git a/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewRemoveCommentDTO.java b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewRemoveCommentDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..43f3b056ba4e8fba9b09121a4713c37f78199abc
--- /dev/null
+++ b/web/src/main/java/lcsb/mapviewer/web/api/project/map/comment/NewRemoveCommentDTO.java
@@ -0,0 +1,25 @@
+package lcsb.mapviewer.web.api.project.map.comment;
+
+import lcsb.mapviewer.api.QueryException;
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.web.api.AbstractDTO;
+
+import javax.validation.constraints.Size;
+
+public class NewRemoveCommentDTO extends AbstractDTO {
+
+  @Size(max = 255)
+  private String reason;
+
+  public void saveToComment(final Comment comment) throws QueryException {
+    comment.setRemoveReason(reason);
+  }
+
+  public String getReason() {
+    return reason;
+  }
+
+  public void setReason(final String reason) {
+    this.reason = reason;
+  }
+}
diff --git a/web/src/main/java/lcsb/mapviewer/web/app/WebConfig.java b/web/src/main/java/lcsb/mapviewer/web/app/WebConfig.java
index 3857526069c3553f0ab859313927609da874f7db..6ae09584e28bb61b5d3b4f4909c4f744ee45c052 100644
--- a/web/src/main/java/lcsb/mapviewer/web/app/WebConfig.java
+++ b/web/src/main/java/lcsb/mapviewer/web/app/WebConfig.java
@@ -7,6 +7,7 @@ import lcsb.mapviewer.api.CustomPageDeserialize;
 import lcsb.mapviewer.api.CustomPageSerializer;
 import lcsb.mapviewer.common.MinervaConfigurationHolder;
 import lcsb.mapviewer.model.map.BioEntity;
+import lcsb.mapviewer.model.map.Comment;
 import lcsb.mapviewer.model.map.OverviewImage;
 import lcsb.mapviewer.model.map.OverviewImageLink;
 import lcsb.mapviewer.model.map.OverviewModelLink;
@@ -19,6 +20,7 @@ import lcsb.mapviewer.modelutils.serializer.ColorSerializer;
 import lcsb.mapviewer.modelutils.serializer.Line2DDeserializer;
 import lcsb.mapviewer.modelutils.serializer.Line2DSerializer;
 import lcsb.mapviewer.modelutils.serializer.Point2DDeserializer;
+import lcsb.mapviewer.modelutils.serializer.model.map.CommentSerializer;
 import lcsb.mapviewer.modelutils.serializer.model.map.MinifiedBioEntitySerializer;
 import lcsb.mapviewer.modelutils.serializer.model.map.OverviewImageLinkSerializer;
 import lcsb.mapviewer.modelutils.serializer.model.map.OverviewImageSerializer;
@@ -121,6 +123,7 @@ public class WebConfig implements WebMvcConfigurer {
         module.addSerializer(ReactionNode.class, new ReactionNodeSerializer());
         module.addSerializer(BioEntity.class, new MinifiedBioEntitySerializer());
         module.addSerializer(CustomPage.class, new CustomPageSerializer());
+        module.addSerializer(Comment.class, new CommentSerializer());
 
         module.addDeserializer(Point2D.class, new Point2DDeserializer());
 
diff --git a/web/src/test/java/lcsb/mapviewer/web/CommentControllerIntegrationTest.java b/web/src/test/java/lcsb/mapviewer/web/CommentControllerIntegrationTest.java
index cacb08235c181a06ae07ea5f0a8fae905fe8361b..de5a422215f3258dc04ec5b691264f47d55d82a5 100644
--- a/web/src/test/java/lcsb/mapviewer/web/CommentControllerIntegrationTest.java
+++ b/web/src/test/java/lcsb/mapviewer/web/CommentControllerIntegrationTest.java
@@ -34,9 +34,9 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.web.servlet.RequestBuilder;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -373,7 +373,7 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
         .andExpect(status().is2xxSuccessful());
 
     comment = commentService.getCommentById(TEST_PROJECT, comment.getId() + "");
-    assertEquals(true, comment.isDeleted());
+    assertTrue(comment.isDeleted());
   }
 
   @Test
@@ -390,7 +390,7 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
 
     comment = commentService.getCommentById(TEST_PROJECT, comment.getId() + "");
 
-    assertEquals(true, comment.isDeleted());
+    assertTrue(comment.isDeleted());
   }
 
   @Test
@@ -422,7 +422,7 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
         .andExpect(status().is2xxSuccessful());
 
     comment = commentService.getCommentById(TEST_PROJECT, comment.getId() + "");
-    assertEquals(true, comment.isDeleted());
+    assertTrue(comment.isDeleted());
   }
 
   @Test
@@ -765,7 +765,7 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
   public void testAddElementCommentWithoutAllNecessaryData() throws Exception {
     MockHttpSession session = createSession(TEST_USER_LOGIN, TEST_USER_PASSWORD);
 
-    String body = EntityUtils.toString(new UrlEncodedFormEntity(Arrays.asList(
+    String body = EntityUtils.toString(new UrlEncodedFormEntity(Collections.singletonList(
         new BasicNameValuePair("modelId", map.getId() + ""))));
 
     RequestBuilder request = post("/minerva/api/projects/{projectId}/comments/models/{mapId}/bioEntities/elements/{elementId}",
@@ -988,19 +988,21 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
         .andExpect(status().is2xxSuccessful());
 
     comment = commentService.getCommentById(TEST_PROJECT, comment.getId() + "");
-    assertEquals(true, comment.isDeleted());
+    assertTrue(comment.isDeleted());
   }
 
   private Comment createReactionComment() throws Exception {
     Comment comment = createComment(map);
     comment.setReaction(reaction);
-    return commentService.add(comment);
+    commentService.add(comment);
+    return comment;
   }
 
   private Comment createElementComment() throws Exception {
     Comment comment = createComment(map);
     comment.setElement(element);
-    return commentService.add(comment);
+    commentService.add(comment);
+    return comment;
   }
 
   @Test
@@ -1023,7 +1025,7 @@ public class CommentControllerIntegrationTest extends ControllerIntegrationTest
     }
   }
 
-  private String createContentBody(final String type, final String value) throws IOException, UnsupportedEncodingException {
+  private String createContentBody(final String type, final String value) throws IOException {
     List<BasicNameValuePair> params = new ArrayList<>(Arrays.asList(
         new BasicNameValuePair("email", "a@a.lu"),
         new BasicNameValuePair("content", "test content"),
diff --git a/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java b/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java
index d1426a44bbf8c8aa1fc2e74088042186ffb7d960..71452b8d64587ddd7b73faa3e7472dc084b0cfa9 100644
--- a/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java
+++ b/web/src/test/java/lcsb/mapviewer/web/ControllerIntegrationTest.java
@@ -886,7 +886,8 @@ public abstract class ControllerIntegrationTest extends TestUtils {
 
   protected Comment createAndPersistComment(final ModelData model, final User owner) {
     final Comment comment = createComment(model, owner);
-    return commentService.add(comment);
+    commentService.add(comment);
+    return comment;
   }
 
   protected ModelData getBuildInModel() {
@@ -899,6 +900,7 @@ public abstract class ControllerIntegrationTest extends TestUtils {
 
   protected Comment createComment(final ModelData map, final User owner) {
     final Comment comment = new Comment();
+    comment.setContent("it's cool");
     comment.setModel(map);
     comment.setUser(owner);
     comment.setCoordinates(new Point2D.Double(10, 20));
@@ -917,6 +919,10 @@ public abstract class ControllerIntegrationTest extends TestUtils {
     return getMapPathParameters().and(parameterWithName("layerId").description("layer identifier"));
   }
 
+  protected PathParametersSnippet getCommentPathParameters() {
+    return getMapPathParameters().and(parameterWithName("commentId").description("comment identifier"));
+  }
+
   protected PathParametersSnippet getLayerImagePathParameters() {
     return getLayerPathParameters().and(parameterWithName("imageId").description("image identifier"));
   }
diff --git a/web/src/test/java/lcsb/mapviewer/web/api/NewApiDocs.java b/web/src/test/java/lcsb/mapviewer/web/api/NewApiDocs.java
index c452810cdfd85158476ad9752f806aba1a1e2259..ccc2e40eb6d97bee5d7c5980125b9ab7ad6ec1ed 100644
--- a/web/src/test/java/lcsb/mapviewer/web/api/NewApiDocs.java
+++ b/web/src/test/java/lcsb/mapviewer/web/api/NewApiDocs.java
@@ -58,6 +58,10 @@ public class NewApiDocs {
     return new NewApiDocs().getLayerFields(prefix);
   }
 
+  public static List<FieldDescriptor> getCommentResponse(final String prefix) {
+    return new NewApiDocs().getCommentFields(prefix);
+  }
+
   public static List<FieldDescriptor> getGlyphResponse(final String prefix) {
     return new NewApiDocs().getGlyphFields(prefix);
   }
@@ -145,6 +149,26 @@ public class NewApiDocs {
     );
   }
 
+  public static RequestFieldsSnippet getAddCommentRequest() {
+    return requestFields(
+        fieldWithPath("content")
+            .description("content")
+            .type(JsonFieldType.STRING),
+        fieldWithPath("coordinates.x")
+            .description("x coordinate on the map")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath("coordinates.y")
+            .description("y coordinate on the map")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath("visible")
+            .description("is content visible")
+            .type(JsonFieldType.BOOLEAN),
+        fieldWithPath("email")
+            .description("reported email address")
+            .type(JsonFieldType.STRING)
+    );
+  }
+
   public static RequestFieldsSnippet getAddLayerImageRequest() {
     return requestFields(
         fieldWithPath("x")
@@ -1061,6 +1085,44 @@ public class NewApiDocs {
     return result;
   }
 
+  private List<FieldDescriptor> getCommentFields(final String prefix) {
+    return Arrays.asList(
+        fieldWithPath(prefix + "id")
+            .description("unique comment identifier")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath(prefix + "map")
+            .description("map identifier")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath(prefix + "content")
+            .description("comment content")
+            .type(JsonFieldType.STRING),
+        fieldWithPath(prefix + "author")
+            .description("login of the author")
+            .optional()
+            .type(JsonFieldType.STRING),
+        fieldWithPath(prefix + "email")
+            .description("email of the author")
+            .optional()
+            .type(JsonFieldType.STRING),
+        fieldWithPath(prefix + "removeReason")
+            .description("why the comment has been removed ")
+            .optional()
+            .type(JsonFieldType.STRING),
+        fieldWithPath(prefix + "coordinates.x")
+            .description("x coordinate where the comment is located on map")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath(prefix + "coordinates.y")
+            .description("y coordinate where the comment is located on map")
+            .type(JsonFieldType.NUMBER),
+        fieldWithPath(prefix + "deleted")
+            .description("is comment deleted")
+            .type(JsonFieldType.BOOLEAN),
+        fieldWithPath(prefix + "visible")
+            .description("is comment visible")
+            .type(JsonFieldType.BOOLEAN)
+    );
+  }
+
   private List<FieldDescriptor> getGlyphFields(final String prefix) {
     final List<FieldDescriptor> result = new ArrayList<>();
     result.add(
@@ -1205,6 +1267,17 @@ public class NewApiDocs {
     return responseFields(fields);
   }
 
+  public static Snippet getCommentsSearchResult() {
+    final String prefix = "content[].";
+    final List<FieldDescriptor> fields = new ArrayList<>();
+    fields.add(fieldWithPath("content")
+        .description("list of comments on the page")
+        .type(JsonFieldType.ARRAY));
+    fields.addAll(getCommentResponse(prefix));
+    fields.addAll(getPageableFields());
+    return responseFields(fields);
+  }
+
   public static Snippet getGlyphsSearchResult() {
     final String prefix = "content[].";
     final List<FieldDescriptor> fields = new ArrayList<>();
diff --git a/web/src/test/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalControllerTest.java b/web/src/test/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalControllerTest.java
index e0b60402256719b573a9adc4a798dc18c01d3cad..766934cedf667c70a38941514421c1c8d257bbc8 100644
--- a/web/src/test/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalControllerTest.java
+++ b/web/src/test/java/lcsb/mapviewer/web/api/project/chemicals/NewChemicalControllerTest.java
@@ -1,32 +1,38 @@
 package lcsb.mapviewer.web.api.project.chemicals;
 
-import static org.junit.Assume.assumeTrue;
-import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
-import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
+import lcsb.mapviewer.model.Project;
+import lcsb.mapviewer.model.map.MiriamData;
+import lcsb.mapviewer.model.map.MiriamType;
+import lcsb.mapviewer.services.interfaces.IProjectService;
+import lcsb.mapviewer.web.ControllerIntegrationTest;
+import lcsb.mapviewer.web.api.NewApiDocs;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.mock.web.MockHttpSession;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.web.servlet.RequestBuilder;
 
-import lcsb.mapviewer.model.Project;
-import lcsb.mapviewer.web.ControllerIntegrationTest;
-import lcsb.mapviewer.web.api.NewApiDocs;
+import static org.junit.Assume.assumeTrue;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @ActiveProfiles("webCtdTestProfile")
 public class NewChemicalControllerTest extends ControllerIntegrationTest {
 
-  private static String TEST_PROJECT = "TEST_PROJECT";
+  private static final String TEST_PROJECT = "TEST_PROJECT";
 
   private Project project;
 
+  @Autowired
+  private IProjectService projectService;
+
   @Before
   public void setup() {
     assumeTrue("DAPI credentials are not provided", isDapiConfigurationAvailable());
@@ -38,6 +44,7 @@ public class NewChemicalControllerTest extends ControllerIntegrationTest {
     if (project != null) {
       removeProject(project);
     }
+    removeProject(TEST_PROJECT_2);
   }
 
   @Test
@@ -55,4 +62,27 @@ public class NewChemicalControllerTest extends ControllerIntegrationTest {
         .andExpect(status().is2xxSuccessful())
         .andReturn().getResponse().getContentAsString();
   }
+
+  @Test
+  public void testSuggestedQueryList() throws Exception {
+    MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+    final MiriamData bloodLossDisease = new MiriamData(MiriamType.MESH_2012, "D016063");
+
+    final Project project = createEmptyProject(TEST_PROJECT_2);
+    project.setDisease(bloodLossDisease);
+    projectService.update(project);
+
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/chemicals/suggestedQueryList", TEST_PROJECT_2)
+        .session(session);
+
+
+    mockMvc.perform(request)
+        .andExpect(status().is2xxSuccessful())
+        .andDo(document("new_api/projects/project_chemicals/suggestedQueryList",
+            getProjectPathParameters(),
+            responseFields(NewApiDocs.getSuggestedQueryListFields())))
+        .andReturn().getResponse().getContentAsString();
+  }
+
+
 }
diff --git a/web/src/test/java/lcsb/mapviewer/web/api/project/drugs/NewDrugControllerTest.java b/web/src/test/java/lcsb/mapviewer/web/api/project/drugs/NewDrugControllerTest.java
index aeff4a34fcd0eb34b5c97f9e485210d4ecb6e2ac..3ea076e86b8adfa5eeabd0492207a3b04193ab8c 100644
--- a/web/src/test/java/lcsb/mapviewer/web/api/project/drugs/NewDrugControllerTest.java
+++ b/web/src/test/java/lcsb/mapviewer/web/api/project/drugs/NewDrugControllerTest.java
@@ -1,10 +1,7 @@
 package lcsb.mapviewer.web.api.project.drugs;
 
-import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
-import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
+import lcsb.mapviewer.web.ControllerIntegrationTest;
+import lcsb.mapviewer.web.api.NewApiDocs;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -13,8 +10,10 @@ import org.springframework.mock.web.MockHttpSession;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.web.servlet.RequestBuilder;
 
-import lcsb.mapviewer.web.ControllerIntegrationTest;
-import lcsb.mapviewer.web.api.NewApiDocs;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 public class NewDrugControllerTest extends ControllerIntegrationTest {
@@ -44,4 +43,19 @@ public class NewDrugControllerTest extends ControllerIntegrationTest {
             responseFields(NewApiDocs.getDrugListResponse(""))));
   }
 
+  @Test
+  public void testSuggestedQueryList() throws Exception {
+    MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+    final RequestBuilder request = get(
+        "/minerva/new_api/projects/{projectId}/drugs/suggestedQueryList", TEST_PROJECT)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().is2xxSuccessful())
+        .andDo(document("new_api/projects/project_drugs/suggestedQueryList",
+            getProjectPathParameters(),
+            responseFields(NewApiDocs.getSuggestedQueryListFields())))
+        .andReturn().getResponse().getContentAsString();
+  }
+
 }
diff --git a/web/src/test/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentControllerTest.java b/web/src/test/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..df51082e3b9224d623690e935c6f553f7ffdb36a
--- /dev/null
+++ b/web/src/test/java/lcsb/mapviewer/web/api/project/map/comment/NewCommentControllerTest.java
@@ -0,0 +1,309 @@
+package lcsb.mapviewer.web.api.project.map.comment;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lcsb.mapviewer.model.map.Comment;
+import lcsb.mapviewer.model.map.model.ModelData;
+import lcsb.mapviewer.model.user.User;
+import lcsb.mapviewer.persist.dao.map.CommentProperty;
+import lcsb.mapviewer.services.interfaces.ICommentService;
+import lcsb.mapviewer.services.interfaces.IMinervaJobService;
+import lcsb.mapviewer.services.interfaces.IModelService;
+import lcsb.mapviewer.web.ControllerIntegrationTest;
+import lcsb.mapviewer.web.api.NewApiDocs;
+import lcsb.mapviewer.web.api.NewApiResponseSerializer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import javax.servlet.http.HttpServletResponse;
+import java.awt.geom.Point2D;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class NewCommentControllerTest extends ControllerIntegrationTest {
+
+  @Autowired
+  private ICommentService commentService;
+
+  @Autowired
+  private IModelService modelService;
+
+  @Autowired
+  private IMinervaJobService minervaJobService;
+
+  @Autowired
+  private NewApiResponseSerializer newApiResponseSerializer;
+
+  private ObjectMapper objectMapper;
+
+  private ModelData modelData;
+  private User user;
+
+  @Before
+  public void setUp() throws Exception {
+    objectMapper = newApiResponseSerializer.getObjectMapper();
+
+    modelData = modelService.getById(BUILT_IN_MAP_ID);
+
+    user = createUser(TEST_USER_LOGIN, TEST_USER_PASSWORD);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    removeUser(user);
+    minervaJobService.waitForTasksToFinish();
+  }
+
+  @Test
+  public void testGetComment() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId())
+        .session(session);
+
+    final HttpServletResponse response = mockMvc.perform(request)
+        .andExpect(status().isOk())
+        .andDo(document("new_api/projects/maps/comments/get_comment",
+            getCommentPathParameters(),
+            responseFields(NewApiDocs.getCommentResponse(""))))
+        .andReturn().getResponse();
+
+    assertNotNull(response.getHeader("ETag"));
+  }
+
+  @Test
+  public void testGetNonExisting() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        -1)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isNotFound());
+  }
+
+  @Test
+  public void testGetCommentWithoutPermission() throws Exception {
+    final MockHttpSession session = createSession(TEST_USER_LOGIN, TEST_USER_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId())
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isForbidden());
+  }
+
+  @Test
+  public void testCreateComment() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    final NewCommentDTO data = createCommentDTO();
+
+    final RequestBuilder request = post("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID)
+        .contentType(MediaType.APPLICATION_JSON)
+        .content(objectMapper.writeValueAsString(data))
+        .session(session);
+
+    final MockHttpServletResponse response = mockMvc.perform(request)
+        .andExpect(status().isOk())
+        .andDo(document("new_api/projects/maps/comments/add_comment",
+            getMapPathParameters(),
+            NewApiDocs.getAddCommentRequest(),
+            responseFields(NewApiDocs.getCommentResponse(""))))
+        .andReturn().getResponse();
+    assertNotNull(response.getHeader("ETag"));
+    final Map<String, Object> result = objectMapper.readValue(response.getContentAsString(), new TypeReference<Map<String, Object>>() {
+    });
+
+    final Map<CommentProperty, Object> filter = new HashMap<>();
+    filter.put(CommentProperty.PROJECT_ID, BUILT_IN_PROJECT);
+    filter.put(CommentProperty.MAP_ID, BUILT_IN_MAP_ID);
+    filter.put(CommentProperty.ID, result.get("id"));
+    final Page<Comment> comments = commentService.getAll(filter, Pageable.unpaged());
+
+    assertEquals(1, comments.getNumberOfElements());
+  }
+
+  private NewCommentDTO createCommentDTO() {
+    final NewCommentDTO data = new NewCommentDTO();
+    data.setContent("blah blah blah");
+    data.setCoordinates(new Point2D.Double(10, 12));
+    data.setEmail("test@test.com");
+    data.setVisible(true);
+    return data;
+  }
+
+  private NewRemoveCommentDTO createRemoveCommentDTO() {
+    final NewRemoveCommentDTO data = new NewRemoveCommentDTO();
+    data.setReason("xyz");
+    return data;
+  }
+
+
+  @Test
+  public void testDeleteComment() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    NewRemoveCommentDTO removeCommentDTO = createRemoveCommentDTO();
+
+    final RequestBuilder request = delete("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId())
+        .contentType(MediaType.APPLICATION_JSON)
+        .content(objectMapper.writeValueAsString(removeCommentDTO))
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isOk())
+        .andDo(document("new_api/projects/maps/comments/delete_comment",
+            getCommentPathParameters()));
+
+    comment = commentService.getById(comment.getId());
+    assertTrue(comment.isDeleted());
+  }
+
+  @Test
+  public void testDeleteNotExistingComment() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    final RequestBuilder request = delete("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        -1)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isNotFound());
+  }
+
+  @Test
+  public void testDeleteNoAccessComment() throws Exception {
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final RequestBuilder request = delete("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId());
+
+    mockMvc.perform(request)
+        .andExpect(status().isForbidden());
+  }
+
+  @Test
+  public void testDeleteCommentWithGoodVersion() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final String originalVersion = comment.getEntityVersion() + "";
+
+    final RequestBuilder request = delete("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId())
+        .header("If-Match", originalVersion)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isOk());
+    comment = commentService.getById(comment.getId());
+    assertTrue(comment.isDeleted());
+  }
+
+  @Test
+  public void testDeleteCommentWithWrongVersion() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final RequestBuilder request = delete("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/{commentId}",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID,
+        comment.getId())
+        .header("If-Match", "-1")
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isPreconditionFailed());
+    comment = commentService.getById(comment.getId());
+    assertFalse(comment.isDeleted());
+  }
+
+  @Test
+  public void testListComments() throws Exception {
+    final MockHttpSession session = createSession(BUILT_IN_TEST_ADMIN_LOGIN, BUILT_IN_TEST_ADMIN_PASSWORD);
+
+    Comment comment = createComment(modelData);
+    commentService.add(comment);
+
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andDo(document("new_api/projects/maps/comments/list_comments",
+            getMapPathParameters(),
+            NewApiDocs.getCommentsSearchResult()))
+        .andExpect(status().isOk());
+  }
+
+  @Test
+  public void testListCommentsWithoutAccess() throws Exception {
+    final MockHttpSession session = createSession(TEST_USER_LOGIN, TEST_USER_PASSWORD);
+    final RequestBuilder request = get("/minerva/new_api/projects/{projectId}/maps/{mapId}/comments/",
+        BUILT_IN_PROJECT,
+        BUILT_IN_MAP_ID)
+        .session(session);
+
+    mockMvc.perform(request)
+        .andExpect(status().isForbidden());
+  }
+
+}