diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java index 25d6eb87..e88ea57a 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java @@ -2,6 +2,7 @@ package com.sismics.docs.core.dao.jpa; import com.google.common.base.Joiner; import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.sismics.docs.core.constant.AuditLogType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria; @@ -239,11 +240,14 @@ public class DocumentDao { } if (criteria.getTagIdList() != null && !criteria.getTagIdList().isEmpty()) { int index = 0; + List tagCriteriaList = Lists.newArrayList(); for (String tagId : criteria.getTagIdList()) { - sb.append(String.format(" join T_DOCUMENT_TAG dt%d on dt%d.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt%d.DOT_IDTAG_C = :tagId%d and dt%d.DOT_DELETEDATE_D is null ", index, index, index, index, index)); + sb.append(String.format("left join T_DOCUMENT_TAG dt%d on dt%d.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt%d.DOT_IDTAG_C = :tagId%d and dt%d.DOT_DELETEDATE_D is null ", index, index, index, index, index)); parameterMap.put("tagId" + index, tagId); + tagCriteriaList.add(String.format("dt%d.DOT_ID_C is not null", index)); index++; } + criteriaList.add(Joiner.on(" OR ").join(tagCriteriaList)); } if (criteria.getShared() != null && criteria.getShared()) { criteriaList.add("(select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) > 0"); diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java index 6c276675..02b6d6f4 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java @@ -195,10 +195,6 @@ public class TagDao { criteriaList.add("t.TAG_NAME_C = :name"); parameterMap.put("name", criteria.getName()); } - if (criteria.getNameLike() != null) { - criteriaList.add("t.TAG_NAME_C like :nameLike"); - parameterMap.put("nameLike", criteria.getNameLike() + "%"); - } criteriaList.add("t.TAG_DELETEDATE_D is null"); diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java index 2665a205..6c52da84 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java @@ -68,13 +68,4 @@ public class TagCriteria { this.name = name; return this; } - - public String getNameLike() { - return nameLike; - } - - public TagCriteria setNameLike(String nameLike) { - this.nameLike = nameLike; - return this; - } } diff --git a/docs-core/src/main/java/com/sismics/docs/core/util/TagUtil.java b/docs-core/src/main/java/com/sismics/docs/core/util/TagUtil.java new file mode 100644 index 00000000..4933a618 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/util/TagUtil.java @@ -0,0 +1,54 @@ +package com.sismics.docs.core.util; + +import com.google.common.collect.Lists; +import com.sismics.docs.core.dao.jpa.dto.TagDto; + +import java.util.List; + +/** + * Tag utilities. + * + * @author bgamard + */ +public class TagUtil { + /** + * Recursively find children of a tags. + * + * @param parentTagDto Parent tag + * @param allTagDtoList List of all tags + * @return Children tags + */ + public static List findChildren(TagDto parentTagDto, List allTagDtoList) { + List childrenTagDtoList = Lists.newArrayList(); + + for (TagDto tagDto : allTagDtoList) { + if (parentTagDto.getId().equals(tagDto.getParentId())) { + childrenTagDtoList.add(tagDto); + childrenTagDtoList.addAll(findChildren(tagDto, allTagDtoList)); + } + } + + return childrenTagDtoList; + } + + /** + * Find tags by name (start with). + * + * @param name Name + * @param allTagDtoList List of all tags + * @return List of filtered tags + */ + public static List findByName(String name, List allTagDtoList) { + List tagDtoList = Lists.newArrayList(); + if (name == null || name.isEmpty()) { + return tagDtoList; + } + name = name.toLowerCase(); + for (TagDto tagDto : allTagDtoList) { + if (tagDto.getName().toLowerCase().startsWith(name)) { + tagDtoList.add(tagDto); + } + } + return tagDtoList; + } +} diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java index 0db1548b..cfcfcb71 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java @@ -17,10 +17,7 @@ import com.sismics.docs.core.event.FileDeletedAsyncEvent; import com.sismics.docs.core.model.jpa.Document; import com.sismics.docs.core.model.jpa.File; import com.sismics.docs.core.model.jpa.User; -import com.sismics.docs.core.util.ConfigUtil; -import com.sismics.docs.core.util.DocumentUtil; -import com.sismics.docs.core.util.FileUtil; -import com.sismics.docs.core.util.PdfUtil; +import com.sismics.docs.core.util.*; import com.sismics.docs.core.util.jpa.PaginatedList; import com.sismics.docs.core.util.jpa.PaginatedLists; import com.sismics.docs.core.util.jpa.SortCriteria; @@ -424,6 +421,7 @@ public class DocumentResource extends BaseResource { } TagDao tagDao = new TagDao(); + List allTagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)), null); UserDao userDao = new UserDao(); DateTimeParser[] parsers = { DateTimeFormat.forPattern("yyyy").getParser(), @@ -448,7 +446,7 @@ public class DocumentResource extends BaseResource { switch (params[0]) { case "tag": // New tag criteria - List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setNameLike(params[1]), null); + List tagDtoList = TagUtil.findByName(params[1], allTagDtoList); if (documentCriteria.getTagIdList() == null) { documentCriteria.setTagIdList(new ArrayList()); } @@ -458,6 +456,10 @@ public class DocumentResource extends BaseResource { } for (TagDto tagDto : tagDtoList) { documentCriteria.getTagIdList().add(tagDto.getId()); + List childrenTagDtoList = TagUtil.findChildren(tagDto, allTagDtoList); + for (TagDto childrenTagDto : childrenTagDtoList) { + documentCriteria.getTagIdList().add(childrenTagDto.getId()); + } } break; case "after": diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java index efd09d02..d84c11e1 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java @@ -1,5 +1,9 @@ package com.sismics.docs.rest; +import com.sismics.util.filter.TokenBasedSecurityFilter; +import org.junit.Assert; +import org.junit.Test; + import javax.json.JsonArray; import javax.json.JsonObject; import javax.ws.rs.client.Entity; @@ -7,11 +11,6 @@ import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import org.junit.Assert; -import org.junit.Test; - -import com.sismics.util.filter.TokenBasedSecurityFilter; - /** * Test the tag resource. * @@ -66,12 +65,13 @@ public class TestTagResource extends BaseJerseyTest { Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus())); // Create a document - target().path("/document").request() + json = target().path("/document").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) .put(Entity.form(new Form() .param("title", "My super document 1") .param("tags", tag3Id) .param("language", "eng")), JsonObject.class); + String document1Id = json.getString("id"); // Create a document json = target().path("/document").request() @@ -81,7 +81,28 @@ public class TestTagResource extends BaseJerseyTest { .param("tags", tag4Id) .param("language", "eng")), JsonObject.class); String document2Id = json.getString("id"); - + + // Search document by parent tag + json = target().path("/document/list") + .queryParam("search", "tag:Tag3") + .queryParam("asc", "true") + .queryParam("sort_column", "1") + .request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) + .get(JsonObject.class); + Assert.assertEquals(2, json.getJsonArray("documents").size()); + Assert.assertEquals(document1Id, json.getJsonArray("documents").getJsonObject(0).getString("id")); + Assert.assertEquals(document2Id, json.getJsonArray("documents").getJsonObject(1).getString("id")); + + // Search document by children tag + json = target().path("/document/list") + .queryParam("search", "tag:Tag4") + .request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) + .get(JsonObject.class); + Assert.assertEquals(1, json.getJsonArray("documents").size()); + Assert.assertEquals(document2Id, json.getJsonArray("documents").getJsonObject(0).getString("id")); + // Check tags on a document json = target().path("/document/" + document2Id).request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token)