Подтвердить что ты не робот

Как я смогу моделировать данные, которые являются иерархическими и реляционными в системе документооборота, такой как RavenDB?

Документированные базы данных (в частности, RavenDB) действительно интригуют меня, и я хочу немного поиграть с ними. Однако, как человек, который очень привык к реляционному сопоставлению, я пытался подумать о том, как правильно моделировать данные в базе данных документов.

Скажем, у меня есть CRM со следующими объектами в моем приложении С# (исключая ненужные свойства):

public class Company
{
    public int Id { get; set; }
    public IList<Contact> Contacts { get; set; }
    public IList<Task> Tasks { get; set; }
}

public class Contact
{
    public int Id { get; set; }
    public Company Company { get; set; }
    public IList<Task> Tasks { get; set; }
}

public class Task
{
    public int Id { get; set; }
    public Company Company { get; set; }
    public Contact Contact { get; set; }
}

Я думал о том, чтобы поместить все это в документ Company, поскольку контакты и задачи не имеют целевой стороны компаний, и в большинстве случаев запрос на задание или контакты также будет содержать информацию о связанной компании,

Проблема связана с объектами Task. Скажите, что бизнес требует, чтобы задача ВСЕГДА была связана с компанией, но, возможно, также связана с задачей.

В реляционной модели это легко, так как у вас есть таблица Tasks и Company.Tasks относится ко всем задачам для компании, а Contact.Tasks показывает только задачи для конкретной задачи.

Для моделирования этого в базе данных документов я подумал о следующих трех идеях:

  • Задачи модели в виде отдельного документа. Это похоже на анти-документ db, так как большую часть времени, когда вы смотрите на компанию или обратитесь к вам, вы захотите увидеть список задач, поэтому вам придется много работать над документами.

  • Сохраняйте задачи, которые не связаны с контактом в списке Company.Tasks, и ставьте задачи, связанные с контактом в списке для каждого отдельного контакта. Это, к сожалению, означает, что если вы хотите увидеть все задачи для компании (что, вероятно, будет много), вы должны объединить все задачи для компании со всеми задачами для каждого отдельного контакта. Я также вижу, что это сложно, когда вы хотите отключить задачу от контакта, поскольку вам нужно переместить ее из контакта в компанию.

  • Сохраняйте все задачи в списке Company.Tasks, и каждый контакт имеет список значений id для задач, с которыми он связан. Это похоже на хороший подход, за исключением того, что нужно вручную принимать значения id и создавать под-список объектов Task для контакта.

Каков рекомендуемый способ моделирования этих данных в базе данных, ориентированной на документ?

4b9b3361

Ответ 1

Использовать денормализованные ссылки:

http://ravendb.net/faq/denormalized-references

по существу у вас есть класс DenormalizedReference:

public class DenormalizedReference<T> where T : INamedDocument
{
    public string Id { get; set; }
    public string Name { get; set; }

    public static implicit operator DenormalizedReference<T> (T doc)
    {
        return new DenormalizedReference<T>
        {
            Id = doc.Id,
            Name = doc.Name
        }
    }
}

ваши документы выглядят так: я реализовал интерфейс INamedDocument - это может быть все, что вам нужно, но:

public class Company : INamedDocument
{
    public string Name{get;set;}
    public int Id { get; set; }
    public IList<DenormalizedReference<Contact>> Contacts { get; set; }
    public IList<DenormalizedReference<Task>> Tasks { get; set; }
}

public class Contact : INamedDocument
{
    public string Name{get;set;}
    public int Id { get; set; }
    public DenormalizedReference<Company> Company { get; set; }
    public IList<DenormalizedReference<Task>> Tasks { get; set; }
}

public class Task : INamedDocument
{
    public string Name{get;set;}
    public int Id { get; set; }
    public DenormalizedReference<Company> Company { get; set; }
    public DenormalizedReference<Contact> Contact { get; set; }
}

Теперь сохранение задачи работает так же, как и раньше:

var task = new Task{
    Company = myCompany,
    Contact = myContact
};

Однако вытащить все это будет означать, что вы только получите денормализованную ссылку для дочерних объектов. Для гидратации они используют индекс:

public class Tasks_Hydrated : AbstractIndexCreationTask<Task>
{
    public Tasks_Hydrated()
    {
        Map = docs => from doc in docs
                      select new
                                 {
                                     doc.Name
                                 };

        TransformResults = (db, docs) => from doc in docs
                                         let Company = db.Load<Company>(doc.Company.Id)
                                         let Contact = db.Load<Contact>(doc.Contact.Id)
                                         select new
                                                    {
                                                        Contact,
                                                        Company,
                                                        doc.Id,
                                                        doc.Name
                                                    };
    }
}

И используя ваш индекс для извлечения увлажненных задач:

var tasks = from c in _session.Query<Projections.Task, Tasks_Hydrated>()
                    where c.Name == "taskmaster"
                    select c;

Что я считаю довольно чистым:)

В качестве дизайнерского разговора - общее правило заключается в том, что если вам когда-либо понадобится загружать дочерние документы самостоятельно, как в - не часть родительского документа. Будь то для редактирования или просмотра - вы должны моделировать его своим собственным Id как собственным документом. Использование вышеописанного метода делает это довольно простым.

Ответ 2

Я новичок в документе dbs... так что с солью...

Как контрастный пример... если вы находитесь в Твиттере, и у вас есть список людей, которых вы придерживаетесь, в котором содержится список их твитов... вы не будете перемещать свои твиты в свою учетную запись Twitter, чтобы читать их, и если вы повторно чирикаете, у вас будет только копия, а не оригинал.

Таким образом, я также считаю, что если Tasks принадлежат компании, то они остаются в компании. Компания является сводным корнем для задач. Контакты могут содержать только ссылки (ids) или копии Заданий и не могут изменять их напрямую. Если у вас есть контакт, удерживайте "копию" задачи, это хорошо, но для того, чтобы изменить задачу (например, пометить ее), вы должны изменить задачу через свой сводный корень (компания). Поскольку копия может быстро устаревать, кажется, что вы хотите, чтобы копия существовала только в памяти и при сохранении контакта, вы сохранили бы только ссылки на Задачи.