Эссе о разработке игр, мышлении и книгах

Тарантога: модель данных

Постепенно пилю Тарантогу. Дело идёт не быстро: отвлекаюсь то на праздники, то на сторонние эксперименты, то на разбирательства с современными пайплайнами. Но постепенно что-то вырисовывается. Довольно странное :-)

Кстати, я завёл отдельный тег для постов про него.

Так вот, о странном и расскажу — о модели данных. Но без обоснования решений, какие обоснования в прототипе.

Мотивация

Кроме того, что это интересно:

  • Возиться со схемами реляционных баз не хочу, так как не знаю насколько разнообразными будут схемы данных Тарантоги и насколько они будут динамическими..
  • Возиться с NoSQL базами не хочу, так как они все «с нюансами» и не расчитаны на тип использования, который я предполагаю для Тарантоги.
  • Милых сердцу логических движков или баз данных я не нашёл, хотя под тип использования они подошли бы. В то же время я не хочу делать свою логическую БД — не хочу писать слишком сложную магию и не хочу чтобы другие писали запросы для этой магии.

Также может потребоваться делать поддержку большого количества типов данных, а встраивать их в сторонние БД не очень удобно.

В итоге я пришёл к выводу, что требуется упрощённый вариант реляционной логики с возможностью легко конструировать запросы а-ля prolog и datadog.

Теоретически, подошла бы база данных с колоночным хранением или вообще OLAP куб, но у меня ноль опыта работы с такими штуками и нет уверенности в работоспособности этого решения.

Поэтому делаю своё.

Принципиальных сложностей возникнуть не должно, так как производительность не стоит во главе угла, а все сложности разработки БД в их оптимизации и стабилизации.

Утверждения

Тарантога оперирует множествами утверждений.

Утверждение — неизменяемый словарь — набор информации об одном факте. Ни перечень свойств словаря ни их значения не могут быть изменены.

Например:

## Пример утверждения о том, что сущность №666 отмечена тегом «gamedev»
{
    "agent": EntityId(100500),
    "schema": "tag",
    "entity": EntityId(666),
    "value": "gamedev",
    "status": True,
    "#created": 213123213123
}

Ключами словаря могут быть только строки. Значениями — любые поддерживаемые типы.

По ключам рекомендуется хранить семантически важную для утверждения информацию.

Информацию, которая важна только для работы со значением, рекомендуется в нём же и хранить (как его часть).

Например:

  • время создания — свойство утверждения;
  • название тега — свойство утверждения;
  • формат картинки — свойство значения;
  • размер картинки — свойство значения;
  • единицы измерения — свойство значения.

Множества

Множество представляет собой набор уникальных утверждений. Подробнее про уникальность будет в следующем разделе.

Каждое множество, кроме корневого, имеет один или несколько исочников утверждений — множеств, на основе содержимого которых оно формируется.

Таким образом, все множества организованы в ациклический граф, а новые утверждения растекаться по нему от корня к листьям.

В корневом множестве хранятся все утверждения, добавленные в базу. Оно наполняется внешними сущностями — агентами.

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

Часть множеств задаётся в конфигурации базы и существует всегда. Часть создаётся временно в рамках обработки запроса агента.

Агенты могут производить две операции с базой:

  • Добавлять утверждения в корневое множество.
  • Получать все утверждения заданного множества, возможно, созданного в рамках запроса.

Не должно быть видимой разницы в работе со временными и постоянными срезами. Вынесение среза в конфигурацию — это оптимизаци.

Уникальность утверждений

Уникальность утверждения в множестве определяется функцией уникальности от утверждения.

Результат работы функции — ключ, по которому и определяется уникальность утверждения в рамках множества. Скорее всего большинство функций будет формировать ключи из значений свойств утверждения.

Множество может иметь только одну функцию уникальности и не может её изменить. Разные множества могут иметь разные функции уникальности.

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

Рассмотрим уникальность на примере уже встреченного утверждения:

{
    "agent": EntityId(100500),
    "schema": "tag",
    "entity": EntityId(666),
    "value": "gamedev",
    "status": True,
    "#created": 213123213123
}

Для разных ключей мы будем получать разные множества:

  • ключ (schema, entity, value, status) создаст множество утверждений о всех сущностях в родительских множествах.
  • ключ (agent, entity) позволит для каждого агента определить о каких сущностях он выносил суждения.
  • ключ (schema, value) сформирует множества значений утверждений каждого типа.

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

Например, если родительское множество содержит утверждения, отфильтрованные по значению schema="tag", то:

  • (entity, value, status) сформирует множество утверждений о всех тегах всех сущностей;
  • (agent, entity) определит сущности, для которых агенты проставляли теги.
  • (value) сформирует множество всех тегов.

Формирование множеств

Создать новое множество можно с помощью двух типов операций.

Создание множества на основе утверждений одного источника:

  • Фильтр утвердений — в множество добавляются только утверждения, для которых заданный предикат является истинным. Сохраняет функцию уникальности источника.
  • Изменение функции уникальности — в множество добавляются все утверждения источника, но с другим ключом. Например вместо ключа (entity, value) используется ключ (value).
  • Изготовление утверждений — в множество добавляются утверждения, полученные применением функции к утверждениям источника. Функция может как конструировать полностью новые утверждения, так и расширять существующие. При этой операции необходимо явно задать новую функцию уникальности.

Создание множества на основе операций между источниками:

  • Пересечение — в итоговом множестве будут только утверждения, ключ которых существует в каждом из истоничков.
  • Объединение — в итоговом множестве будут утверждения, ключ которых существует хотя бы в одном множестве.
  • Вычитание — в итоговом множестве будут утверждения, ключ которых находятся в первом множестве, но не в последующих.

Очевидно, операции между множествами могут проводиться только для множеств с совпадающими функциями уникальности.

Язык описания множеств

И запросов от агентов, так как они должны быть эквивалентны.

Множество будет описываться набором правил, которые задают очерёдность его формирования.

Пример выдуманного DSL со всеми предполагаемыми операциям:

setA filter schema="tag" and value="gamedev" => setB

## упрощаем ключ, удаляя из него значение поля entity
setB key remove "entity" => setC

setC add new_property=property_1 + property_2 => setD

setX intersect setY => setZ

setZ unite setQ => setW

setW extract setR => setT

setT unite setD => MyResult

return MyResult

Как видно, запрос также представляет собой ациклический граф

Преимуществом такого описания данных я вижу простоту восприятия и, надеюсь, понимания.

Чуть более сложные запросы, конечно, тоже могут быть:

setA filter schema="tag" and value="gamedev" => setB

setC filter value in setB.value => setD

return setD

Производительность

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

В то же время, представление данных в виде ациклического графа множеств, на мой взгляд, представляет большой простор для оптимизаций. Как со стороны пользователя — через формирование оптимальных последовательностей вычислений. Так и с стороны базы — через сбор статистики использования срезов и кэширование множеств. Особенно, если удастся наложить ограничения на структуру утверждений.