public async Task <List <GroupResult> > AggregateCountAsync(string author, GroupTimeRange groupTimeRange, FilterSettings filterSettings)
        {
            /*
             * { "$match" : { "Author" : "test" } },
             * { "$group" : { "_id" : { "$dateToString" : { "format" : "%Y-%m-%d", "date" : "$Created", "timezone" : "Europe/Berlin" } }, "count" : { "$sum" : 1 } } },
             * { "$project" : { "_id" : 0, "Key" : "$_id", "Value" : "$count" } },
             * { "$sort" : { "Key" : 1 } }
             */

            var projectStage = new BsonDocument
            {
                { "_id", 0 },
                { "Key", "$_id" },
                { "Value", "$count" }
            };

            var watch           = Stopwatch.StartNew();
            var aggregateResult = this.documentCollection.Aggregate()
                                  .Match(this.BuildMatchStage(author, filterSettings))
                                  .Group(MongoOp.GroupByDate("$Created", groupTimeRange, MongoOp.Count("count")))
                                  .Project(projectStage)
                                  .Sort(MongoOp.SortBy("Key", asc: true));

            var bsonResult = await aggregateResult.ToListAsync();

            var result = bsonResult.Select(doc => BsonSerializer.Deserialize <GroupResult>(doc))
                         .ToList();

            watch.Stop();

            if (this.logger.IsEnabled(LogLevel.Debug))
            {
                this.logger.LogDebug("Explore took {ms}ms.", watch.ElapsedMilliseconds);
            }

            return(result);
        }
        private FilterDefinition <Document> BuildMatchStage(string author, FilterSettings filterSettings)
        {
            var filterList = new List <FilterDefinition <Document> >();

            filterList.Add(this.filterBuilder.Where(d => d.Author == author));

            if (filterSettings.Tags.Any())
            {
                filterList.Add(this.filterBuilder.All(doc => doc.Tags, filterSettings.Tags));
            }

            if (filterSettings.Values.Any())
            {
                var valueFilter = filterSettings.Values.Select(v => MongoOp.Exists($"Values.{v}"));
                filterList.Add(new BsonDocument()
                {
                    { "$or", new BsonArray(valueFilter) }
                });
            }

            var matchStage = this.filterBuilder.And(filterList);

            return(matchStage);
        }
        public async Task <List <ValuesResult> > AggregateValuesAsync(string author, GroupTimeRange groupTimeRange, Aggregate aggregation, FilterSettings filterSettings)
        {
            /*
             *  db.getCollection('documents').aggregate([
             *      { "$match" : { "Author" : "test" } },
             *      { "$project" : { "date" : { "$dateToString" : { "format" : "%Y-%m-%d", "date" : "$Created", "timezone" : "Europe/Berlin" } }, "values" : { "$objectToArray" : "$Values" } } },
             *      { "$unwind" : "$values" },
             *      { "$group" : { "_id" : { "date" : "$date", "key" : "$values.k" }, "value" : { "$sum" : "$values.v" } } },
             *      { "$group" : { "_id" : "$_id.key", "values" : { "$push" : { "$arrayToObject" : [[{ "k" : "$_id.date", "v" : "$value" }]] } } } },
             *      { "$project": { "key": "$_id", "values": 1, "_id": 0 } }
             *  ])
             * */

            var projectStage = new BsonDocument
            {
                { "date", MongoOp.DateToString("Created", groupTimeRange) },
                { "values", new BsonDocument {
                      { "$objectToArray", "$Values" }
                  } }
            };

            var aggregationOp = aggregation switch
            {
                Aggregate.Sum => "$sum",
                Aggregate.Average => "$avg",
                _ => throw new InvalidOperationException($"{aggregation} not supported")
            };

            var groupByDateAndKey = new BsonDocument
            {
                { "_id", new BsonDocument {
                      { "date", "$date" }, { "key", "$values.k" }
                  } },
                { "value", new BsonDocument {
                      { aggregationOp, "$values.v" }
                  } }
            };

            var groupByDateOnly = new BsonDocument
            {
                { "_id", "$_id.key" },
                { "Values", new BsonDocument {
                      { "$push", new BsonDocument {
                            { "$arrayToObject", new BsonArray {
                                                           new BsonArray {
                                                               new BsonDocument {
                                                                   { "k", "$_id.date" }, { "v", "$value" }
                                                               }
                                                           }
                                                       } }
                        } }
                  } }
            };

            var jssson = groupByDateOnly.ToJson();

            var endProjectStage = new BsonDocument
            {
                { "Key", "$_id" },
                { "Values", 1 },
                { "_id", 0 },
            };

            var watch           = Stopwatch.StartNew();
            var aggregateResult = this.documentCollection.Aggregate()
                                  .Match(this.BuildMatchStage(author, filterSettings))
                                  .Project(projectStage)
                                  .Unwind("values")
                                  .Group(groupByDateAndKey)
                                  .Group(groupByDateOnly)
                                  .Project(endProjectStage);

            var bsonResult = await aggregateResult.ToListAsync();

            var result = bsonResult.Select(doc => BsonSerializer.Deserialize <ValuesResult>(doc))
                         .ToList();

            watch.Stop();

            if (this.logger.IsEnabled(LogLevel.Debug))
            {
                this.logger.LogDebug("Explore took {ms}ms.", watch.ElapsedMilliseconds);
            }

            return(result);
        }