RavenDB Map/Reduce/Transform для вложенных массивов переменной длины

Я новичок в RavenDB, и я люблю это до сих пор. У меня есть один оставшийся индекс для моего проекта.

Эта проблема

У меня тысячи ответов на опросы (т.е.Submissions"), и каждая заявка имеет массив ответов на конкретные вопросы (то есть"Answers"), и у каждого ответа есть набор параметров, которые были выбраны (т.е."Values").

Вот какой Submission в основном выглядит так:

{
  "SurveyId": 1,
  "LocationId": 1,
  "Answers": [
    {
      "QuestionId": 1,
      "Values": [2,8,32],
      "Comment": null
    },
    {
      "QuestionId": 2,
      "Values": [4],
      "Comment": "Lorem ipsum"
    },
    ...more answers...
  ]
}

Еще проблема: я должен фильтровать по SurveyId, LocationId, QuestionId, дате создания. Насколько я понимаю, это делается во время запроса... Мне просто нужно убедиться, что эти свойства присутствуют в результате преобразования (или это результат сокращения? Или оба?). Если я прав, то это не проблема.

Требуемый результат

Нам нужен один объект на вопрос для каждого опроса, который дает сумму каждого варианта. Надеюсь, это говорит само за себя:

[
    {
        SurveyId: 1,
        QuestionId: 1,
        NumResponses: 976,
        NumComments: 273,
        Values: {
            "1": 452, // option 1 selected 452 times
            "2": 392, // option 2 selected 392 times
            "4": 785  // option 4 selected 785 times
        }
    },
    {
        SurveyId: 1,
        QuestionId: 2,
        NumResponses: 921,
        NumComments: 46,
        Values: {
            "1": 325,
            "2": 843,
            "4": 119,
            "8": 346,
            "32": 524
        }
    },
    ...
]

Моя попытка

Я не очень далеко продвинулся, и я думаю, что этот пост ведет меня по правильному пути, но он не помогает мне со списком ценностей. Я искал и искал, но не могу найти направление для того, что делать с таким вложенным массивом. Вот что у меня так далеко:

КАРТА:

from submission in docs.Submissions
from answer in submission.Answers
where answer.WasSkipped != true && answer.Value != null
select new {
    SubmissionDate = submission["@metadata"]["Last-Modified"],
    SurveyId = submission.SurveyId,
    LocationId = submission.LocationId,
    QuestionId = answer.QuestionId,
    Value = answer.Value
}

УМЕНЬШИТЬ:

??

ПРЕОБРАЗОВАНИЯ:

from result in results
from answer in result.Answers
where answer.WasSkipped != true && answer.Value != null
select new {
    SubmissionDate = result["@metadata"]["Last-Modified"],
    SurveyId = result.SurveyId,
    LocationId = result.LocationId,
    QuestionId = answer.QuestionId,
    Value = answer.Value
}

Для чего это стоит, это размещено на RavenHQ.

Это было так долго, что я работал над этим и не могу понять это правильно. Любая помощь в достижении желаемого результата очень ценится!

1 ответ

Решение

Предполагая, что ваши классы C# выглядят так:

public class Submission
{
    public int SurveyId { get; set; }
    public int LocationId { get; set; }
    public IList<Answer> Answers { get; set; }
}

public class Answer
{
    public int QuestionId { get; set; }
    public int[] Values { get; set; }
    public string Comment { get; set; }
}

Если вы используете RavenDB 2.5.2637 или выше, теперь вы можете использовать тип результатов словаря:

public class Result
{
    public int SurveyId { get; set; }
    public int QuestionId { get; set; }
    public int NumResponses { get; set; }
    public int NumComments { get; set; }
    public Dictionary<int, int> Values { get; set; }
}

Если вы запускаете что-либо ранее (включая версии 2.0), то вы не сможете использовать словарь, но вы можете использовать IList<KeyValuePair<int,int>> вместо.

Вот индекс:

public class TestIndex : AbstractIndexCreationTask<Submission, Result>
{
    public TestIndex()
    {
        Map = submissions =>
              from submission in submissions
              from answer in submission.Answers
              select new
              {
                  submission.SurveyId,
                  answer.QuestionId,
                  NumResponses = 1,
                  NumComments = answer.Comment == null ? 0 : 1,
                  Values = answer.Values.ToDictionary(x => x, x => 1)
                  //Values = answer.Values.Select(x => new KeyValuePair<int, int>(x, 1))
              };

        Reduce = results =>
                 from result in results
                 group result by new { result.SurveyId, result.QuestionId }
                 into g
                 select new
                 {
                     g.Key.SurveyId,
                     g.Key.QuestionId,
                     NumResponses = g.Sum(x => x.NumResponses),
                     NumComments = g.Sum(x => x.NumComments),
                     Values = g.SelectMany(x => x.Values)
                               .GroupBy(x => x.Key)
                               .ToDictionary(x => x.Key, x => x.Sum(y => y.Value))
                               //.Select(x => new KeyValuePair<int, int>(x.Key, x.Sum(y => y.Value)))
                 };
    }
}

(Шаг преобразования не требуется.)

Если вы не можете использовать 2.5.2637 или выше, замените .ToDictionary строки с закомментированными строками чуть ниже их, и используйте IList<KeyValuePair<int,int>> в результате класс.

Исправление, позволяющее размещать словари в карте / сокращении, основывалось на этой проблеме, которую помог определить ваш пост. Спасибо!

Другие вопросы по тегам