From 6a1f4318b0da85b24686267c72ae6e24f17594fe Mon Sep 17 00:00:00 2001 From: Eike Cochu <eike@cochu.com> Date: Fri, 19 Feb 2016 22:29:45 +0100 Subject: [PATCH] updated info page, fixed procession bugs info page now shows more information fixed a bug where text procession would not use lemmatized words fixed a bug where text procession would add words twice added constants for minimum likeliness and minimum word frequency added query to count method --- .../vipra/rest/resource/ArticleResource.java | 2 +- .../de/vipra/rest/resource/InfoResource.java | 32 +++- .../de/vipra/rest/resource/TopicResource.java | 16 +- .../de/vipra/rest/resource/WordResource.java | 2 +- .../java/de/vipra/cmd/lda/JGibbAnalyzer.java | 7 + .../de/vipra/cmd/option/StatsCommand.java | 6 +- .../java/de/vipra/cmd/option/TestCommand.java | 2 +- vipra-ui/app/html/about.html | 163 ++++++++++++++++++ vipra-ui/app/html/articles/show.html | 2 +- vipra-ui/app/html/topics/articles.html | 23 ++- vipra-ui/app/html/topics/show.html | 5 +- vipra-ui/app/html/words/show.html | 2 +- vipra-ui/app/js/app.js | 2 +- vipra-ui/app/js/controllers.js | 35 +++- .../main/java/de/vipra/util/Constants.java | 9 +- .../java/de/vipra/util/model/TopicFull.java | 2 +- .../de/vipra/util/service/MongoService.java | 44 +++-- .../java/de/vipra/util/service/Service.java | 2 +- 18 files changed, 306 insertions(+), 50 deletions(-) diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java index d37c7e36..2d93b45e 100644 --- a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java @@ -63,7 +63,7 @@ public class ArticleResource { List<ArticleFull> articles = dbArticles.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addHeader("total", dbArticles.count()); + res.addHeader("total", dbArticles.count(null)); else res.addHeader("total", articles.size()); diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java index 9a9aac2a..fbcd8576 100644 --- a/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/InfoResource.java @@ -16,6 +16,7 @@ import org.bson.types.ObjectId; import de.vipra.rest.model.ResponseWrapper; import de.vipra.util.BuildInfo; import de.vipra.util.Config; +import de.vipra.util.Constants; import de.vipra.util.NestedMap; import de.vipra.util.StringUtils; import de.vipra.util.model.Article; @@ -64,9 +65,34 @@ public class InfoResource { info.put("app.builddate", buildInfo.getBuildDate()); // database info - info.put("db.articles", dbArticles.count()); - info.put("db.topics", dbTopics.count()); - info.put("db.words", dbWords.count()); + info.put("db.articles", dbArticles.count(null)); + info.put("db.topics", dbTopics.count(null)); + info.put("db.words", dbWords.count(null)); + + // configuration + info.put("config.analyzer", config.analyzer); + info.put("config.processor", config.processor); + info.put("config.windowres", config.windowResolution); + + // constants + info.put("const.importbuf", Constants.IMPORT_BUFFER_MAX); + info.put("const.esboosttopics", Constants.ES_BOOST_TOPICS); + info.put("const.esboosttitles", Constants.ES_BOOST_TITLES); + info.put("const.topicautoname", Constants.TOPIC_AUTO_NAMING_WORDS); + info.put("const.ktopics", Constants.K_TOPICS); + info.put("const.ktopicwords", Constants.K_TOPIC_WORDS); + info.put("const.likeprecision", Constants.LIKELINESS_PRECISION); + info.put("const.minimumlike", Constants.MINIMUM_LIKELINESS); + info.put("const.topicthresh", Constants.TOPIC_THRESHOLD); + info.put("const.docminfreq", Constants.DOCUMENT_MIN_WORD_FREQ); + info.put("const.docminlength", Constants.DOCUMENT_MIN_LENGTH); + info.put("const.charsdisallow", Constants.CHARS_DISALLOWED); + info.put("const.regexemail", Constants.REGEX_EMAIL); + info.put("const.regexurl", Constants.REGEX_URL); + info.put("const.regexnumber", Constants.REGEX_NUMBER); + info.put("const.regexchar", Constants.REGEX_SINGLECHAR); + info.put("const.excerptlength", Constants.EXCERPT_LENGTH); + info.put("const.dateformat", Constants.DATETIME_FORMAT); } catch (Exception e) { info.put("error", e.getMessage()); } diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java index 6a0d7bde..acbbf0a9 100644 --- a/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/TopicResource.java @@ -65,7 +65,7 @@ public class TopicResource { List<TopicFull> topics = dbTopics.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addHeader("total", dbTopics.count()); + res.addHeader("total", dbTopics.count(null)); else res.addHeader("total", topics.size()); @@ -112,14 +112,24 @@ public class TopicResource { @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Path("{id}/articles") - public Response getArticles(@PathParam("id") String id, @QueryParam("fields") String fields) { + public Response getArticles(@PathParam("id") String id, @QueryParam("skip") Integer skip, + @QueryParam("limit") Integer limit, @QueryParam("sort") @DefaultValue("title") String sortBy, + @QueryParam("fields") String fields) { ResponseWrapper<List<ArticleFull>> res = new ResponseWrapper<>(); try { Topic topic = new Topic(MongoUtils.objectId(id)); - QueryBuilder query = QueryBuilder.builder().criteria("topics.topic", topic); + QueryBuilder query = QueryBuilder.builder().criteria("topics.topic", topic).skip(skip).limit(limit) + .sortBy(sortBy); if (fields != null && !fields.isEmpty()) query.fields(true, StringUtils.getFields(fields)); + List<ArticleFull> articles = dbArticles.getMultiple(query); + + if ((skip != null && skip > 0) || (limit != null && limit > 0)) + res.addHeader("total", dbArticles.count(QueryBuilder.builder().criteria("topics.topic", topic))); + else + res.addHeader("total", articles.size()); + return res.ok(articles); } catch (Exception e) { e.printStackTrace(); diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java index 951e398c..9cdb550a 100644 --- a/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java +++ b/vipra-backend/src/main/java/de/vipra/rest/resource/WordResource.java @@ -57,7 +57,7 @@ public class WordResource { List<Word> words = dbWords.getMultiple(skip, limit, sortBy, StringUtils.getFields(fields)); if ((skip != null && skip > 0) || (limit != null && limit > 0)) - res.addHeader("total", dbWords.count()); + res.addHeader("total", dbWords.count(null)); else res.addHeader("total", words.size()); diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/JGibbAnalyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/JGibbAnalyzer.java index 1845ac5a..8fa94370 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/JGibbAnalyzer.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/JGibbAnalyzer.java @@ -106,8 +106,15 @@ public class JGibbAnalyzer extends Analyzer { String[] parts = nextLine.trim().split("\\s+"); try { Word word = wordMap.get(parts[0]); + // round likeliness precision double likeliness = NumberUtils.roundToPrecision(Double.parseDouble(parts[1]), Constants.LIKELINESS_PRECISION); + + // check if word likely enough to relate to + // topic + if (likeliness < Constants.MINIMUM_LIKELINESS) + continue; + TopicWord topicWord = new TopicWord(word, likeliness); topicWords.add(topicWord); } catch (NumberFormatException e) { diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java index 31d7c3c5..225d13bf 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/StatsCommand.java @@ -20,9 +20,9 @@ public class StatsCommand implements Command { private MongoService<Word, String> dbWords; private void stats() { - log.info("# of articles: " + dbArticles.count()); - log.info("# of topics : " + dbTopics.count()); - log.info("# of words : " + dbWords.count()); + log.info("# of articles: " + dbArticles.count(null)); + log.info("# of topics : " + dbTopics.count(null)); + log.info("# of words : " + dbWords.count(null)); } @Override diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java index 96abc641..c57fe1ea 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/TestCommand.java @@ -24,7 +24,7 @@ public class TestCommand implements Command { // test if database is accessible log.info("testing mongodb connection..."); MongoService<Article, ObjectId> dbArticles = MongoService.getDatabaseService(config, Article.class); - dbArticles.count(); + dbArticles.count(null); // test if elasticsearch is accessible log.info("testing elasticsearch connection..."); diff --git a/vipra-ui/app/html/about.html b/vipra-ui/app/html/about.html index 2f363e4a..d040c3ea 100644 --- a/vipra-ui/app/html/about.html +++ b/vipra-ui/app/html/about.html @@ -98,4 +98,167 @@ </tr> </tbody> </table> + + <h3>Configuration</h3> + + <table class="table table-bordered table-fixed"> + <tbody> + <tr> + <th style="width:33%">Analyzer</th> + <td ng-bind-template="{{::info.config.analyzer}}"></td> + </tr> + <tr> + <th>Processor</th> + <td ng-bind-template="{{::info.config.processor}}"></td> + </tr> + <tr> + <th>Window resolution</th> + <td ng-bind-template="{{::info.config.windowres}}"></td> + </tr> + </tbody> + </table> + + <h3>Constants</h3> + + <table class="table table-bordered table-fixed"> + <tbody> + <tr> + <th style="width:33%">Import buffer</th> + <td ng-bind-template="{{::info.const.importbuf}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The number of articles that are imported in one go. + </td> + </tr> + <tr> + <th>ES boost topics</th> + <td ng-bind-template="{{::info.const.esboosttopics}}"></td> + </tr> + <tr> + <th>ES boost titles</th> + <td ng-bind-template="{{::info.const.esboosttitles}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + Boost parameters to modify the importance of search fields. Default is 1, all greater values raise field importance. + </td> + </tr> + <tr> + <th>Topic auto naming words</th> + <td ng-bind-template="{{::info.const.topicautoname}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The number of topic words to be used to automatically generate a topic name. Words are sorted by descending topic association likeliness. + </td> + </tr> + <tr> + <th>K topics</th> + <td ng-bind-template="{{::info.const.ktopics}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The number of topics to be generated in the topic modeling process. + </td> + </tr> + <tr> + <th>K topic words</th> + <td ng-bind-template="{{::info.const.ktopicwords}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The maximum number of words that are associated to a single topic. + </td> + </tr> + <tr> + <th>Likeliness precision</th> + <td ng-bind-template="{{::info.const.likeprecision}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The resulting likeliness precision of topic words. + </td> + </tr> + <tr> + <th>Minimum likeliness</th> + <td ng-bind-template="{{::info.const.minimumlike}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The minimum likeliness of topic words. Words with a lesser likeliness are ignored. + </td> + </tr> + <tr> + <th>Topic share threshold</th> + <td ng-bind-template="{{::info.const.topicthresh}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The minimum share value of a topic to be considered associated to an article. Topics with a lower share are ignored. + </td> + </tr> + <tr> + <th>Word minimum frequency</th> + <td ng-bind-template="{{::info.const.docminfreq}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The minimum word frequency for unique words in an article to be used in the topic modeling process. Unique words with a lower frequency are ignored. + </td> + </tr> + <tr> + <th>Document minimum word count</th> + <td ng-bind-template="{{::info.const.docminlength}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The minimum article word count. Articles with less words are not included in the topic modeling process. + </td> + </tr> + <tr> + <th>Regex disallowed chars</th> + <td ng-bind-template="{{::info.const.charsdisallow}}"></td> + </tr> + <tr> + <th>Regex email</th> + <td ng-bind-template="{{::info.const.regexemail}}"></td> + </tr> + <tr> + <th>Regex URL</th> + <td ng-bind-template="{{::info.const.regexurl}}"></td> + </tr> + <tr> + <th>Regex number</th> + <td ng-bind-template="{{::info.const.regexnumber}}"></td> + </tr> + <tr> + <th>Regex single characters</th> + <td ng-bind-template="{{::info.const.regexchar}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + Regular expressions used in the article text procession. These regular expressions are used to remove unwanted text passages. Each match is replaced by empty strings. + </td> + </tr> + <tr> + <th>Excerpt length</th> + <td ng-bind-template="{{::info.const.excerptlength}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The maximum excerpt length that is inserted in the ElasticSearch index for display on the search result page. + </td> + </tr> + <tr> + <th>Date time format</th> + <td ng-bind-template="{{::info.const.dateformat}}"></td> + </tr> + <tr class="well"> + <td colspan="2"> + The date and time format for backend-frontend communication. This is not the format used for frontend display, which is based on localization. + </td> + </tr> + </tbody> + </table> </div> \ No newline at end of file diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html index 00b486cf..e2e4fff5 100644 --- a/vipra-ui/app/html/articles/show.html +++ b/vipra-ui/app/html/articles/show.html @@ -1,4 +1,4 @@ -<div ng-cloak> +<div ui-view ng-cloak> <div class="page-header"> <h1 ng-bind="::article.title"></h1> diff --git a/vipra-ui/app/html/topics/articles.html b/vipra-ui/app/html/topics/articles.html index 90d1e2d0..40c7f7b7 100644 --- a/vipra-ui/app/html/topics/articles.html +++ b/vipra-ui/app/html/topics/articles.html @@ -1,4 +1,4 @@ -<div ng-cloak> +<div ui-view ng-cloak> <div class="page-header"> <h1> <div ng-bind="topic.name" ng-hide="isRename"></div> @@ -22,17 +22,24 @@ <table class="item-actions"> <tr> <td> - <bs-dropdown label="Actions"> - <li><a ng-click="startRename()">Rename</a></li> - </bs-dropdown> - </td> - <td> - <a class="btn btn-default" ui-sref="network({type:'topics', id:topic.id})">Network graph</a> + <a class="btn btn-default" ui-sref="topics.show({id:topic.id})">Back</a> </td> </tr> </table> </div> - <h3>Info <hide-link target="#info"/></h3> + <h3>Articles</h3> + <div class="well"> + Found <span ng-bind="articlesTotal"></span> articles in the database.<br> + </div> + + <ul class="dashed"> + <li ng-repeat="article in articles"> + <a ui-sref="articles.show({id: article.id})" ng-bind="::article.title"></a> + </li> + </ul> + + <pagination total="articlesTotal" page="page" limit="limit" change="changePage"/> + </div> \ No newline at end of file diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html index 964d9743..0ebb0cf6 100644 --- a/vipra-ui/app/html/topics/show.html +++ b/vipra-ui/app/html/topics/show.html @@ -1,4 +1,4 @@ -<div ng-cloak> +<div ui-view ng-cloak> <div class="page-header"> <h1> <div ng-bind="topic.name" ng-hide="isRename"></div> @@ -29,6 +29,9 @@ <td> <a class="btn btn-default" ui-sref="network({type:'topics', id:topic.id})">Network graph</a> </td> + <td> + <a class="btn btn-default" ui-sref="topics.show.articles({id:topic.id})">Articles</a> + </td> </tr> </table> </div> diff --git a/vipra-ui/app/html/words/show.html b/vipra-ui/app/html/words/show.html index 445cc90a..6174e8c3 100644 --- a/vipra-ui/app/html/words/show.html +++ b/vipra-ui/app/html/words/show.html @@ -1,4 +1,4 @@ -<div ng-cloak> +<div ui-view ng-cloak> <div class="page-header"> <h1 ng-bind="::word.id"></h1> </div> diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index a2d1b64e..acc34073 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -71,7 +71,7 @@ }); $stateProvider.state('topics.show.articles', { - url: '/articles', + url: '/articles?page', templateUrl: Vipra.const.tplBase + '/topics/articles.html', controller: 'TopicsArticlesController' }); diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index 684e4336..ee2b2baf 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -307,8 +307,11 @@ // calculate percentage share var topicShareSeries = [], topics = $scope.article.topics; + topicsCount = 0; + for(var i = 0; i < topics.length; i++) + topicsCount += topics[i].count; for(var i = 0; i < topics.length; i++) { - var share = Vipra.toPercent(topics[i].count / $scope.article.stats.wordCount); + var share = Vipra.toPercent(topics[i].count / topicsCount); topics[i].share = share; topicShareSeries.push({name: topics[i].topic.name.ellipsize(20), y: share}); } @@ -418,12 +421,32 @@ /** * Topic Show Articles route */ - app.controller('TopicArticlesController', ['$scope', '$stateParams', 'TopicFactory', - function($scope, $stateParams, TopicFactory) { + app.controller('TopicsArticlesController', ['$scope', '$stateParams', 'Store', 'TopicFactory', + function($scope, $stateParams, Store, TopicFactory) { - TopicFactory.articles({id: $stateParams.id}, function(data) { - $scope.articles = data; - }); + $scope.page = Math.max($stateParams.page || 1, 1); + $scope.limit = Vipra.const.pageSize; + $scope.sort = Store('sortarticles') || 'title'; + $scope.order = Store('orderarticles') || ''; + + $scope.reload = function() { + TopicFactory.articles({ + id: $stateParams.id, + skip: ($scope.page-1)*$scope.limit, + limit: $scope.limit, + sort: $scope.order+$scope.sort + }, function(data, headers) { + $scope.articles = data; + $scope.articlesTotal = headers("V-Total"); + }); + }; + + $scope.changePage = function(page) { + $scope.page = page; + $scope.reload(); + }; + + $scope.reload(); }]); diff --git a/vipra-util/src/main/java/de/vipra/util/Constants.java b/vipra-util/src/main/java/de/vipra/util/Constants.java index 9f431ebb..32d35b3e 100644 --- a/vipra-util/src/main/java/de/vipra/util/Constants.java +++ b/vipra-util/src/main/java/de/vipra/util/Constants.java @@ -62,7 +62,7 @@ public class Constants { * The number of words to be used to generate a topic name. The top n words * (sorted by likeliness) are used to generate a name for unnamed topics. */ - public static final int AUTO_TOPIC_WORDS = 4; + public static final int TOPIC_AUTO_NAMING_WORDS = 4; /** * Number of topics to discover with topic modeling, if the selected topic @@ -81,6 +81,11 @@ public class Constants { * belong to topics. */ public static final int LIKELINESS_PRECISION = 6; + + /** + * Minimum likeliness of words. Words with lower likeliness are ignored + */ + public static final double MINIMUM_LIKELINESS = 0; /** * Topics with a share greater or equal to this number are regarded as @@ -93,7 +98,7 @@ public class Constants { * below this frequency in a document are filtered out before generating the * topic model. */ - public static final int DOCUMENT_MIN_WORD_FREQ = 20; + public static final int DOCUMENT_MIN_WORD_FREQ = 10; /** * Minumum number of words per document. 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 126abc28..6dd42ea1 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 @@ -114,7 +114,7 @@ public class TopicFull implements Model<ObjectId>, Serializable { public static String getNameFromWords(List<TopicWord> words) { String name = null; if (words != null && words.size() > 0) { - int size = Math.min(Constants.AUTO_TOPIC_WORDS, words.size()); + int size = Math.min(Constants.TOPIC_AUTO_NAMING_WORDS, words.size()); List<String> topWords = new ArrayList<>(size); for (int i = 0; i < size; i++) { topWords.add(words.get(i).getWord().getId()); diff --git a/vipra-util/src/main/java/de/vipra/util/service/MongoService.java b/vipra-util/src/main/java/de/vipra/util/service/MongoService.java index e7bf6d59..2f4a4bc4 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/MongoService.java +++ b/vipra-util/src/main/java/de/vipra/util/service/MongoService.java @@ -60,12 +60,12 @@ public class MongoService<Type extends Model<IdType>, IdType> implements Service if (id == null) throw new DatabaseException(new NullPointerException("id is null")); - Query<Type> q = datastore.createQuery(clazz).field("_id").equal(id); + Query<Type> query = datastore.createQuery(clazz).field("_id").equal(id); if (fields != null && fields.length > 0) - q.retrievedFields(true, fields); + query.retrievedFields(true, fields); else if (!allFields && ignoredFieldsSingleQuery.length > 0) - q.retrievedFields(false, ignoredFieldsSingleQuery); - Type t = q.get(); + query.retrievedFields(false, ignoredFieldsSingleQuery); + Type t = query.get(); return t; } @@ -83,32 +83,32 @@ public class MongoService<Type extends Model<IdType>, IdType> implements Service @Override public List<Type> getMultiple(QueryBuilder builder) { - Query<Type> q = datastore.createQuery(clazz); + Query<Type> query = datastore.createQuery(clazz); if (builder != null) { if (builder.getSkip() != null && builder.getSkip() > 0) - q.offset(builder.getSkip()); + query.offset(builder.getSkip()); if (builder.getLimit() != null && builder.getLimit() > 0) - q.limit(builder.getLimit()); + query.limit(builder.getLimit()); if (builder.getSortBy() != null) - q.order(builder.getSortBy()); + query.order(builder.getSortBy()); if (builder.getCriteria() != null) for (Pair<String, Object> criteria : builder.getCriteria()) - q.field(criteria.x()).equal(criteria.y()); + query.field(criteria.x()).equal(criteria.y()); if (builder.getFields() != null) { String[] fields = builder.getFields(); if (builder.isInclude()) { - q.retrievedFields(true, fields); + query.retrievedFields(true, fields); } else { - q.retrievedFields(false, fields); + query.retrievedFields(false, fields); } } else if (ignoredFieldsMultiQuery.length > 0) { - q.retrievedFields(false, ignoredFieldsMultiQuery); + query.retrievedFields(false, ignoredFieldsMultiQuery); } } else if (ignoredFieldsMultiQuery.length > 0) { - q.retrievedFields(false, ignoredFieldsMultiQuery); + query.retrievedFields(false, ignoredFieldsMultiQuery); } - List<Type> list = q.asList(); + List<Type> list = query.asList(); return list; } @@ -209,8 +209,20 @@ public class MongoService<Type extends Model<IdType>, IdType> implements Service } @Override - public long count() { - return datastore.getCount(clazz); + public long count(QueryBuilder builder) { + if (builder == null) + return datastore.getCount(clazz); + + Query<Type> query = datastore.createQuery(clazz); + if (builder.getSkip() != null && builder.getSkip() > 0) + query.offset(builder.getSkip()); + if (builder.getLimit() != null && builder.getLimit() > 0) + query.limit(builder.getLimit()); + if (builder.getCriteria() != null) + for (Pair<String, Object> criteria : builder.getCriteria()) + query.field(criteria.x()).equal(criteria.y()); + + return datastore.getCount(query); } public static <Type extends Model<IdType>, IdType> MongoService<Type, IdType> getDatabaseService(Config config, 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 c1b9725e..d14eba74 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 @@ -145,7 +145,7 @@ public interface Service<Type extends Model<IdType>, IdType, E extends Exception * @return number of entities in the database * @throws E */ - long count() throws E; + long count(QueryBuilder builder) throws E; /** * QueryBuilder instances are used to create complex queries for use with -- GitLab