Я хочу рассказать про созданный мною web редактор для «визуального программирования» и его историю создания.

Случилось как-то мне столкнуться с одной очень надоедливой задачей на работе в компании PimPay, в которой я занимаю должность технического директора. Есть у нас отдел, занимающийся претензионной деятельностью с Почтой России. Ребята ищут потерянные/не оплаченные/не доставленные отправления. В сухом остатке для разработчиков эта задача сводилась к написанию очень большого количества разнообразных и огромных (>150 строк) SQL запросов на диалекте PostgreSQL, которые ОЧЕНЬ часто менялись и дополнялись в силу активного появления новых гипотез, а также поступления новых сведений о заказах.

Естественно, нам, как программистам, это дело очень быстро надоело, и сразу захотелось предложить отстать от нас какой-то визуальный инструмент конструирования фильтров, которые в итоге транслируются в SQL. Сначала захотели налепить просто много формочек с кучей «инпутов», но идея быстро провалилась, т.к. было понятно, что все фильтры надо уметь как-то «композиционировать» (compose): объединять в группы, пересекать с разными условиями И, ИЛИ, параметризовать и прочее. И даже простейший реально используемый фильтр начинал выглядеть ужасно. Плюс сбоку закрадывалась мысль, что в принципе данные фильтры являются частным вариантом просто общего сборщика SQL, и вообще, раз уж дошло дело до «визуального программирования», то можно постараться полностью абстрагироваться от предметной области.

И тут, как в фильме «Проблеск гениальности» («Flash of Genius»), у меня перед глазами всплыла картина визуального редактора схем (blueprints) из UE4 (Unreal Engine 4), с помощью которых персонажи запускали файрболы в своих врагов:

image

Прибежав в тот же вечер домой, я взял первую попавшуюся JavaScript библиотеку, умеющую рисовать красивые прямоугольники и сложные линии — ей оказалась Raphael от нашего соотечественника DmitryBaranovskiy. Нарисовав пару прямоугольников и подёргав их с помощью библиотечных drag-and-drop, я сразу написал автору библиотеки с вопросом поддерживает ли он её. И не дождавшись ответа (до сих пор), я в ту же ночь наплодил более 1000 строк кода на JavaScript, и моя мечта на глазах почти стала явью! Но предстояло ещё много работы.

В итоге, что же захотелось сделать:

  • Красивый и удобный редактор в вебе, который предоставляет средства для манипуляции и связывания «узлов» разных типов, которые описываются в доменной схеме, передающейся редактору при инициализации. Тем самым сделать редактор независимым от предметной области.
  • Возможность сериализации ацикличного графа пользователя в древовидную структуру, которую очень легко разбирать (parse) (например JSON) и интерпретировать на любом языке.
  • Предоставить удобный формат для описания типов и компонентов предметной области.
  • Придумать богатую систему типов и ограничений, которая поможет пользователю создавать графы, корректные с точки зрения предметной области.
  • Дать возможность пользователю создавать свои компоненты из комбинаций существующих.

В итоге вот что получилось:

image

Видно, что чертёж (blueprint) состоит из узлов (nodes), которые являются конкретными экземплярами компонентов (component), описанных в схеме предметной области (schema). Узлы соединяют проводами (wires). Провод всегда идёт от выхода (output) одного узла к входу (input) другого (и наоборот). Из одного выхода может идти много проводов, но на вход можно привязать только один.

Все входы и выходы имеют тип, и соответственно накладывают ограничения на возможные связи. Например, возьмём следующую систему типов:

types:
    # Всегда есть тип Any, который супер-родитель всех типов
    Scalar:
    Numeric:
       extends: Scalar
    String:
       extends: Scalar
    List:
       typeParams: [A]

Таким образом можно выход типа Numeric связать с входом типа Scalar, но не наоборот. Для параметризованных типов вроде List подразумевается ковариативность, т.е. List[String] можно передать в List[Scalar], но не наоборот. Плюс всегда присутствует супер тип Any, наследником которого являются все остальные типы.

У узлов также могу присутствовать атрибуты, которые не настраиваются с помощью проводов, а пользователь сам вводит в них значения. Для этого существуют концепция valuePicker-ов, с помощью которых можно задавать свой интерфейс ввода значений атрибутов. Из коробки есть просто текстовый инпут и возможность выбора из заранее определённого набора констант.

Также узлы бывают параметризованы по типам. Например, дан компонент:

IfThenElse:
  typeParams: [T]
  in:
    C: Boolean  
  out:
    onTrue: @T
    onFalse: @T

При создании узла на базе компонента IfThenElse редактор попросит нас указать тип T и подставит его во все места с T:

image

Типы входов и выходов также помогают пользователю при проектировании. Если вы потянете проводок из выхода с типом Numeric и отпустите мышку, то вылезет окно создания компонентов, отфильтрованных таким образом, что там останутся только те, вход которых совместим (conforms) с типом Numeric. И даже автоматически привяжется проводок.

Заняло всё это примерно чистых тройку человеко-недель, растянутых на добрых 5-6 месяцев. И ещё спустя полгода появились силы что-то задокументировать и заявить об этом миру.

Итак, господа, настало время творить! Возьмём самый нереальный случай, когда вам надо предоставить «не техническому» пользователю возможность визуально программировать процесс складывания чисел. Мы пониманием, что нам нужен всего лишь один тип Numeric и пару компонентов: возможность задать число (Literal) и возможность сложить два таких числа (Plus). Далее приведён пример схемы данной предметной области: (все примеры схем описаны в формате YAML для наглядности, в реальности же вам надо будет передавать нативные javascript объекты):

types:
    # Всегда есть тип Any, который супер-родитель всех типов
    Numeric:
        color: "#fff"
      
