В последнее время стал активно изучать и использовать NoSQL-решение RavenDB. И вот я решился написать вводный пост про то, как начать использовать RavenDB. По сути эта статья не сильно отличается от «Hello world» инструкции на сайте проекта (да и примеры я взял оттуда же), но я постараюсь дать некоторые дополнения к тому, что там написано. Сразу хочу сказать, что я не являюсь экспертом в RavenDB, и использую его для своих проектов и задач достаточно недавно.
P.S. Почему не MongoDB или CouchDB? Не знаю, просто захотелось попробовать именно это решение. Да и на хабре это решение не было еще освещено.
Для начала необходимо скачать последнюю версию с сайта проекта.
В архиве есть папка Server, в ней и находится RavenDB-сервер. Если его запустить, то рядом появится папка Data с базой. Её размер будет около 90Мб, и это связано с тем, что сервер резервирует под будущие данные большой кусок дискового пространства. Если вы хотите уменьшить этот размер, то можно добавить в конфигурационный файл сервера параметр — Raven/Esent/LogFileSize, его значение по умолчанию равно 16Мб, как говорит документация, но на самом деле размер базы уменьшился до 40Мб после того, как я выставил его значение в 1.
Запущенный сервер предоставляет веб-интерфейс для доступа к базе данных. По-умолчанию его адрес — httр://localhost:8080. В нем можно управлять документами, которые находятся в базе, добавлять и удалять индексы.
После того как сервер запущен и база создана, откроем Visual Studio и создадим консольное приложение. Если у вас установлен NuGet, то щелкаем на References правой кнопкой и нажимаем Add Library Package Reference. Заходим на вкладку Online, находим там RavenDB и устанавливаем его. Если у вас нет NuGet, топоставьте его вам необходимо добавить 3 сборки из папки Client (если вы используете .NET 3.5, то из папки Client-3.5):
Теперь можно приступать к написанию кода. Следующие 2 строчки создают объект класса DocumentStore, который и будет использоваться дальше для работы с базой данных:
Теперь напишем несколько классов, которые будут описывать нашу доменную модель.
Обратите внимание, что каждый класс имеет свойство Id, это обязательное условие, в этом свойстве RavenDB будет хранить уникальный номер вашего документа.
Следующий код добавляет 2 документа в базу:
API реализует паттерн Unit of work. Сначала мы создаем объект сессии, далее сохраняем в нем объект (метод Store). На данном этапе объект product находится в памяти у нашего приложения и только после вызова метода SaveChanges отправляется в базу.
После выполнения приложения можно зайти на наш сервер (http://localhost:8080) и на вкладке с документами можно будет увидеть 2 документа. Если открыть документ, то можно увидеть все его свойства из описанных выше классов, а также свойства с вкладки Document Metadata. Они описывают .NET тип объекта, версию RavenDB-сервера, название сущностей в базе.
Для чтения из своего приложения этих объектов, достаточно написать следующий код:
Т.е. мы загружаем документ c Id равным 1 из сущностей Orders.
Для реальных приложений такого функционала по поиску документов явно недостаточно, поэтому необходим поиска по полям, а для этого необходимо добавить индексы:
Теперь можно выбирать документы по ProductId:
Этого вполне достаточно для того, чтобы поиграться с RavenDB. Единственное хотел бы отметить пару моментов:
Сайт проекта
Why Raven DB?
RavenDB — An Introduction
P.S. Почему не MongoDB или CouchDB? Не знаю, просто захотелось попробовать именно это решение. Да и на хабре это решение не было еще освещено.
Для начала необходимо скачать последнюю версию с сайта проекта.
В архиве есть папка Server, в ней и находится RavenDB-сервер. Если его запустить, то рядом появится папка Data с базой. Её размер будет около 90Мб, и это связано с тем, что сервер резервирует под будущие данные большой кусок дискового пространства. Если вы хотите уменьшить этот размер, то можно добавить в конфигурационный файл сервера параметр — Raven/Esent/LogFileSize, его значение по умолчанию равно 16Мб, как говорит документация, но на самом деле размер базы уменьшился до 40Мб после того, как я выставил его значение в 1.
Запущенный сервер предоставляет веб-интерфейс для доступа к базе данных. По-умолчанию его адрес — httр://localhost:8080. В нем можно управлять документами, которые находятся в базе, добавлять и удалять индексы.
После того как сервер запущен и база создана, откроем Visual Studio и создадим консольное приложение. Если у вас установлен NuGet, то щелкаем на References правой кнопкой и нажимаем Add Library Package Reference. Заходим на вкладку Online, находим там RavenDB и устанавливаем его. Если у вас нет NuGet, то
- Newtonsoft.Json.dll
- Raven.Abstractions.dll
- Raven.Client.Lightweight.dll
Теперь можно приступать к написанию кода. Следующие 2 строчки создают объект класса DocumentStore, который и будет использоваться дальше для работы с базой данных:
var store = new DocumentStore {Url = "http://localhost:8080"};
store.Initialize();
Теперь напишем несколько классов, которые будут описывать нашу доменную модель.
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Cost { get; set; }
}
public class Order
{
public string Id { get; set; }
public string Customer { get; set; }
public IList OrderLines { get; set; }
public Order()
{
OrderLines = new List();
}
}
public class OrderLine
{
public string ProductId { get; set; }
public int Quantity { get; set; }
}
Обратите внимание, что каждый класс имеет свойство Id, это обязательное условие, в этом свойстве RavenDB будет хранить уникальный номер вашего документа.
Следующий код добавляет 2 документа в базу:
using (var session = store.OpenSession())
{
var product = new Product
{
Cost = 3.99m,
Name = "Milk",
};
session.Store(product);
session.SaveChanges();
session.Store(new Order
{
Customer = "customers/ayende",
OrderLines =
{
new OrderLine
{
ProductId = product.Id,
Quantity = 3
},
}
});
session.SaveChanges();
}
API реализует паттерн Unit of work. Сначала мы создаем объект сессии, далее сохраняем в нем объект (метод Store). На данном этапе объект product находится в памяти у нашего приложения и только после вызова метода SaveChanges отправляется в базу.
После выполнения приложения можно зайти на наш сервер (http://localhost:8080) и на вкладке с документами можно будет увидеть 2 документа. Если открыть документ, то можно увидеть все его свойства из описанных выше классов, а также свойства с вкладки Document Metadata. Они описывают .NET тип объекта, версию RavenDB-сервера, название сущностей в базе.
Для чтения из своего приложения этих объектов, достаточно написать следующий код:
using (var session = store.OpenSession())
{
var order = session.Load("orders/1");
Console.WriteLine("Customer: {0}", order.Customer);
foreach (var orderLine in order.OrderLines)
{
Console.WriteLine("Product: {0} x {1}", orderLine.ProductId, orderLine.Quantity);
}
}
Т.е. мы загружаем документ c Id равным 1 из сущностей Orders.
Для реальных приложений такого функционала по поиску документов явно недостаточно, поэтому необходим поиска по полям, а для этого необходимо добавить индексы:
store.DatabaseCommands.PutIndex("OrdersContainingProduct", new IndexDefinition
{
Map = orders => from order in orders
from line in order.OrderLines
select new { line.ProductId }
});
Теперь можно выбирать документы по ProductId:
using (var session = store.OpenSession())
{
var orders = session.LueneQuery("OrdersContainingProduct")
.Where("ProductId:products/1")
.ToArray();
foreach (var order in orders)
{
Console.WriteLine("Id: {0}", order.Id);
Console.WriteLine("Customer: {0}", order.Customer);
foreach (var orderLine in order.OrderLines)
{
Console.WriteLine("Product: {0} x {1}", orderLine.ProductId, orderLine.Quantity);
}
}
}
Этого вполне достаточно для того, чтобы поиграться с RavenDB. Единственное хотел бы отметить пару моментов:
- Если вам надо загрузить очень много объектов, то их необходимо разбить на несколько частей и загружать частями. Всё потому, что до выполнения методы SaveChanges все данные хранятся в памяти, и ее может запросто не хватить. А сделать больше 30 вызовов SaveChanges библиотеки RavenDB не позволяют.
- Когда вы производите поиск по базе, движок RavenDB возвращает данные страницами по 128 штук, поэтому для того, чтобы вывести их все, приходится писать что-то такое:
var query = session.Advanced.LuceneQuery<Order>("OrdersContainingProduct")
.Where("ProductId:products/1");
int actual = 0;
const int pageSize = 128;
int count = query.QueryResult.TotalResults;
do
{
var orders = session.Advanced
.LuceneQuery<Order>("OrdersContainingProduct")
.Where("ProductId:products/1")
.Skip(actual)
.Take(pageSize);
actual = actual + pageSize;
foreach (var order in orders)
{
App.Logger.Info("Id: {0}", order.Id);
App.Logger.Info("Customer: {0}", order.Customer);
}
if (actual >= count)
break;
} while (true);
Ссылки
Сайт проекта
Why Raven DB?
RavenDB — An Introduction