MongoDB С# агрегация драйверов между датами возвращает нулевые поля

Я использую драйвер С# для mongodb и хочу использовать запрос агрегирования для веб-API, который я создаю. Для моего запроса на агрегирование меня интересует класс профиля, который имеет имена пользователей, даты и шаги. Я хочу создать запрос, который выбирает имена пользователей и получает их общие шаги за заданную неделю, в порядке убывания по общим шагам. Я просто хочу показать свое имя пользователя и их общие действия.

Когда я пытаюсь выполнить запрос агрегирования, у меня возникает проблема с некоторыми моими полями, которые появляются как null. Итак, я считаю, что мой запрос не структурирован правильно.

У меня есть класс "Профиль", который я использую для своих данных в настоящее время.

[BsonIgnoreExtraElements]
[DataContract]
public class Profile 
{   
    [DataMember]
    public string Username { get; set; }

    [DataMember]
    public DateTime Date { get; set; }

    [DataMember]
    public uint? Steps { get; set; }
}

Я создал некоторые тестовые данные, например, для профилей, используя следующие

//Test data
for (uint index = 1; index < 20; index++)
{      
    Profile aprofile = new Profile
    {   
        Username = string.Format("testuser{0}", index),
        Date = RandomDay(),
        Steps = (index + index + index)*2

    };
    AddProfile(aprofile);
}

Если я запустил код несколько раз и запрошу конкретного пользователя, я получаю данные, подобные этому:

[{"Username":"testuser1","Date":"2014-07-03T00:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-07T05:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-17T05:00:00Z","Steps":6},
{"Username":"testuser1","Date":"2014-07-18T05:00:00Z","Steps":6}]

Затем у меня есть несколько статических методов, чтобы найти самую раннюю дату и самую последнюю дату для моего запроса на агрегацию.

//gets a datetime for the earlist date and time possible for the current week
public static DateTime GetStartOfCurrentWeek()
{
    int DaysToSubtract = (int)DateTime.Now.DayOfWeek;
    DateTime dt = DateTime.Now.Subtract(TimeSpan.FromDays(DaysToSubtract));
    return new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0, 0);
}

//gets a datetime for the latest date and time possible for the current week
public static DateTime GetEndOfCurrentWeek()
{
    DateTime dt = GetStartOfCurrentWeek().AddDays(6);
    return new DateTime(dt.Year, dt.Month, dt.Day, 23, 59, 59, 999);
}

Моя попытка запроса агрегирования ниже.

//Here is my aggregation query to get all profiles within a given week, 
public IEnumerable<Profile> GetAllProfiles()
{
    DateTime StartofWeek = GetStartOfCurrentWeek();
    DateTime EndofWeek = GetEndOfCurrentWeek();


    var match = new BsonDocument 
                {{ "$match", new BsonDocument 
                     {{ "Date", new BsonDocument {
                            {"$gte", StartofWeek},
                            {"$lt", EndofWeek}
                      }}}
                }};

    var group = new BsonDocument
                {{"$group",
                    new BsonDocument
                    {{ "_id", new BsonDocument
                         {{"id", "$Username"},

                    }},
                         {"Steps",  new BsonDocument
                              {{"$sum", "$Steps"}}
                    }}
                }};


    var sort =  new BsonDocument
                {{"$sort", new BsonDocument
                    {{"Steps", -1}}

                }};    



    var pipeline = new[] {match, group, sort};      

    var args = new AggregateArgs { Pipeline = pipeline, OutputMode = AggregateOutputMode.Inline };

    // run the aggregation query and get a list of BsonDocuments
    IEnumerable<BsonDocument> documents = _profiles.Aggregate(args);
}

Тем не менее, результаты, которые я получаю, показывают имена пользователей как Null и даты как null.

[{"Username":null,"Date":"0001-01-01T00:00:00","Steps":96},
{"Username":null,"Date":"0001-01-01T00:00:00","Steps":66},
{"Username":null,"Date":"0001-01-01T00:00:00","Steps":24}]

Что мне нужно сделать, чтобы получить мой массив документов, чтобы отображались имена пользователей вместе с полными шагами, которые у меня есть (которые, похоже, работают). Я не хочу Даты в результатах моих запросов. Просто имя пользователя и их общие действия на этой неделе.

Ответ 1

Я действительно не понимаю, почему вам нужно запросить свое монго с помощью BSon. У вас есть свой модельный класс. Агрегация, которую вы пытаетесь выполнить, будет выглядеть так:

var result = collection.Aggregate()
    .Match(r => r.Date >= StartofWeek && r.Date < EndofWeek)
    .Group(r=>r.Username, r=> new {UserName = r.Key, Dates  = r.Select(d=>d.Date),  Steps = r.Sum(x=>x.Steps)})
    .ToList();

Обратите внимание, что вы можете получить не одну дату, а массив, поэтому я суммирую ее как Dates.

Причина, почему вы не видите Дата и имя пользователя - это то, что вы не запрашиваете его, в операторе вашей группы я вижу только сумму шагов. если вы действительно хотите продолжить запрос таким образом, вы должны добавить его на свой групповой этап, например:

var group = new BsonDocument
             {
                    { "_id", new BsonDocument{{"id", "$Username"},
             }},
             {"Steps",  new BsonDocument{{"$sum", "$Steps"}}},
             { "Dates", new BsonDocument{{"$addToSet", "$Date"}} },
             { "Username", new BsonDocument{{"$first", "$Username"}} },
        };

Таким образом, я могу видеть имя пользователя и даты в полученном BsonDocument.

Но, как я уже сказал, этот способ запросить Mongo не самый лучший в С#

Ответ 2

У вашего этапа group есть один недостаток, и вы не указали поле, которое вы хотите, вне вашего запроса.

Просто изменив этап group, вы должны получить нужное поле.

var group = new BsonDocument
    {{"$group",
        new BsonDocument
        {
            { "_id", new BsonDocument
                {{"id", "$Username"}}
            },
            { "Steps",  new BsonDocument
                {{"$sum", "$Steps"}}
            },
            { "Username", new BsonDocument
                {{"$first", "$Username"}} // since the rest of the selected object is groupped, we should give a strategy to pick a $Username, and it is safe to use $first aggregate function here
            }
        }
    }};

И если вам не нравится дата в результатах вывода, просто добавьте этап project и измените свой выходной документ.