components:
  Literal: # Название компонента
    attrs: # Атрибуты
      V: Numeric 
    out: # Исходящие сокеты
      O: Numeric

  Plus:
    in: # Входящие сокеты
      A: Numeric
      B: Numeric
    out:
      O: Numeric

Пример собранного редактора с данной схемой и простым графом можно посмотреть тут.

Поиграйтесь! Нажмите X для создания нового элемента, удалите элемент двойным кликом. Соедините узлы проводками, выделите их все и скопируйте и вставьте через Ctrl+C и Ctrl+V. Потом выделите все Ctrl+A и удалите с помощью Delete. Ведь всегда можно сделать Undo, прибегнув к Ctrl+Z!

Теперь допустим, наш нехитрый пользователь собрал следующий граф:

image

Если мы попросим редактор сохранить наш граф в дерево, то получим:

[
  {
    "id": 8,
    "c": "Plus",
    "links": {
      "A": {
        "id": 2,
        "c": "Literal",
        "a": {
          "V": "2"
        },
        "links": {},
        "out": "O"
      },
      "B": {
        "id": 5,
        "c": "Literal",
        "a": {
          "V": "2"
        },
        "links": {},
        "out": "O"
      }
    }
  }
]

Как видим нам тут пришло дерево, которое очень легко рекурсивно обойти и получить какой-то результат. Допустим, языком нашего бэкэнда является тоже JavaScript (хотя может быть любой).

Пишем тривиальный код:

function walk(node) {
    switch (node.c) {
        case 'Literal':
            return parseFloat(node.a.V);

        case 'Plus':
            return walk(node.links.A) + walk(node.links.B);

        default:
            throw new Error("Unsupported node component: " + node.component);
    }
}

walk(tree);

Если мы прогуляемся такой функцией по вышеуказанному дереву, то получим 2+2=4. Вуаля!

Очень приятным бонусом является возможность у пользователя определять свои «функции», объединяя существующие компоненты.

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



Теперь у нас появилась пользовательская функция x3:



Которой можно воспользоваться, как новым компонентом:



При этом на backend можно послать дерево, где все пользовательские функции развёрнуты (inlined), и разработчик даже не будет знать, что некоторые узлы были в составе пользовательской функции. Получается, что пользователи сами могу обогащать свой язык проектирования при должных фантазии и упорстве.

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

Возьмём к примеру замечательный язык SQL. Если присмотреться, то любой SQL запрос на самом деле очень легко раскладывается в дерево (этим и занимается первым делом БД, когда получает ваш запрос). Понаписав достаточное количество типов и компонентов можно получить нечто уже более устрашающее:

image

P.S. Если какой-то из примеров не открывается...
Возможно это связано с тем, что вы уже попытались сохранить пользовательскую функцию для одной из схем. А так как по умолчанию (но можно и нужно определять свои обработчики) все пользовательские функции хранятся в localStorage, то может возникнуть ситуация, когда редактор попытается загрузить компоненты или типы, не описанные в текущей схеме.
Для этого просто очистите текущий localStorage с помощью:

localStorage.clear()

К сожалению, демонстрацию превращения данного графа в реальный SQL провести не получится, т.к. она сильно завязана на наш проект. Но в реальности это собирается в следующий SQL:

SELECT 
     COUNT(o.id) AS cnt
   , (o.created_at)::DATE AS "Дата" 
FROM tbl_order AS o 
WHERE o.created_at BETWEEN '2017-1-1'::DATE AND CURRENT_DATE 
GROUP BY (o.created_at)::DATE 
HAVING ( COUNT(o.id) ) > ( 100 ) 
ORDER BY (o.created_at)::DATE ASC

Который сразу же исполняется и отдаёт готовый отчёт в формате Excel. Переведя данный SQL на человечий, получаем:

Показать количество загруженных заказов за каждую день в хронологическом порядке, начинания с 1 января 2017 года, выкидывания дни, где загрузили меньше чем 100 заказов. Вполне себе реальный отчёт для бизнеса!

Схему в формате JSON для данного примера можно посмотреть тут.

Я не буду в статье приводить полное описание системы типов и компонентов, за этим отправляю в соответствующий раздел документации. Но для подогрева интереса лишь немного «вброшу», что можно писать вот так (немного косячит подсветка вычурного синтаксиса YAML):

Plus:
        typeParams: [T]
        typeBounds: {T: {<: Expr}} # Параметризованный тип 'T' ограничен сверху типом 'Expr',
                                   # что означает, что нам надо сюда передать наследник типа 'Expr'
        in:
            A: @T
            B: @T
        out:
            O: @T

Это как если бы вы в Scala объявили функцию:

def Plus[T <: Expr](A: T, B: T): T = A + B

В итоге, подготовив достаточное количество компонентов, и придумав хорошую систему типов (и написав много немного backend кода для обхода деревьев) мы сбагрили дали возможность пользователю составлять свои ad-hoc отчёты на базе SQL. Немного расширив доменную область, мы сильно упростили исходную задачу с фильтрами для поиска проблемных заказов, описанную в начале статьи. А самое главное дали бизнесу возможность самостоятельно тестировать свои гипотезы без привлечения разработчиков. Теперь правда, приходится обучать и писать новые компоненты, но это куда более приятное занятие! Надеюсь, что на SQL и фильтрах дело не остановится, и мы воспользуемся этим инструментом в другим областях проекта.

Самое главное, что я с радостью передаю этот инструмент в общественное достояние на GitHub под лицензией MIT.

