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