Deep это:
- Связи
- Пакеты, содержащие связи (сам пакет это тоже связь)
- Обработчики вставки/обновления/удаления в любой поддерживаемой среде
- Материализованный путь связей, деревья
У каждой связи может быть:
- id (число)
- Тип (type) (число)
- Начало (from) (число)
- Конец (to) (число)
- Значение (строковое/числовое/объектное)
И типом, и началом, и концом является связь. Связи указывают на связи. То есть любая связь может быть типом/началом/концом для другой связи.
- Message type: Type (всегда есть в дипе, находится в пакете @deep-foundation/core) from: 0 to: 0
- Conversation type: Type from: 0 to: 0
- Reply type: Type from: Message to: Conversation
- UrgentMessage type: Message from: 0 to: 0
- UrgentReply type: Reply from: UrgentMessage to: Conversation
В Deep есть встроенный тип Contain, который указывает принадлежность. Чаще всего он используется для того, что бы пакет Containил (содержал) в себе определённые связи, нужные для его работы. Если пакету для работы нужна связь, которая содержится в другом пакете - не нужно её Contain'ить в этот пакет. Достаточно того, что бы связь из текущего пакета указывала на связь из другого пакета любой стороной (началом или концом) То есть, если пакет deep-memo при установке должен установить пакет capacitor-device и capacitor-geolocation, то должны быть связи, которые указывают на связи из этих двух пакетов. Например можно воспользоваться пакетом @freephoenix888/dependency, создав связь типа Dependency с началом и концом указывающим на связь из capacitor-device, аналогично с capacitor-geolocation
await deep.insert({
type_id: await deep.id("@freephoenix888/dependency", "Dependency"),
from_id: await deep.id("deep-memo"),
to_id: await deep.id("capacitor-device"),
in: {
// Входящая связь, то есть связь, у которой to указывает на текущую, то есть на Dependency
data: [
{
type_id: containTypeLinkId,
from_id: dependantPackageLinkId,
string: {
data: {
value: `MyDependency`,
},
},
},
],
},
});
Note: выше в запросе использовался in, помимо этого есть out, который означает "Исходящую" связь, то есть связь, у которой from это текущая связь. Rule of thumb: Исходящая/out-from, Входящая/in-to
Обработчики позволяют обрабатывать такие события у связей:
- Вставка
- Обновление
- Удаление
Обработчики могут запускаться в разных средах. Одна из самых популярных - докер среда, которая использует определённый инструмент, допустим node, что бы запустить JavaScript код. Так же есть и синхронный хендлер, который принято называть plv8 хендлером, так как он использует plv8 для запуска JavaScript кода внутри PostgreSQL. Синхронный хендлер выполняется сразу же после события, и если хендлер выполнится не успешно, то ошибка выпадет, а операция не выполнится. Допустим ваш синхронный обработчик может валидировать данные в связях определённого типа, и если оне не валидны - выбрасывать ошибку, следовательно обновление связи не будет выполнено по этой причине
Рассмотрим простейший пример асинхронного обработчика:
- Имеем связь
Double
(type:Type
, from:0
, to:0
) - Имеем
SyncTextFile
содержащий такой код:
async ({ data: { newLink: doubleLink } }) => {
const value = doubleLink.value?.value;
if (!value) {
throw new Error(`Link ${doubleLink.id} has not value`);
}
return value * 2;
};
- Имеем
Handler
(from:dockerSupportsJs
(из@deep-foundation/core
), to: нашSyncTextFile
) - Имеем
HandleUpdate
(from: типDouble
, to: нашHandler
) Note:HandleUpdate
имеет начало в виде ТИПАDouble
, а не конкретногоDouble
(экземпляра), то есть "ТипDouble
обрабатывается хендлером" - Вставляем связь
Double
- Обновляем связь
Double
(присваиваем ей значение) - Видим результат асинхронного хендлера в виде связей: При успешном выполнении: Наша связь --Then-> Promise --Resolved-> PromiseResult (с object value) При ошибке: Наша связь --Then-> Promise --Rejected-> PromiseResult (с object value)
Note: не обязательно полагаться на результат в PromiseResult. Вы можете сделать так, что бы ваш обработчик не возвращал ничего, но вставлял определённые связи как результат. Например, если вы хотите, что бы при вставке связи Double
вставлялась связь DoubleResult
с результатом умножения на 2, то вы можете это сделать, воспользовавшись Дип Клиентом, который так же пробрасывается в ваш хендлер
- links
- strings
- numbers
- objects (на самом деле JSON, следовательно можно вставить число/строку в таблицу objects, а не только объект)
- selectors
await deep.insert({
type_id: 1,
});
await deep.insert({
type_id: await deep.id("@deep-foundation/core", "Type"),
});
await deep.insert({
type_id: await deep.id("messenger", "Reply"),
from_id: await deep.id(deep.linkId, "Моё сообщение"),
to_id: await deep.id(deep.linkId, "Мой диалог"),
});
await deep.insert(
{
link_id: 100,
value: "Моё значение",
},
{
table: "strings",
}
);
await deep.select({
type_id: 1,
});
await deep.select({
type_id: await deep.id("@deep-foundation/core", "Type"),
});
await deep.select({
type_id: await deep.id("messenger", "Reply"),
from_id: await deep.id(deep.linkId, "Моё сообщение"),
to_id: await deep.id(deep.linkId, "Мой диалог"),
});
await deep.select(
{
link_id: 100,
value: "Моё значение",
},
{
table: "strings",
}
);
Note: Прошу обратить внимание, что в примерах для упрощения используется {id: 1} в качестве ограничения, но использовать можно любое другое ограничение, например {from: 100, to: 150}
await deep.update(
{
id: 1,
},
{
from_id: 5,
}
);
await deep.update(
{
link_id: 1,
},
{
value: "Моё новое значение",
},
{
table: "strings",
}
);
await deep.delete({
type_id: 1,
});
await deep.delete({
type_id: await deep.id("@deep-foundation/core", "Type"),
});
await deep.delete({
type_id: await deep.id("messenger", "Reply"),
from_id: await deep.id(deep.linkId, "Моё сообщение"),
to_id: await deep.id(deep.linkId, "Мой диалог"),
});
await deep.delete(
{
link_id: 100,
value: "Моё значение",
},
{
table: "strings",
}
);