Кому интересно, есть идеи/задачи по дальнейшему развитию инструмента:
  • Более удобная навигация по компонентам в схеме + их документация для пользователя
  • Сокеты-атрибуты (как в UE4)
  • Возможность определять атрибуты в пользовательских функциях.
  • Режим read-only для отображения схем
  • Узлы кастомной формы (как в UE4), а не только прямоугольники
  • Ускорение и оптимизация для работы с большим количеством элементов
  • Интернационализация
  • Экспорт картинки в SVG/PNG
  • You name it!

Будет круто, если кому-то захочется воспользоваться этим инструментом на практике и поделиться своим опытом.

P.S. Ещё круче будет, если кто-то присоединится к разработке инструмента!

P.P.S. Я проводил презентацию инструмента в компании с помощью данного документа.
Поделиться с друзьями
-->

Комментарии (83)


  1. tempik
    20.07.2017 18:10
    +1

    Большую работу проделали.
    Рекомендую обратить внимание на проект The Graph (github, opensource), демо.
    Он является частью целой системы для Flow Based программирования, , есть среда выполнения графов как минимум для nodeJS, и браузера. Можно взять только сам редактор графов, а выполняющее их ядро реализовать самостоятельно. Я например таким образом реализовал ETL загрузчик для OLAP системы.


    1. aveic
      20.07.2017 18:23

      Спасибо, буду изучать!


    1. Idot
      21.07.2017 13:52

      В MS Access тоже есть похожее.


    1. Ivan22
      21.07.2017 13:55

      ETL загрузчиков же кстати вообще превеликое множество.


    1. Rathil
      21.07.2017 15:29

      Огромное спасибо за наводку, как раз нужно было что-то именно такое! Просто супер!


  1. potan
    20.07.2017 18:21

    Я думал над похожем подходе для реализации визуального Prolog. Но руки пока не дошли.


    1. aveic
      20.07.2017 18:25

      Теперь может дойдут )


  1. Sirion
    20.07.2017 19:04

    Очень любопытно. Возможно, эта штукенция пригодится мне для работы с Web Audio API. Спасибо за статью.


    1. aveic
      21.07.2017 13:07

      Хороший пример


  1. morikvendy
    20.07.2017 19:42

    Штука прикольная, но есть небольшой баг — если от переменной рисовать стрелку к занятому входу, то все ок — она не рисуется, но если протянуть стрелку в обратном направлении — от занятого входа к переменной, то она создастся, т.е. в один вход будет две стрелки:
    image


    1. aveic
      20.07.2017 19:43
      +1

      Спасибо, я думал никто не заметит :) Буду править!


      1. morikvendy
        20.07.2017 20:43

        Кстати, еще по тому же скриншоту такая фишка — на схеме в углу экрана показаны только первые две линии, но в реальности он работает по вторым. т.е. у меня 4 числа 1, 10, 100, 1000 сначала я на вход дал 1 и 10, потом от входов протянул линии к 100 и 1000, на мелкой схеме в углу показаны только лини к 1 и 10, а при нажатии на eval получаю 1100


        1. aveic
          20.07.2017 21:15

          Да, из одного места все ошибки


      1. FunApple
        21.07.2017 11:32

        Вы создали тему на хабре и думали, что среди ее читателей нету тестировщиков? =)


        1. aveic
          21.07.2017 11:32

          Спасибо, я думал никто не заметит :) Буду править!

          Это была шутка, возможно не смешная.
          Не хочется просто верить, что в твоём творении есть изъяны, да ещё и такие очевидные :)


  1. Ogoun
    20.07.2017 21:08

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

    И не дождавшись ответа (до сих пор), я в ту же ночь наплодил более 1000 строк кода на JavaScript
    , так все таки используется рафаэль? Или своя реализация отрисовки. В исходниках вижу что он подключен.


    1. aveic
      20.07.2017 21:14

      Да raphael, в нём было всё, что нужно.


  1. DaneSoul
    20.07.2017 22:41

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


    1. aveic
      20.07.2017 23:20

      Извините, ответил ниже на том же уровне комментария


  1. aveic
    20.07.2017 23:13
    +2

    В нашем случае — да оказалось всё просто.
    Как минимум, мы вытащили все значимые таблицы и колонки для пользователей с человеческими именами. С помощью системы типов облегчили JOINы. Для всех enum-like полей создали соответствующие компоненты. Пользователю не надо знать как в системы называются статусы вроде executed, он просто видит удобный выпадающий список.
    Также мы очень активно пользуемся всеми навороченными фичами PostgreSQL, и в данном случае, очевидно, что работу с hstore/jsonb/array/CTE/хранимками лучше вытащить в визуальные компоненты. Например, такие SQL:

    SELECT 
    	  o.delivery_service_external_order_id AS "ШПИ"
    	, (clm.details#>>'{status,sent_to_russian_post}')::DATE AS "Дата подачи претензии"
    	, clm.status AS status 
    FROM tbl_order AS o 
    INNER JOIN claim.tbl_claim_order AS co  ON o.id = co.order_id 
    INNER JOIN claim.tbl_claim AS clm  ON co.claim_id = clm.id 
    WHERE fn_get_param('minimal_faccept_date', ARRAY['CT#' || clm.customer_id])::DATE > '2017-03-01'::DATE

    вряд ли кто либо из не-технарей согласиться писать :)

    Плюс бесплатно у нас появляется возможность явно выделить эти фильтры, и отдельно запускать каждый, чтобы в дальнейшем проводить аудит, по какой именно причине заказ по ним отсеялся или наоборот. А имея 200 строчный SQL со ста OR-ами или AND-им тяжело понять, что именно отработало, и надо много чего доделывать.

    Вообще, SQL не самая сложная область, как я упоминал в статье, всё это дело было вдохновлено UE4 Blueprints, которые вообще в С++ код транслируются.

    Ну и ещё раз хочу сказать, что сам инструмент не привязан никак конкретно к SQL — его можно использовать в любой области.


  1. MOZGIII
    21.07.2017 06:07

    В статье написано что инструмент передан в общественное достояние, а в репо на github стоит лицензия MIT. Я не юрист, но на мой взгляд тут есть противоречие, и должно быть и в репо тогда public domain указано. Но я, опять же, не разбираюсь, возможно так и должно быть.

    Сам проект хорош, обязательно попробую, если подойдёт — буду пользоваться.
    Читать сложновато, всё в одном файле. Есть планы по переоформлению кодовой базы с использованием какой-нибудь системы сборки?


    1. aveic
      21.07.2017 11:45
      +1

      В статье написано что инструмент передан в общественное достояние, а в репо на github стоит лицензия MIT. Я не юрист, но на мой взгляд тут есть противоречие, и должно быть и в репо тогда public domain указано.

      Я тоже не силён в лицензировании ПО, и сама тема не очень интересна, но при публикации проекта пришлось что-то всё таки поизучать. Сначала сразу захотелось повесить WTFPL, но бегло ознакомившись с содержимым https://habrahabr.ru/post/243091/, и посмотрев, что используют некоторые популярные open-source проекты — решил, что MIT вроде краткая и понятная, и без ругательных слов, и вроде позволяет делать с кодом что вам заблагорассудиться.

      Есть планы по переоформлению кодовой базы с использованием какой-нибудь системы сборки?

      Да, конечно. Как-то так вышло, что за пару-тройку дней файл разросся до 3к строчек, и мне не хотелось тратить запал на разбитие на файлы :) Плюс, я не front-end разработчик по специализации, и отстал от современных тенденций, поэтому, если посоветуете что-то конкретное — буду благодарен.


      1. MOZGIII
        21.07.2017 15:25

        Насчёт общественного достояния понятно — по закону России нельзя вот так просто взять и перевести код в общественное достояние, а только по истечении времени (https://habrahabr.ru/post/243091/), отсюда не возникает противоречий и код лицензируется по MIT.

        Ну, я бы предложил перейти на ES8 (или 7/6) чтобы использовать сахар новых версий, например через https://babeljs.io. Ну и, разбить код как-то, и научиться его собирать в обычную и минифицированную версию. Для сборки модулей берите наверное webpack, а если хотите просто компиляцию общего назначения — gulp.

        Чуть не забыл: выберите и настройте линтер, например ESLint, JSLint или JSHint — это очень помогает. А ещё пишите тесты: это Mocha или Jasmine.


        1. aveic
          21.07.2017 16:29

          Насчёт общественного достояния понятно — по закону России нельзя вот так просто взять и перевести код в общественное достояние, а только по истечении времени (https://habrahabr.ru/post/243091/), отсюда не возникает противоречий и код лицензируется по MIT.

          Всё что я хотел сказать, так это — пользуйтесь на здоровье :) Главное, что лицензия MIT позволяет всем это делать.


    1. domix32
      21.07.2017 15:00

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


      1. MOZGIII
        21.07.2017 15:11

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


        1. domix32
          21.07.2017 20:59

          А есть где-то примеры передачи кода в public domain? Просто насколько я знаю и лицензия MIT и лицензии Creative Commons так и остаются лицензиями, стараясь приблизиться к идее общественного достояния. Вроде как нигде в мире на данный момент нет возможности как-то иначе отчуждать свои права на произведение.
          Как вариант форкнуть пока оно под MIT и пользовать как собственный продукт.


          1. Mendel
            22.07.2017 15:13
            +2

            Авторское право — неотчуждаемо.
            Автор остается автором всегда.
            Есть сложные случаи типа работы по договору и т.п., но это дебри.
            Соответственно все вариации свободных и копилефт лицензий описывают случаи интеллектуальной СОБСТВЕННОСТИ а не авторства. Это важный вопрос а не придирка.
            Собственно наиболее широкие лицензии вроде MIT реально накладывают только ограничения на необходимость указывать авторство. ИМХО не велика проблема. Есть лицензии которые не требуют и этого.
            Упомянутая СС0 предназначена для целей передачи в общественное достояние. Лучше процитирую:

            CC0 — передача в общественное достояние от Creative Commons. Произведение, выпущенное по CC0, передано в общественное достояние в максимальной степени, разрешенной законом. Если это невозможно по любой причине, CC0 также предоставляет простую пермиссивную лицензию в качестве запасного варианта. И произведения в общественном достоянии, и простая лицензия, предоставляемая CC0, совместимы с GNU GPL. Если вы хотите передать ваше произведение в общественное достояние, мы рекомендуем вам использовать CC0.

            Пермессивная лицензия это когда от тебя требуют только автора указывать. Вроде СС0 лучше, не требуется даже этого… вроде как больше свобод и все такое, но…
            Вот не указываешь ты автора. А потом возникают вопросы правомочности использования этого кода. Придется доказывать всю цепочку. что код взял тут, что его давали под СС0, и т.п… Зачем?
            Укажи автора и спи спокойно.

            ИМХО MIT самая удобная. Единственное что — лично для себя я хочу к ней добавить один дополнительный пункт, что если кто-то присылает пулреквесты или другим образом отправляет свои патчи и доработки, то он автоматически (если не было обсуждено другое) отдает свои права правообладателю основного продукта. Чисто для простоты. Чтобы и код был свободен, и основной разработчик имел развязанные руки — кому не нравится сделает форк.


            1. domix32
              22.07.2017 23:18

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


  1. Refridgerator
    21.07.2017 09:11
    +1

    Круто, сам большой поклонник такого подхода к программированию.


  1. neopaf
    21.07.2017 10:59

    Забавно, но никто из комментаторов не указал Blockly

    Оно, конечно, выглядит попроще.
    Но дело делает.


    1. aveic
      21.07.2017 11:52

      Да тоже крутой инструмент, спасибо! Но он больше, возможно, подходит именно для написания кода или того же SQL. И с использованием переменных и функций, в принципе, можно легко комбинировать вещи.
      VAX больше про работу с графом. И SQL здесь, возможно, даже сбивающий пример :)


    1. Leo5700
      21.07.2017 20:42

      О, scratch для взрослых, спасибо.


  1. PFight77
    21.07.2017 11:05
    +1

    На простых примерах выглядит вдохновляюще, но как посмотришь на сложные схемы — уж лучше какой-то текстовый формат...


    1. aveic
      21.07.2017 11:58

      Ощущение понятное. Но оно пропадает, когда начинаете сами творить. Да, чужие графы читать тяжелее. Для борьбы с этим пока есть миникарта и комментарии (клавиша C), которые добавляют пояснения и служат группировкой элементов. Т.е. потянув комментарий, тянутся все элементы в нём. В UE4 Blueprints ещё есть Zoom In/Out, но я ещё не успел реализовать его. На уровне raphael не получилось, ибо и так у меня кастомный panning, Но в планах «логическое» зумирование, когда я меняю саму отрисовку элемента (делаю его меньше, убираю отступы и прочее).


      1. aveic
        21.07.2017 12:09

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


        1. Mendel
          21.07.2017 17:44

          Не факт что проще писать текстом.
          Сейчас медленно пытаюсь довести свой фреймворк до вида когда можно показывать.
          У меня похожая концепция ориентированная на визуальное редактирование, но пока без визуального редактирования.
          У меня в ORM есть понятие «правило».
          У модели (активрекорд) есть набор полей у каждого набор правил.
          Также есть правила которые влияют на бокс (что-то вроде репозитория, в простейшем случае таблица или папка/файл где лежат все однотипные модели).
          Хранится сие в JSON. Например так:

          Заголовок спойлера
          {
              "#default": {
                  "#class" : "app\\prototype\\Model",
                  "scenarios": {
                      "default": {
                      },
                      "list": {
                          "id": "default"
                      },
                      "view": {
                          "id": "default"
                      },
                      "search": {
                      }
                  },
                  "box":{
                      "#class" : "app\\prototype\\Box",
                      "limit": 20
                  }
              },
              "registry" : {
                  "scenarios": {
                      "default": {
                          "id": ["string"],
                          "type": ["modelType","boxType&value=registry"],
                          "created": ["int","ordered","timeCreate","timestamp"],
                          "updated": ["int","ordered","timeUpdate","timestamp"],
                          "updateHistory":["updateHistory"]
                      },
                      "list": {
                          "id": "default",
                          "type":"default"
                      },
                      "advancedInfo": {
                          "id": "default",
                          "created": "default",
                          "updated": "default"
                      }
                  },
                  "box":{
                      "#class": "\\drycore\\orm\\box\\FolderBox",
                      "runtimeFolder":"registry"
                  }
              },
              "dbConfig": {
                  "#parent": "registry",
                  "scenarios": {
                      "default": {
                          "host": ["string","default&value=localhost","block&name=default"],
                          "dbname": ["string","block&name=default"],
                          "user": ["string","block&name=default"],
                          "password": ["string","block&name=default"],
                          "updateHistory":null
                      }
                  }
              },
              "user":{
                  "#parent":"drycart/site/#user",
                  "box":{
                      "#class": "\\drycore\\core\\user\\UserBox"
                  }
              },
              "#sitePage": {
                  "#parent": "drycart/cms/#sitePage",
                  "box":{
                      "where": [{"0": "`type` != :type","type": "adminPage"}]
                  }
              },
          }
          



          1. aveic
            21.07.2017 17:48

            Я и в тему зашел в надежде что будет просто это утащить себе. Но похоже не судьба.

            Почему не судьба?


            1. Mendel
              21.07.2017 18:06

              Ну мне больше интересно добавление/редактирование полей, чем связи.
              Пока вижу только применение к построению запросов (но писать много надо), к дереву наследования (правда тоже надо будет свои контролы сюда пристраивать), но все это пока не совсем то.
              Появилась идея сделать на базе Вашего редактора графов конструктор для правил, чтобы можно было делать цепочки действий и т.п. но сыро. В общем статья интересная, но дальше…


              1. aveic
                21.07.2017 19:54

                Ну мне больше интересно добавление/редактирование полей, чем связи.

                Почти что угодно можно представить в виде связи:
                image

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

                Подскажите, пожалуйста, что именно сыро?

                В общем статья интересная, но дальше…

                Не пойму, о чём вы говорите. Поясните, пожалуйста.


                1. Mendel
                  22.07.2017 17:21

                  Сыро не у Вас, сыро у меня).
                  Мне скорее нужен инструмент по написанию своей грамматики а не по написанию с помощью уже готовой грамматики. А для готовой грамматики да, прикольно выглядит.


                  1. aveic
                    22.07.2017 18:15

                    Вроде такого http://www.eclipse.org/Xtext/?


                    1. Mendel
                      22.07.2017 18:53

                      Нет, не совсем.
                      Вот типичный юзкейс:
                      Мы хотим на сайте сделать опрос клиентов.
                      Для этого нужно вывести форму с каким-то набором полей и неким стандартным поведением. Допустим у нас уже есть абстрактная модель #опрос
                      Админу нужно отнаследоваться от нее (тут просто — мы или из списка выбираем родителя или он фиксированный) и добавить к ней полей.
                      Допустим моделька уже умеет разбираться с дополнительными полями. Или сериализирует в жсон, или EAV использует, не суть.
                      Нам нужно дать пользователю красивенько добавить поля. Есть кнопка добавления полей, есть кнопочки добавления правил к этим полям. Допустим это будет опрос «ваш любимый мультик».
                      Мы хотим добавить поле «Ваш любимый мультик». К этому полю мы добавим правило «связь с таблицей Мультики, поле связи — id_мультика». К нему мы добавим правило «выбор из выпадающего списка», и еще правило «если вариант не выбран, то по умолчанию — Щенячий патруль».
                      Потом мы добавляем поле «Ваш возраст». К нему добавляем правило «целое», к нему добавляем «минимум 5», и правило «максимум 80». Также добавляем правило «если не заполнено, то по умолчанию берем возраст из такого-то поля текущего пользователя».
                      Ну и раз уж инструмент универсальный то у нас должны быть видны поля из родителя, например связь по ИД текущего пользователя, время создания, айпи опрошенного и т.п.
                      Ну и соответственно например если мы захотим сделать совсем анонимный опрос, то мы переопределим поле «ид пользователя» на «по умолчанию NULL» (грязный хак, не SOLID, но сходу более удачный пример не приходит в голову).

                      Что я ожидал из заголовка (заголовок правильный, это мои тараканы) — интерфейс создания мышкой структуры базы данных. Добавления полей, их свойств и т.п.
                      Ну или на худой конец именно построитель запросов (жестко к предметной области, как у 1С).
                      Вы сделали красиво и интересно, это просто мои тараканы, не заморачивайтесь.
                      Ну и плюс функция резинового утенка. Пока писал, у меня лучше сформировалась концепция как оно должно быть. Жаль времени пока нет.


    1. domix32
      21.07.2017 14:07
      +1

      Можно же делать целые компоненты с интерфейсами, а подробности реализации скрывать в подсхемах/текстовой реализации


      1. PFight77
        22.07.2017 15:58

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


        1. aveic
          22.07.2017 17:10

          Для этого уже есть пользовательские функции. Но возможно, есть смысл их сделать «анонимными», чтоб прям на схеме выбрал группу компонентов и схлопнул в функцию, которую потом по даблклику редактируешь. Но там очень много вопросов всё равно возникает, с отображением проводов, и дальнейшей возможностью редактировать изолированно в новом окне.


  1. GrafDL
    21.07.2017 11:57
    +1

    и как же теперь выглядят запросы на 150 строк?


  1. Kioju
    21.07.2017 11:59

    Вообще интересно, напоминает тот же визуальный редактор SQL-запросов в Microsoft Acces.

    А в обратную сторону SQL-запросы он нормально по узлам раскидывает?

    З.Ы. под спойлером пример визуального программирования в вебе — мойлюбимыйкорезоид

    Картинка
    image


    1. aveic
      21.07.2017 12:04

      А в обратную сторону SQL-запросы он нормально по узлам раскидывает?

      VAX вообще ничего не знает про SQL, он просто работает со схемой доменной области, которую вы ему скормите. И на выходе даёт граф либо деревья, сериализованные в JSON. Это уже backend нашего проекта превратил их в SQL.
      Но естественно, можно в VAX загружать графы, т.е. если вы придумаете как разбить ваш SQL на узлы из вашей схемы, то можно смело рендерить такой граф. Но, вряд ли, такая задача кому нужна.


  1. DenisKoronchik
    21.07.2017 12:35

    Спасибо огромное, собирался в своем проекте делать такую штуку для веб (по пути Blueprint в UE4), но вы сделали это раньше и теперь я могу сэкономить уйму времени.


    1. aveic
      21.07.2017 12:40
      +1

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


      1. DenisKoronchik
        21.07.2017 13:02

        Обязательно


  1. Pa3pA6oT4uK
    21.07.2017 13:03

    спасибо


  1. Barcel
    21.07.2017 13:03

    Какая-то непонятная сеть из нитей и блоков. Нетехнаря не надо заставлять писать такие запросы, как в примере. SQL-щику приятнее и понятнее обычный SQL.


    1. aveic
      21.07.2017 14:00

      SQL-щику приятнее и понятнее обычный SQL.

      С этим никто не спорит, но суть статьи не про это.


  1. Ivan22
    21.07.2017 13:37
    -1

    Визуальный тул для обработки данных из БД ??? Это же ETL!


  1. Dzen1
    21.07.2017 14:00

    Отличная работа. Класс!


  1. AndreyDmitriev
    21.07.2017 15:55

    Кстати, ещё LabVIEW можете посмотреть как источник идей для визуального программирования.
    Ну вот, к примеру, пара первых упражнений из «Проекта Эйлера»:


    Цветом кодируется тип данных на линии — синие — целочисленные данные, зелёный пунктир — булевы.


    1. aveic
      21.07.2017 20:32
      +1

      Бесспорно, в любом редакторе, заточенном на определённую предметную область, будет всё проще и нагляднее.
      Но с помощью VAX, вот что можно получить:
      image


      1. AndreyDmitriev
        22.07.2017 10:43

        Это на самом деле здорово, то что вы сделали, когда графический подход позволяет решать в том числе задачи «общего назначения».
        Одна из самых больших проблем в графическом программировании — это использование места на экране и постоянно разрастающаяся диаграмма. При работе в той же LabVIEW одно из основных правил — любая диаграмма должна занимать не более одного экрана (и этого вполне можно достичь, грамотно разбивая программу на подпрограммы). Но я это к тому, что вы можете немного сэкономить место — сравните ваш «Equals 0?» с похожим LabVIEW элементом — в общем-то нет необходимости указывать тип Integer/Boolean на каждом коннекторе.
        На самом деле LabVIEW не заточена под определённую предметную область (да, там есть специфические элементы для сбора данных с железок NI, но это всё на уровне библиотек), а так там совершенно «классические» базовые конструкции и типы данных, присутствующие практически в любом языке, и позволяющие писать вполне себе нормальные десктопные приложения. Я вообще придерживаюсь мнения, что через много много лет (несколько десятилетий, вероятно) классический текстовый подход к написанию программ постепенно заменится на графическое программирование на уровне диаграмм и в этом смысле вы двигаетесь в правильном направлении.


        1. aveic
          22.07.2017 14:29

          При работе в той же LabVIEW одно из основных правил — любая диаграмма должна занимать не более одного экрана (и этого вполне можно достичь, грамотно разбивая программу на подпрограммы). Но я это к тому, что вы можете немного сэкономить место — сравните ваш «Equals 0?» с похожим LabVIEW элементом — в общем-то нет необходимости указывать тип Integer/Boolean на каждом коннекторе.

          Да верно подметили, это большая проблема. Среди задач на будущее есть возможность кастомизировать отображение узлов, чтобы их делать более компактными и/или информативными, возможность зумировать.

          ...LabVIEW элементом — в общем-то нет необходимости указывать тип Integer/Boolean на каждом коннекторе.

          Хорошая идея, спасибо. Можно их в принципе показывать только по наведению, или как-то ещё.


          1. Mendel
            22.07.2017 18:20

            По юзабилити меня смущает немного неудобное добавление узлов. Узел появляется в произвольном месте (в демке на сайте это выше зоны просмотра), плюс надо листать список и т.п. Опять же места для комментариев в списке не очень много. Мне кажется тут было бы удобнее сделать панель с элементами из которой мы бы перетягивали мышкой.(Несколько панелей, выпадающее меню....).
            Создание функций не интуитивно. Если я сходу не понял, то и юзер не поймет. Я могу прочитать документацию, а юзер нет… Ограниченное колво входов у функции (т.е. фиксированный список входов, 1,2,3?) удивляет.
            Хочется больше гибкости в грамматике.
            И хочется более компактного вида у узлов, да и у всей схемы.
            Общее ощущение после более детального ознакомления — интересно поиграться. Интересно поучить на нем детей. Но в бою пока не вижу как использовать.


            1. aveic
              22.07.2017 18:34

              узел появляется в произвольном месте (в демке на сайте это выше зоны просмотра),

              При нажатии кнопки X он появляется в месте, где был курсор, при создании из провода, он тоже под курсором появляется. Единственное, когда его создаёшь из кнопки на меню, да он появляется где-то непонятно. Но я не думаю, что тулбаром есть смысл пользоваться.
              плюс надо листать список и т.п.

              там есть autosuggest

              1) Опять же места для комментариев в списке не очень много.
              2) Мне кажется тут было бы удобнее сделать панель с элементами из которой мы бы перетягивали мышкой.

              Да список элементов, наверное, надо очень крутым делать. Но в большинстве существующих подобных редакторов (в том же UE4) это просто список c autoSuggestом, т.к, как правило, очень много элементов.
              Создание функций не интуитивно.

              Да, это пока хромает.

              Ограниченное колво входов у функции (т.е. фиксированный список входов, 1,2,3?) удивляет.
              Не понял о чём вы. Вы всё определяете в схеме. Их там может быть сколько угодно. Если вы про пользовательские, то их там тоже может быть сколько угодно.

              Хочется больше гибкости в грамматике.

              Если есть конкретные идеи, будет круто.

              И хочется более компактного вида у узлов, да и у всей схемы.

              Да это в TODO всё.

              Но суть в том, что это всё просто недоделки редактора, которые понятно как улучшать, сама модель вряд ли претерпит изменения.


              1. Mendel
                22.07.2017 20:20

                image
                Выбор из трех вариантов.
                Я так понимаю что в грамматике можно указать хоть десять, но если пользователю понадобится одиннадцать, то внезапно не получится. При этом в описании будет куча кода для шести параметров, которых в реальных кейсах ни разу не будет.
                Ну как-то так я понимаю.


                1. aveic
                  22.07.2017 20:37

                  Это параметризованные типы, а не количество параметров (в идеологии VAX — инпутов). Инпутов может быть сколько угодно. А типы-параметры это так называемые genericи, например в Scala:

                  def map[A,B](l: List[A])(f: A=>B):List[B] = l map f
                  или в Java:
                  class BoxPrinter<T> { 
                      private T val; 
                   
                      public BoxPrinter(T arg) { 
                          val = arg; 
                      } 
                   
                      public String toString() { 
                          return "{" + val + "}"; 
                      } 
                   
                      public T getValue() { 
                          return val; 
                      } 
                  } 
                  или template в C++:
                  template<typename T>
                  void f(T s)
                  {
                      std::cout << s << '\n';
                  }

                  И ваши функции тоже могут быть параметризованы типами, как если бы создали сами такой компонент:
                  components:
                     FoldArray:      
                          typeParams: [T]
                          in:
                              A: Array[@T]
                          out:
                              O: @T

                  Я не думаю, что на практике, кому-то понадобиться более 3 типов-параметров (если вообще кому-то хоть 1 тип понадобиться :))


          1. AndreyDmitriev
            24.07.2017 18:35

            Хорошая идея, спасибо. Можно их в принципе показывать только по наведению, или как-то ещё.

            В LabVIEW именно по наведению и сделано. Если я нажму Ctrl+H, то появится всплывающее окошко и по наведению мыши на проводник мне будет показан тип данных. Кроме того, если происходит приведение типов (ну, скажем я складываю int и double, то int будет автоматически приведён к double, о чём LabVIEW сообщит красной точкой (coercion dot) на входе. Ну как-то так это выглядит:


            1. aveic
              24.07.2017 22:45

              Круто, спасибо за идеи!


  1. grundic
    21.07.2017 16:03
    +1

    Супер! Отличная работа!!!
    Вот несколько ссылок на аналогичные библиотеки для работы с визуальными графами:



    А вот этот проект не видели — noflojs? Чем-то похож на Ваш.


    1. grundic
      21.07.2017 16:25

      Картинка для привлечения внимания :)

      image


    1. aveic
      21.07.2017 16:36

      Спасибо!

      http://js.cytoscape.org
      https://www.graphdracula.net
      https://jsplumbtoolkit.com
      http://sigmajs.org


      Эти вроде (не утверждаю) все либо просто для рисования/анализа/layout графов, либо с захардкоженной доменной областью.
      Сама работа с графом в моём случае не является центральной идеей.

      Да, noflojs интересный и похожий инструмент. Возможно есть смысл подумать над интеграцией с форматом FBP, если кому-то будет нужно.


      1. grundic
        21.07.2017 16:42

        Да, это замена Raphael — насколько я понял, Вы пытались связаться с автором, но безуспешно. Поэтому накидал несколько альтернатив для визуализации :)


        1. aveic
          21.07.2017 16:58

          Ну эти библиотеки именно про рисование графов. С ними бы я не справился.
          Raphael более низкоуровневая библиотека. Там можно только рисовать прямоугольники, окружности, текст и линии. Всё остальное пришлось делать самому :)


          1. aveic
            21.07.2017 17:01

            Прямым алтернативами Raphael вроде являются вещи вроде SVG.js/просто canvas/Processing.js


  1. mrigi
    21.07.2017 17:07
    -1

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


  1. daiver19
    23.07.2017 07:25

    Я не очень понимаю в чем профит, от представления короткого запроса в виде сложного графа, ну да ладно. Просто хотел описать несколько иной подход к визуальной оболочке для работы с данными.
    Вы видите сразу схему вашей базы. Если вас интересует какое-то поле — вы его вытягиваете из схемы и сразу получаете табличку которая делает group by по этому полю и считает count. Потом можно в неё кинуть другое поле как значение и оно тоже саггрегируется. Тип аггреграции — свойство колонки. Надо фильтрацию — или вписываем условие вида «поле больше x» или клацаем на нужной строке для фильтрации. Ну и так далее. Общая идея в том, чтоб работать сразу с данными, а не представлять те же абстракции в другом виде.


    1. Mendel
      23.07.2017 11:50

      Вы описываете что-то вроде конструктора запросов 1с. ИМХО у них лучшая концепция построения сложных запросов мышкой. И там без графов.


  1. zivgta
    24.07.2017 20:20

    aveic, есть возможность экспорта схемы-sql в готового вида запрос? (возможно пропустил комментарий с ответом на подобный вопрос, простите заранее)


    1. aveic
      24.07.2017 22:51

      На нашем проекте есть, именно это и происходит, но он содержит много project-specific логики, и вам вряд ли понадобиться. Да и сама грамматика больше подходит только для нашего проекта, нежели для generic sql. Сам инструмент, как бы, планировалось развивать без привязки к определённой доменной области (в данном случае SQL) и конкретному бэкэнду, в этом его суть.

      P.S. Странно, что уже комментариев 5 к этой статье я насчитал, которые явно не поняли суть инструмента.


      1. Mendel
        25.07.2017 18:21

        Ничего странного. Человек внимательно читает заголовок, а текст уже в зависимости от заинтересованности.
        В заголовке сказано «или как написать SQL мышкой». Соответственно люди думают что в статье об этом написано. А по факту нет, не написано, вот и спрашивают.


  1. Idol_11
    27.07.2017 23:44
    -1

    вот наверное ваш ДБА был рад. Такая фигня производительность сервера роняет на раз.
    Я своим девелоперам руки обрываю за такие творения.

    Было бы очень интересно узнать ситуацию с производительностью до и после. Или вы такой ерундой не заморачиваетесь?


    1. aveic
      27.07.2017 23:47

      Было бы очень интересно узнать ситуацию с производительностью до и после.

      Это сугубо аналитические запросы, вынесенные за OLTP основного приложения, запускаемые поверх read-only аналитических реплик. Раз в месяц по всем клиентам, и изредка (максимум раз в день) для теста новых гипотез.
      Или вы такой ерундой не заморачиваетесь?

      Мы такой ерундой заморачиваемся.


      1. aveic
        27.07.2017 23:52

        Но и самое главное, статья и инструмент совсем не про SQL.


    1. Mendel
      28.07.2017 13:19
      +1

      У вас небось всех на ассемблере заставляют писать?
      Мышакодинг для единичных запросов на много порядков улучшает производительность.
      У меня бывали запросы в 1с которые выполнялись по 20-30 секунд. Даже с учетом их продвинутого кеширования (всякие регистры с предвычислениями сильно облегчающие групповые операции).
      Безумно много если выполнять регулярно. Но пофиг если написание запроса заняло час, и повторно выполняться он не будет никогда.
      А вот оптимизировать написание самого запроса — бесценно.
      Для относительно простых аналитических запросов больше половины времени уходит на то чтобы манагер которому нужен запрос и программист поняли друг-друга. При этом 90% запросов никто не пишет по принципу «ой, пока напишут, пока посчитают, та ну...».
      Когда мышкой потыкать и получить инфу — намного эффективнее.