Индекс RavenDb для фильтрации и сортировки по свойствам вложенной структуры / коллекции (индекс разветвления) - PullRequest
0 голосов
/ 06 июня 2018

Я ищу способ создания статического индекса для обслуживания запросов фильтрации / сортировки для комбинации значений свойств во вложенной структуре (коллекции объектов) вместе со структурой-контейнером.Кажется, это не тривиально по следующим причинам:

  • Если свойства вложенной структуры / коллекции разделены на отдельные поля индекса (отдельные коллекции), то это делает невозможным использование AND условие при фильтрации по 2+ свойствам вложенной структуры / коллекции.
  • Индекс разветвления сложность (см. Пример ), который позволяет выполнить любое решениеслишком медленно.

Учитывая следующую постоянную модель:

public class Document
{
    public string Title { get; set; }

    public List<UserChange> RecentModifications { get; set; }
}

где

public class UserChange
{
    public string UserId { get; set; }
    public DateTime Timestamp { get; set; }
}

Вопрос: Какпостроить индекс для Document для фильтрации / сортировки по комбинации всех полей: Title, UserId и Timestamp?

Возможные варианты использования:

  • получить все документы, содержащие слово «контракт» для определенного пользователя и диапазон дат
  • , отсортировать документы, содержащие слово «контракт», по последнему изменению, внесенному пользователем.

PS Я понимаю, что ограничения по индексированию могутпутем обхода реструктуризации модели персистентности - сохранения структуры для недавно измененных документов в документе User, но это наложило бы некоторые другие ограничения, которых я бы хотел избежать.

Ответы [ 2 ]

0 голосов
/ 08 июля 2018

Проблема может быть решена с помощью Индексы с динамическими полями .Это позволяет сохранить логическую структуру данных и избежать создания индекса разветвления .

Решение

Создать следующий индекс для коллекции Document извыше:

public class MyIndex : AbstractIndexCreationTask<Document, DocumentIndDto>
{
    public MyIndex()
    {
        // Add fields that are used for filtering and sorting
        Map = docs =>
            from e in docs
            select new
            {
                Title = e.Title, 
                _ = e.RecentModifications.Select( x => CreateField ($"{nameof(Document.RecentModifications)}_{x.UserId}", x.Timestamp))
            };
    }
}

public class DocumentIndDto
{
    public string Title { get; set; }
    public Dictionary<string,DateTime> RecentModifications { get; set; }
}

Запрос на MyIndex как

var q = s.Query<DocumentIndDto, MyIndex>()
                .Where(p => p.Title == "Super" && p. RecentModifications["User1"] < DateTime.Now);

Пояснение

Указанный индекс с динамическими полями будет генерировать дополнительные поляи термины для каждой записи в следующем формате:

RecentModifications_User1 = '2018-07-01';
RecentModifications_User2 = '2018-07-02';

Формат важен, потому что когда вы используете словарь в запросе высокого уровня, например myDic[key], он преобразуется в myDic_key всгенерированный RQL.Следовательно, это позволит нам использовать эти поля в запросах.

Если вы запрашиваете с использованием обычного Query вместо DocumentQuery (см. docs ), то вам нужнотип данных для LINQ для работы.Для этого я создал класс DocumentIndDto, где мой RecentModifications стал словарем, поэтому я мог использовать его в высокоуровневом запросе и получить правильный RQL, такой как

from index 'MyIndex' where Title = $p0 and RecentModifications_User1 = $p1

Подробнее см.моя дискуссия на эту тему с Ореном Эйни (он же Айенде Райен).

0 голосов
/ 06 июня 2018

Используйте следующий RQL в определении индекса:

from doc in docs.Documents
from modification in doc.RecentModifications 
select new {
    modification.UserId,
    modification.Timestamp
}

Примечание : 'UserId' и 'timestamp' НЕ разделены в базовой записи индекса.

Таким образом, фильтрация по комбинации UserId = 'A' AND Timestamp = '2018-01-01' WILL возвращает записи, измененные пользователем 'A' в '2018-01-01'.

См. Также Индексы разветвления

Примечание2 : «Заголовок» также можно индексировать и искать при помощи:

from doc in docs.Documents
from modification in doc.RecentModifications 
select new {
    doc.Title,
    modification.UserId,
    modification.Timestamp
}

Таким образом, каждая результирующая «запись индекса» будет содержать «UserId» & «Отметка времени» , как и прежде, и соответствующую «Заголовок»

...