From b724e923134f7a0889f46a4582e253374b6340b1 Mon Sep 17 00:00:00 2001 From: Eike Cochu <eike@cochu.com> Date: Thu, 4 Feb 2016 18:56:35 +0100 Subject: [PATCH] updated db service with query builder added query builder to create more complex queries to database service added reverse query to fetch articles for topics, topics for words renamed service to dbServicetype in rest resources added pair class for query equality --- ma-impl.sublime-workspace | 87 +++++------------ .../rest/provider/ObjectMapperProvider.java | 2 +- .../vipra/rest/resource/ArticleResource.java | 20 ++-- .../vipra/rest/resource/ImportResource.java | 16 ++-- .../de/vipra/rest/resource/TopicResource.java | 34 ++++--- .../de/vipra/rest/resource/WordResource.java | 27 ++++-- vipra-ui/html/topics/show.html | 10 +- vipra-ui/html/words/show.html | 18 ++++ vipra-ui/js/controllers.js | 1 + vipra-ui/js/directives.js | 12 +++ .../src/main/java/de/vipra/util/Pair.java | 35 +++++++ .../de/vipra/util/model/ArticleStats.java | 8 +- .../main/java/de/vipra/util/model/Import.java | 18 ++-- .../java/de/vipra/util/model/TopicFull.java | 18 +++- .../java/de/vipra/util/model/TopicRef.java | 6 +- .../java/de/vipra/util/model/TopicWord.java | 8 +- .../main/java/de/vipra/util/model/Word.java | 12 +++ .../vipra/util/service/DatabaseService.java | 62 ++++++++++--- .../java/de/vipra/util/service/Service.java | 93 ++++++++++++++++++- 19 files changed, 349 insertions(+), 138 deletions(-) create mode 100644 vipra-util/src/main/java/de/vipra/util/Pair.java diff --git a/ma-impl.sublime-workspace b/ma-impl.sublime-workspace index bd0a79ef..776533a4 100644 --- a/ma-impl.sublime-workspace +++ b/ma-impl.sublime-workspace @@ -287,14 +287,6 @@ }, "buffers": [ - { - "file": "vipra-ui/css/app.less", - "settings": - { - "buffer_size": 1761, - "line_ending": "Unix" - } - } ], "build_system": "", "build_system_choices": @@ -478,31 +470,42 @@ "/home/eike/repos/master/ma-impl/vipra-ui/css", "/home/eike/repos/master/ma-impl/vipra-ui/html", "/home/eike/repos/master/ma-impl/vipra-ui/html/articles", + "/home/eike/repos/master/ma-impl/vipra-ui/html/directives", "/home/eike/repos/master/ma-impl/vipra-ui/html/topics", "/home/eike/repos/master/ma-impl/vipra-ui/html/words", - "/home/eike/repos/master/ma-impl/vipra-ui/js" + "/home/eike/repos/master/ma-impl/vipra-ui/js", + "/home/eike/repos/master/ma-impl/vm/data" ], "file_history": [ - "/home/eike/repos/master/ma-impl/vipra-ui/html/index.html", - "/home/eike/repos/master/ma-impl/vipra-ui/html/words/index.html", - "/home/eike/repos/master/ma-impl/vipra-ui/html/topics/index.html", - "/home/eike/repos/master/ma-impl/vipra-ui/html/articles/index.html", + "/home/eike/repos/master/ma-impl/vipra-ui/js/controllers.js", + "/home/eike/repos/master/ma-impl/vipra-ui/html/words/show.html", + "/home/eike/repos/master/ma-impl/vipra-ui/html/topics/show.html", "/home/eike/repos/master/ma-impl/vipra-ui/js/directives.js", "/home/eike/repos/master/ma-impl/vipra-ui/js/app.js", - "/home/eike/repos/master/ma-impl/vipra-ui/js/controllers.js", - "/home/eike/repos/master/ma-impl/vipra-ui/html/articles/show.html", - "/home/eike/repos/master/ma-impl/vipra-ui/css/app.less", - "/home/eike/repos/master/ma-impl/vipra-ui/js/filters.js", + "/home/eike/repos/master/ma-impl/vipra-ui/html/index.html", "/home/eike/repos/master/ma-impl/vipra-ui/index.html", "/home/eike/repos/master/ma-impl/vm/data/data.json", + "/home/eike/repos/master/ma-impl/vm/test/2011-q3/4.txt", + "/home/eike/repos/master/ma-impl/vm/test/2011-q4/5.txt", + "/home/eike/repos/master/ma-impl/vm/test/2011-q1/6.txt", + "/home/eike/repos/master/ma-impl/vm/data/test-10.json", + "/home/eike/repos/master/ma-impl/vm/test/2011-q1/3.txt", + "/home/eike/repos/master/ma-impl/vm/test/2011-q2/2.txt", + "/home/eike/repos/master/ma-impl/vm/test/2011-q3/1.txt", + "/home/eike/repos/master/ma-impl/vipra-ui/html/articles/index.html", + "/home/eike/repos/master/ma-impl/vipra-ui/js/helpers.js", + "/home/eike/repos/master/ma-impl/vipra-ui/js/filters.js", + "/home/eike/repos/master/ma-impl/vipra-ui/html/articles/show.html", + "/home/eike/repos/master/ma-impl/vipra-ui/css/app.less", + "/home/eike/repos/master/ma-impl/vipra-ui/js/services.js", + "/home/eike/repos/master/ma-impl/vipra-ui/html/directives/pagination.html", + "/home/eike/repos/master/ma-impl/vipra-ui/html/words/index.html", + "/home/eike/repos/master/ma-impl/vipra-ui/html/topics/index.html", "/home/eike/repos/master/ma-impl/vm/data/test-1.json", "/home/eike/repos/master/ma-impl/vm/data/test-2.json", - "/home/eike/repos/master/ma-impl/vm/data/test-10.json", "/home/eike/repos/master/ma-impl/vipra-ui/js/factories.js", "/run/user/1000/gvfs/smb-share:server=eike-ain,share=share/interceptors.js", - "/home/eike/repos/master/ma-impl/vipra-ui/html/topics/show.html", - "/home/eike/repos/master/ma-impl/vipra-ui/html/words/show.html", "/home/eike/repos/master/ma-impl/vipra-ui/gulpfile.js", "/home/eike/repos/master/ma-impl/vipra-ui/css/footer.less", "/home/eike/repos/master/ma-impl/vipra-ui/less/vendor.less", @@ -511,7 +514,6 @@ "/home/eike/repos/master/ma-impl/vipra-ui/public/index.html", "/home/eike/repos/master/ma-impl/vipra-ui/js/vendor.js", "/home/eike/repos/master/ma-impl/vipra-ui/less/main.less", - "/home/eike/repos/master/ma-impl/vipra-ui/js/services.js", "/home/eike/repos/master/ma-impl/vipra-ui/css/main.less", "/home/eike/.config/sublime-text-3/Packages/User/Preferences.sublime-settings", "/home/eike/repos/master/ma-impl/vipra-ui/app/adapters/application.js", @@ -603,15 +605,7 @@ "/home/eike/repos/master/ma-impl/vipra-cmd/build2.xml", "/home/eike/repos/master/ma-impl/vipra-ui/README.md", "/home/eike/repos/testasd/bower.json", - "/home/eike/repos/master/ma-impl/vipra-ui2/package.json", - "/home/eike/repos/master/ma-impl/vipra-ui/app/routes/articles/list.js", - "/home/eike/repos/master/ma-impl/vipra-ui/package.json", - "/home/eike/repos/master/ma-impl/vipra-ui/node_modules/ember-highcharts/package.json", - "/home/eike/repos/master/ma-impl/vm/config/initd-mongod", - "/home/eike/repos/master/ma-impl/vm/webapps/vipra-rest/WEB-INF/web.xml", - "/core", - "/home/eike/repos/master/ma-impl/vm/config/environment", - "/home/eike/repos/master/ma-impl/vm/config/initd-tomcat" + "/home/eike/repos/master/ma-impl/vipra-ui2/package.json" ], "find": { @@ -935,39 +929,8 @@ "groups": [ { - "selected": 0, "sheets": [ - { - "buffer": 0, - "file": "vipra-ui/css/app.less", - "semi_transient": false, - "settings": - { - "buffer_size": 1761, - "regions": - { - }, - "selection": - [ - [ - 492, - 492 - ] - ], - "settings": - { - "syntax": "Packages/LESS/LESS.tmLanguage", - "tab_size": 2, - "translate_tabs_to_spaces": true - }, - "translation.x": -0.0, - "translation.y": 102.0, - "zoom_level": 1.0 - }, - "stack_index": 0, - "type": "text" - } ] } ], @@ -1122,7 +1085,7 @@ "selected_items": [ ], - "width": 378.0 + "width": 604.0 }, "selected_group": 0, "settings": diff --git a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java index 490abad9..e78b661e 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java +++ b/vipra-rest/src/main/java/de/vipra/rest/provider/ObjectMapperProvider.java @@ -42,7 +42,7 @@ public class ObjectMapperProvider implements ContextResolver<ObjectMapper> { final ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); - mapper.setSerializationInclusion(Include.NON_NULL); + mapper.setSerializationInclusion(Include.NON_EMPTY); mapper.setDateFormat(new SimpleDateFormat(Constants.DATETIME_FORMAT)); mapper.registerModule(module); return mapper; diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java index a919f92a..bbea795e 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java @@ -46,11 +46,11 @@ public class ArticleResource { UriInfo uri; final Cache<String, ArticleFull> cache; - final DatabaseService<ArticleFull, ObjectId> service; + final DatabaseService<ArticleFull, ObjectId> dbArticles; public ArticleResource(@Context ServletContext servletContext) throws ConfigException, IOException { Config config = Config.getConfig(); - service = DatabaseService.getDatabaseService(config, ArticleFull.class); + dbArticles = DatabaseService.getDatabaseService(config, ArticleFull.class); CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager"); Cache<String, ArticleFull> articleCache = manager.getCache("articlecache", String.class, ArticleFull.class); @@ -68,16 +68,16 @@ public class ArticleResource { Wrapper<List<ArticleFull>> res = new Wrapper<>(); if (skip != null && limit != null) - res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, service.count()); + res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbArticles.count()); if (res.hasErrors()) return res.badRequest(); try { - List<ArticleFull> articles = service.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); + List<ArticleFull> articles = dbArticles.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addMeta("total", service.count()); + res.addMeta("total", dbArticles.count()); else res.addMeta("total", articles.size()); @@ -123,7 +123,7 @@ public class ArticleResource { public Response createArticle(ArticleFull article) { Wrapper<ArticleFull> res; try { - article = service.createSingle(article); + article = dbArticles.createSingle(article); res = new Wrapper<>(article); URI newUri = new URL(uri.getAbsolutePath().toURL(), article.getId().toString()).toURI(); return res.created(newUri); @@ -140,7 +140,7 @@ public class ArticleResource { Wrapper<ArticleFull> res = new Wrapper<>(); long deleted; try { - deleted = service.deleteSingle(MongoUtils.objectId(id)); + deleted = dbArticles.deleteSingle(MongoUtils.objectId(id)); } catch (DatabaseException e) { res = new Wrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be deleted", "item could not be created due to an internal server error")); @@ -168,7 +168,7 @@ public class ArticleResource { ArticleFull article = wrapper.getData(); Wrapper<ArticleFull> res = new Wrapper<>(); try { - service.updateSingle(article); + dbArticles.updateSingle(article); cache.put(id, article); return res.ok(article); } catch (DatabaseException e) { @@ -182,13 +182,13 @@ public class ArticleResource { if (fields == null || fields.length == 0) { ArticleFull article = cache.get(id); if (article == null) { - article = service.getSingle(MongoUtils.objectId(id)); + article = dbArticles.getSingle(MongoUtils.objectId(id)); if (article != null) cache.put(id, article); } return article; } else - return service.getSingle(MongoUtils.objectId(id), fields); + return dbArticles.getSingle(MongoUtils.objectId(id), fields); } } diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ImportResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ImportResource.java index f67a0b49..a2886395 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/ImportResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ImportResource.java @@ -38,11 +38,11 @@ public class ImportResource { UriInfo uri; final Cache<String, Import> cache; - final DatabaseService<Import, ObjectId> service; + final DatabaseService<Import, ObjectId> dbImports; public ImportResource(@Context ServletContext servletContext) throws ConfigException, IOException { Config config = Config.getConfig(); - service = DatabaseService.getDatabaseService(config, Import.class); + dbImports = DatabaseService.getDatabaseService(config, Import.class); CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager"); Cache<String, Import> importCache = manager.getCache("importcache", String.class, Import.class); @@ -59,16 +59,16 @@ public class ImportResource { Wrapper<List<Import>> res = new Wrapper<>(); if (skip != null && limit != null) - res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, service.count()); + res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbImports.count()); if (res.hasErrors()) return Response.status(Response.Status.BAD_REQUEST).entity(res).build(); try { - List<Import> imports = service.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); + List<Import> imports = dbImports.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addMeta("total", service.count()); + res.addMeta("total", dbImports.count()); else res.addMeta("total", imports.size()); @@ -85,7 +85,7 @@ public class ImportResource { @Path("latest") public Response getLatestImport(@QueryParam("fields") String fields) { Wrapper<Import> res = new Wrapper<>(); - List<Import> latestImport = service.getMultiple(0, 1, "date", false, StringUtils.getFields(fields)); + List<Import> latestImport = dbImports.getMultiple(0, 1, "date", null, false, StringUtils.getFields(fields)); if (latestImport == null || latestImport.size() != 1) { return res.noContent(); @@ -127,13 +127,13 @@ public class ImportResource { if (fields == null || fields.length == 0) { Import importOp = cache.get(id); if (importOp == null) { - importOp = service.getSingle(MongoUtils.objectId(id)); + importOp = dbImports.getSingle(MongoUtils.objectId(id)); if (importOp != null) cache.put(id, importOp); } return importOp; } else - return service.getSingle(MongoUtils.objectId(id), fields); + return dbImports.getSingle(MongoUtils.objectId(id), fields); } } diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java index 7675508c..f158154c 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/TopicResource.java @@ -17,6 +17,8 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.bson.types.ObjectId; import org.ehcache.Cache; import org.ehcache.CacheManager; @@ -30,8 +32,10 @@ import de.vipra.util.MongoUtils; import de.vipra.util.StringUtils; import de.vipra.util.ex.ConfigException; import de.vipra.util.ex.DatabaseException; +import de.vipra.util.model.ArticleFull; import de.vipra.util.model.TopicFull; import de.vipra.util.service.DatabaseService; +import de.vipra.util.service.Service.QueryBuilder; @Path("topics") public class TopicResource { @@ -40,11 +44,15 @@ public class TopicResource { UriInfo uri; final Cache<String, TopicFull> cache; - final DatabaseService<TopicFull, ObjectId> service; + final DatabaseService<TopicFull, ObjectId> dbTopics; + final DatabaseService<ArticleFull, ObjectId> dbArticles; + + public static final Logger log = LogManager.getLogger(TopicResource.class); public TopicResource(@Context ServletContext servletContext) throws ConfigException, IOException { Config config = Config.getConfig(); - service = DatabaseService.getDatabaseService(config, TopicFull.class); + dbTopics = DatabaseService.getDatabaseService(config, TopicFull.class); + dbArticles = DatabaseService.getDatabaseService(config, ArticleFull.class); CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager"); Cache<String, TopicFull> topicCache = manager.getCache("topiccache", String.class, TopicFull.class); @@ -61,16 +69,16 @@ public class TopicResource { Wrapper<List<TopicFull>> res = new Wrapper<>(); if (skip != null && limit != null) - res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, service.count()); + res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbTopics.count()); if (res.hasErrors()) return Response.status(Response.Status.BAD_REQUEST).entity(res).build(); try { - List<TopicFull> topics = service.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); + List<TopicFull> topics = dbTopics.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addMeta("total", service.count()); + res.addMeta("total", dbTopics.count()); else res.addMeta("total", topics.size()); @@ -85,7 +93,8 @@ public class TopicResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Path("{id}") - public Response getTopic(@PathParam("id") String id, @QueryParam("fields") String fields) { + public Response getTopic(@PathParam("id") String id, @QueryParam("fields") String fields) + throws ConfigException, IOException { Wrapper<TopicFull> res = new Wrapper<>(); if (id == null || id.trim().length() == 0) { res.addError(new APIError(Response.Status.BAD_REQUEST, "ID is empty", @@ -101,9 +110,12 @@ public class TopicResource { return res.badRequest(); } - if (topic != null) + if (topic != null) { + List<ArticleFull> articles = dbArticles.getMultiple(QueryBuilder.builder().criteria("topics.topic", topic)); + topic.setArticles(articles); + return res.ok(topic); - else { + } else { res.addError(new APIError(Response.Status.NOT_FOUND, "Resource not found", String.format(Messages.NOT_FOUND, "topic", id))); return res.notFound(); @@ -118,7 +130,7 @@ public class TopicResource { TopicFull topic = wrapper.getData(); Wrapper<TopicFull> res = new Wrapper<>(); try { - service.updateSingle(topic); + dbTopics.updateSingle(topic); cache.put(id, topic); return res.ok(topic); } catch (DatabaseException e) { @@ -132,13 +144,13 @@ public class TopicResource { if (fields == null || fields.length == 0) { TopicFull topic = cache.get(id); if (topic == null) { - topic = service.getSingle(MongoUtils.objectId(id)); + topic = dbTopics.getSingle(MongoUtils.objectId(id)); if (topic != null) cache.put(id, topic); } return topic; } else - return service.getSingle(MongoUtils.objectId(id), fields); + return dbTopics.getSingle(MongoUtils.objectId(id), fields); } } diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java index b05dbef5..c8cb5df4 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/WordResource.java @@ -16,6 +16,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.bson.types.ObjectId; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.CacheConfigurationBuilder; @@ -26,8 +27,10 @@ import de.vipra.rest.model.Wrapper; import de.vipra.util.Config; import de.vipra.util.StringUtils; import de.vipra.util.ex.ConfigException; +import de.vipra.util.model.TopicFull; import de.vipra.util.model.Word; import de.vipra.util.service.DatabaseService; +import de.vipra.util.service.Service.QueryBuilder; @Path("words") public class WordResource { @@ -36,11 +39,13 @@ public class WordResource { UriInfo uri; final Cache<String, Word> cache; - final DatabaseService<Word, String> service; + final DatabaseService<Word, String> dbWords; + final DatabaseService<TopicFull, ObjectId> dbTopics; public WordResource(@Context ServletContext servletContext) throws ConfigException, IOException { Config config = Config.getConfig(); - service = DatabaseService.getDatabaseService(config, Word.class); + dbWords = DatabaseService.getDatabaseService(config, Word.class); + dbTopics = DatabaseService.getDatabaseService(config, TopicFull.class); CacheManager manager = (CacheManager) servletContext.getAttribute("cachemanager"); Cache<String, Word> wordCache = manager.getCache("wordcache", String.class, Word.class); @@ -57,16 +62,16 @@ public class WordResource { Wrapper<List<Word>> res = new Wrapper<>(); if (skip != null && limit != null) - res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, service.count()); + res.addPaginationLinks(uri.getAbsolutePath(), skip, limit, dbWords.count()); if (res.hasErrors()) return res.badRequest(); try { - List<Word> words = service.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); + List<Word> words = dbWords.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addMeta("total", service.count()); + res.addMeta("total", dbWords.count()); else res.addMeta("total", words.size()); @@ -97,9 +102,13 @@ public class WordResource { return res.badRequest(); } - if (word != null) + if (word != null) { + List<TopicFull> topics = dbTopics.getMultiple( + QueryBuilder.builder().fields(false, "index", "created", "modified").criteria("words.word", word)); + word.setTopics(topics); + return res.ok(word); - else { + } else { String msg = String.format(Messages.NOT_FOUND, "word", id); res.addError(new APIError(Response.Status.NOT_FOUND, "Resource not found", msg)); return res.notFound(); @@ -110,13 +119,13 @@ public class WordResource { if (fields == null || fields.length == 0) { Word word = cache.get(id); if (word == null) { - word = service.getSingle(id); + word = dbWords.getSingle(id); if (word != null) cache.put(id, word); } return word; } else - return service.getSingle(id, fields); + return dbWords.getSingle(id, fields); } } diff --git a/vipra-ui/html/topics/show.html b/vipra-ui/html/topics/show.html index 45b65958..38d1648e 100644 --- a/vipra-ui/html/topics/show.html +++ b/vipra-ui/html/topics/show.html @@ -21,7 +21,15 @@ </tbody> </table> -<h4>Words</h4> +<h3>Articles</h3> + +<ul class="list-unstyled"> + <li ng-repeat="article in ::topic.articles"> + <article-link article="article"/> + </li> +</ul> + +<h3>Words</h3> <table class="table table-bordered table-condensed"> <thead> diff --git a/vipra-ui/html/words/show.html b/vipra-ui/html/words/show.html index e69de29b..1ab0373b 100644 --- a/vipra-ui/html/words/show.html +++ b/vipra-ui/html/words/show.html @@ -0,0 +1,18 @@ +<h1 ng-bind="::word.id"></h1> + +<table class="table table-bordered table-condensed"> + <tbody> + <tr> + <th>Created</th> + <td ng-bind="::word.created"></td> + </tr> + </tbody> +</table> + +<h3>Topics</h3> + +<ul class="list-unstyled"> + <li ng-repeat="topic in ::word.topics"> + <topic-link topic="topic"/> + </li> +</ul> \ No newline at end of file diff --git a/vipra-ui/js/controllers.js b/vipra-ui/js/controllers.js index fe19b5f0..0eabe9f4 100644 --- a/vipra-ui/js/controllers.js +++ b/vipra-ui/js/controllers.js @@ -127,6 +127,7 @@ WordFactory.get({id: $stateParams.id}, function(response) { $scope.word = response.data; + $scope.word.created = formatDateTime($scope.word.created); $scope.wordMeta = response.meta; $scope.queryTime = response.$queryTime; }); diff --git a/vipra-ui/js/directives.js b/vipra-ui/js/directives.js index a5681be6..46bff530 100644 --- a/vipra-ui/js/directives.js +++ b/vipra-ui/js/directives.js @@ -18,6 +18,18 @@ } }); + app.directive('articleLink', function() { + return { + scope: { + article: '=' + }, + restrict: 'E', + replace: true, + transclude: true, + template: '<a class="article-link" ui-sref="articles.show({id:article.id})"><span ng-bind="article.title"></span><ng-transclude/></a>' + } + }); + app.directive('queryTime', function() { return { restrict: 'E', diff --git a/vipra-util/src/main/java/de/vipra/util/Pair.java b/vipra-util/src/main/java/de/vipra/util/Pair.java new file mode 100644 index 00000000..23c2e378 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/Pair.java @@ -0,0 +1,35 @@ +package de.vipra.util; + +public class Pair<X, Y> { + + private X x; + private Y y; + + public Pair() {} + + public Pair(X x, Y y) { + this.x = x; + this.y = y; + } + + public X x() { + return x; + } + + public void setX(X x) { + this.x = x; + } + + public Y y() { + return y; + } + + public void setY(Y y) { + this.y = y; + } + + public static <X, Y> Pair<X, Y> pair(X x, Y y) { + return new Pair<>(x, y); + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/model/ArticleStats.java b/vipra-util/src/main/java/de/vipra/util/model/ArticleStats.java index 4449f5b2..afa7d461 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/ArticleStats.java +++ b/vipra-util/src/main/java/de/vipra/util/model/ArticleStats.java @@ -11,20 +11,20 @@ public class ArticleStats implements Serializable { private static final long serialVersionUID = -4712841724990200627L; - private long wordCount; + private Long wordCount; - public long getWordCount() { + public Long getWordCount() { return wordCount; } - public void setWordCount(long wordCount) { + public void setWordCount(Long wordCount) { this.wordCount = wordCount; } public static ArticleStats generateFromText(final String text, final WordMap wordMap) { ArticleStats stats = new ArticleStats(); String[] words = text.split("\\s+"); - stats.setWordCount(words.length); + stats.setWordCount((long) words.length); return stats; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/Import.java b/vipra-util/src/main/java/de/vipra/util/model/Import.java index a8969738..4ed9ce31 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/Import.java +++ b/vipra-util/src/main/java/de/vipra/util/model/Import.java @@ -24,25 +24,25 @@ public class Import implements Model<ObjectId>, Serializable { private Date date; - private long duration; + private Long duration; @QueryIgnore(multi = true) @Reference(ignoreMissing = true) private List<Article> articles; - private int articlesCount; + private Integer articlesCount; @QueryIgnore(multi = true) @Reference(ignoreMissing = true) private List<Topic> topics; - private int topicsCount; + private Integer topicsCount; @QueryIgnore(multi = true) @Reference(ignoreMissing = true) private List<Word> words; - private int wordsCount; + private Integer wordsCount; @Override public ObjectId getId() { @@ -62,11 +62,11 @@ public class Import implements Model<ObjectId>, Serializable { this.date = date; } - public long getDuration() { + public Long getDuration() { return duration; } - public void setDuration(long duration) { + public void setDuration(Long duration) { this.duration = duration; } @@ -80,7 +80,7 @@ public class Import implements Model<ObjectId>, Serializable { articlesCount = articles.size(); } - public int getArticlesCount() { + public Integer getArticlesCount() { return articlesCount; } @@ -94,7 +94,7 @@ public class Import implements Model<ObjectId>, Serializable { topicsCount = topics.size(); } - public int getTopicsCount() { + public Integer getTopicsCount() { return topicsCount; } @@ -108,7 +108,7 @@ public class Import implements Model<ObjectId>, Serializable { wordsCount = words.size(); } - public int getWordsCount() { + public Integer getWordsCount() { return wordsCount; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java index 63750c08..f004d837 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicFull.java @@ -10,6 +10,7 @@ import org.mongodb.morphia.annotations.Embedded; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.PrePersist; +import org.mongodb.morphia.annotations.Transient; import de.vipra.util.Constants; import de.vipra.util.MongoUtils; @@ -25,12 +26,15 @@ public class TopicFull implements Model<ObjectId>, Serializable { private String name; - private int index; + private Integer index; @Embedded @QueryIgnore(multi = true) private List<TopicWord> words; + @Transient + private List<ArticleFull> articles; + private Date created; private Date modified; @@ -57,11 +61,11 @@ public class TopicFull implements Model<ObjectId>, Serializable { this.name = name; } - public int getIndex() { + public Integer getIndex() { return index; } - public void setIndex(int index) { + public void setIndex(Integer index) { this.index = index; } @@ -73,6 +77,14 @@ public class TopicFull implements Model<ObjectId>, Serializable { this.words = topicWords; } + public List<ArticleFull> getArticles() { + return articles; + } + + public void setArticles(List<ArticleFull> articles) { + this.articles = articles; + } + public Date getCreated() { return created; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicRef.java b/vipra-util/src/main/java/de/vipra/util/model/TopicRef.java index 920abdce..c5de02e6 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicRef.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicRef.java @@ -14,7 +14,7 @@ public class TopicRef implements Comparable<TopicRef>, Serializable { private String topicIndex; @Reference(ignoreMissing = true) private Topic topic; - private int count; + private Integer count; public String getTopicIndex() { return topicIndex; @@ -24,11 +24,11 @@ public class TopicRef implements Comparable<TopicRef>, Serializable { this.topicIndex = index; } - public int getCount() { + public Integer getCount() { return count; } - public void setCount(int count) { + public void setCount(Integer count) { this.count = count; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java index abed0a8a..bd79ed2b 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java @@ -20,11 +20,11 @@ public class TopicWord implements Comparable<TopicWord>, Serializable { @JsonProperty("word") private String wordString; - private double likeliness; + private Double likeliness; public TopicWord() {} - public TopicWord(Word word, double likeliness) { + public TopicWord(Word word, Double likeliness) { this.word = word; this.likeliness = likeliness; } @@ -41,11 +41,11 @@ public class TopicWord implements Comparable<TopicWord>, Serializable { return wordString; } - public double getLikeliness() { + public Double getLikeliness() { return likeliness; } - public void setLikeliness(double likeliness) { + public void setLikeliness(Double likeliness) { this.likeliness = likeliness; } diff --git a/vipra-util/src/main/java/de/vipra/util/model/Word.java b/vipra-util/src/main/java/de/vipra/util/model/Word.java index 06758a80..7b5c4ed6 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/Word.java +++ b/vipra-util/src/main/java/de/vipra/util/model/Word.java @@ -2,6 +2,7 @@ package de.vipra.util.model; import java.io.Serializable; import java.util.Date; +import java.util.List; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; @@ -36,6 +37,9 @@ public class Word implements Model<String>, Serializable { @JsonIgnore private String word; + @Transient + private List<TopicFull> topics; + @QueryIgnore(multi = true) private Date created; @@ -73,6 +77,14 @@ public class Word implements Model<String>, Serializable { this.id = word; } + public List<TopicFull> getTopics() { + return topics; + } + + public void setTopics(List<TopicFull> topics) { + this.topics = topics; + } + public boolean isCreated() { return isCreated; } diff --git a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java index da1e780a..692dc93e 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java +++ b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java @@ -13,6 +13,7 @@ import org.mongodb.morphia.query.Query; import de.vipra.util.Config; import de.vipra.util.ListUtils; import de.vipra.util.Mongo; +import de.vipra.util.Pair; import de.vipra.util.an.QueryIgnore; import de.vipra.util.ex.ConfigException; import de.vipra.util.ex.DatabaseException; @@ -57,29 +58,56 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv @Override public List<Type> getMultiple(Integer skip, Integer limit, String sortBy, String... fields) { - return getMultiple(skip, limit, sortBy, true, fields); + return getMultiple(QueryBuilder.builder().skip(skip).limit(limit).sortBy(sortBy).fields(true, fields)); } @Override - public List<Type> getMultiple(Integer skip, Integer limit, String sortBy, boolean defaultIgnore, String... fields) { + public List<Type> getMultiple(Integer skip, Integer limit, String sortBy, Pair<String, Object> criteria, + String... fields) { + return getMultiple( + QueryBuilder.builder().skip(skip).limit(limit).sortBy(sortBy).criteria(criteria).fields(true, fields)); + } + + @Override + public List<Type> getMultiple(Integer skip, Integer limit, String sortBy, Pair<String, Object> criteria, + boolean defaultIgnore, String... fields) { + return getMultiple(QueryBuilder.builder().skip(skip).limit(limit).sortBy(sortBy).fields(true, fields) + .criteria(criteria).defaultIgnore(defaultIgnore)); + } + + @Override + public List<Type> getMultiple(QueryBuilder builder) { Query<Type> q = datastore.createQuery(clazz); - if (skip != null && skip > 0) - q.offset(skip); - if (limit != null && limit > 0) - q.limit(limit); - if (sortBy != null) - q.order(sortBy); - if (fields != null && fields.length > 0) - q.retrievedFields(true, setMinus(fields, ignoredFieldsMulti)); - else if (defaultIgnore && ignoredFieldsMulti.length > 0) + if (builder.getSkip() != null && builder.getSkip() > 0) + q.offset(builder.getSkip()); + if (builder.getLimit() != null && builder.getLimit() > 0) + q.limit(builder.getLimit()); + if (builder.getSortBy() != null) + q.order(builder.getSortBy()); + if (builder.getCriteria() != null) + for (Pair<String, Object> criteria : builder.getCriteria()) + q.field(criteria.x()).equal(criteria.y()); + if (builder.getFields() != null) { + String[] fields = builder.getFields(); + if (builder.isInclude()) { + if (builder.isDefaultIgnore() && ignoredFieldsMulti.length > 0) + fields = setMinus(fields, ignoredFieldsMulti); + q.retrievedFields(true, fields); + } else { + if (builder.isDefaultIgnore() && ignoredFieldsMulti.length > 0) + fields = setPlus(fields, ignoredFieldsMulti); + q.retrievedFields(false, fields); + } + } else if (ignoredFieldsMulti.length > 0) { q.retrievedFields(false, ignoredFieldsMulti); + } List<Type> list = q.asList(); return list; } @Override public List<Type> getAll(String... fields) { - return getMultiple(null, null, null, fields); + return getMultiple(null, null, null, null, fields); } @Override @@ -131,4 +159,14 @@ public class DatabaseService<Type extends Model<IdType>, IdType> implements Serv return a; } + private String[] setPlus(String[] a, String[] b) { + if (a != null && b != null) { + Set<String> sa = new HashSet<>(Arrays.asList(a)); + Set<String> sb = new HashSet<>(Arrays.asList(b)); + sa.addAll(sb); + return sa.toArray(new String[sa.size()]); + } + return a; + } + } diff --git a/vipra-util/src/main/java/de/vipra/util/service/Service.java b/vipra-util/src/main/java/de/vipra/util/service/Service.java index 3282b172..dbdc07ee 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/Service.java +++ b/vipra-util/src/main/java/de/vipra/util/service/Service.java @@ -1,7 +1,9 @@ package de.vipra.util.service; +import java.util.ArrayList; import java.util.List; +import de.vipra.util.Pair; import de.vipra.util.model.Model; public interface Service<Type extends Model<IdType>, IdType, E extends Exception> { @@ -10,9 +12,14 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception List<Type> getMultiple(Integer skip, Integer limit, String sortBy, String... fields) throws E; - List<Type> getMultiple(Integer skip, Integer limit, String sortBy, boolean noDefaultIgnore, String... fields) + List<Type> getMultiple(Integer skip, Integer limit, String sortBy, Pair<String, Object> criteria, String... fields) throws E; + List<Type> getMultiple(Integer skip, Integer limit, String sortBy, Pair<String, Object> criteria, + boolean noDefaultIgnore, String... fields) throws E; + + List<Type> getMultiple(QueryBuilder builder) throws E; + List<Type> getAll(String... fields) throws E; Type createSingle(Type t) throws E; @@ -27,4 +34,88 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception long count() throws E; + public static class QueryBuilder { + + private Integer skip; + private Integer limit; + private String sortBy; + private List<Pair<String, Object>> criteria; + private String[] fields; + private boolean include; + private boolean defaultIgnore = true; + + private QueryBuilder() {} + + public static QueryBuilder builder() { + return new QueryBuilder(); + } + + public QueryBuilder skip(Integer skip) { + this.skip = skip; + return this; + } + + public QueryBuilder limit(Integer limit) { + this.limit = limit; + return this; + } + + public QueryBuilder sortBy(String sortBy) { + this.sortBy = sortBy; + return this; + } + + public QueryBuilder criteria(String field, Object value) { + return criteria(Pair.pair(field, value)); + } + + public QueryBuilder criteria(Pair<String, Object> pair) { + if (criteria == null) { + criteria = new ArrayList<>(); + } + criteria.add(pair); + return this; + } + + public QueryBuilder fields(boolean include, String... strings) { + this.include = include; + this.fields = strings; + return this; + } + + public QueryBuilder defaultIgnore(boolean defaultIgnore) { + this.defaultIgnore = defaultIgnore; + return this; + } + + public Integer getSkip() { + return skip; + } + + public Integer getLimit() { + return limit; + } + + public String getSortBy() { + return sortBy; + } + + public List<Pair<String, Object>> getCriteria() { + return criteria; + } + + public boolean isInclude() { + return include; + } + + public String[] getFields() { + return fields; + } + + public boolean isDefaultIgnore() { + return defaultIgnore; + } + + } + } -- GitLab