Pull to refresh

Hello, RavenDB

Reading time5 min
Views12K
В последнее время стал активно изучать и использовать 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):
  • 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. Единственное хотел бы отметить пару моментов:
  1. Если вам надо загрузить очень много объектов, то их необходимо разбить на несколько частей и загружать частями. Всё потому, что до выполнения методы SaveChanges все данные хранятся в памяти, и ее может запросто не хватить. А сделать больше 30 вызовов SaveChanges библиотеки RavenDB не позволяют.
  2. Когда вы производите поиск по базе, движок 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
Tags:
Hubs:
Total votes 41: ↑29 and ↓12+17
Comments19

Articles