Как правильно создать карту / уменьшить индекс для RavenDB в C#
Я работаю над приложением, которое использует RavenDB на серверной части. Я впервые использую Raven, и я борюсь с Map/Reduce.
Я читал документы, но, к сожалению, я никуда не деться.
В основном у меня есть тысячи таких документов.
{
.....
"Severity": {
"Code": 6,
"Data": "Info"
},
"Facility": {
"Code": 16,
"Data": "Local Use 0 (local0)"
},
.....
}
И из этого мне нужно сделать один запрос с выводом, который выглядит следующим образом.
{"Severity": [
{"Emergency":0},
{"Alert":0},
{"Critical":0},
{"Error":0},
{"Warning":0},
{"Notice":0},
{"Info":2711},
{"Debug":410}
],
"Facility": [
{"Kernel Messages":0},
{"User-Level Messages":0},
{"Mail System":0},
{"System Daemons":0},
{"Security/Authorization Messages":0},
{"Internal Syslogd Messages":0},
{"Line Printer Subsystem":2711},
{"Network News Subsystem":410},
....
{"Local Use 0 (local0)": 2574},
...
]}
При этом "Ключ" в массиве серьезности / объекта является Data
часть вышеупомянутых данных json, а "значение" в массиве серьезности / средства является документом Count
для каждого Code
тип.
Пример:
Используя приведенные выше данные в качестве ориентира,
В моей базе данных 2711 документов с
Info
строгость.
В моей базе 410 документов сDebug
строгость.
В моей базе 2574 документа сlocal0
объект.
так далее...
Я хотел бы создать соответствующие индексы при запуске приложения (или проверить, существуют ли они уже), но я даже не знаю, с чего начать.
примечание: приложению необходимо сгенерировать индекс, недостаточно просто вручную записать его в веб-интерфейс RavenDB.
1 ответ
Вам нужно будет объединить несколько методов для достижения этой цели, но это вполне выполнимо.
Вот индекс, который должен хорошо работать для вас.
public class MyIndex : AbstractMultiMapIndexCreationTask<MyIndex.ReduceResult>
{
public class ReduceResult
{
public string Source { get; set; }
public string Code { get; set; }
public string Data { get; set; }
public int Count { get; set; }
}
public MyIndex()
{
AddMap<MyDoc>(docs => from doc in docs
select new
{
Source = "Severity",
doc.Severity.Code,
doc.Severity.Data,
Count = 1
});
AddMap<MyDoc>(docs => from doc in docs
select new
{
Source = "Facility",
doc.Facility.Code,
doc.Facility.Data,
Count = 1
});
Reduce = results => from result in results
group result by new { result.Source, result.Code }
into g
select new
{
g.Key.Source,
g.Key.Code,
g.First().Data,
Count = g.Sum(x => x.Count)
};
TransformResults = (database, results) =>
from result in results
group result by 0
into g
select new
{
Severity = g.Where(x => x.Source == "Severity")
.ToDictionary(x => x.Data, x => x.Count),
Facility = g.Where(x => x.Source == "Facility")
.ToDictionary(x => x.Data, x => x.Count)
};
}
}
Вам также нужен класс контейнера для преобразованного результата:
public class MyDocCounts
{
public IDictionary<string, int> Severity { get; set; }
public IDictionary<string, int> Facility { get; set; }
}
Вы бы запросили это так:
var result = session.Query<MyIndex.ReduceResult, MyIndex>()
.As<MyDocCounts>()
.ToList().First();
.ToList()
может показаться излишним, но это необходимо, потому что мы группируемся в преобразовании.
Полный тестовый модуль здесь. Выход которого выглядит так:
{
"Severity": {
"AAA": 20,
"BBB": 20,
"CCC": 20,
"DDD": 20,
"EEE": 20
},
"Facility": {
"FFF": 20,
"GGG": 20,
"HHH": 20,
"III": 20,
"JJJ": 20
}
}