A7 Data Server: управление данными онлайн
Привет, Хабр!
Мы пилотируем публикации с расшифровками докладов, прозвучавших на PiterJS.
Так как это первый опыт, будем рады услышать конструктивную критику и предложения по улучшению.
Смотрите видео и слайды, а за расшифровкой — добро пожаловать под кат.
Доклад и расшифровку подготовил Андрей Логинов, технический директор в «A7 Systems».
Начнем
Fast Big Data Server — cервер для больших быстрых данных. Изначально A7 DS предназначен для Digital Twin, управления ролями и паттернами данных. Но это не всё, что он умеет.
Что внутри.
Давайте посмотрим:
- Объектная база данных
- Темпоральная (хронологическая) база данных
- Виртуальная машина JavaScript (если быть честным, то js-like языка)
- Система уровня доступа
- Cервер приложения
Объектная база данных помимо типизации и наследования идеревьевмеет несколько особенностей:
- поддержка деревьев
- поддержка графов
- монтирование и ссылки
- Spaces (пространства)
- поддержка биндинга (реактивность)
Space
Самое необычное здесь — это Space (пространство).
Space — это экземпляр типовой рабочей области.
Space включает:
- data (данные)
- shared-data (общие данные для нескольких пространств. Например, погода или курс валюты)
- roles (owner, users, groups)
То есть, Space является достаточно изолированным от сервера A7 DS и других Spaces пространством.
Как пользоваться
Теперь вопрос: как этим пользоваться. Это наверное самый главный вопрос.
Создадим небольшое мобильное приложение на ECMAScript.
Нам потребуется:
- Минимальное знание С++. (возможно в рамках школьной программы)
- Знание ECMAScript и QML (Qt)
- Android NDK (просто чтобы скомпилировать это)
По моему ощущению, лучшие примеры всегда связаны с деньгами, поэтому попробуем создать онлайн семейный кошелёк)).
Создадим объекты в объектной базе данных. Точнее мы создадим типы. Для описания объектов удобнее использовать редактор, но мы «не такие», и создадим описание объектов в JSON.
Сначала создадим объект деньги, в котором у нас есть есть наличные, кредитка, и итоговая сумма:
{
"name": "Money",
"fields": [
{"name": "card","fieldtype": "value","datatype": "double", "def": 0},
{"name": "cash","fieldtype": "value","datatype": "double","def": 0},
{"name": "sum","fieldtype": "formula","datatype": "double",
"def": "card+credit"}
]
}
Поля card и cash это простые значения (по умолчанию равные 0), можно записать немного короче:
{"name": "card","value": 0.0},
{"name": "cash","value": 0.0}
Поле sum это формула (привет реактивность!), тоже можно записать немного короче:
{"name": "sum","formula":"card+credit"}
Теперь мы создадим пару из юноши и девушки.
{
"name": "Pair",
"fields": [
{"name": "boyfriend","fieldtype": "value","datatype": "Money", "def": "Money"},
{"name": "girlfriend","fieldtype": "value","datatype": "Money","def": "Money"},
{"name": "sum","fieldtype": "formula","datatype": "double",
"def": "boyfriend.sum+girlfriend.sum"}
]
}
Поле sum (снова привет реактивность!), стало включать ссылки на подобъекты:
{"name": "sum","formula":"boyfriend.sum+girlfriend.sum"}
Теперь, при каждом изменении любой цифры мы автоматически будем получать пересчет текущего баланса.
Но нам полезно добавить немного истории.
{"name": "history","fieldtype": "list", "list":{"datatype": "History"}}
В короткой записи
{"name": "history", "list":{"datatype": "History"}}
Ну и сам объект истории. Кто, что, и сколько изменил.
{
"name": "History",
"fields": [
{"name": "who","fieldtype": "value","datatype": "string", "def": "”},
{"name": "which","fieldtype": "value","datatype": "string","def": "”},
{"name": "delta","fieldtype": "value","datatype": "double","def": 0}
]
}
Добавим триггеров к Pair:
"functions": [{"functiontype": "before",
"arguments": [boyfriend.cash],
"code": "..."
}
]
И сам код триггера:
{
var historyItem= history.add(new History());
historyItem.who=”boyfriend”;
historyItem.which=”cash”;
history.delta=value-boyfriend.cash;
return true;
}
По аналогии добавим триггеры для boyfriend.card
, girlfriend.card
, girlfriend.cash
.
Поскольку мы хотим сделать наше приложение большому количеству пар, то создаем типовое пространство SpacePair
, и делаем его корневым элементом Pair
.
Добавляем двух пользователей по умолчаниюGirl
Boy
Собственно все, генератор пространств для контроля кошельков готов.
Добавим несколько пространств. При добавлении пространства автоматически создается область данных (и сами данные со значениями по умолчанию). Также создаются предустановленные пользователи и группы (для пространства).
Каждое пространство имеет своих пользователей и свои группы.
Начинаем делать клиент:
Добавим в проект библиотеки
android {
debug{
LIBS+= ../A7DS/Libs/android/libA17EDboClientBaseBind.a
LIBS+= ../A7DS/Libs/android/libA17ClientLibBind.a
}
release{
LIBS+= ../A7DS/Libs/android/libA17EDboClientBaseBin.a
LIBS+= ../A7DS/Libs/android/libA17ClientLibBin.a
}
}
Немного поправим файл main.cpp
#include
#include
#include
#include
// Добавим ссылку на *.h файлы
#include "../A7DS/A17EBase/A17EDboClientBaseBin/a17edboclientbasebin.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
// Создаем клиент для A7DS
A17EDboClientBaseBin*client=new A17EDboClientBaseBin(engine,&app);
// Инициализируем клиент для A7DS
client->init(engine);
// Дальше стандартный код
engine.load(QUrl(QLatin1String(QString("qrc:/main.qml").toLatin1())));
return app.exec();
}
На этом C++ часть закончена, и мы можем переходить к QML.
Сначала создадим пару компонентов.
Добавим компонент для отображения данных
MyLabelView.qml
import QtQuick 2.7
import Astra.Dbo 17.0
Item {id: viewItem
property alias field: field
property string label: "что и кто"
width: parent.width
height: 100
DboField{
id: field
}
Text {
id: labelItem
text: viewItem.label
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
}
Text {
id: valueItem
text: field.value
anchors.right: parent.right
anchors.left: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}
MyLabelEdit.qml
import QtQuick 2.7
import Astra.Dbo 17.0
Item {id: viewItem
property alias field: field
property string label: "что и кто"
width: parent.width
height: 100
DboField{
id: field
}
Text {
id: labelItem
text: viewItem.label
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
}
TextInput {
id: valueItem
text: field.value
anchors.right: parent.right
anchors.left: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
onEditingFinished:{
field.value=text;
}
}
}
Теперь соберем главное окно
MyLabelEdit.qml
import QtQuick 2.7
import Astra.Dbo 17.0
import QtQuick.Controls 1.5
{id:appWindow
visible: true
width: 640
height: 480
property var component;
property var sprite;
ApplicationWindow {id: viewItem
property alias field: field
property string label: "что и кто"
property string host: "127.0.0.1" //// адрес A7 DS
property int port: 8989 // порт A7 DS
property string isBoy: (dboconnection.login=="Boy")
property var myselfMoney: (isBoy)?boyfriend:girlfriend
property var myfriendMoney: (!isBoy)?boyfriend:girlfriend
/*
Данные, которые начнут автоматическую синхронизацию с сервером после соединения
*/
DboObject{id:boyfriend
parentObject: rootData
parentFieldName: "boyfriend"
}
DboObject{id:girlfriend
parentObject: rootData
parentFieldName: "girlfriend"
}
DboModel{id:history
parentObject: rootData
parentFieldName: "history"
}
/*
То, что мы отображаем, если нет соединения с сервером A7 DS
*/
Column{
z: 10
visible: (! dboconnection.isConnect)
anchors.fill: parent
TextInput{id:login
width: parent.width
height: 100
}
TextInput{id:password
width: parent.width
height: 100
}
Button{id:btn
width: parent.width
height: 100
text: ”Подключиться”
onClicked: dboconnection.connectToDbo(
login.text,
password..text,
viewItem.host,
viewItem.port);
}
}
SwipeView{
anchors.fill: parent
currentIndex: 1
/// Здесь таблица отражающая историю изменений
Page{
ListView{
model: history
delegate: Text{
text: model.who+” ”+model.which+” ”+model.delta
}
}
}
/// Здесь список дающий текущую картину
Page{
Column{
anchors.fill: parent
MyLabelEdit{id:myCash; label: "мои наличные”
field.name: "cash”; field.parentObject: myselfMoney
}
MyLabelEdit{id:myCard; label: "моя карта”
field.name: "card”; field.parentObject: myselfMoney
}
MyLabelView{id:mySum; label: "все мои деньги”
field.name: "sum”; field.parentObject: myselfMoney
}
MyLabelView{id:myfriendCash; label: "наличные друга”
field.name: "cash”; field.parentObject: myfriendMoney
}
MyLabelView{id:myfriendCard; label: "карта друга”
field.name: "card”; field.parentObject: myfriendMoney
}
MyLabelView{id:myfriendSum; label: "все деньги друга”
field.name: "sum”; field.parentObject: myfriendMoney
}
MyLabelView{id:mypairSum; label: "все наши деньги”
field.name: "sum”; field.parentObject: mypairMoney
}
}
}
}
}
Text {
id: labelItem
text: viewItem.label
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
}
TextInput {
id: valueItem
text: field.value
anchors.right: parent.right
anchors.left: parent.horizontalCenter
anchors.top: parent.top
anchors.bottom: parent.bottom
onEditingFinished:{
field.value=text;
}
}
}
Эмм. «А как же обещанные Digital Twin, и прочие ништяки?» — спросит внимательный читатель.
«Биндинг это конечно хорошо, но где монтирование и графы?» — добавит он.
Это справедливые вопросы, и на ответы на них будут дан в следующих статьях ;).