2013-07-04 3 views
4

Я новичок в RavenDB и до сих пор люблю его. У меня есть один оставшийся индекс для моего проекта.RavenDB Map/Reduce/Transform на вложенных массивах переменной длины

Проблема

У меня есть тысячи ответов на опросы (т.е. «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 
     } 
    }, 
    ... 
] 

Моя попытка

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

КАРТА:

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.

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

ответ

6

Предполагая, что ваши классы 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>> в классе результатов.

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

+0

... и спасибо за подсказку использовать IList > на v2.0 –

+0

Это хорошо работает, но есть также требование фильтрации по полям, не присутствующим в преобразовании. Класс результата (например, я только хотите, чтобы данные поступали в течение определенного периода времени). Это достаточно большой вопрос, который я должен задать в качестве отдельного вопроса? –

+0

Ну, я не вижу никаких дат ни на одном из этих объектов, и решение здесь не имеет преобразования, поэтому, вероятно, вы должны открыть новый вопрос только с конкретной спецификой этой проблемы. –